You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
==========================
MAJOR FEATURES + CHANGES
==========================
Change 2945755 on 2016/04/15 by Frank.Fella
Sequencer - Fix issues with level visibility.
+ Don't mark sub-levels as dirty when the track evaluates.
+ Fix an issue where sequencer gets into a refresh loop because drawing thumbnails causes levels to be added which was rebuilding the tree, which was redrawing thumbnails.
+ Null check for when an objects world is null but the track is still evaluating.
+ Remove UnrealEd references.
Change 2947197 on 2016/04/18 by Max.Chen
Sequencer: Expose settings sequencer settings in the Editor Preferences page. Note, UMG and Niagara have separate sequencer settings pages.
#jira UE-29516
Change 2948468 on 2016/04/19 by Max.Chen
Sequencer: Fix particles not firing on loop.
#jira UE-27881
Change 2948590 on 2016/04/19 by Max.Chen
Sequencer: Fix spawnables not getting default tracks.
#jira UE-29644
Change 2955993 on 2016/04/26 by Max.Chen
Sequencer: Refresh instances when done recording. This fixes a bug where spawned recorded actors aren't visible when done recording.
#jira UE-29841
Change 2958567 on 2016/04/27 by Max.Preussner
RHI: Made SetReferencedTexture public, so that the referenced texture can be set
Change 2958718 on 2016/04/28 by Max.Chen
Sequencer: Folder colors. Right click on a folder and choose "Set Color"
#jira UE-28669
Change 2960172 on 2016/04/28 by Max.Preussner
Slate: Slate Remote Server (for the iOS touch input app) is now disabled by default, so we don't open up the socket unless desired by the user
Change 2960411 on 2016/04/28 by Max.Chen
Sequencer: Don't remove label if it's not being used.
#jira UE-24283
Change 2960414 on 2016/04/28 by Max.Chen
Matinee: Don't automatically turn frustums on/off when entering and exiting Matinee.
#jira UE-1020
Change 2962784 on 2016/05/02 by Max.Chen
Sequencer: Add master sequence
#jira UE-29799
Change 2964399 on 2016/05/03 by Andrew.Rodham
Sequencer: Added ability to apply cook-time optimization to tracks and objects
- For now, if a spawnable has a spawn track that is disabled, or will never spawn, the entire spawnable object will be removed from a cooked package.
- Possessables also afford the same optimization, although none is currently implemented
- We could, in future, also remove any tracks that are completely disabled
- Deprecated UMovieSceneBoolSection::DefaultValue in favor of the default stored on FIntegralCurve
Change 2967549 on 2016/05/05 by Max.Chen
Sequencer: Fix crash converting possessable to spawnable when the possessable doesn't exist.
#jira UE-30360
Change 2967670 on 2016/05/05 by Max.Chen
Sequencer: Set ui min/max for sequencer settings
#jira UE-30344
Change 2978969 on 2016/05/16 by Max.Chen
Sequencer: Restore state when focusing on a shot level sequence. This fixes issues where tracks in the movie scene that are active before switching to the new movie scene need to return to
their initial state. For example, setting a fade track in the master sequence and switching into a shot should disable the effects of the fade track in the master sequence.
#jira UE-30798
Change 2983237 on 2016/05/19 by Andrew.Rodham
Protocol settings for movie captures are now set up correctly when a capture type is specified on the command line
Thanks to original github author, yuhe00
#pr
#2257
Change 2991115 on 2016/05/26 by Andrew.Rodham
Sequencer: Added {shot} and {shot_frame} format args for movie captures
- Additionally, rendering out movie scenes as videos will now generate a new video for each unique filename it encounters. This allows us to render out a video per shot by using {shot} as
the output format.
- Frame numbers are now zero-padded as per the sequencer setting.
Change 2991920 on 2016/05/26 by Max.Chen
Sequencer: Fix movie scene getting dirtied unnecessarily when the fixed frame interval changes.
#jira UE-31343
Change 2992387 on 2016/05/26 by Max.Chen
Sequencer: Fix crash when getting the color key properties of a collapsed key that doesn't have all channels keyed.
#jira UE-31392
Change 2993553 on 2016/05/27 by Andrew.Rodham
Sequencer: Added the ability to add burn-ins to level sequences
- A default burn-in is provided which hosts a great level of flexibility
- 6 regions (L/C/R + T/B) on a 30% black border allow positioning of a range of frame statistics such as shot name ({ShotName}), frame numbers ({MasterFrame}, {ShotFrame}), and other
information.
- Watermark is provided by default (currently no tiling is exposed)
- Users can use the default built in UMG widget as a guideline for their own custom implementations.
Change 2993554 on 2016/05/27 by Andrew.Rodham
Sequencer: Default level sequence burn ins
- Also made a font asset out of our fixed width font shipped with the engine
Change 2993856 on 2016/05/30 by Max.Chen
Sequencer: Import/Export EDL
- Added a new option in the render movie dialog to export an Edit Decision List (EDL) in cmx and rv formats if there is a shot track. The default is true.
- Added "Import EDL" to shot track right click menu which imports a cmx EDL and conforms the shot order and cut information to it.
- Added "Export EDL" to shot track right click menu which exports EDLs in cmx and rv formats.
- Added "Render Shot" to shot right click menu which loads up the render movie dialog with the start and end frames of the selected shot.
#jira UETOOL-829, UETOOL-830
Change 2994761 on 2016/05/31 by Max.Chen
Sequence Recorder: Add a setting to allow recording of actors that are spawned by sequencer itself.
Change 2995648 on 2016/06/01 by HaarmPieter.Duiker
Sequencer EXR output gamut controls
Change 2996241 on 2016/06/01 by Frank.Fella
Sequencer - Add a small epsilon when "force fixed frame interval" is enabled, to make sure we're in the start of the next frame.
Change 2996244 on 2016/06/01 by Frank.Fella
Sequencer - Set the tick prerequisite for all components, not just the root.
Change 2997865 on 2016/06/02 by Max.Preussner
Sequencer: Fixed Crash in Sequencer play rate track when setting negative play rate (UE-31431)
#jira UE-31431
Change 2999631 on 2016/06/03 by Frank.Fella
Sequencer - At runtime, make sure to stop playing skeletal animations to prevent them from being double updated each frame, once by sequencer, and then again by tick.
Change 3000820 on 2016/06/03 by Max.Chen
Sequencer: Add hotkey (ctrl-T) to toggle between showing frame numbers and time.
#jira UE-31497
Change 3001056 on 2016/06/05 by Max.Chen
Sequencer: Fix fade color section crash by using an inline color picker in the details panel instead of a popup color picker.
#jira UE-31647
Change 3001057 on 2016/06/05 by Max.Chen
Movie Capture: Fix audio getting disabled after recording a movie.
Change 3001690 on 2016/06/06 by Andrew.Rodham
Sequencer: Fixed recording video sequences when not overwriting existing videos
Change 3001823 on 2016/06/06 by Max.Chen
Sequencer: Fix filtered nodes in folders so that other unfiltered children aren't visible.
#jira UE-31499
#lockdown Nick.Penwarden
[CL 3003974 by Max Chen in Main branch]
611 lines
19 KiB
C++
611 lines
19 KiB
C++
// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "SequencerPrivatePCH.h"
|
|
#include "MovieSceneSequence.h"
|
|
#include "MovieSceneSection.h"
|
|
#include "MovieSceneTrack.h"
|
|
#include "MovieSceneCinematicShotTrack.h"
|
|
#include "SequencerNodeTree.h"
|
|
#include "Sequencer.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "MovieScene.h"
|
|
#include "MovieSceneFolder.h"
|
|
#include "MovieSceneTrackEditor.h"
|
|
#include "SequencerSectionLayoutBuilder.h"
|
|
#include "ISequencerSection.h"
|
|
#include "ISequencerTrackEditor.h"
|
|
#include "SequencerSpacerNode.h"
|
|
|
|
void FSequencerNodeTree::Empty()
|
|
{
|
|
RootNodes.Empty();
|
|
ObjectBindingMap.Empty();
|
|
Sequencer.GetSelection().EmptySelectedOutlinerNodes();
|
|
EditorMap.Empty();
|
|
FilteredNodes.Empty();
|
|
HoveredNode = nullptr;
|
|
}
|
|
|
|
|
|
int32 NodeTypeToFolderSortId(ESequencerNode::Type NodeType)
|
|
{
|
|
switch ( NodeType )
|
|
{
|
|
case ESequencerNode::Folder:
|
|
return 0;
|
|
case ESequencerNode::Track:
|
|
return 1;
|
|
case ESequencerNode::Object:
|
|
return 2;
|
|
default:
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
|
|
int32 NodeTypeToObjectSortId( ESequencerNode::Type NodeType )
|
|
{
|
|
switch ( NodeType )
|
|
{
|
|
case ESequencerNode::Object:
|
|
return 0;
|
|
case ESequencerNode::Track:
|
|
return 1;
|
|
default:
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
|
|
struct FDisplayNodeSorter
|
|
{
|
|
bool operator()( const TSharedRef<FSequencerDisplayNode>& A, const TSharedRef<FSequencerDisplayNode>& B ) const
|
|
{
|
|
TSharedPtr<FSequencerDisplayNode> ParentNode = A->GetParent();
|
|
|
|
// If the nodes are root nodes, or in folders and they are the same type, sort by name.
|
|
if ( (ParentNode.IsValid() == false || ParentNode->GetType() == ESequencerNode::Folder) && A->GetType() == B->GetType() )
|
|
{
|
|
return A->GetDisplayName().ToString() < B->GetDisplayName().ToString();
|
|
}
|
|
|
|
int32 SortIdA;
|
|
int32 SortIdB;
|
|
|
|
// Otherwise if they are root nodes or in folders use the folder sort id.
|
|
if ( ParentNode.IsValid() == false || ParentNode->GetType() == ESequencerNode::Folder )
|
|
{
|
|
SortIdA = NodeTypeToFolderSortId( A->GetType() );
|
|
SortIdB = NodeTypeToFolderSortId( B->GetType() );
|
|
}
|
|
// Otherwise if they are in an object node use the object node sort id.
|
|
else if ( ParentNode->GetType() == ESequencerNode::Object )
|
|
{
|
|
SortIdA = NodeTypeToObjectSortId( A->GetType() );
|
|
SortIdB = NodeTypeToObjectSortId( B->GetType() );
|
|
}
|
|
// Otherwise they are equal, and in a stable sort shouldn't change position.
|
|
else
|
|
{
|
|
SortIdA = 0;
|
|
SortIdB = 0;
|
|
}
|
|
|
|
return SortIdA < SortIdB;
|
|
}
|
|
};
|
|
|
|
|
|
void FSequencerNodeTree::Update()
|
|
{
|
|
HoveredNode = nullptr;
|
|
|
|
// @todo Sequencer - This update pass is too aggressive. Some nodes may still be valid
|
|
Empty();
|
|
|
|
UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene();
|
|
UMovieSceneCinematicShotTrack* CinematicShotTrack = MovieScene->FindMasterTrack<UMovieSceneCinematicShotTrack>();
|
|
|
|
// Get the master tracks so we can get sections from them
|
|
const TArray<UMovieSceneTrack*>& MasterTracks = MovieScene->GetMasterTracks();
|
|
TArray<TSharedRef<FSequencerTrackNode>> MasterTrackNodes;
|
|
|
|
for (UMovieSceneTrack* Track : MasterTracks)
|
|
{
|
|
if (Track != CinematicShotTrack)
|
|
{
|
|
UMovieSceneTrack& TrackRef = *Track;
|
|
|
|
TSharedRef<FSequencerTrackNode> SectionNode = MakeShareable(new FSequencerTrackNode(TrackRef, *FindOrAddTypeEditor(TrackRef), true, nullptr, *this));
|
|
MasterTrackNodes.Add(SectionNode);
|
|
|
|
MakeSectionInterfaces(TrackRef, SectionNode);
|
|
}
|
|
}
|
|
|
|
const TArray<FMovieSceneBinding>& Bindings = MovieScene->GetBindings();
|
|
TMap<FGuid, const FMovieSceneBinding*> GuidToBindingMap;
|
|
|
|
for (const FMovieSceneBinding& Binding : Bindings)
|
|
{
|
|
GuidToBindingMap.Add(Binding.GetObjectGuid(), &Binding);
|
|
}
|
|
|
|
// Make nodes for all object bindings
|
|
TArray<TSharedRef<FSequencerObjectBindingNode>> ObjectNodes;
|
|
for( const FMovieSceneBinding& Binding : Bindings )
|
|
{
|
|
TSharedRef<FSequencerObjectBindingNode> ObjectBindingNode = AddObjectBinding( Binding.GetName(), Binding.GetObjectGuid(), GuidToBindingMap, ObjectNodes );
|
|
|
|
const TArray<UMovieSceneTrack*>& Tracks = Binding.GetTracks();
|
|
|
|
for( UMovieSceneTrack* Track : Tracks )
|
|
{
|
|
UMovieSceneTrack& TrackRef = *Track;
|
|
TSharedRef<FSequencerTrackNode> SectionAreaNode = ObjectBindingNode->AddSectionAreaNode(TrackRef, *FindOrAddTypeEditor(TrackRef));
|
|
MakeSectionInterfaces( TrackRef, SectionAreaNode );
|
|
}
|
|
}
|
|
|
|
|
|
// Cinematic shot track always comes first
|
|
if (CinematicShotTrack)
|
|
{
|
|
TSharedRef<FSequencerTrackNode> SectionNode = MakeShareable(new FSequencerTrackNode(*CinematicShotTrack, *FindOrAddTypeEditor(*CinematicShotTrack), false, nullptr, *this));
|
|
|
|
RootNodes.Add(SectionNode);
|
|
MakeSectionInterfaces(*CinematicShotTrack, SectionNode);
|
|
}
|
|
|
|
// Then comes the camera cut track
|
|
UMovieSceneTrack* CameraCutTrack = MovieScene->GetCameraCutTrack();
|
|
|
|
if (CameraCutTrack)
|
|
{
|
|
TSharedRef<FSequencerTrackNode> SectionNode = MakeShareable(new FSequencerTrackNode(*CameraCutTrack, *FindOrAddTypeEditor(*CameraCutTrack), false, nullptr, *this));
|
|
|
|
RootNodes.Add(SectionNode);
|
|
MakeSectionInterfaces(*CameraCutTrack, SectionNode);
|
|
}
|
|
|
|
// Add all other nodes after the camera cut track
|
|
TArray<TSharedRef<FSequencerDisplayNode>> FolderAndObjectNodes;
|
|
TArray<TSharedRef<FSequencerDisplayNode>> MasterTrackNodesNotInFolders;
|
|
CreateAndPopulateFolderNodes( MasterTrackNodes, ObjectNodes, MovieScene->GetRootFolders(), FolderAndObjectNodes, MasterTrackNodesNotInFolders );
|
|
|
|
// Add all other master tracks after the camera cut track
|
|
MasterTrackNodesNotInFolders.Sort(FDisplayNodeSorter());
|
|
for ( TSharedRef<FSequencerDisplayNode> Node : MasterTrackNodesNotInFolders)
|
|
{
|
|
Node->SortChildNodes(FDisplayNodeSorter());
|
|
}
|
|
|
|
RootNodes.Append( MasterTrackNodesNotInFolders );
|
|
|
|
// Sort the created nodes.
|
|
FolderAndObjectNodes.Sort(FDisplayNodeSorter());
|
|
for ( TSharedRef<FSequencerDisplayNode> Node : FolderAndObjectNodes )
|
|
{
|
|
Node->SortChildNodes(FDisplayNodeSorter());
|
|
}
|
|
|
|
RootNodes.Append( FolderAndObjectNodes );
|
|
|
|
RootNodes.Reserve(RootNodes.Num()*2);
|
|
for (int32 Index = 0; Index < RootNodes.Num(); Index += 2)
|
|
{
|
|
RootNodes.Insert(MakeShareable(new FSequencerSpacerNode(1.f, nullptr, *this)), Index);
|
|
}
|
|
RootNodes.Add(MakeShareable(new FSequencerSpacerNode(1.f, nullptr, *this)));
|
|
|
|
// Set up virtual offsets, expansion states, and tints
|
|
float VerticalOffset = 0.f;
|
|
|
|
for (TSharedRef<FSequencerDisplayNode>& Node : RootNodes)
|
|
{
|
|
Node->Traverse_ParentFirst([&](FSequencerDisplayNode& InNode) {
|
|
|
|
// Set up the virtual node position
|
|
float VerticalTop = VerticalOffset;
|
|
VerticalOffset += InNode.GetNodeHeight() + InNode.GetNodePadding().Combined();
|
|
InNode.Initialize(VerticalTop, VerticalOffset);
|
|
|
|
return true;
|
|
});
|
|
}
|
|
|
|
// Re-filter the tree after updating
|
|
// @todo sequencer: Newly added sections may need to be visible even when there is a filter
|
|
FilterNodes( FilterString );
|
|
}
|
|
|
|
|
|
TSharedRef<ISequencerTrackEditor> FSequencerNodeTree::FindOrAddTypeEditor( UMovieSceneTrack& InTrack )
|
|
{
|
|
TSharedPtr<ISequencerTrackEditor> Editor = EditorMap.FindRef( &InTrack );
|
|
|
|
if( !Editor.IsValid() )
|
|
{
|
|
const TArray<TSharedPtr<ISequencerTrackEditor>>& TrackEditors = Sequencer.GetTrackEditors();
|
|
|
|
// Get a tool for each track
|
|
// @todo sequencer: Should probably only need to get this once and it shouldn't be done here. It depends on when movie scene tool modules are loaded
|
|
TSharedPtr<ISequencerTrackEditor> SupportedTool;
|
|
|
|
for (const auto& TrackEditor : TrackEditors)
|
|
{
|
|
if (TrackEditor->SupportsType(InTrack.GetClass()))
|
|
{
|
|
EditorMap.Add(&InTrack, TrackEditor);
|
|
Editor = TrackEditor;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return Editor.ToSharedRef();
|
|
}
|
|
|
|
|
|
void FSequencerNodeTree::MakeSectionInterfaces( UMovieSceneTrack& Track, TSharedRef<FSequencerTrackNode>& SectionAreaNode )
|
|
{
|
|
const TArray<UMovieSceneSection*>& MovieSceneSections = Track.GetAllSections();
|
|
|
|
TSharedRef<ISequencerTrackEditor> Editor = FindOrAddTypeEditor( Track );
|
|
|
|
for (int32 SectionIndex = 0; SectionIndex < MovieSceneSections.Num(); ++SectionIndex )
|
|
{
|
|
UMovieSceneSection* SectionObject = MovieSceneSections[SectionIndex];
|
|
TSharedRef<ISequencerSection> Section = Editor->MakeSectionInterface( *SectionObject, Track );
|
|
|
|
// Ask the section to generate it's inner layout
|
|
FSequencerSectionLayoutBuilder Builder( SectionAreaNode );
|
|
Section->GenerateSectionLayout( Builder );
|
|
SectionAreaNode->AddSection( Section );
|
|
}
|
|
|
|
SectionAreaNode->FixRowIndices();
|
|
}
|
|
|
|
|
|
const TArray<TSharedRef<FSequencerDisplayNode>>& FSequencerNodeTree::GetRootNodes() const
|
|
{
|
|
return RootNodes;
|
|
}
|
|
|
|
|
|
TSharedRef<FSequencerObjectBindingNode> FSequencerNodeTree::AddObjectBinding(const FString& ObjectName, const FGuid& ObjectBinding, TMap<FGuid, const FMovieSceneBinding*>& GuidToBindingMap, TArray<TSharedRef<FSequencerObjectBindingNode>>& OutNodeList)
|
|
{
|
|
TSharedPtr<FSequencerObjectBindingNode> ObjectNode;
|
|
TSharedPtr<FSequencerObjectBindingNode>* FoundObjectNode = ObjectBindingMap.Find(ObjectBinding);
|
|
if (FoundObjectNode != nullptr)
|
|
{
|
|
ObjectNode = *FoundObjectNode;
|
|
}
|
|
else
|
|
{
|
|
// The node name is the object guid
|
|
FName ObjectNodeName = *ObjectBinding.ToString();
|
|
|
|
// Try to get the parent object node if there is one.
|
|
TSharedPtr<FSequencerObjectBindingNode> ParentNode;
|
|
|
|
UMovieSceneSequence* Sequence = Sequencer.GetFocusedMovieSceneSequence();
|
|
TSharedRef<FMovieSceneSequenceInstance> SequenceInstance = Sequencer.GetFocusedMovieSceneSequenceInstance();
|
|
|
|
// Prefer to use the parent spawnable if possible, rather than relying on runtime object presence
|
|
FMovieScenePossessable* Possessable = Sequence->GetMovieScene()->FindPossessable(ObjectBinding);
|
|
if (Possessable && Possessable->GetParent().IsValid())
|
|
{
|
|
const FMovieSceneBinding* ParentBinding = GuidToBindingMap.FindRef(Possessable->GetParent());
|
|
if (ParentBinding)
|
|
{
|
|
ParentNode = AddObjectBinding( ParentBinding->GetName(), Possessable->GetParent(), GuidToBindingMap, OutNodeList );
|
|
}
|
|
}
|
|
|
|
UObject* RuntimeObject = SequenceInstance->FindObject(ObjectBinding, Sequencer);
|
|
|
|
// fallback to using the parent runtime object
|
|
if (!ParentNode.IsValid() && RuntimeObject)
|
|
{
|
|
UObject* ParentObject = Sequence->GetParentObject(RuntimeObject);
|
|
|
|
if (ParentObject != nullptr)
|
|
{
|
|
FGuid ParentBinding = SequenceInstance->FindObjectId(*ParentObject);
|
|
TSharedPtr<FSequencerObjectBindingNode>* FoundParentNode = ObjectBindingMap.Find( ParentBinding );
|
|
if ( FoundParentNode != nullptr )
|
|
{
|
|
ParentNode = *FoundParentNode;
|
|
}
|
|
else
|
|
{
|
|
const FMovieSceneBinding** FoundParentMovieSceneBinding = GuidToBindingMap.Find( ParentBinding );
|
|
if ( FoundParentMovieSceneBinding != nullptr )
|
|
{
|
|
ParentNode = AddObjectBinding( (*FoundParentMovieSceneBinding)->GetName(), ParentBinding, GuidToBindingMap, OutNodeList );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// get human readable name of the object
|
|
AActor* RuntimeActor = Cast<AActor>(RuntimeObject);
|
|
const FString& DisplayString = (RuntimeActor != nullptr)
|
|
? RuntimeActor->GetActorLabel()
|
|
: ObjectName;
|
|
|
|
// Create the node.
|
|
ObjectNode = MakeShareable(new FSequencerObjectBindingNode(ObjectNodeName, FText::FromString(DisplayString), ObjectBinding, ParentNode, *this));
|
|
|
|
if (ParentNode.IsValid())
|
|
{
|
|
ParentNode->AddObjectBindingNode(ObjectNode.ToSharedRef());
|
|
}
|
|
else
|
|
{
|
|
OutNodeList.Add( ObjectNode.ToSharedRef() );
|
|
}
|
|
|
|
// Map the guid to the object binding node for fast lookup later
|
|
ObjectBindingMap.Add( ObjectBinding, ObjectNode );
|
|
}
|
|
|
|
return ObjectNode.ToSharedRef();
|
|
}
|
|
|
|
|
|
TSharedRef<FSequencerDisplayNode> CreateFolderNode(
|
|
UMovieSceneFolder& MovieSceneFolder, FSequencerNodeTree& NodeTree,
|
|
TMap<UMovieSceneTrack*, TSharedRef<FSequencerTrackNode>>& MasterTrackToDisplayNodeMap,
|
|
TMap<FGuid, TSharedRef<FSequencerObjectBindingNode>>& ObjectGuidToDisplayNodeMap )
|
|
{
|
|
TSharedRef<FSequencerFolderNode> FolderNode( new FSequencerFolderNode( MovieSceneFolder, TSharedPtr<FSequencerDisplayNode>(), NodeTree ) );
|
|
|
|
for ( UMovieSceneFolder* ChildFolder : MovieSceneFolder.GetChildFolders() )
|
|
{
|
|
FolderNode->AddChildNode( CreateFolderNode( *ChildFolder, NodeTree, MasterTrackToDisplayNodeMap, ObjectGuidToDisplayNodeMap ) );
|
|
}
|
|
|
|
for ( UMovieSceneTrack* MasterTrack : MovieSceneFolder.GetChildMasterTracks() )
|
|
{
|
|
TSharedRef<FSequencerTrackNode>* TrackNodePtr = MasterTrackToDisplayNodeMap.Find( MasterTrack );
|
|
if ( TrackNodePtr != nullptr)
|
|
{
|
|
// TODO: Log this.
|
|
FolderNode->AddChildNode( *TrackNodePtr );
|
|
MasterTrackToDisplayNodeMap.Remove( MasterTrack );
|
|
}
|
|
}
|
|
|
|
for (const FGuid& ObjectGuid : MovieSceneFolder.GetChildObjectBindings() )
|
|
{
|
|
TSharedRef<FSequencerObjectBindingNode>* ObjectNodePtr = ObjectGuidToDisplayNodeMap.Find( ObjectGuid );
|
|
if ( ObjectNodePtr != nullptr )
|
|
{
|
|
// TODO: Log this.
|
|
FolderNode->AddChildNode( *ObjectNodePtr );
|
|
ObjectGuidToDisplayNodeMap.Remove( ObjectGuid );
|
|
}
|
|
}
|
|
|
|
return FolderNode;
|
|
}
|
|
|
|
|
|
void FSequencerNodeTree::CreateAndPopulateFolderNodes(
|
|
TArray<TSharedRef<FSequencerTrackNode>>& MasterTrackNodes, TArray<TSharedRef<FSequencerObjectBindingNode>>& ObjectNodes,
|
|
TArray<UMovieSceneFolder*>& MovieSceneFolders, TArray<TSharedRef<FSequencerDisplayNode>>& FolderAndObjectNodes, TArray<TSharedRef<FSequencerDisplayNode>>& MasterTrackNodesNotInFolders )
|
|
{
|
|
TMap<UMovieSceneTrack*, TSharedRef<FSequencerTrackNode>> MasterTrackToDisplayNodeMap;
|
|
for ( TSharedRef<FSequencerTrackNode> MasterTrackNode : MasterTrackNodes )
|
|
{
|
|
MasterTrackToDisplayNodeMap.Add( MasterTrackNode->GetTrack(), MasterTrackNode );
|
|
}
|
|
|
|
TMap<FGuid, TSharedRef<FSequencerObjectBindingNode>> ObjectGuidToDisplayNodeMap;
|
|
for ( TSharedRef<FSequencerObjectBindingNode> ObjectBindingNode : ObjectNodes )
|
|
{
|
|
ObjectGuidToDisplayNodeMap.Add( ObjectBindingNode->GetObjectBinding(), ObjectBindingNode );
|
|
}
|
|
|
|
for ( UMovieSceneFolder* MovieSceneFolder : MovieSceneFolders )
|
|
{
|
|
FolderAndObjectNodes.Add( CreateFolderNode( *MovieSceneFolder, *this, MasterTrackToDisplayNodeMap, ObjectGuidToDisplayNodeMap ) );
|
|
}
|
|
|
|
TArray<TSharedRef<FSequencerTrackNode>> NonFolderTrackNodes;
|
|
MasterTrackToDisplayNodeMap.GenerateValueArray( NonFolderTrackNodes );
|
|
for ( TSharedRef<FSequencerTrackNode> NonFolderTrackNode : NonFolderTrackNodes )
|
|
{
|
|
MasterTrackNodesNotInFolders.Add( NonFolderTrackNode );
|
|
}
|
|
|
|
TArray<TSharedRef<FSequencerObjectBindingNode>> NonFolderObjectNodes;
|
|
ObjectGuidToDisplayNodeMap.GenerateValueArray( NonFolderObjectNodes );
|
|
for ( TSharedRef<FSequencerObjectBindingNode> NonFolderObjectNode : NonFolderObjectNodes )
|
|
{
|
|
FolderAndObjectNodes.Add( NonFolderObjectNode );
|
|
}
|
|
}
|
|
|
|
|
|
void FSequencerNodeTree::SaveExpansionState(const FSequencerDisplayNode& Node, bool bExpanded)
|
|
{
|
|
// @todo Sequencer - This should be moved to the sequence level
|
|
UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene();
|
|
FMovieSceneEditorData& EditorData = MovieScene->GetEditorData();
|
|
|
|
EditorData.ExpansionStates.Add(Node.GetPathName(), FMovieSceneExpansionState(bExpanded));
|
|
}
|
|
|
|
|
|
bool FSequencerNodeTree::GetSavedExpansionState(const FSequencerDisplayNode& Node) const
|
|
{
|
|
// @todo Sequencer - This should be moved to the sequence level
|
|
UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene();
|
|
FMovieSceneEditorData& EditorData = MovieScene->GetEditorData();
|
|
FMovieSceneExpansionState* ExpansionState = EditorData.ExpansionStates.Find( Node.GetPathName() );
|
|
|
|
return ExpansionState ? ExpansionState->bExpanded : GetDefaultExpansionState(Node);
|
|
}
|
|
|
|
|
|
bool FSequencerNodeTree::GetDefaultExpansionState( const FSequencerDisplayNode& Node ) const
|
|
{
|
|
// For now, only object nodes are expanded by default
|
|
return Node.GetType() == ESequencerNode::Object;
|
|
}
|
|
|
|
|
|
bool FSequencerNodeTree::IsNodeFiltered( const TSharedRef<const FSequencerDisplayNode> Node ) const
|
|
{
|
|
return FilteredNodes.Contains( Node );
|
|
}
|
|
|
|
void FSequencerNodeTree::SetHoveredNode(const TSharedPtr<FSequencerDisplayNode>& InHoveredNode)
|
|
{
|
|
if (InHoveredNode != HoveredNode)
|
|
{
|
|
HoveredNode = InHoveredNode;
|
|
}
|
|
}
|
|
|
|
const TSharedPtr<FSequencerDisplayNode>& FSequencerNodeTree::GetHoveredNode() const
|
|
{
|
|
return HoveredNode;
|
|
}
|
|
|
|
/*
|
|
* Add node as filtered and include any parent folders
|
|
*/
|
|
static void AddFilteredNode(const TSharedRef<FSequencerDisplayNode>& StartNode, TSet<TSharedRef<const FSequencerDisplayNode>>& OutFilteredNodes)
|
|
{
|
|
OutFilteredNodes.Add(StartNode);
|
|
|
|
// Gather parent folders up the chain
|
|
TSharedPtr<FSequencerDisplayNode> ParentNode = StartNode->GetParent();
|
|
while (ParentNode.IsValid() && ParentNode.Get()->GetType() == ESequencerNode::Folder)
|
|
{
|
|
OutFilteredNodes.Add(ParentNode.ToSharedRef());
|
|
ParentNode = ParentNode->GetParent();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Recursively filters nodes
|
|
*
|
|
* @param StartNode The node to start from
|
|
* @param FilterStrings The filter strings which need to be matched
|
|
* @param OutFilteredNodes The list of all filtered nodes
|
|
* @return Whether the text filter was passed
|
|
*/
|
|
static bool FilterNodesRecursive( FSequencer& Sequencer, const TSharedRef<FSequencerDisplayNode>& StartNode, const TArray<FString>& FilterStrings, TSet<TSharedRef<const FSequencerDisplayNode>>& OutFilteredNodes )
|
|
{
|
|
// check labels - only one of the labels needs to match
|
|
bool bMatchedLabel = false;
|
|
bool bObjectHasLabels = false;
|
|
for (const FString& String : FilterStrings)
|
|
{
|
|
if (String.StartsWith(TEXT("label:")) && String.Len() > 6)
|
|
{
|
|
if (StartNode->GetType() == ESequencerNode::Object)
|
|
{
|
|
bObjectHasLabels = true;
|
|
auto ObjectBindingNode = StaticCastSharedRef<FSequencerObjectBindingNode>(StartNode);
|
|
auto Labels = Sequencer.GetLabelManager().GetObjectLabels(ObjectBindingNode->GetObjectBinding());
|
|
|
|
if (Labels != nullptr && Labels->Strings.Contains(String.RightChop(6)))
|
|
{
|
|
bMatchedLabel = true;
|
|
break;
|
|
}
|
|
}
|
|
else if (!StartNode->GetParent().IsValid())
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bObjectHasLabels && !bMatchedLabel)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// assume the filter is acceptable
|
|
bool bPassedTextFilter = true;
|
|
|
|
// check each string in the filter strings list against
|
|
for (const FString& String : FilterStrings)
|
|
{
|
|
if (!String.StartsWith(TEXT("label:")) && !StartNode->GetDisplayName().ToString().Contains(String))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// whether or the start node is in the filter
|
|
bool bInFilter = false;
|
|
|
|
if (bPassedTextFilter)
|
|
{
|
|
// This node is now filtered
|
|
AddFilteredNode(StartNode, OutFilteredNodes);
|
|
|
|
bInFilter = true;
|
|
}
|
|
|
|
// check each child node to determine if it is filtered
|
|
if (StartNode->GetType() != ESequencerNode::Folder)
|
|
{
|
|
const TArray<TSharedRef<FSequencerDisplayNode>>& ChildNodes = StartNode->GetChildNodes();
|
|
|
|
for (const auto& Node : ChildNodes)
|
|
{
|
|
// Mark the parent as filtered if any child node was filtered
|
|
bPassedTextFilter |= FilterNodesRecursive(Sequencer, Node, FilterStrings, OutFilteredNodes);
|
|
|
|
if (bPassedTextFilter && !bInFilter)
|
|
{
|
|
AddFilteredNode(StartNode, OutFilteredNodes);
|
|
|
|
bInFilter = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bPassedTextFilter;
|
|
}
|
|
|
|
|
|
void FSequencerNodeTree::FilterNodes(const FString& InFilter)
|
|
{
|
|
FilteredNodes.Empty();
|
|
|
|
if (InFilter.IsEmpty())
|
|
{
|
|
// No filter
|
|
FilterString.Empty();
|
|
}
|
|
else
|
|
{
|
|
// Build a list of strings that must be matched
|
|
TArray<FString> FilterStrings;
|
|
|
|
FilterString = InFilter;
|
|
// Remove whitespace from the front and back of the string
|
|
FilterString.Trim();
|
|
FilterString.TrimTrailing();
|
|
FilterString.ParseIntoArray(FilterStrings, TEXT(" "), true /*bCullEmpty*/);
|
|
|
|
for (auto It = ObjectBindingMap.CreateIterator(); It; ++It)
|
|
{
|
|
// Recursively filter all nodes, matching them against the list of filter strings. All filter strings must be matched
|
|
FilterNodesRecursive(Sequencer, It.Value().ToSharedRef(), FilterStrings, FilteredNodes);
|
|
}
|
|
}
|
|
}
|