You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Per-node constant data is now held on a generated struct as part of sparse class data. Per-node mutable data (i.e. pin links/property access mappings) is now held on a generated 'mutable data' struct that is compiled as part of the generated class. The anim BP compiler is now extended more conventionally using UAnimBlueprintExtension, derived from UBlueprintExtension. This directly replaces the older 'compiler handler' pattern that was added in an emergency fashion for 4.26. Anim graph nodes now request their required extensions and these are held on the UAnimBlueprint in the UBlueprint::Extensions array. The Extensions array is potentially refreshed with any node addition or removal. The Extensions array is force-refreshed each time an anim BP is compiled for the first time to deal with newly added or removed requirements. Const-corrected a bunch of UAnimInstance/FAnimInstanceProxy APIs that rely on (now truly) const data. Added a split state/constant version of FInputScaleBiasClamp to allow some of its data to be split into constants. Tweaked alignment/ordering of FPoseLinkBase to save a few bytes per pose link. Deprecated FAnimNode_Base::OverrideAsset in favor of a more UAnimGraphNode_Base-based approach. Individual nodes can still have runtime overrides via specific accessors. The new approach will also give us the oppurtunity to override multiple assets per node if required in the future. Moved property access into Engine module & removed event support from it - this was never used. Reworked property access compilation API a little - construction/lifetime was a bit confusing previously. Optimized path used to create UK2Node_StructMemberSet nodes in per-node custom events. When using mutable data, the structure used is large and very sparsely connected (i.e. only a few properties are written) so we only create pins that are actually going to be used, rather than creating all of them and conly connecting a few. Patched the following nodes to use the new data approach: - Asset players (sequences, blendspaces, aim offsets) - Blend lists - Ref poses - Roots #rb Jurre.deBaare, Martin.Wilson, Keith.Yerex [CL 16090510 by Thomas Sarkanen in ue5-main branch]
560 lines
24 KiB
C++
560 lines
24 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimBlueprintExtension_StateMachine.h"
|
|
#include "Animation/AnimBlueprintGeneratedClass.h"
|
|
#include "K2Node_AnimGetter.h"
|
|
#include "K2Node_TransitionRuleGetter.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "AnimGraphNode_Base.h"
|
|
#include "K2Node_StructMemberGet.h"
|
|
#include "AnimStateNode.h"
|
|
#include "AnimStateTransitionNode.h"
|
|
#include "AnimGraphNode_StateMachineBase.h"
|
|
#include "EdGraphUtilities.h"
|
|
#include "AnimationStateMachineSchema.h"
|
|
#include "IAnimBlueprintGeneratedClassCompiledData.h"
|
|
#include "IAnimBlueprintCompilerCreationContext.h"
|
|
#include "IAnimBlueprintCompilationContext.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "AnimBlueprintExtension_StateMachine"
|
|
|
|
void UAnimBlueprintExtension_StateMachine::HandleBeginCompilation(IAnimBlueprintCompilerCreationContext& InCreationContext)
|
|
{
|
|
InCreationContext.RegisterKnownGraphSchema(UAnimationStateMachineSchema::StaticClass());
|
|
}
|
|
|
|
void UAnimBlueprintExtension_StateMachine::HandleStartCompilingClass(const UClass* InClass, IAnimBlueprintCompilationBracketContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
|
|
{
|
|
FoundGetterNodes.Empty();
|
|
RootTransitionGetters.Empty();
|
|
RootGraphAnimGetters.Empty();
|
|
}
|
|
|
|
void UAnimBlueprintExtension_StateMachine::HandlePreProcessAnimationNodes(TArrayView<UAnimGraphNode_Base*> InAnimNodes, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
|
|
{
|
|
InCompilationContext.GetConsolidatedEventGraph()->GetNodesOfClass<UK2Node_TransitionRuleGetter>(RootTransitionGetters);
|
|
|
|
// Get anim getters from the root anim graph (processing the nodes below will collect them in nested graphs)
|
|
InCompilationContext.GetConsolidatedEventGraph()->GetNodesOfClass<UK2Node_AnimGetter>(RootGraphAnimGetters);
|
|
}
|
|
|
|
void UAnimBlueprintExtension_StateMachine::HandlePostProcessAnimationNodes(TArrayView<UAnimGraphNode_Base*> InAnimNodes, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
|
|
{
|
|
// Process the getter nodes in the graph if there were any
|
|
for (auto GetterIt = RootTransitionGetters.CreateIterator(); GetterIt; ++GetterIt)
|
|
{
|
|
ProcessTransitionGetter(*GetterIt, nullptr, InCompilationContext, OutCompiledData); // transition nodes should not appear at top-level
|
|
}
|
|
|
|
// Wire root getters
|
|
for(UK2Node_AnimGetter* RootGraphGetter : RootGraphAnimGetters)
|
|
{
|
|
AutoWireAnimGetter(RootGraphGetter, nullptr, InCompilationContext, OutCompiledData);
|
|
}
|
|
|
|
// Wire nested getters
|
|
for(UK2Node_AnimGetter* Getter : FoundGetterNodes)
|
|
{
|
|
AutoWireAnimGetter(Getter, nullptr, InCompilationContext, OutCompiledData);
|
|
}
|
|
}
|
|
|
|
UK2Node_CallFunction* UAnimBlueprintExtension_StateMachine::SpawnCallAnimInstanceFunction(IAnimBlueprintCompilationContext& InCompilationContext, UEdGraphNode* SourceNode, FName FunctionName)
|
|
{
|
|
//@TODO: SKELETON: This is a call on a parent function (UAnimInstance::StaticClass() specifically), should we treat it as self or not?
|
|
UK2Node_CallFunction* FunctionCall = InCompilationContext.SpawnIntermediateNode<UK2Node_CallFunction>(SourceNode);
|
|
FunctionCall->FunctionReference.SetSelfMember(FunctionName);
|
|
FunctionCall->AllocateDefaultPins();
|
|
|
|
return FunctionCall;
|
|
}
|
|
|
|
void UAnimBlueprintExtension_StateMachine::ProcessTransitionGetter(UK2Node_TransitionRuleGetter* Getter, UAnimStateTransitionNode* TransitionNode, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
|
|
{
|
|
// Get common elements for multiple getters
|
|
UEdGraphPin* OutputPin = Getter->GetOutputPin();
|
|
|
|
UEdGraphPin* SourceTimePin = NULL;
|
|
UAnimationAsset* AnimAsset= NULL;
|
|
int32 PlayerNodeIndex = INDEX_NONE;
|
|
|
|
if (UAnimGraphNode_Base* SourcePlayerNode = Getter->AssociatedAnimAssetPlayerNode)
|
|
{
|
|
// This check should never fail as the source state is always processed first before handling it's rules
|
|
UAnimGraphNode_Base* TrueSourceNode = InCompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimGraphNode_Base>(SourcePlayerNode);
|
|
UAnimGraphNode_Base* UndertypedPlayerNode = InCompilationContext.GetSourceNodeToProcessedNodeMap().FindRef(TrueSourceNode);
|
|
|
|
if (UndertypedPlayerNode == NULL)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("ICE: Player node @@ was not processed prior to handling a transition getter @@ that used it"), SourcePlayerNode, Getter);
|
|
return;
|
|
}
|
|
|
|
// Make sure the node is still relevant
|
|
UEdGraph* PlayerGraph = UndertypedPlayerNode->GetGraph();
|
|
if (!PlayerGraph->Nodes.Contains(UndertypedPlayerNode))
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not associated with a node in @@; please delete and recreate it"), Getter, PlayerGraph);
|
|
}
|
|
|
|
// Make sure the referenced AnimAsset player has been allocated
|
|
PlayerNodeIndex = InCompilationContext.GetAllocationIndexOfNode(UndertypedPlayerNode);
|
|
if (PlayerNodeIndex == INDEX_NONE)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(*LOCTEXT("BadAnimAssetNodeUsedInGetter", "@@ doesn't have a valid associated AnimAsset node. Delete and recreate it").ToString(), Getter);
|
|
}
|
|
|
|
// Grab the AnimAsset, and time pin if needed
|
|
UScriptStruct* TimePropertyInStructType = NULL;
|
|
const TCHAR* TimePropertyName = NULL;
|
|
if (UndertypedPlayerNode->DoesSupportTimeForTransitionGetter())
|
|
{
|
|
AnimAsset = UndertypedPlayerNode->GetAnimationAsset();
|
|
TimePropertyInStructType = UndertypedPlayerNode->GetTimePropertyStruct();
|
|
TimePropertyName = UndertypedPlayerNode->GetTimePropertyName();
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is associated with @@, which is an unexpected type"), Getter, UndertypedPlayerNode);
|
|
}
|
|
|
|
bool bNeedTimePin = false;
|
|
|
|
// Determine if we need to read the current time variable from the specified sequence player
|
|
switch (Getter->GetterType)
|
|
{
|
|
case ETransitionGetter::AnimationAsset_GetCurrentTime:
|
|
case ETransitionGetter::AnimationAsset_GetCurrentTimeFraction:
|
|
case ETransitionGetter::AnimationAsset_GetTimeFromEnd:
|
|
case ETransitionGetter::AnimationAsset_GetTimeFromEndFraction:
|
|
bNeedTimePin = true;
|
|
break;
|
|
default:
|
|
bNeedTimePin = false;
|
|
break;
|
|
}
|
|
|
|
if (bNeedTimePin && (PlayerNodeIndex != INDEX_NONE) && (TimePropertyName != NULL) && (TimePropertyInStructType != NULL))
|
|
{
|
|
const FProperty* NodeProperty = InCompilationContext.GetAllocatedPropertiesByIndex().FindChecked(PlayerNodeIndex);
|
|
|
|
// Create a struct member read node to grab the current position of the sequence player node
|
|
UK2Node_StructMemberGet* TimeReadNode = InCompilationContext.SpawnIntermediateNode<UK2Node_StructMemberGet>(Getter, InCompilationContext.GetConsolidatedEventGraph());
|
|
TimeReadNode->VariableReference.SetSelfMember(NodeProperty->GetFName());
|
|
TimeReadNode->StructType = TimePropertyInStructType;
|
|
|
|
TimeReadNode->AllocatePinsForSingleMemberGet(TimePropertyName);
|
|
SourceTimePin = TimeReadNode->FindPinChecked(TimePropertyName);
|
|
}
|
|
}
|
|
|
|
// Expand it out
|
|
UK2Node_CallFunction* GetterHelper = NULL;
|
|
switch (Getter->GetterType)
|
|
{
|
|
case ETransitionGetter::AnimationAsset_GetCurrentTime:
|
|
if ((AnimAsset != NULL) && (SourceTimePin != NULL))
|
|
{
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceAssetPlayerTime"));
|
|
GetterHelper->FindPinChecked(TEXT("AssetPlayerIndex"))->DefaultValue = FString::FromInt(PlayerNodeIndex);
|
|
}
|
|
else
|
|
{
|
|
if (Getter->AssociatedAnimAssetPlayerNode)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("Please replace @@ with Get Relevant Anim Time. @@ has no animation asset"), Getter, Getter->AssociatedAnimAssetPlayerNode);
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not asscociated with an asset player"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
case ETransitionGetter::AnimationAsset_GetLength:
|
|
if (AnimAsset != NULL)
|
|
{
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceAssetPlayerLength"));
|
|
GetterHelper->FindPinChecked(TEXT("AssetPlayerIndex"))->DefaultValue = FString::FromInt(PlayerNodeIndex);
|
|
}
|
|
else
|
|
{
|
|
if (Getter->AssociatedAnimAssetPlayerNode)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("Please replace @@ with Get Relevant Anim Length. @@ has no animation asset"), Getter, Getter->AssociatedAnimAssetPlayerNode);
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not asscociated with an asset player"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
case ETransitionGetter::AnimationAsset_GetCurrentTimeFraction:
|
|
if ((AnimAsset != NULL) && (SourceTimePin != NULL))
|
|
{
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceAssetPlayerTimeFraction"));
|
|
GetterHelper->FindPinChecked(TEXT("AssetPlayerIndex"))->DefaultValue = FString::FromInt(PlayerNodeIndex);
|
|
}
|
|
else
|
|
{
|
|
if (Getter->AssociatedAnimAssetPlayerNode)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("Please replace @@ with Get Relevant Anim Time Fraction. @@ has no animation asset"), Getter, Getter->AssociatedAnimAssetPlayerNode);
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not asscociated with an asset player"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
case ETransitionGetter::AnimationAsset_GetTimeFromEnd:
|
|
if ((AnimAsset != NULL) && (SourceTimePin != NULL))
|
|
{
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceAssetPlayerTimeFromEnd"));
|
|
GetterHelper->FindPinChecked(TEXT("AssetPlayerIndex"))->DefaultValue = FString::FromInt(PlayerNodeIndex);
|
|
}
|
|
else
|
|
{
|
|
if (Getter->AssociatedAnimAssetPlayerNode)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("Please replace @@ with Get Relevant Anim Time Remaining. @@ has no animation asset"), Getter, Getter->AssociatedAnimAssetPlayerNode);
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not asscociated with an asset player"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
case ETransitionGetter::AnimationAsset_GetTimeFromEndFraction:
|
|
if ((AnimAsset != NULL) && (SourceTimePin != NULL))
|
|
{
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceAssetPlayerTimeFromEndFraction"));
|
|
GetterHelper->FindPinChecked(TEXT("AssetPlayerIndex"))->DefaultValue = FString::FromInt(PlayerNodeIndex);
|
|
}
|
|
else
|
|
{
|
|
if (Getter->AssociatedAnimAssetPlayerNode)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("Please replace @@ with Get Relevant Anim Time Remaining Fraction. @@ has no animation asset"), Getter, Getter->AssociatedAnimAssetPlayerNode);
|
|
}
|
|
else
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not asscociated with an asset player"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ETransitionGetter::CurrentTransitionDuration:
|
|
{
|
|
check(TransitionNode);
|
|
if(UAnimStateNode* SourceStateNode = InCompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimStateNode>(TransitionNode->GetPreviousState()))
|
|
{
|
|
if(UObject* SourceTransitionNode = InCompilationContext.GetMessageLog().FindSourceObject(TransitionNode))
|
|
{
|
|
if(FStateMachineDebugData* DebugData = OutCompiledData.GetAnimBlueprintDebugData().StateMachineDebugData.Find(SourceStateNode->GetGraph()))
|
|
{
|
|
if(int32* pStateIndex = DebugData->NodeToStateIndex.Find(SourceStateNode))
|
|
{
|
|
const int32 StateIndex = *pStateIndex;
|
|
|
|
// This check should never fail as all animation nodes should be processed before getters are
|
|
UAnimGraphNode_Base* CompiledMachineInstanceNode = InCompilationContext.GetSourceNodeToProcessedNodeMap().FindChecked(DebugData->MachineInstanceNode.Get());
|
|
const int32 MachinePropertyIndex = InCompilationContext.GetAllocatedAnimNodeIndices().FindChecked(CompiledMachineInstanceNode);
|
|
int32 TransitionPropertyIndex = INDEX_NONE;
|
|
|
|
for(auto TransIt = DebugData->NodeToTransitionIndex.CreateConstIterator(); TransIt; ++TransIt)
|
|
{
|
|
const UEdGraphNode* CurrTransNode = TransIt.Key().Get();
|
|
|
|
if(CurrTransNode == SourceTransitionNode)
|
|
{
|
|
TransitionPropertyIndex = TransIt.Value();
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(TransitionPropertyIndex != INDEX_NONE)
|
|
{
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceTransitionCrossfadeDuration"));
|
|
GetterHelper->FindPinChecked(TEXT("MachineIndex"))->DefaultValue = FString::FromInt(MachinePropertyIndex);
|
|
GetterHelper->FindPinChecked(TEXT("TransitionIndex"))->DefaultValue = FString::FromInt(TransitionPropertyIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ETransitionGetter::ArbitraryState_GetBlendWeight:
|
|
{
|
|
if (Getter->AssociatedStateNode)
|
|
{
|
|
if (UAnimStateNode* SourceStateNode = InCompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimStateNode>(Getter->AssociatedStateNode))
|
|
{
|
|
if (FStateMachineDebugData* DebugData = OutCompiledData.GetAnimBlueprintDebugData().StateMachineDebugData.Find(SourceStateNode->GetGraph()))
|
|
{
|
|
if (int32* pStateIndex = DebugData->NodeToStateIndex.Find(SourceStateNode))
|
|
{
|
|
const int32 StateIndex = *pStateIndex;
|
|
//const int32 MachineIndex = DebugData->MachineIndex;
|
|
|
|
// This check should never fail as all animation nodes should be processed before getters are
|
|
UAnimGraphNode_Base* CompiledMachineInstanceNode = InCompilationContext.GetSourceNodeToProcessedNodeMap().FindChecked(DebugData->MachineInstanceNode.Get());
|
|
const int32 MachinePropertyIndex = InCompilationContext.GetAllocatedAnimNodeIndices().FindChecked(CompiledMachineInstanceNode);
|
|
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceStateWeight"));
|
|
GetterHelper->FindPinChecked(TEXT("MachineIndex"))->DefaultValue = FString::FromInt(MachinePropertyIndex);
|
|
GetterHelper->FindPinChecked(TEXT("StateIndex"))->DefaultValue = FString::FromInt(StateIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (GetterHelper == NULL)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not associated with a valid state"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ETransitionGetter::CurrentState_ElapsedTime:
|
|
{
|
|
check(TransitionNode);
|
|
if (UAnimStateNode* SourceStateNode = InCompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimStateNode>(TransitionNode->GetPreviousState()))
|
|
{
|
|
if (FStateMachineDebugData* DebugData = OutCompiledData.GetAnimBlueprintDebugData().StateMachineDebugData.Find(SourceStateNode->GetGraph()))
|
|
{
|
|
// This check should never fail as all animation nodes should be processed before getters are
|
|
UAnimGraphNode_Base* CompiledMachineInstanceNode = InCompilationContext.GetSourceNodeToProcessedNodeMap().FindChecked(DebugData->MachineInstanceNode.Get());
|
|
const int32 MachinePropertyIndex = InCompilationContext.GetAllocatedAnimNodeIndices().FindChecked(CompiledMachineInstanceNode);
|
|
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceCurrentStateElapsedTime"));
|
|
GetterHelper->FindPinChecked(TEXT("MachineIndex"))->DefaultValue = FString::FromInt(MachinePropertyIndex);
|
|
}
|
|
}
|
|
if (GetterHelper == NULL)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not associated with a valid state"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case ETransitionGetter::CurrentState_GetBlendWeight:
|
|
{
|
|
check(TransitionNode);
|
|
if (UAnimStateNode* SourceStateNode = InCompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimStateNode>(TransitionNode->GetPreviousState()))
|
|
{
|
|
{
|
|
if (FStateMachineDebugData* DebugData = OutCompiledData.GetAnimBlueprintDebugData().StateMachineDebugData.Find(SourceStateNode->GetGraph()))
|
|
{
|
|
if (int32* pStateIndex = DebugData->NodeToStateIndex.Find(SourceStateNode))
|
|
{
|
|
const int32 StateIndex = *pStateIndex;
|
|
//const int32 MachineIndex = DebugData->MachineIndex;
|
|
|
|
// This check should never fail as all animation nodes should be processed before getters are
|
|
UAnimGraphNode_Base* CompiledMachineInstanceNode = InCompilationContext.GetSourceNodeToProcessedNodeMap().FindChecked(DebugData->MachineInstanceNode.Get());
|
|
const int32 MachinePropertyIndex = InCompilationContext.GetAllocatedAnimNodeIndices().FindChecked(CompiledMachineInstanceNode);
|
|
|
|
GetterHelper = SpawnCallAnimInstanceFunction(InCompilationContext, Getter, TEXT("GetInstanceStateWeight"));
|
|
GetterHelper->FindPinChecked(TEXT("MachineIndex"))->DefaultValue = FString::FromInt(MachinePropertyIndex);
|
|
GetterHelper->FindPinChecked(TEXT("StateIndex"))->DefaultValue = FString::FromInt(StateIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (GetterHelper == NULL)
|
|
{
|
|
InCompilationContext.GetMessageLog().Error(TEXT("@@ is not associated with a valid state"), Getter);
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
InCompilationContext.GetMessageLog().Error(TEXT("Unrecognized getter type on @@"), Getter);
|
|
break;
|
|
}
|
|
|
|
// Finish wiring up a call function if needed
|
|
if (GetterHelper != NULL)
|
|
{
|
|
check(GetterHelper->IsNodePure());
|
|
|
|
UEdGraphPin* NewReturnPin = GetterHelper->FindPinChecked(TEXT("ReturnValue"));
|
|
InCompilationContext.GetMessageLog().NotifyIntermediatePinCreation(NewReturnPin, OutputPin);
|
|
|
|
NewReturnPin->CopyPersistentDataFromOldPin(*OutputPin);
|
|
}
|
|
|
|
// Remove the getter from the equation
|
|
Getter->BreakAllNodeLinks();
|
|
}
|
|
|
|
void UAnimBlueprintExtension_StateMachine::AutoWireAnimGetter(class UK2Node_AnimGetter* Getter, UAnimStateTransitionNode* InTransitionNode, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
|
|
{
|
|
UEdGraphPin* ReferencedNodeTimePin = nullptr;
|
|
int32 ReferencedNodeIndex = INDEX_NONE;
|
|
int32 SubNodeIndex = INDEX_NONE;
|
|
|
|
UAnimGraphNode_Base* ProcessedNodeCheck = NULL;
|
|
|
|
if(UAnimGraphNode_Base* SourceNode = Getter->SourceNode)
|
|
{
|
|
UAnimGraphNode_Base* ActualSourceNode = InCompilationContext.GetMessageLog().FindSourceObjectTypeChecked<UAnimGraphNode_Base>(SourceNode);
|
|
|
|
if(UAnimGraphNode_Base* ProcessedSourceNode = InCompilationContext.GetSourceNodeToProcessedNodeMap().FindRef(ActualSourceNode))
|
|
{
|
|
ProcessedNodeCheck = ProcessedSourceNode;
|
|
|
|
ReferencedNodeIndex = InCompilationContext.GetAllocationIndexOfNode(ProcessedSourceNode);
|
|
|
|
if(ProcessedSourceNode->DoesSupportTimeForTransitionGetter())
|
|
{
|
|
UScriptStruct* TimePropertyInStructType = ProcessedSourceNode->GetTimePropertyStruct();
|
|
const TCHAR* TimePropertyName = ProcessedSourceNode->GetTimePropertyName();
|
|
|
|
if(ReferencedNodeIndex != INDEX_NONE && TimePropertyName && TimePropertyInStructType)
|
|
{
|
|
FProperty* NodeProperty = InCompilationContext.GetAllocatedPropertiesByIndex().FindChecked(ReferencedNodeIndex);
|
|
|
|
UK2Node_StructMemberGet* ReaderNode = InCompilationContext.SpawnIntermediateNode<UK2Node_StructMemberGet>(Getter, InCompilationContext.GetConsolidatedEventGraph());
|
|
ReaderNode->VariableReference.SetSelfMember(NodeProperty->GetFName());
|
|
ReaderNode->StructType = TimePropertyInStructType;
|
|
ReaderNode->AllocatePinsForSingleMemberGet(TimePropertyName);
|
|
|
|
ReferencedNodeTimePin = ReaderNode->FindPinChecked(TimePropertyName);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(Getter->SourceStateNode)
|
|
{
|
|
UObject* SourceObject = InCompilationContext.GetMessageLog().FindSourceObject(Getter->SourceStateNode);
|
|
if(UAnimStateNode* SourceStateNode = Cast<UAnimStateNode>(SourceObject))
|
|
{
|
|
if(FStateMachineDebugData* DebugData = OutCompiledData.GetAnimBlueprintDebugData().StateMachineDebugData.Find(SourceStateNode->GetGraph()))
|
|
{
|
|
if(int32* StateIndexPtr = DebugData->NodeToStateIndex.Find(SourceStateNode))
|
|
{
|
|
SubNodeIndex = *StateIndexPtr;
|
|
}
|
|
}
|
|
}
|
|
else if(UAnimStateTransitionNode* TransitionNode = Cast<UAnimStateTransitionNode>(SourceObject))
|
|
{
|
|
if(FStateMachineDebugData* DebugData = OutCompiledData.GetAnimBlueprintDebugData().StateMachineDebugData.Find(TransitionNode->GetGraph()))
|
|
{
|
|
TArray<int32> TransitionIndices;
|
|
DebugData->NodeToTransitionIndex.MultiFind(TransitionNode, TransitionIndices);
|
|
const int32 TransNum = TransitionIndices.Num();
|
|
|
|
if (TransNum > 0)
|
|
{
|
|
SubNodeIndex = TransitionIndices[0];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
check(Getter->IsNodePure());
|
|
|
|
for(UEdGraphPin* Pin : Getter->Pins)
|
|
{
|
|
// Hook up autowired parameters / pins
|
|
if(Pin->PinName == TEXT("CurrentTime"))
|
|
{
|
|
Pin->MakeLinkTo(ReferencedNodeTimePin);
|
|
}
|
|
else if(Pin->PinName == TEXT("AssetPlayerIndex") || Pin->PinName == TEXT("MachineIndex"))
|
|
{
|
|
Pin->DefaultValue = FString::FromInt(ReferencedNodeIndex);
|
|
}
|
|
else if(Pin->PinName == TEXT("StateIndex") || Pin->PinName == TEXT("TransitionIndex"))
|
|
{
|
|
Pin->DefaultValue = FString::FromInt(SubNodeIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 UAnimBlueprintExtension_StateMachine::ExpandGraphAndProcessNodes(UEdGraph* SourceGraph, UAnimGraphNode_Base* SourceRootNode, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData, UAnimStateTransitionNode* TransitionNode, TArray<UEdGraphNode*>* ClonedNodes)
|
|
{
|
|
// Clone the nodes from the source graph
|
|
// Note that we outer this graph to the ConsolidatedEventGraph to allow ExpansionStep to
|
|
// correctly retrieve the context for any expanded function calls (custom make/break structs etc.)
|
|
UEdGraph* ClonedGraph = FEdGraphUtilities::CloneGraph(SourceGraph, InCompilationContext.GetConsolidatedEventGraph(), &InCompilationContext.GetMessageLog(), true);
|
|
|
|
// Grab all the animation nodes and find the corresponding root node in the cloned set
|
|
UAnimGraphNode_Base* TargetRootNode = nullptr;
|
|
TArray<UAnimGraphNode_Base*> AnimNodeList;
|
|
TArray<UK2Node_TransitionRuleGetter*> Getters;
|
|
TArray<UK2Node_AnimGetter*> AnimGetterNodes;
|
|
|
|
for (auto NodeIt = ClonedGraph->Nodes.CreateIterator(); NodeIt; ++NodeIt)
|
|
{
|
|
UEdGraphNode* Node = *NodeIt;
|
|
|
|
if (UK2Node_TransitionRuleGetter* GetterNode = Cast<UK2Node_TransitionRuleGetter>(Node))
|
|
{
|
|
Getters.Add(GetterNode);
|
|
}
|
|
else if(UK2Node_AnimGetter* NewGetterNode = Cast<UK2Node_AnimGetter>(Node))
|
|
{
|
|
AnimGetterNodes.Add(NewGetterNode);
|
|
}
|
|
else if (UAnimGraphNode_Base* TestNode = Cast<UAnimGraphNode_Base>(Node))
|
|
{
|
|
AnimNodeList.Add(TestNode);
|
|
|
|
//@TODO: There ought to be a better way to determine this
|
|
if (InCompilationContext.GetMessageLog().FindSourceObject(TestNode) == InCompilationContext.GetMessageLog().FindSourceObject(SourceRootNode))
|
|
{
|
|
TargetRootNode = TestNode;
|
|
}
|
|
}
|
|
|
|
if (ClonedNodes != NULL)
|
|
{
|
|
ClonedNodes->Add(Node);
|
|
}
|
|
}
|
|
check(TargetRootNode);
|
|
|
|
// Run another expansion pass to catch the graph we just added (this is slightly wasteful
|
|
InCompilationContext.ExpansionStep(ClonedGraph, false);
|
|
|
|
// Validate graph now we have expanded/pruned
|
|
InCompilationContext.ValidateGraphIsWellFormed(ClonedGraph);
|
|
|
|
// Move the cloned nodes into the consolidated event graph
|
|
const bool bIsLoading = InCompilationContext.GetBlueprint()->bIsRegeneratingOnLoad || IsAsyncLoading();
|
|
const bool bIsCompiling = InCompilationContext.GetBlueprint()->bBeingCompiled;
|
|
ClonedGraph->MoveNodesToAnotherGraph(InCompilationContext.GetConsolidatedEventGraph(), bIsLoading, bIsCompiling);
|
|
|
|
// Process any animation nodes
|
|
{
|
|
TArray<UAnimGraphNode_Base*> RootSet;
|
|
RootSet.Add(TargetRootNode);
|
|
|
|
InCompilationContext.PruneIsolatedAnimationNodes(RootSet, AnimNodeList);
|
|
|
|
InCompilationContext.ProcessAnimationNodes(AnimNodeList);
|
|
}
|
|
|
|
// Process the getter nodes in the graph if there were any
|
|
for (auto GetterIt = Getters.CreateIterator(); GetterIt; ++GetterIt)
|
|
{
|
|
ProcessTransitionGetter(*GetterIt, TransitionNode, InCompilationContext, OutCompiledData);
|
|
}
|
|
|
|
// Wire anim getter nodes
|
|
for(UK2Node_AnimGetter* GetterNode : AnimGetterNodes)
|
|
{
|
|
FoundGetterNodes.Add(GetterNode);
|
|
}
|
|
|
|
// Returns the index of the processed cloned version of SourceRootNode
|
|
return InCompilationContext.GetAllocationIndexOfNode(TargetRootNode);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |