You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Refactored tick record sync into utility structure - FAnimSync. This improves the odd API around tick records, better encapsulating functionality and leaving less for the caller to get wrong. This will also eventually allow this to be refactored out into a scriptable pipeline stage. Added sync node and scoped sync message for new 'graph based sync'. Nodes that subscribe to graph-based-sync determine their sync group based on the scope that they are in. Removed 4.26-style sync scopes - pushed all syncing up to the main anim instance. Linked anim instances no longer sync their own tick records. To make graph based sync more useful, surfaced graph attributes and their visualizations as labels on pins and parallel wires to visualize flow. This involves statically determining the attribute flow of the graph at compile time. Added a new compiler handler to deal with this new debug data. Updated a lot of nodes to specify their attributes so graph flow can be correctly visualized. Added tracing of attributes and sync records and visualization of traced records when debugging the anim graph. #rb Jurre.deBaare, Martin.Wilson [CL 14998555 by Thomas Sarkanen in ue5-main branch]
614 lines
24 KiB
C++
614 lines
24 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimGraphNode_StateMachineBase.h"
|
|
#include "EdGraph/EdGraph.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
|
|
#include "Kismet2/Kismet2NameValidators.h"
|
|
|
|
#include "AnimationGraph.h"
|
|
#include "AnimationStateMachineGraph.h"
|
|
#include "AnimationStateMachineSchema.h"
|
|
#include "AnimGraphNode_StateMachine.h"
|
|
#include "Kismet2/KismetEditorUtilities.h"
|
|
#include "AnimBlueprintCompiler.h"
|
|
#include "AnimStateNode.h"
|
|
#include "AnimStateTransitionNode.h"
|
|
#include "AnimGraphNode_TransitionResult.h"
|
|
#include "AnimGraphNode_StateResult.h"
|
|
#include "AnimStateEntryNode.h"
|
|
#include "AnimationStateGraph.h"
|
|
#include "AnimationTransitionGraph.h"
|
|
#include "AnimationCustomTransitionGraph.h"
|
|
#include "AnimGraphNode_SequencePlayer.h"
|
|
#include "AnimStateConduitNode.h"
|
|
#include "AnimGraphNode_LinkedAnimLayer.h"
|
|
#include "AnimGraphNode_TransitionPoseEvaluator.h"
|
|
#include "AnimGraphNode_CustomTransitionResult.h"
|
|
#include "AnimBlueprintCompilerHandler_StateMachine.h"
|
|
#include "IAnimBlueprintGeneratedClassCompiledData.h"
|
|
#include "IAnimBlueprintCompilationContext.h"
|
|
#include "Animation/AnimNode_Inertialization.h"
|
|
|
|
/////////////////////////////////////////////////////
|
|
// FAnimStateMachineNodeNameValidator
|
|
|
|
class FAnimStateMachineNodeNameValidator : public FStringSetNameValidator
|
|
{
|
|
public:
|
|
FAnimStateMachineNodeNameValidator(const UAnimGraphNode_StateMachineBase* InStateMachineNode)
|
|
: FStringSetNameValidator(FString())
|
|
{
|
|
TArray<UAnimGraphNode_StateMachineBase*> Nodes;
|
|
|
|
UAnimationGraph* StateMachine = CastChecked<UAnimationGraph>(InStateMachineNode->GetOuter());
|
|
StateMachine->GetNodesOfClassEx<UAnimGraphNode_StateMachine, UAnimGraphNode_StateMachineBase>(Nodes);
|
|
|
|
for (auto Node : Nodes)
|
|
{
|
|
if (Node != InStateMachineNode)
|
|
{
|
|
Names.Add(Node->GetStateMachineName());
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
/////////////////////////////////////////////////////
|
|
// UAnimGraphNode_StateMachineBase
|
|
|
|
#define LOCTEXT_NAMESPACE "A3Nodes"
|
|
|
|
UAnimGraphNode_StateMachineBase::UAnimGraphNode_StateMachineBase(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
}
|
|
|
|
FLinearColor UAnimGraphNode_StateMachineBase::GetNodeTitleColor() const
|
|
{
|
|
return FLinearColor(0.8f, 0.8f, 0.8f);
|
|
}
|
|
|
|
FText UAnimGraphNode_StateMachineBase::GetTooltipText() const
|
|
{
|
|
return LOCTEXT("StateMachineTooltip", "Animation State Machine");
|
|
}
|
|
|
|
FText UAnimGraphNode_StateMachineBase::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
|
{
|
|
if ((TitleType == ENodeTitleType::MenuTitle || TitleType == ENodeTitleType::ListView) && (EditorStateMachineGraph == nullptr))
|
|
{
|
|
return LOCTEXT("AddNewStateMachine", "Add New State Machine...");
|
|
}
|
|
else if (EditorStateMachineGraph == nullptr)
|
|
{
|
|
if (TitleType == ENodeTitleType::FullTitle)
|
|
{
|
|
return LOCTEXT("NullStateMachineFullTitle", "Error: No Graph\nState Machine");
|
|
}
|
|
else
|
|
{
|
|
return LOCTEXT("ErrorNoGraph", "Error: No Graph");
|
|
}
|
|
}
|
|
else if (TitleType == ENodeTitleType::FullTitle)
|
|
{
|
|
if (CachedFullTitle.IsOutOfDate(this))
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("Title"), FText::FromName(EditorStateMachineGraph->GetFName()));
|
|
// FText::Format() is slow, so we cache this to save on performance
|
|
CachedFullTitle.SetCachedText(FText::Format(LOCTEXT("StateMachineFullTitle", "{Title}\nState Machine"), Args), this);
|
|
}
|
|
return CachedFullTitle;
|
|
}
|
|
|
|
return FText::FromName(EditorStateMachineGraph->GetFName());
|
|
}
|
|
|
|
FString UAnimGraphNode_StateMachineBase::GetNodeCategory() const
|
|
{
|
|
return TEXT("State Machines");
|
|
}
|
|
|
|
void UAnimGraphNode_StateMachineBase::PostPlacedNewNode()
|
|
{
|
|
// Create a new animation graph
|
|
check(EditorStateMachineGraph == NULL);
|
|
EditorStateMachineGraph = CastChecked<UAnimationStateMachineGraph>(FBlueprintEditorUtils::CreateNewGraph(this, NAME_None, UAnimationStateMachineGraph::StaticClass(), UAnimationStateMachineSchema::StaticClass()));
|
|
check(EditorStateMachineGraph);
|
|
EditorStateMachineGraph->OwnerAnimGraphNode = this;
|
|
|
|
// Find an interesting name
|
|
TSharedPtr<INameValidatorInterface> NameValidator = FNameValidatorFactory::MakeValidator(this);
|
|
FBlueprintEditorUtils::RenameGraphWithSuggestion(EditorStateMachineGraph, NameValidator, TEXT("New State Machine"));
|
|
|
|
// Initialize the anim graph
|
|
const UEdGraphSchema* Schema = EditorStateMachineGraph->GetSchema();
|
|
Schema->CreateDefaultNodesForGraph(*EditorStateMachineGraph);
|
|
|
|
// Add the new graph as a child of our parent graph
|
|
UEdGraph* ParentGraph = GetGraph();
|
|
|
|
if(ParentGraph->SubGraphs.Find(EditorStateMachineGraph) == INDEX_NONE)
|
|
{
|
|
ParentGraph->Modify();
|
|
ParentGraph->SubGraphs.Add(EditorStateMachineGraph);
|
|
}
|
|
}
|
|
|
|
UObject* UAnimGraphNode_StateMachineBase::GetJumpTargetForDoubleClick() const
|
|
{
|
|
// Open the state machine graph
|
|
return EditorStateMachineGraph;
|
|
}
|
|
|
|
void UAnimGraphNode_StateMachineBase::JumpToDefinition() const
|
|
{
|
|
if (UObject* HyperlinkTarget = GetJumpTargetForDoubleClick())
|
|
{
|
|
FKismetEditorUtilities::BringKismetToFocusAttentionOnObject(HyperlinkTarget);
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_StateMachineBase::DestroyNode()
|
|
{
|
|
UEdGraph* GraphToRemove = EditorStateMachineGraph;
|
|
|
|
EditorStateMachineGraph = NULL;
|
|
Super::DestroyNode();
|
|
|
|
if (GraphToRemove)
|
|
{
|
|
UBlueprint* Blueprint = GetBlueprint();
|
|
GraphToRemove->Modify();
|
|
FBlueprintEditorUtils::RemoveGraph(Blueprint, GraphToRemove, EGraphRemoveFlags::Recompile);
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_StateMachineBase::PostPasteNode()
|
|
{
|
|
Super::PostPasteNode();
|
|
|
|
// Add the new graph as a child of our parent graph
|
|
UEdGraph* ParentGraph = GetGraph();
|
|
|
|
if(ParentGraph->SubGraphs.Find(EditorStateMachineGraph) == INDEX_NONE)
|
|
{
|
|
ParentGraph->SubGraphs.Add(EditorStateMachineGraph);
|
|
}
|
|
|
|
for (UEdGraphNode* GraphNode : EditorStateMachineGraph->Nodes)
|
|
{
|
|
GraphNode->CreateNewGuid();
|
|
GraphNode->PostPasteNode();
|
|
GraphNode->ReconstructNode();
|
|
}
|
|
|
|
// Find an interesting name
|
|
TSharedPtr<INameValidatorInterface> NameValidator = FNameValidatorFactory::MakeValidator(this);
|
|
FBlueprintEditorUtils::RenameGraphWithSuggestion(EditorStateMachineGraph, NameValidator, EditorStateMachineGraph->GetName());
|
|
|
|
//restore transactional flag that is lost during copy/paste process
|
|
EditorStateMachineGraph->SetFlags(RF_Transactional);
|
|
}
|
|
|
|
FString UAnimGraphNode_StateMachineBase::GetStateMachineName()
|
|
{
|
|
return (EditorStateMachineGraph != NULL) ? *(EditorStateMachineGraph->GetName()) : TEXT("(null)");
|
|
}
|
|
|
|
TSharedPtr<class INameValidatorInterface> UAnimGraphNode_StateMachineBase::MakeNameValidator() const
|
|
{
|
|
return MakeShareable(new FAnimStateMachineNodeNameValidator(this));
|
|
}
|
|
|
|
FString UAnimGraphNode_StateMachineBase::GetDocumentationLink() const
|
|
{
|
|
return TEXT("Shared/GraphNodes/AnimationStateMachine");
|
|
}
|
|
|
|
void UAnimGraphNode_StateMachineBase::OnRenameNode(const FString& NewName)
|
|
{
|
|
FBlueprintEditorUtils::RenameGraph(EditorStateMachineGraph, NewName);
|
|
}
|
|
|
|
void UAnimGraphNode_StateMachineBase::OnProcessDuringCompilation(IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
|
|
{
|
|
struct FMachineCreator
|
|
{
|
|
public:
|
|
int32 MachineIndex;
|
|
TMap<UAnimStateNodeBase*, int32> StateIndexTable;
|
|
TMap<UAnimStateTransitionNode*, int32> TransitionIndexTable;
|
|
UAnimGraphNode_StateMachineBase* StateMachineInstance;
|
|
TArray<FBakedAnimationStateMachine>& BakedMachines;
|
|
IAnimBlueprintGeneratedClassCompiledData& CompiledData;
|
|
IAnimBlueprintCompilationContext& CompilationContext;
|
|
|
|
public:
|
|
FMachineCreator(UAnimGraphNode_StateMachineBase* InStateMachineInstance, int32 InMachineIndex, TArray<FBakedAnimationStateMachine>& InBakedMachines, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& InCompiledData)
|
|
: MachineIndex(InMachineIndex)
|
|
, StateMachineInstance(InStateMachineInstance)
|
|
, BakedMachines(InBakedMachines)
|
|
, CompiledData(InCompiledData)
|
|
, CompilationContext(InCompilationContext)
|
|
{
|
|
FStateMachineDebugData& MachineInfo = GetMachineSpecificDebugData();
|
|
MachineInfo.MachineIndex = MachineIndex;
|
|
MachineInfo.MachineInstanceNode = CompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimGraphNode_StateMachineBase>(InStateMachineInstance);
|
|
|
|
StateMachineInstance->GetNode().StateMachineIndexInClass = MachineIndex;
|
|
|
|
FBakedAnimationStateMachine& BakedMachine = GetMachine();
|
|
BakedMachine.MachineName = StateMachineInstance->EditorStateMachineGraph->GetFName();
|
|
BakedMachine.InitialState = INDEX_NONE;
|
|
}
|
|
|
|
FBakedAnimationStateMachine& GetMachine()
|
|
{
|
|
return BakedMachines[MachineIndex];
|
|
}
|
|
|
|
FStateMachineDebugData& GetMachineSpecificDebugData()
|
|
{
|
|
UAnimationStateMachineGraph* SourceGraph = CompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimationStateMachineGraph>(StateMachineInstance->EditorStateMachineGraph);
|
|
return CompiledData.GetAnimBlueprintDebugData().StateMachineDebugData.FindOrAdd(SourceGraph);
|
|
}
|
|
|
|
int32 FindOrAddState(UAnimStateNodeBase* StateNode)
|
|
{
|
|
if (int32* pResult = StateIndexTable.Find(StateNode))
|
|
{
|
|
return *pResult;
|
|
}
|
|
else
|
|
{
|
|
FBakedAnimationStateMachine& BakedMachine = GetMachine();
|
|
|
|
const int32 StateIndex = BakedMachine.States.Num();
|
|
StateIndexTable.Add(StateNode, StateIndex);
|
|
new (BakedMachine.States) FBakedAnimationState();
|
|
|
|
UAnimStateNodeBase* SourceNode = CompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimStateNodeBase>(StateNode);
|
|
GetMachineSpecificDebugData().NodeToStateIndex.Add(SourceNode, StateIndex);
|
|
if (UAnimStateNode* SourceStateNode = Cast<UAnimStateNode>(SourceNode))
|
|
{
|
|
CompiledData.GetAnimBlueprintDebugData().StateGraphToNodeMap.Add(SourceStateNode->BoundGraph, SourceStateNode);
|
|
}
|
|
|
|
return StateIndex;
|
|
}
|
|
}
|
|
|
|
int32 FindOrAddTransition(UAnimStateTransitionNode* TransitionNode)
|
|
{
|
|
if (int32* pResult = TransitionIndexTable.Find(TransitionNode))
|
|
{
|
|
return *pResult;
|
|
}
|
|
else
|
|
{
|
|
FBakedAnimationStateMachine& BakedMachine = GetMachine();
|
|
|
|
const int32 TransitionIndex = BakedMachine.Transitions.Num();
|
|
TransitionIndexTable.Add(TransitionNode, TransitionIndex);
|
|
new (BakedMachine.Transitions) FAnimationTransitionBetweenStates();
|
|
|
|
UAnimStateTransitionNode* SourceTransitionNode = CompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimStateTransitionNode>(TransitionNode);
|
|
GetMachineSpecificDebugData().NodeToTransitionIndex.Add(SourceTransitionNode, TransitionIndex);
|
|
CompiledData.GetAnimBlueprintDebugData().TransitionGraphToNodeMap.Add(SourceTransitionNode->BoundGraph, SourceTransitionNode);
|
|
|
|
if (SourceTransitionNode->CustomTransitionGraph != NULL)
|
|
{
|
|
CompiledData.GetAnimBlueprintDebugData().TransitionBlendGraphToNodeMap.Add(SourceTransitionNode->CustomTransitionGraph, SourceTransitionNode);
|
|
}
|
|
|
|
return TransitionIndex;
|
|
}
|
|
}
|
|
|
|
void Validate()
|
|
{
|
|
FBakedAnimationStateMachine& BakedMachine = GetMachine();
|
|
|
|
// Make sure there is a valid entry point
|
|
if (BakedMachine.InitialState == INDEX_NONE)
|
|
{
|
|
CompilationContext.GetMessageLog().Warning(*LOCTEXT("NoEntryNode", "There was no entry state connection in @@").ToString(), StateMachineInstance);
|
|
BakedMachine.InitialState = 0;
|
|
}
|
|
else
|
|
{
|
|
// Make sure the entry node is a state and not a conduit
|
|
if (BakedMachine.States[BakedMachine.InitialState].bIsAConduit)
|
|
{
|
|
UEdGraphNode* StateNode = GetMachineSpecificDebugData().FindNodeFromStateIndex(BakedMachine.InitialState);
|
|
CompilationContext.GetMessageLog().Error(*LOCTEXT("BadStateEntryNode", "A conduit (@@) cannot be used as the entry node for a state machine").ToString(), StateNode);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
FAnimBlueprintCompilerHandler_StateMachine* CompilerHandler = InCompilationContext.GetHandler<FAnimBlueprintCompilerHandler_StateMachine>("AnimBlueprintCompilerHandler_StateMachine");
|
|
check(CompilerHandler);
|
|
|
|
if (EditorStateMachineGraph == NULL)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(*LOCTEXT("BadStateMachineNoGraph", "@@ does not have a corresponding graph").ToString(), this);
|
|
return;
|
|
}
|
|
|
|
TMap<UAnimGraphNode_TransitionResult*, int32> AlreadyMergedTransitionList;
|
|
|
|
TArray<FBakedAnimationStateMachine>& BakedStateMachines = OutCompiledData.GetBakedStateMachines();
|
|
const int32 MachineIndex = BakedStateMachines.AddDefaulted();
|
|
FMachineCreator Oven(this, MachineIndex, BakedStateMachines, InCompilationContext, OutCompiledData);
|
|
|
|
// Map of states that contain a single player node (from state root node index to associated sequence player)
|
|
TMap<int32, UObject*> SimplePlayerStatesMap;
|
|
|
|
// Process all the states/transitions
|
|
for (auto StateNodeIt = EditorStateMachineGraph->Nodes.CreateIterator(); StateNodeIt; ++StateNodeIt)
|
|
{
|
|
UEdGraphNode* Node = *StateNodeIt;
|
|
|
|
if (UAnimStateEntryNode* EntryNode = Cast<UAnimStateEntryNode>(Node))
|
|
{
|
|
// Handle the state graph entry
|
|
FBakedAnimationStateMachine& BakedMachine = Oven.GetMachine();
|
|
if (BakedMachine.InitialState != INDEX_NONE)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(*LOCTEXT("TooManyStateMachineEntryNodes", "Found an extra entry node @@").ToString(), EntryNode);
|
|
}
|
|
else if (UAnimStateNodeBase* StartState = Cast<UAnimStateNodeBase>(EntryNode->GetOutputNode()))
|
|
{
|
|
BakedMachine.InitialState = Oven.FindOrAddState(StartState);
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Warning(*LOCTEXT("NoConnection", "Entry node @@ is not connected to state").ToString(), EntryNode);
|
|
}
|
|
}
|
|
else if (UAnimStateTransitionNode* TransitionNode = Cast<UAnimStateTransitionNode>(Node))
|
|
{
|
|
TransitionNode->ValidateNodeDuringCompilation(InCompilationContext.GetMessageLog());
|
|
|
|
const int32 TransitionIndex = Oven.FindOrAddTransition(TransitionNode);
|
|
FAnimationTransitionBetweenStates& BakedTransition = Oven.GetMachine().Transitions[TransitionIndex];
|
|
|
|
BakedTransition.CrossfadeDuration = TransitionNode->CrossfadeDuration;
|
|
BakedTransition.StartNotify = OutCompiledData.FindOrAddNotify(TransitionNode->TransitionStart);
|
|
BakedTransition.EndNotify = OutCompiledData.FindOrAddNotify(TransitionNode->TransitionEnd);
|
|
BakedTransition.InterruptNotify = OutCompiledData.FindOrAddNotify(TransitionNode->TransitionInterrupt);
|
|
BakedTransition.BlendMode = TransitionNode->BlendMode;
|
|
BakedTransition.CustomCurve = TransitionNode->CustomBlendCurve;
|
|
BakedTransition.BlendProfile = TransitionNode->BlendProfile;
|
|
BakedTransition.LogicType = TransitionNode->LogicType;
|
|
|
|
UAnimStateNodeBase* PreviousState = TransitionNode->GetPreviousState();
|
|
UAnimStateNodeBase* NextState = TransitionNode->GetNextState();
|
|
|
|
if ((PreviousState != NULL) && (NextState != NULL))
|
|
{
|
|
const int32 PreviousStateIndex = Oven.FindOrAddState(PreviousState);
|
|
const int32 NextStateIndex = Oven.FindOrAddState(NextState);
|
|
|
|
if (TransitionNode->Bidirectional)
|
|
{
|
|
InCompilationContext.GetMessageLog().Warning(*LOCTEXT("BidirectionalTransWarning", "Bidirectional transitions aren't supported yet @@").ToString(), TransitionNode);
|
|
}
|
|
|
|
BakedTransition.PreviousState = PreviousStateIndex;
|
|
BakedTransition.NextState = NextStateIndex;
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Warning(*LOCTEXT("BogusTransition", "@@ is incomplete, without a previous and next state").ToString(), TransitionNode);
|
|
BakedTransition.PreviousState = INDEX_NONE;
|
|
BakedTransition.NextState = INDEX_NONE;
|
|
}
|
|
}
|
|
else if (UAnimStateNode* StateNode = Cast<UAnimStateNode>(Node))
|
|
{
|
|
StateNode->ValidateNodeDuringCompilation(InCompilationContext.GetMessageLog());
|
|
|
|
const int32 StateIndex = Oven.FindOrAddState(StateNode);
|
|
FBakedAnimationState& BakedState = Oven.GetMachine().States[StateIndex];
|
|
|
|
if (StateNode->BoundGraph != NULL)
|
|
{
|
|
BakedState.StateName = StateNode->BoundGraph->GetFName();
|
|
BakedState.StartNotify = OutCompiledData.FindOrAddNotify(StateNode->StateEntered);
|
|
BakedState.EndNotify = OutCompiledData.FindOrAddNotify(StateNode->StateLeft);
|
|
BakedState.FullyBlendedNotify = OutCompiledData.FindOrAddNotify(StateNode->StateFullyBlended);
|
|
BakedState.bIsAConduit = false;
|
|
BakedState.bAlwaysResetOnEntry = StateNode->bAlwaysResetOnEntry;
|
|
|
|
// Process the inner graph of this state
|
|
if (UAnimGraphNode_StateResult* AnimGraphResultNode = CastChecked<UAnimationStateGraph>(StateNode->BoundGraph)->GetResultNode())
|
|
{
|
|
InCompilationContext.ValidateGraphIsWellFormed(StateNode->BoundGraph);
|
|
|
|
BakedState.StateRootNodeIndex = CompilerHandler->ExpandGraphAndProcessNodes(StateNode->BoundGraph, AnimGraphResultNode, InCompilationContext, OutCompiledData);
|
|
|
|
// See if the state consists of a single sequence player node, and remember the index if so
|
|
for (UEdGraphPin* TestPin : AnimGraphResultNode->Pins)
|
|
{
|
|
if ((TestPin->Direction == EGPD_Input) && (TestPin->LinkedTo.Num() == 1))
|
|
{
|
|
if (UAnimGraphNode_SequencePlayer* SequencePlayer = Cast<UAnimGraphNode_SequencePlayer>(TestPin->LinkedTo[0]->GetOwningNode()))
|
|
{
|
|
SimplePlayerStatesMap.Add(BakedState.StateRootNodeIndex, InCompilationContext.GetMessageLog().FindSourceObject(SequencePlayer));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BakedState.StateRootNodeIndex = INDEX_NONE;
|
|
InCompilationContext.GetMessageLog().Error(*LOCTEXT("StateWithNoResult", "@@ has no result node").ToString(), StateNode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BakedState.StateName = NAME_None;
|
|
InCompilationContext.GetMessageLog().Error(*LOCTEXT("StateWithBadGraph", "@@ has no bound graph").ToString(), StateNode);
|
|
}
|
|
|
|
// If this check fires, then something in the machine has changed causing the states array to not
|
|
// be a separate allocation, and a state machine inside of this one caused stuff to shift around
|
|
checkSlow(&BakedState == &(Oven.GetMachine().States[StateIndex]));
|
|
}
|
|
else if (UAnimStateConduitNode* ConduitNode = Cast<UAnimStateConduitNode>(Node))
|
|
{
|
|
ConduitNode->ValidateNodeDuringCompilation(InCompilationContext.GetMessageLog());
|
|
|
|
const int32 StateIndex = Oven.FindOrAddState(ConduitNode);
|
|
FBakedAnimationState& BakedState = Oven.GetMachine().States[StateIndex];
|
|
|
|
BakedState.StateName = ConduitNode->BoundGraph ? ConduitNode->BoundGraph->GetFName() : TEXT("OLD CONDUIT");
|
|
BakedState.bIsAConduit = true;
|
|
|
|
if (ConduitNode->BoundGraph != NULL)
|
|
{
|
|
if (UAnimGraphNode_TransitionResult* EntryRuleResultNode = CastChecked<UAnimationTransitionGraph>(ConduitNode->BoundGraph)->GetResultNode())
|
|
{
|
|
BakedState.EntryRuleNodeIndex = CompilerHandler->ExpandGraphAndProcessNodes(ConduitNode->BoundGraph, EntryRuleResultNode, InCompilationContext, OutCompiledData);
|
|
}
|
|
}
|
|
|
|
// If this check fires, then something in the machine has changed causing the states array to not
|
|
// be a separate allocation, and a state machine inside of this one caused stuff to shift around
|
|
checkSlow(&BakedState == &(Oven.GetMachine().States[StateIndex]));
|
|
}
|
|
}
|
|
|
|
// Process transitions after all the states because getters within custom graphs may want to
|
|
// reference back to other states, which are only valid if they have already been baked
|
|
for (auto StateNodeIt = Oven.StateIndexTable.CreateIterator(); StateNodeIt; ++StateNodeIt)
|
|
{
|
|
UAnimStateNodeBase* StateNode = StateNodeIt.Key();
|
|
const int32 StateIndex = StateNodeIt.Value();
|
|
|
|
FBakedAnimationState& BakedState = Oven.GetMachine().States[StateIndex];
|
|
|
|
// Add indices to all player and layer nodes
|
|
TArray<UEdGraph*> GraphsToCheck;
|
|
GraphsToCheck.Add(StateNode->GetBoundGraph());
|
|
StateNode->GetBoundGraph()->GetAllChildrenGraphs(GraphsToCheck);
|
|
|
|
TArray<UAnimGraphNode_LinkedAnimLayer*> LinkedAnimLayerNodes;
|
|
TArray<UAnimGraphNode_AssetPlayerBase*> AssetPlayerNodes;
|
|
for (UEdGraph* ChildGraph : GraphsToCheck)
|
|
{
|
|
ChildGraph->GetNodesOfClass(AssetPlayerNodes);
|
|
ChildGraph->GetNodesOfClass(LinkedAnimLayerNodes);
|
|
}
|
|
|
|
for (UAnimGraphNode_AssetPlayerBase* Node : AssetPlayerNodes)
|
|
{
|
|
if (int32* IndexPtr = OutCompiledData.GetAnimBlueprintDebugData().NodeGuidToIndexMap.Find(Node->NodeGuid))
|
|
{
|
|
BakedState.PlayerNodeIndices.Add(*IndexPtr);
|
|
}
|
|
}
|
|
|
|
for (UAnimGraphNode_LinkedAnimLayer* Node : LinkedAnimLayerNodes)
|
|
{
|
|
if (int32* IndexPtr = OutCompiledData.GetAnimBlueprintDebugData().NodeGuidToIndexMap.Find(Node->NodeGuid))
|
|
{
|
|
BakedState.LayerNodeIndices.Add(*IndexPtr);
|
|
}
|
|
}
|
|
// Handle all the transitions out of this node
|
|
TArray<class UAnimStateTransitionNode*> TransitionList;
|
|
StateNode->GetTransitionList(/*out*/ TransitionList, /*bWantSortedList=*/ true);
|
|
|
|
for (auto TransitionIt = TransitionList.CreateIterator(); TransitionIt; ++TransitionIt)
|
|
{
|
|
UAnimStateTransitionNode* TransitionNode = *TransitionIt;
|
|
const int32 TransitionIndex = Oven.FindOrAddTransition(TransitionNode);
|
|
|
|
// Validate the blend profile for this transition - incase the skeleton of the node has
|
|
// changed or the blend profile no longer exists.
|
|
TransitionNode->ValidateBlendProfile();
|
|
|
|
FBakedStateExitTransition& Rule = *new (BakedState.Transitions) FBakedStateExitTransition();
|
|
Rule.bDesiredTransitionReturnValue = (TransitionNode->GetPreviousState() == StateNode);
|
|
Rule.TransitionIndex = TransitionIndex;
|
|
|
|
if (UAnimGraphNode_TransitionResult* TransitionResultNode = CastChecked<UAnimationTransitionGraph>(TransitionNode->BoundGraph)->GetResultNode())
|
|
{
|
|
if (int32* pIndex = AlreadyMergedTransitionList.Find(TransitionResultNode))
|
|
{
|
|
Rule.CanTakeDelegateIndex = *pIndex;
|
|
}
|
|
else
|
|
{
|
|
Rule.CanTakeDelegateIndex = CompilerHandler->ExpandGraphAndProcessNodes(TransitionNode->BoundGraph, TransitionResultNode, InCompilationContext, OutCompiledData, TransitionNode);
|
|
AlreadyMergedTransitionList.Add(TransitionResultNode, Rule.CanTakeDelegateIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Rule.CanTakeDelegateIndex = INDEX_NONE;
|
|
InCompilationContext.GetMessageLog().Error(*LOCTEXT("TransitionWithNoResult", "@@ has no result node").ToString(), TransitionNode);
|
|
}
|
|
|
|
// Handle automatic time remaining rules
|
|
Rule.bAutomaticRemainingTimeRule = TransitionNode->bAutomaticRuleBasedOnSequencePlayerInState;
|
|
Rule.SyncGroupNameToRequireValidMarkersRule = TransitionNode->SyncGroupNameToRequireValidMarkersRule;
|
|
|
|
// Handle custom transition graphs
|
|
Rule.CustomResultNodeIndex = INDEX_NONE;
|
|
if (UAnimationCustomTransitionGraph* CustomTransitionGraph = Cast<UAnimationCustomTransitionGraph>(TransitionNode->CustomTransitionGraph))
|
|
{
|
|
TArray<UEdGraphNode*> ClonedNodes;
|
|
if (CustomTransitionGraph->GetResultNode())
|
|
{
|
|
Rule.CustomResultNodeIndex = CompilerHandler->ExpandGraphAndProcessNodes(TransitionNode->CustomTransitionGraph, CustomTransitionGraph->GetResultNode(), InCompilationContext, OutCompiledData, nullptr, &ClonedNodes);
|
|
}
|
|
|
|
// Find all the pose evaluators used in this transition, save handles to them because we need to populate some pose data before executing
|
|
TArray<UAnimGraphNode_TransitionPoseEvaluator*> TransitionPoseList;
|
|
for (auto ClonedNodesIt = ClonedNodes.CreateIterator(); ClonedNodesIt; ++ClonedNodesIt)
|
|
{
|
|
UEdGraphNode* Node = *ClonedNodesIt;
|
|
if (UAnimGraphNode_TransitionPoseEvaluator* TypedNode = Cast<UAnimGraphNode_TransitionPoseEvaluator>(Node))
|
|
{
|
|
TransitionPoseList.Add(TypedNode);
|
|
}
|
|
}
|
|
|
|
Rule.PoseEvaluatorLinks.Empty(TransitionPoseList.Num());
|
|
|
|
for (auto TransitionPoseListIt = TransitionPoseList.CreateIterator(); TransitionPoseListIt; ++TransitionPoseListIt)
|
|
{
|
|
UAnimGraphNode_TransitionPoseEvaluator* TransitionPoseNode = *TransitionPoseListIt;
|
|
Rule.PoseEvaluatorLinks.Add( InCompilationContext.GetAllocationIndexOfNode(TransitionPoseNode) );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Oven.Validate();
|
|
}
|
|
|
|
void UAnimGraphNode_StateMachineBase::GetOutputLinkAttributes(FNodeAttributeArray& OutAttributes) const
|
|
{
|
|
for (const UEdGraphNode* Node : EditorStateMachineGraph->Nodes)
|
|
{
|
|
if (const UAnimStateTransitionNode* TransitionNode = Cast<UAnimStateTransitionNode>(Node))
|
|
{
|
|
if(TransitionNode->LogicType == ETransitionLogicType::TLT_Inertialization)
|
|
{
|
|
OutAttributes.Add(UE::Anim::IInertializationRequester::Attribute);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|