Files
UnrealEngineUWP/Engine/Source/Editor/Sequencer/Private/SequencerNodeTree.cpp
Andrew Grant d077828526 Copying //UE4/Orion-Staging to Dev-Main (Originating from //Orion/Main at CL-2777663)
#lockdown Nick.Penwarden

 Change 2777555 on 2015/11/23 by Antony.Carter

	Friend List Sub Menu restyling to new designs

	#RB Nicholas.Davies
	#TESTS Check 3 sub menus of friends list (Online Status, Friends List, Settings) still function correctly with new styling.

Change 2777506 on 2015/11/23 by Andrew.Rodham

	Sequencer: Copy/Paste command binding is no longer active if the seuqnece widget is not focused

	This addresses UE-23423

	#tests Tested copy/paste inside and outside of sequencer
	#codereview Max.Chen
	#rb Max.Chen

Change 2777505 on 2015/11/23 by Andrew.Rodham

	Sequencer: Undoing the addition of a spawnable now ensures its actor instance is deleted correctly

	This addresses UE-23450

	#tests tested the repro steps on the bug
	#codereview Max.Chen
	#rb Max.Chen

Change 2777489 on 2015/11/23 by Andrew.Rodham

	Sequencer: Workflow optimizations for spawnables

	 - Editing a property on an instance of a spawnable now automatically propagates to the spawnable defaults, provided the property is not keyed
	 - Fixed a few cases where spawnables were left lingering around while scrubbing or switching between sub-sequences
	 - Fixed the root sequence instance being evaluated when there was a sub-sequence focused.
	 - Selection states are now remembered for spawnable objects when they are destroyed/re-spawned

	#codereview Max.Chen
	#tests tested loks of object types as spawnables in PIE and in editor
	#rb Max.Chen

Change 2777321 on 2015/11/23 by Terence.Burns

	Updated the usage of World->UpdateStreamingLevels to FlushStreaming levels on the advice of Dmitriy. Need this to ensure that the streaming is completed before we send it off to lightmass.

	#rb Dmitriy.Dyomin
	#Tests Run the RebuildLightmaps UAT script.

Change 2777091 on 2015/11/22 by Andrew.Grant

	Changed "inappropriate outmost" warning on package load to an error. At the very least we want this for a day or two on Orion to surface errors quickly, but may be a good thing to make a standard error since it indicates something that's likely broken.

	#rb none
	#tests Golden path in game, cooked content
	#codereview Nick.Penwarden, Michael.Noland

Change 2777037 on 2015/11/22 by Laurent.Delayen

	Additional debug info to track down https://jira.ol.epicgames.net/browse/OR-9675

	#rb martin.wilson
	#codereview martin.wilson
	#tests Golden path (PIE) + compiled for PS4

Change 2777030 on 2015/11/22 by Sam.Zamani

	#online,externalui,ps4
	- added footer option for closing the embedded web browser

	#rb none
	#tests exec command to try on ps4

Change 2777019 on 2015/11/22 by Marcus.Wassmer

	Possible fix for OR-9851
	#rb none
	#test GoldenPath, PS4
	#codereview Nick.Darnell,Matt.Kuhlenschmidt

Change 2776932 on 2015/11/22 by Max.Chen

	Sequencer: Fix editor selection so that it's not modified in response to the sequencer outliner tree node changing selection if the user is not explicitly selecting in the tree.

	#RB none
	#tests Select an actor that Sequencer doesn't control and it shouldn't deselect.

Change 2776900 on 2015/11/21 by Marcus.Wassmer

	HighQuality particle lights.
	#rb Brian.Karis
	#test GoldenPath, HQ Particles w/wo ShadowCasting.
	#codereview Olaf.Piesche, Simon.Tovey, Tim.Elek

Change 2776868 on 2015/11/21 by Brian.Karis

	Reduced temporal aa responsiveness back where it was.

Change 2776867 on 2015/11/21 by Brian.Karis

	Removed shading terminator bias meant for shadow map acne but it made character faces look worse.

Change 2776840 on 2015/11/21 by Brian.Karis

	Hair indirect lighting implemented.

	Improvements to hair shading model. No longer uses backlit parameter.

	#rb marcus.wassmer
	#tests editor

Change 2776748 on 2015/11/21 by Max.Preussner

	Sequencer: Continued to implement track label editor

	Note: still disabled, because there are a couple remaining issues

	#codereview: max.chen
	#rb: max.chen
	#test: Editor, Runtime

Change 2776493 on 2015/11/20 by Max.Preussner

	Sequencer: Wrapped the node tree context menu actions for editing in an 'Edit' section

	#codereview: max.chen
	#rb: max.chen
2015-11-24 16:45:24 -05:00

412 lines
12 KiB
C++

