You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Users can now select the blend profile mode: Blend Weight Factor based or Time Factor based. The Blend Weight Factor method is the legacy method, which multiplies the transition blend weight with the per bone factor. The new Time Factor method allows you to specify a time factor. A value of 0.5 would mean the bone would take half the time of the transition to reach its target state, while a value of 0.1 would mean it takes a tenth of the time, etc. A value of 0 would make the bone instantly transition into the target state. #jira UE-100994 #review-14530265 @Aaron.Cox, @Thomas.Sarkanen [CL 14539393 by john vanderburg in ue5-main branch]
599 lines
23 KiB
C++
599 lines
23 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"
|
|
|
|
/////////////////////////////////////////////////////
|
|
// 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.BlendProfileMode = TransitionNode->BlendProfileMode;
|
|
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();
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|