// Copyright 1998-2015 Epic Games, Inc. All Rights Reserved.
#include "SequencerPrivatePCH.h"
#include "MovieSceneSequence.h"
#include "MovieSceneSection.h"
#include "MovieSceneTrack.h"
#include "SequencerNodeTree.h"
#include "Sequencer.h"
#include "ScopedTransaction.h"
#include "MovieScene.h"
#include "MovieSceneTrackEditor.h"
#include "SequencerSectionLayoutBuilder.h"
#include "ISequencerSection.h"
#include "ISequencerTrackEditor.h"
void FSequencerNodeTree::Empty()
{
RootNodes.Empty();
ObjectBindingMap.Empty();
Sequencer.GetSelection().EmptySelectedOutlinerNodes();
EditorMap.Empty();
FilteredNodes.Empty();
}
void FSequencerNodeTree::Update()
{
// @todo Sequencer - This update pass is too aggressive. Some nodes may still be valid
Empty();
UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene();
TArray<TSharedRef<FSequencerDisplayNode>> NewRootNodes;
// Get the master tracks so we can get sections from them
const TArray<UMovieSceneTrack*>& MasterTracks = MovieScene->GetMasterTracks();
for (UMovieSceneTrack* Track : MasterTracks)
{
UMovieSceneTrack& TrackRef = *Track;
TSharedRef<FSequencerTrackNode> SectionNode = MakeShareable(new FSequencerTrackNode(TrackRef, *FindOrAddTypeEditor(TrackRef), nullptr, *this));
NewRootNodes.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<FSequencerDisplayNode>> NewObjectNodes;
for( const FMovieSceneBinding& Binding : Bindings )
{
TSharedRef<FSequencerObjectBindingNode> ObjectBindingNode = AddObjectBinding( Binding.GetName(), Binding.GetObjectGuid(), GuidToBindingMap, NewObjectNodes );
const TArray<UMovieSceneTrack*>& Tracks = Binding.GetTracks();
for( UMovieSceneTrack* Track : Tracks )
{
UMovieSceneTrack& TrackRef = *Track;
TSharedRef<FSequencerTrackNode> SectionAreaNode = ObjectBindingNode->AddSectionAreaNode(TrackRef, *FindOrAddTypeEditor(TrackRef));
MakeSectionInterfaces( TrackRef, SectionAreaNode );
}
}
struct FObjectNodeSorter
{
bool operator()( const TSharedRef<FSequencerDisplayNode>& A, const TSharedRef<FSequencerDisplayNode>& B ) const
{
if (A->GetType() == ESequencerNode::Object && B->GetType() != ESequencerNode::Object)
{
return true;
}
if (A->GetType() != ESequencerNode::Object && B->GetType() == ESequencerNode::Object)
{
return false;
}
if ( A->GetType() == ESequencerNode::Object && B->GetType() == ESequencerNode::Object )
{
return A->GetDisplayName().ToString() < B->GetDisplayName().ToString();
}
return 0;
}
};
NewObjectNodes.Sort( FObjectNodeSorter() );
for (TSharedRef<FSequencerDisplayNode> NewObjectNode : NewObjectNodes)
{
NewObjectNode->SortChildNodes(FObjectNodeSorter());
}
NewRootNodes.Append(NewObjectNodes);
// Look for a shot track. It will always come first if it exists
UMovieSceneTrack* ShotTrack = MovieScene->GetShotTrack();
if(ShotTrack)
{
TSharedRef<FSequencerTrackNode> SectionNode = MakeShareable(new FSequencerTrackNode(*ShotTrack, *FindOrAddTypeEditor(*ShotTrack), nullptr, *this));
// Shot track always comes first
RootNodes.Add(SectionNode);
MakeSectionInterfaces(*ShotTrack, SectionNode);
}
// Add all other nodes after the shot track
RootNodes.Append(NewRootNodes);
// Set up virtual offsets, and expansion states
float VerticalOffset = 0.f;
for (auto& Node : RootNodes)
{
Node->Traverse_ParentFirst([&](FSequencerDisplayNode& InNode) {
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<FSequencerDisplayNode>>& 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();
}
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 );
}
/**
* 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
*/
static bool FilterNodesRecursive( FSequencer& Sequencer, const TSharedRef<FSequencerDisplayNode>& StartNode, const TArray<FString>& FilterStrings, TSet<TSharedRef<const FSequencerDisplayNode>>& OutFilteredNodes )
{
// assume the filter is acceptable
bool bFilterAcceptable = true;
// check each string in the filter strings list against
UMovieScene* MovieScene = Sequencer.GetFocusedMovieSceneSequence()->GetMovieScene();
for (const FString& String : FilterStrings)
{
if (String.StartsWith(TEXT("label:")))
{
if (StartNode->GetType() == ESequencerNode::Object)
{
auto ObjectBindingNode = StaticCastSharedRef<FSequencerObjectBindingNode>(StartNode);
auto Labels = MovieScene->GetObjectLabels(ObjectBindingNode->GetObjectBinding());
if (!Labels.Strings.Contains(String.RightChop(6)))
{
bFilterAcceptable = false;
}
}
else
{
bFilterAcceptable = false;
}
}
else if (!StartNode->GetDisplayName().ToString().Contains(String))
{
bFilterAcceptable = false;
}
if (!bFilterAcceptable)
{
break;
}
}
// whether or the start node is in the filter
bool bInFilter = false;
if (bFilterAcceptable)
{
// This node is now filtered
OutFilteredNodes.Add(StartNode);
bInFilter = true;
}
// check each child node to determine if it is filtered
const TArray<TSharedRef<FSequencerDisplayNode>>& ChildNodes = StartNode->GetChildNodes();
for (const auto& Node : ChildNodes)
{
// Mark the parent as filtered if any child node was filtered
bFilterAcceptable |= FilterNodesRecursive(Sequencer, Node, FilterStrings, OutFilteredNodes);
if (bFilterAcceptable && !bInFilter)
{
OutFilteredNodes.Add(StartNode);
bInFilter = true;
}
}
return bFilterAcceptable;
}
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);
}
}
}