2020-09-01 14:07:48 -04:00
// Copyright Epic Games, Inc. All Rights Reserved.
2021-04-22 04:57:09 -04:00
# include "AnimBlueprintExtension_Base.h"
2020-09-01 14:07:48 -04:00
# include "AnimGraphNode_Base.h"
# include "AnimationGraphSchema.h"
# include "AnimGraphNode_CustomProperty.h"
# include "K2Node_CustomEvent.h"
# include "K2Node_VariableSet.h"
# include "K2Node_StructMemberSet.h"
# include "K2Node_StructMemberGet.h"
# include "K2Node_CallArrayFunction.h"
# include "Kismet/KismetArrayLibrary.h"
# include "K2Node_Knot.h"
# include "String/ParseTokens.h"
# include "K2Node_VariableGet.h"
# include "K2Node_BreakStruct.h"
# include "K2Node_MakeStruct.h"
# include "Kismet/KismetMathLibrary.h"
# include "K2Node_TransitionRuleGetter.h"
# include "Animation/AnimNode_LinkedAnimGraph.h"
# include "Kismet2/BlueprintEditorUtils.h"
2020-09-09 08:32:25 -04:00
# include "PropertyAccessCompilerHandler.h"
2020-09-01 14:07:48 -04:00
# include "IPropertyAccessEditor.h"
# include "IPropertyAccessCompiler.h"
2020-09-09 08:32:25 -04:00
# include "IAnimBlueprintGeneratedClassCompiledData.h"
# include "IAnimBlueprintCompilerCreationContext.h"
# include "IAnimBlueprintCompilationContext.h"
# include "IAnimBlueprintCopyTermDefaultsContext.h"
# include "IAnimBlueprintPostExpansionStepContext.h"
# include "IAnimBlueprintCompilationBracketContext.h"
2021-04-22 04:57:09 -04:00
# include "Features/IModularFeatures.h"
2020-09-01 14:07:48 -04:00
2021-04-22 04:57:09 -04:00
# define LOCTEXT_NAMESPACE "AnimBlueprintExtension_Base"
2020-09-01 14:07:48 -04:00
2021-04-22 04:57:09 -04:00
DECLARE_CYCLE_STAT ( TEXT ( " Create Evaluation Handler " ) , EAnimBlueprintCompilerStats_CreateEvaluationHandler , STATGROUP_KismetCompiler )
DECLARE_CYCLE_STAT ( TEXT ( " Create Evaluation Handler - Node Properties " ) , EAnimBlueprintCompilerStats_CreateEvaluationHandler_NodeProperties , STATGROUP_KismetCompiler )
DECLARE_CYCLE_STAT ( TEXT ( " Create Evaluation Handler - Create Assignment Node " ) , EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateAssignmentNode , STATGROUP_KismetCompiler )
DECLARE_CYCLE_STAT ( TEXT ( " Create Evaluation Handler - Create Instance Assignment Node " ) , EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateInstanceAssignmentNode , STATGROUP_KismetCompiler )
DECLARE_CYCLE_STAT ( TEXT ( " Create Evaluation Handler - Create Instance Assignment Node - Build Property List " ) , EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateInstanceAssignmentNode_BuildPropertyList , STATGROUP_KismetCompiler )
DECLARE_CYCLE_STAT ( TEXT ( " Create Evaluation Handler - Create Instance Assignment Node - Create Visible Pins " ) , EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateInstanceAssignmentNode_CreateVisiblePins , STATGROUP_KismetCompiler )
2020-09-09 08:32:25 -04:00
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : HandleCopyTermDefaultsToDefaultObject ( UObject * InDefaultObject , IAnimBlueprintCopyTermDefaultsContext & InCompilationContext , IAnimBlueprintExtensionCopyTermDefaultsContext & InPerExtensionContext )
2020-09-01 14:07:48 -04:00
{
UAnimInstance * DefaultAnimInstance = Cast < UAnimInstance > ( InDefaultObject ) ;
if ( DefaultAnimInstance )
{
for ( const FEvaluationHandlerRecord & EvaluationHandler : ValidEvaluationHandlerList )
{
2021-05-24 04:47:52 -04:00
if ( EvaluationHandler . AnimGraphNode )
2020-09-01 14:07:48 -04:00
{
2021-05-24 04:47:52 -04:00
UAnimGraphNode_Base * Node = CastChecked < UAnimGraphNode_Base > ( EvaluationHandler . AnimGraphNode ) ;
UAnimGraphNode_Base * TrueNode = InCompilationContext . GetMessageLog ( ) . FindSourceObjectTypeChecked < UAnimGraphNode_Base > ( Node ) ;
if ( EvaluationHandler . EvaluationHandlerIdx ! = INDEX_NONE & & EvaluationHandler . ServicedProperties . Num ( ) > 0 )
2020-09-01 14:07:48 -04:00
{
2021-05-24 04:47:52 -04:00
const FAnimNodeSinglePropertyHandler & Handler = EvaluationHandler . ServicedProperties . CreateConstIterator ( ) - > Value ;
check ( Handler . CopyRecords . Num ( ) > 0 ) ;
2020-09-01 14:07:48 -04:00
2021-04-22 04:57:09 -04:00
const FExposedValueHandler & ValueHandler = Subsystem . ExposedValueHandlers [ EvaluationHandler . EvaluationHandlerIdx ] ;
2020-09-01 14:07:48 -04:00
TrueNode - > BlueprintUsage = ValueHandler . BoundFunction ! = NAME_None ? EBlueprintUsage : : UsesBlueprint : EBlueprintUsage : : DoesNotUseBlueprint ;
# if WITH_EDITORONLY_DATA // ANIMINST_PostCompileValidation
2020-09-09 08:32:25 -04:00
const bool bWarnAboutBlueprintUsage = InCompilationContext . GetAnimBlueprint ( ) - > bWarnAboutBlueprintUsage | | DefaultAnimInstance - > PCV_ShouldWarnAboutNodesNotUsingFastPath ( ) ;
2020-09-01 14:07:48 -04:00
const bool bNotifyAboutBlueprintUsage = DefaultAnimInstance - > PCV_ShouldNotifyAboutNodesNotUsingFastPath ( ) ;
# else
2020-09-09 08:32:25 -04:00
const bool bWarnAboutBlueprintUsage = InCompilationContext . GetAnimBlueprint ( ) - > bWarnAboutBlueprintUsage ;
2020-09-01 14:07:48 -04:00
const bool bNotifyAboutBlueprintUsage = false ;
# endif
if ( ( TrueNode - > BlueprintUsage = = EBlueprintUsage : : UsesBlueprint ) & & ( bWarnAboutBlueprintUsage | | bNotifyAboutBlueprintUsage ) )
{
const FString MessageString = LOCTEXT ( " BlueprintUsageWarning " , " Node @@ uses Blueprint to update its values, access member variables directly or use a constant value for better performance. " ) . ToString ( ) ;
if ( bWarnAboutBlueprintUsage )
{
2020-09-09 08:32:25 -04:00
InCompilationContext . GetMessageLog ( ) . Warning ( * MessageString , Node ) ;
2020-09-01 14:07:48 -04:00
}
else
{
2020-09-09 08:32:25 -04:00
InCompilationContext . GetMessageLog ( ) . Note ( * MessageString , Node ) ;
2020-09-01 14:07:48 -04:00
}
}
}
}
}
}
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : HandlePostExpansionStep ( const UEdGraph * InGraph , IAnimBlueprintPostExpansionStepContext & InCompilationContext , IAnimBlueprintGeneratedClassCompiledData & OutCompiledData )
2020-09-01 14:07:48 -04:00
{
2020-09-09 08:32:25 -04:00
UEdGraph * ConsolidatedEventGraph = InCompilationContext . GetConsolidatedEventGraph ( ) ;
2020-09-01 14:07:48 -04:00
if ( InGraph = = ConsolidatedEventGraph )
{
// Skip fast-path generation if the property access system is unavailable.
// Note that this wont prevent property access 'binding' copy records from running, only
// old-style 'fast-path' records that are derived from BP pure chains
2021-11-18 14:37:34 -05:00
if ( IModularFeatures : : Get ( ) . IsModularFeatureAvailable ( " PropertyAccessEditor " ) )
2020-09-01 14:07:48 -04:00
{
for ( FEvaluationHandlerRecord & HandlerRecord : ValidEvaluationHandlerList )
{
2021-04-22 04:57:09 -04:00
HandlerRecord . BuildFastPathCopyRecords ( InCompilationContext ) ;
2020-09-01 14:07:48 -04:00
if ( HandlerRecord . IsFastPath ( ) )
{
for ( UEdGraphNode * CustomEventNode : HandlerRecord . CustomEventNodes )
{
// Remove custom event nodes as we dont need it any more
ConsolidatedEventGraph - > RemoveNode ( CustomEventNode ) ;
}
}
}
}
// Cull out all anim nodes as they dont contribute to execution at all
for ( int32 NodeIndex = 0 ; NodeIndex < ConsolidatedEventGraph - > Nodes . Num ( ) ; + + NodeIndex )
{
if ( UAnimGraphNode_Base * Node = Cast < UAnimGraphNode_Base > ( ConsolidatedEventGraph - > Nodes [ NodeIndex ] ) )
{
Node - > BreakAllNodeLinks ( ) ;
ConsolidatedEventGraph - > Nodes . RemoveAtSwap ( NodeIndex ) ;
- - NodeIndex ;
}
}
}
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : PatchEvaluationHandlers ( IAnimBlueprintCompilationBracketContext & InCompilationContext , IAnimBlueprintGeneratedClassCompiledData & OutCompiledData )
2020-09-01 14:07:48 -04:00
{
2021-04-22 04:57:09 -04:00
TArray < FExposedValueHandler > & ExposedValueHandlers = Subsystem . ExposedValueHandlers ;
// Exposed value handlers indices must match the index of anim node properties,
// so we iterate over anim node properties here when patching up
const int32 NumAllocatedNodes = InCompilationContext . GetAllocatedAnimNodeIndices ( ) . Num ( ) ;
ExposedValueHandlers . SetNum ( NumAllocatedNodes ) ;
for ( const TPair < UAnimGraphNode_Base * , int32 > & GraphNodePair : InCompilationContext . GetAllocatedAnimNodeIndices ( ) )
{
if ( int32 * EvaluationHandlerIndexPtr = ValidEvaluationHandlerMap . Find ( GraphNodePair . Key ) )
{
// Indices here are in reverse order with respect to iterated properties as properties are prepended to the linked list when they are added
const int32 NodePropertyIndex = NumAllocatedNodes - 1 - GraphNodePair . Value ;
FEvaluationHandlerRecord & EvaluationHandlerRecord = ValidEvaluationHandlerList [ * EvaluationHandlerIndexPtr ] ;
EvaluationHandlerRecord . EvaluationHandlerIdx = NodePropertyIndex ;
EvaluationHandlerRecord . PatchFunctionNameAndCopyRecordsInto ( ExposedValueHandlers [ NodePropertyIndex ] ) ;
}
}
}
void UAnimBlueprintExtension_Base : : HandleStartCompilingClass ( const UClass * InClass , IAnimBlueprintCompilationBracketContext & InCompilationContext , IAnimBlueprintGeneratedClassCompiledData & OutCompiledData )
{
PerNodeStructEvalHandlers . Empty ( ) ;
ValidEvaluationHandlerList . Empty ( ) ;
ValidEvaluationHandlerMap . Empty ( ) ;
HandlerFunctionNames . Empty ( ) ;
2021-04-22 15:39:05 -04:00
PreLibraryCompiledDelegateHandle . Reset ( ) ;
PostLibraryCompiledDelegateHandle . Reset ( ) ;
2021-04-22 04:57:09 -04:00
UAnimBlueprintExtension_PropertyAccess * PropertyAccessExtension = UAnimBlueprintExtension : : GetExtension < UAnimBlueprintExtension_PropertyAccess > ( GetAnimBlueprint ( ) ) ;
if ( PropertyAccessExtension )
2020-09-01 14:07:48 -04:00
{
2021-04-22 15:39:05 -04:00
PreLibraryCompiledDelegateHandle = PropertyAccessExtension - > OnPreLibraryCompiled ( ) . AddLambda ( [ this , PropertyAccessExtension , InClass ] ( )
2020-09-01 14:07:48 -04:00
{
2021-04-22 15:39:05 -04:00
if ( IModularFeatures : : Get ( ) . IsModularFeatureAvailable ( " PropertyAccessEditor " ) )
2020-09-01 14:07:48 -04:00
{
2021-04-22 15:39:05 -04:00
IPropertyAccessEditor & PropertyAccessEditor = IModularFeatures : : Get ( ) . GetModularFeature < IPropertyAccessEditor > ( " PropertyAccessEditor " ) ;
2020-09-01 14:07:48 -04:00
2021-04-22 15:39:05 -04:00
// Build the classes property access library before the library is compiled
2020-09-01 14:07:48 -04:00
for ( FEvaluationHandlerRecord & HandlerRecord : ValidEvaluationHandlerList )
{
for ( TPair < FName , FAnimNodeSinglePropertyHandler > & PropertyHandler : HandlerRecord . ServicedProperties )
{
2021-04-22 15:39:05 -04:00
for ( FPropertyCopyRecord & Record : PropertyHandler . Value . CopyRecords )
2020-09-01 14:07:48 -04:00
{
2021-04-22 15:39:05 -04:00
if ( Record . IsFastPath ( ) )
2020-09-01 14:07:48 -04:00
{
2021-05-24 04:47:52 -04:00
Record . LibraryHandle = PropertyAccessExtension - > AddCopy ( Record . SourcePropertyPath , Record . DestPropertyPath , Record . BindingContextId , HandlerRecord . AnimGraphNode ) ;
2020-09-01 14:07:48 -04:00
}
}
}
}
2021-04-22 15:39:05 -04:00
}
2021-04-22 04:57:09 -04:00
2021-04-22 15:39:05 -04:00
PropertyAccessExtension - > OnPreLibraryCompiled ( ) . Remove ( PreLibraryCompiledDelegateHandle ) ;
} ) ;
2021-04-22 04:57:09 -04:00
2021-04-22 15:39:05 -04:00
PostLibraryCompiledDelegateHandle = PropertyAccessExtension - > OnPostLibraryCompiled ( ) . AddLambda ( [ this , PropertyAccessExtension ] ( IAnimBlueprintCompilationBracketContext & InCompilationContext , IAnimBlueprintGeneratedClassCompiledData & OutCompiledData )
{
for ( FEvaluationHandlerRecord & HandlerRecord : ValidEvaluationHandlerList )
{
2021-05-24 04:47:52 -04:00
UAnimGraphNode_Base * OriginalNode = Cast < UAnimGraphNode_Base > ( InCompilationContext . GetMessageLog ( ) . FindSourceObject ( HandlerRecord . AnimGraphNode ) ) ;
2021-04-22 15:39:05 -04:00
// Map global copy index to batched indices
for ( TPair < FName , FAnimNodeSinglePropertyHandler > & PropertyHandler : HandlerRecord . ServicedProperties )
{
for ( FPropertyCopyRecord & CopyRecord : PropertyHandler . Value . CopyRecords )
{
if ( CopyRecord . IsFastPath ( ) )
{
2021-05-24 04:47:52 -04:00
CopyRecord . LibraryCompiledHandle = PropertyAccessExtension - > GetCompiledHandle ( CopyRecord . LibraryHandle ) ;
2021-10-12 21:21:22 -04:00
// Push compiled desc back to original node for feedback
FName BindingName = CopyRecord . DestProperty - > GetFName ( ) ;
if ( CopyRecord . DestArrayIndex ! = INDEX_NONE )
{
BindingName . SetNumber ( CopyRecord . DestArrayIndex + 1 ) ;
}
if ( FAnimGraphNodePropertyBinding * Binding = OriginalNode - > PropertyBindings . Find ( BindingName ) )
2021-05-24 04:47:52 -04:00
{
if ( CopyRecord . LibraryCompiledHandle . IsValid ( ) )
{
Binding - > CompiledContext = UAnimBlueprintExtension_PropertyAccess : : GetCompiledHandleContext ( CopyRecord . LibraryCompiledHandle ) ;
Binding - > CompiledContextDesc = UAnimBlueprintExtension_PropertyAccess : : GetCompiledHandleContextDesc ( CopyRecord . LibraryCompiledHandle ) ;
}
else
{
Binding - > CompiledContext = FText : : GetEmpty ( ) ;
Binding - > CompiledContextDesc = FText : : GetEmpty ( ) ;
}
}
2021-04-22 15:39:05 -04:00
}
}
}
}
PatchEvaluationHandlers ( InCompilationContext , OutCompiledData ) ;
PropertyAccessExtension - > OnPostLibraryCompiled ( ) . Remove ( PostLibraryCompiledDelegateHandle ) ;
} ) ;
2020-09-01 14:07:48 -04:00
}
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : HandleFinishCompilingClass ( const UClass * InClass , IAnimBlueprintCompilationBracketContext & InCompilationContext , IAnimBlueprintGeneratedClassCompiledData & OutCompiledData )
2020-09-01 14:07:48 -04:00
{
2021-04-22 04:57:09 -04:00
UAnimBlueprintExtension_PropertyAccess * PropertyAccessExtension = UAnimBlueprintExtension : : GetExtension < UAnimBlueprintExtension_PropertyAccess > ( GetAnimBlueprint ( ) ) ;
if ( PropertyAccessExtension = = nullptr )
2020-09-01 14:07:48 -04:00
{
// Without the property access system we need to patch generated function names here
2021-04-22 04:57:09 -04:00
PatchEvaluationHandlers ( InCompilationContext , OutCompiledData ) ;
2020-09-01 14:07:48 -04:00
}
}
2021-05-24 04:47:52 -04:00
void UAnimBlueprintExtension_Base : : ProcessNodePins ( UAnimGraphNode_Base * InNode , IAnimBlueprintCompilationContext & InCompilationContext , IAnimBlueprintGeneratedClassCompiledData & OutCompiledData )
2020-09-01 14:07:48 -04:00
{
const UAnimationGraphSchema * AnimGraphDefaultSchema = GetDefault < UAnimationGraphSchema > ( ) ;
2020-09-09 08:32:25 -04:00
FStructProperty * NodeProperty = CastFieldChecked < FStructProperty > ( InCompilationContext . GetAllocatedPropertiesByNode ( ) . FindChecked ( InNode ) ) ;
2020-09-01 14:07:48 -04:00
for ( auto SourcePinIt = InNode - > Pins . CreateIterator ( ) ; SourcePinIt ; + + SourcePinIt )
{
UEdGraphPin * SourcePin = * SourcePinIt ;
bool bConsumed = false ;
// Register pose links for future use
if ( ( SourcePin - > Direction = = EGPD_Input ) & & ( AnimGraphDefaultSchema - > IsPosePin ( SourcePin - > PinType ) ) )
{
// Input pose pin, going to need to be linked up
FPoseLinkMappingRecord LinkRecord = InNode - > GetLinkIDLocation ( NodeProperty - > Struct , SourcePin ) ;
if ( LinkRecord . IsValid ( ) )
{
2020-09-09 08:32:25 -04:00
InCompilationContext . AddPoseLinkMappingRecord ( LinkRecord ) ;
2020-09-01 14:07:48 -04:00
bConsumed = true ;
}
}
2021-05-24 04:47:52 -04:00
else if ( ! InNode - > ShouldCreateStructEvalHandlers ( ) )
{
bConsumed = true ;
}
2020-09-01 14:07:48 -04:00
else
{
2021-05-24 04:47:52 -04:00
FEvaluationHandlerRecord & EvalHandler = PerNodeStructEvalHandlers . FindOrAdd ( InNode ) ;
2020-09-01 14:07:48 -04:00
// The property source for our data, either the struct property for an anim node, or the
// owning anim instance if using a linked instance node.
FProperty * SourcePinProperty = nullptr ;
int32 SourceArrayIndex = INDEX_NONE ;
bool bInstancePropertyExists = false ;
// We have special handling below if we're targeting a linked instance instead of our own instance properties
UAnimGraphNode_CustomProperty * CustomPropertyNode = Cast < UAnimGraphNode_CustomProperty > ( InNode ) ;
InNode - > GetPinAssociatedProperty ( NodeProperty - > Struct , SourcePin , /*out*/ SourcePinProperty , /*out*/ SourceArrayIndex ) ;
// Does this pin have an associated evaluation handler?
if ( ! SourcePinProperty & & CustomPropertyNode )
{
// Custom property nodes use instance properties not node properties as they aren't UObjects
// and we can't store non-native properties there
2020-09-09 08:32:25 -04:00
CustomPropertyNode - > GetInstancePinProperty ( InCompilationContext , SourcePin , SourcePinProperty ) ;
2020-09-01 14:07:48 -04:00
bInstancePropertyExists = true ;
}
if ( SourcePinProperty ! = NULL )
{
if ( SourcePin - > LinkedTo . Num ( ) = = 0 )
{
// Literal that can be pushed into the CDO instead of re-evaluated every frame
bConsumed = true ;
}
else
{
// Dynamic value that needs to be wired up and evaluated each frame
const FString & EvaluationHandlerStr = SourcePinProperty - > GetMetaData ( AnimGraphDefaultSchema - > NAME_OnEvaluate ) ;
FName EvaluationHandlerName ( * EvaluationHandlerStr ) ;
if ( EvaluationHandlerName ! = NAME_None )
{
// warn that NAME_OnEvaluate is deprecated:
2020-09-09 08:32:25 -04:00
InCompilationContext . GetMessageLog ( ) . Warning ( * LOCTEXT ( " OnEvaluateDeprecated " , " OnEvaluate meta data is deprecated, found on @@ " ) . ToString ( ) , SourcePinProperty ) ;
2020-09-01 14:07:48 -04:00
}
ensure ( EvalHandler . NodeVariableProperty = = nullptr | | EvalHandler . NodeVariableProperty = = NodeProperty ) ;
EvalHandler . AnimGraphNode = InNode ;
EvalHandler . NodeVariableProperty = NodeProperty ;
EvalHandler . RegisterPin ( SourcePin , SourcePinProperty , SourceArrayIndex ) ;
// if it's not instance property, ensure we mark it
EvalHandler . bServicesNodeProperties = EvalHandler . bServicesNodeProperties | ! bInstancePropertyExists ;
if ( CustomPropertyNode )
{
EvalHandler . bServicesInstanceProperties = EvalHandler . bServicesInstanceProperties | bInstancePropertyExists ;
FAnimNodeSinglePropertyHandler * SinglePropHandler = EvalHandler . ServicedProperties . Find ( SourcePinProperty - > GetFName ( ) ) ;
check ( SinglePropHandler ) ; // Should have been added in RegisterPin
// Flag that the target property is actually on the instance class and not the node
SinglePropHandler - > bInstanceIsTarget = bInstancePropertyExists ;
}
bConsumed = true ;
}
2020-09-09 08:32:25 -04:00
UEdGraphPin * TrueSourcePin = InCompilationContext . GetMessageLog ( ) . FindSourcePin ( SourcePin ) ;
2020-09-01 14:07:48 -04:00
if ( TrueSourcePin )
{
2020-09-09 08:32:25 -04:00
OutCompiledData . GetBlueprintDebugData ( ) . RegisterClassPropertyAssociation ( TrueSourcePin , SourcePinProperty ) ;
2020-09-01 14:07:48 -04:00
}
}
}
if ( ! bConsumed & & ( SourcePin - > Direction = = EGPD_Input ) )
{
//@TODO: ANIMREFACTOR: It's probably OK to have certain pins ignored eventually, but this is very helpful during development
2020-09-09 08:32:25 -04:00
InCompilationContext . GetMessageLog ( ) . Note ( TEXT ( " @@ was visible but ignored " ) , SourcePin ) ;
2020-09-01 14:07:48 -04:00
}
}
// Add any property bindings
for ( const TPair < FName , FAnimGraphNodePropertyBinding > & PropertyBinding : InNode - > PropertyBindings )
{
if ( PropertyBinding . Value . bIsBound )
{
2021-05-24 04:47:52 -04:00
FEvaluationHandlerRecord & EvalHandler = PerNodeStructEvalHandlers . FindOrAdd ( InNode ) ;
2020-09-01 14:07:48 -04:00
EvalHandler . AnimGraphNode = InNode ;
2021-05-24 04:47:52 -04:00
// for array properties we need to account for the extra FName number
FName ComparisonName = PropertyBinding . Key ;
ComparisonName . SetNumber ( 0 ) ;
if ( FProperty * Property = FindFProperty < FProperty > ( NodeProperty - > Struct , ComparisonName ) )
2020-09-01 14:07:48 -04:00
{
2021-10-12 21:21:22 -04:00
EvalHandler . NodeVariableProperty = NodeProperty ;
EvalHandler . bServicesNodeProperties = true ;
2020-09-01 14:07:48 -04:00
EvalHandler . RegisterPropertyBinding ( Property , PropertyBinding . Value ) ;
}
2021-10-12 21:21:22 -04:00
else if ( FProperty * ClassProperty = FindFProperty < FProperty > ( InCompilationContext . GetBlueprint ( ) - > SkeletonGeneratedClass , PropertyBinding . Value . PropertyName ) )
{
EvalHandler . NodeVariableProperty = NodeProperty ;
EvalHandler . bServicesInstanceProperties = true ;
EvalHandler . RegisterPropertyBinding ( ClassProperty , PropertyBinding . Value ) ;
}
2020-09-01 14:07:48 -04:00
else
{
2021-05-24 04:47:52 -04:00
InCompilationContext . GetMessageLog ( ) . Warning ( * FString : : Printf ( TEXT ( " ICE: @@ Failed to find a property '%s' " ) , * ComparisonName . ToString ( ) ) , InNode ) ;
2020-09-01 14:07:48 -04:00
}
}
}
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : CreateEvaluationHandlerForNode ( IAnimBlueprintCompilationContext & InCompilationContext , UAnimGraphNode_Base * InNode )
2020-09-01 14:07:48 -04:00
{
if ( FEvaluationHandlerRecord * RecordPtr = PerNodeStructEvalHandlers . Find ( InNode ) )
{
// Generate a new event to update the value of these properties
FEvaluationHandlerRecord & Record = * RecordPtr ;
if ( Record . NodeVariableProperty )
{
2020-09-09 08:32:25 -04:00
CreateEvaluationHandler ( InCompilationContext , InNode , Record ) ;
2020-09-01 14:07:48 -04:00
2021-04-22 04:57:09 -04:00
RedirectPropertyAccesses ( InCompilationContext , InNode , Record ) ;
2020-09-01 14:07:48 -04:00
int32 NewIndex = ValidEvaluationHandlerList . Add ( Record ) ;
ValidEvaluationHandlerMap . Add ( InNode , NewIndex ) ;
}
}
}
2021-04-22 04:57:09 -04:00
// Optional pin manager used to optimize the creation of internal struct member set nodes
struct FInternalOptionalPinManager : public FOptionalPinManager
2020-09-01 14:07:48 -04:00
{
2021-04-22 04:57:09 -04:00
FInternalOptionalPinManager ( UAnimGraphNode_Base * InNode , FStructProperty * InNodeProperty , IAnimBlueprintCompilationContext & InCompilationContext )
: Node ( InNode )
, NodeProperty ( InNodeProperty )
, CompilationContext ( InCompilationContext )
{ }
void BuildPropertyList ( TArray < FOptionalPinFromProperty > & Properties , UStruct * SourceStruct )
{
// Build optional pins for all properties
for ( TFieldIterator < FProperty > It ( SourceStruct ) ; It ; + + It )
{
FOptionalPinFromProperty & OptionalPin = Properties . AddDefaulted_GetRef ( ) ;
OptionalPin . PropertyName = It - > GetFName ( ) ;
}
// Then expose only those that have records for this node
for ( TFieldIterator < FProperty > It ( NodeProperty - > Struct ) ; It ; + + It )
{
if ( const IAnimBlueprintCompilationContext : : FFoldedPropertyRecord * FoldedPropertyRecord = CompilationContext . GetFoldedPropertyRecord ( Node , It - > GetFName ( ) ) )
{
2021-10-12 21:21:22 -04:00
// Dont expose array properties here - they are handled by a struct member get-by-ref
if ( ! FoldedPropertyRecord - > bIsOnClass & & ! FoldedPropertyRecord - > GeneratedProperty - > IsA < FArrayProperty > ( ) )
2021-04-22 04:57:09 -04:00
{
FOptionalPinFromProperty & OptionalPin = Properties [ Properties . Num ( ) - 1 - FoldedPropertyRecord - > PropertyIndex ] ;
check ( OptionalPin . PropertyName = = FoldedPropertyRecord - > GeneratedProperty - > GetFName ( ) ) ;
OptionalPin . bShowPin = true ;
}
}
}
}
// Duplicated & re-worked from base class (because we are never re-creating) to optimize our case
void CreateVisiblePinsEx ( TArray < FOptionalPinFromProperty > & Properties , UStruct * SourceStruct , EEdGraphPinDirection Direction , UK2Node * TargetNode )
{
const UEdGraphSchema_K2 * Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
for ( FOptionalPinFromProperty & PropertyEntry : Properties )
{
if ( PropertyEntry . bShowPin )
{
if ( FProperty * OuterProperty = FindFieldChecked < FProperty > ( SourceStruct , PropertyEntry . PropertyName ) )
{
// Not an array property
FEdGraphPinType PinType ;
if ( Schema - > ConvertPropertyToPinType ( OuterProperty , /*out*/ PinType ) )
{
// Create the pin
const FName PinName = PropertyEntry . PropertyName ;
UEdGraphPin * NewPin = TargetNode - > CreatePin ( Direction , PinType , PinName ) ;
NewPin - > PinFriendlyName = FText : : FromString ( PropertyEntry . PropertyFriendlyName . IsEmpty ( ) ? PinName . ToString ( ) : PropertyEntry . PropertyFriendlyName ) ;
NewPin - > bNotConnectable = ! PropertyEntry . bIsSetValuePinVisible ;
NewPin - > bDefaultValueIsIgnored = ! PropertyEntry . bIsSetValuePinVisible ;
Schema - > ConstructBasicPinTooltip ( * NewPin , PropertyEntry . PropertyTooltip , NewPin - > PinToolTip ) ;
}
}
}
}
}
UAnimGraphNode_Base * Node ;
FStructProperty * NodeProperty ;
IAnimBlueprintCompilationContext & CompilationContext ;
} ;
void UAnimBlueprintExtension_Base : : CreateEvaluationHandler ( IAnimBlueprintCompilationContext & InCompilationContext , UAnimGraphNode_Base * InNode , FEvaluationHandlerRecord & Record )
{
BP_SCOPED_COMPILER_EVENT_STAT ( EAnimBlueprintCompilerStats_CreateEvaluationHandler ) ;
2020-09-01 14:07:48 -04:00
// Shouldn't create a handler if there is nothing to work with
check ( Record . ServicedProperties . Num ( ) > 0 ) ;
check ( Record . NodeVariableProperty ! = NULL ) ;
const UEdGraphSchema_K2 * K2Schema = GetDefault < UEdGraphSchema_K2 > ( ) ;
const UAnimationGraphSchema * AnimGraphDefaultSchema = GetDefault < UAnimationGraphSchema > ( ) ;
// Use the node GUID for a stable name across compiles
FString FunctionName = FString : : Printf ( TEXT ( " %s_%s_%s_%s " ) , * AnimGraphDefaultSchema - > DefaultEvaluationHandlerName . ToString ( ) , * InNode - > GetOuter ( ) - > GetName ( ) , * InNode - > GetClass ( ) - > GetName ( ) , * InNode - > NodeGuid . ToString ( ) ) ;
Record . HandlerFunctionName = FName ( * FunctionName ) ;
// check function name isnt already used (data exists that can contain duplicate GUIDs) and apply a numeric extension until it is unique
int32 ExtensionIndex = 0 ;
FName * ExistingName = HandlerFunctionNames . Find ( Record . HandlerFunctionName ) ;
while ( ExistingName ! = nullptr )
{
FunctionName = FString : : Printf ( TEXT ( " %s_%s_%s_%s_%d " ) , * AnimGraphDefaultSchema - > DefaultEvaluationHandlerName . ToString ( ) , * InNode - > GetOuter ( ) - > GetName ( ) , * InNode - > GetClass ( ) - > GetName ( ) , * InNode - > NodeGuid . ToString ( ) , ExtensionIndex ) ;
Record . HandlerFunctionName = FName ( * FunctionName ) ;
ExistingName = HandlerFunctionNames . Find ( Record . HandlerFunctionName ) ;
ExtensionIndex + + ;
}
HandlerFunctionNames . Add ( Record . HandlerFunctionName ) ;
// Add a custom event in the graph
2020-09-09 08:32:25 -04:00
UK2Node_CustomEvent * CustomEventNode = InCompilationContext . SpawnIntermediateEventNode < UK2Node_CustomEvent > ( InNode , nullptr , InCompilationContext . GetConsolidatedEventGraph ( ) ) ;
2020-09-01 14:07:48 -04:00
CustomEventNode - > bInternalEvent = true ;
CustomEventNode - > CustomFunctionName = Record . HandlerFunctionName ;
CustomEventNode - > AllocateDefaultPins ( ) ;
Record . CustomEventNodes . Add ( CustomEventNode ) ;
// The ExecChain is the current exec output pin in the linear chain
UEdGraphPin * ExecChain = K2Schema - > FindExecutionPin ( * CustomEventNode , EGPD_Output ) ;
if ( Record . bServicesInstanceProperties )
{
// Need to create a variable set call for each serviced property in the handler
for ( TPair < FName , FAnimNodeSinglePropertyHandler > & PropHandlerPair : Record . ServicedProperties )
{
FAnimNodeSinglePropertyHandler & PropHandler = PropHandlerPair . Value ;
FName PropertyName = PropHandlerPair . Key ;
2021-04-22 04:57:09 -04:00
// We only want to deal with instance targets in here
2020-09-01 14:07:48 -04:00
if ( PropHandler . bInstanceIsTarget )
{
for ( FPropertyCopyRecord & CopyRecord : PropHandler . CopyRecords )
{
2021-10-12 21:21:22 -04:00
if ( CopyRecord . DestPin )
2020-09-01 14:07:48 -04:00
{
2021-10-12 21:21:22 -04:00
// New set node for the property
UK2Node_VariableSet * VarAssignNode = InCompilationContext . SpawnIntermediateNode < UK2Node_VariableSet > ( InNode , InCompilationContext . GetConsolidatedEventGraph ( ) ) ;
VarAssignNode - > VariableReference . SetSelfMember ( CopyRecord . DestProperty - > GetFName ( ) ) ;
VarAssignNode - > AllocateDefaultPins ( ) ;
Record . CustomEventNodes . Add ( VarAssignNode ) ;
2020-09-01 14:07:48 -04:00
2021-10-12 21:21:22 -04:00
// Wire up the exec line, and update the end of the chain
UEdGraphPin * ExecVariablesIn = K2Schema - > FindExecutionPin ( * VarAssignNode , EGPD_Input ) ;
ExecChain - > MakeLinkTo ( ExecVariablesIn ) ;
ExecChain = K2Schema - > FindExecutionPin ( * VarAssignNode , EGPD_Output ) ;
// Find the property pin on the set node and configure
for ( UEdGraphPin * TargetPin : VarAssignNode - > Pins )
2020-09-01 14:07:48 -04:00
{
2021-10-12 21:21:22 -04:00
FName PinPropertyName ( TargetPin - > PinName ) ;
2020-09-01 14:07:48 -04:00
2021-10-12 21:21:22 -04:00
if ( PinPropertyName = = PropertyName )
{
// This is us, wire up the variable
UEdGraphPin * DestPin = CopyRecord . DestPin ;
// Copy the data (link up to the source nodes)
TargetPin - > CopyPersistentDataFromOldPin ( * DestPin ) ;
InCompilationContext . GetMessageLog ( ) . NotifyIntermediatePinCreation ( TargetPin , DestPin ) ;
break ;
}
2020-09-01 14:07:48 -04:00
}
}
}
}
}
}
if ( Record . bServicesNodeProperties )
{
2021-04-22 04:57:09 -04:00
BP_SCOPED_COMPILER_EVENT_STAT ( EAnimBlueprintCompilerStats_CreateEvaluationHandler_NodeProperties ) ;
UK2Node_StructMemberSet * AssignmentNode ;
{
BP_SCOPED_COMPILER_EVENT_STAT ( EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateAssignmentNode ) ;
// Create a struct member write node to store the parameters into the animation node
AssignmentNode = InCompilationContext . SpawnIntermediateNode < UK2Node_StructMemberSet > ( InNode , InCompilationContext . GetConsolidatedEventGraph ( ) ) ;
AssignmentNode - > VariableReference . SetSelfMember ( Record . NodeVariableProperty - > GetFName ( ) ) ;
AssignmentNode - > StructType = Record . NodeVariableProperty - > Struct ;
2021-11-18 14:37:34 -05:00
AssignmentNode - > AllocateExecPins ( ) ;
// Simple FOptionalPinManager that exposes all pins. The default used in UK2Node_StructMemberSet::AllocateDefaultPins will hide any that
// are PinHiddenByDefault, so for this instance we dont want that as users may have exposed some pins
struct FAssignmentNodeOptionalPinManager : public FOptionalPinManager
{
// FOptionalPinManager Interface
virtual void GetRecordDefaults ( FProperty * TestProperty , FOptionalPinFromProperty & Record ) const override
{
// Pins are always visible
Record . bCanToggleVisibility = true ;
Record . bShowPin = true ;
}
} ;
FAssignmentNodeOptionalPinManager OptionalPinManager ;
OptionalPinManager . RebuildPropertyList ( AssignmentNode - > ShowPinForProperties , AssignmentNode - > StructType ) ;
OptionalPinManager . CreateVisiblePins ( AssignmentNode - > ShowPinForProperties , AssignmentNode - > StructType , EGPD_Input , AssignmentNode ) ;
2021-04-22 04:57:09 -04:00
Record . CustomEventNodes . Add ( AssignmentNode ) ;
}
2020-09-01 14:07:48 -04:00
2021-04-22 04:57:09 -04:00
// If we have folded properties we will need to set members on the classes generated mutable data block
const FStructProperty * MutableDataProperty = InCompilationContext . GetMutableDataProperty ( ) ;
UK2Node_StructMemberSet * InstanceAssignmentNode = nullptr ;
if ( InCompilationContext . IsAnimGraphNodeFolded ( InNode ) & & MutableDataProperty ! = nullptr )
{
BP_SCOPED_COMPILER_EVENT_STAT ( EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateInstanceAssignmentNode ) ;
2021-05-24 04:47:52 -04:00
InstanceAssignmentNode = InCompilationContext . SpawnIntermediateNode < UK2Node_StructMemberSet > ( InNode , InCompilationContext . GetConsolidatedEventGraph ( ) ) ;
2021-04-22 04:57:09 -04:00
InstanceAssignmentNode - > VariableReference . SetSelfMember ( MutableDataProperty - > GetFName ( ) ) ;
InstanceAssignmentNode - > StructType = MutableDataProperty - > Struct ;
// We build this struct member set node using specialized logic to optimize its creation
// as it can have 1000's of properties harvested from animation nodes
InstanceAssignmentNode - > AllocateExecPins ( ) ;
{
FInternalOptionalPinManager OptionalPinManager ( InNode , InNode - > GetFNodeProperty ( ) , InCompilationContext ) ;
{
BP_SCOPED_COMPILER_EVENT_STAT ( EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateInstanceAssignmentNode_BuildPropertyList ) ;
OptionalPinManager . BuildPropertyList ( InstanceAssignmentNode - > ShowPinForProperties , MutableDataProperty - > Struct ) ;
}
{
BP_SCOPED_COMPILER_EVENT_STAT ( EAnimBlueprintCompilerStats_CreateEvaluationHandler_CreateInstanceAssignmentNode_CreateVisiblePins ) ;
OptionalPinManager . CreateVisiblePinsEx ( InstanceAssignmentNode - > ShowPinForProperties , MutableDataProperty - > Struct , EGPD_Input , InstanceAssignmentNode ) ;
}
}
Record . CustomEventNodes . Add ( InstanceAssignmentNode ) ;
}
2020-09-01 14:07:48 -04:00
// Run thru each property
TSet < FName > PropertiesBeingSet ;
for ( auto TargetPinIt = AssignmentNode - > Pins . CreateIterator ( ) ; TargetPinIt ; + + TargetPinIt )
{
UEdGraphPin * TargetPin = * TargetPinIt ;
FName PropertyName ( TargetPin - > PinName ) ;
// Does it get serviced by this handler?
if ( FAnimNodeSinglePropertyHandler * SourceInfo = Record . ServicedProperties . Find ( PropertyName ) )
{
2021-04-22 04:57:09 -04:00
// Skip if the property is folded, we should have handled it above
const IAnimBlueprintCompilationContext : : FFoldedPropertyRecord * FoldedPropertyRecord = InCompilationContext . GetFoldedPropertyRecord ( InNode , PropertyName ) ;
if ( FoldedPropertyRecord ! = nullptr )
{
2021-10-12 21:21:22 -04:00
// We only support per-instance members here.
if ( ! FoldedPropertyRecord - > bIsOnClass )
{
// We must have created an assignment node for the mutable data block by now
check ( InstanceAssignmentNode ! = nullptr ) ;
2021-04-22 04:57:09 -04:00
2021-10-12 21:21:22 -04:00
// Redirect to the instance's mutable data area assignment node
PropertyName = FoldedPropertyRecord - > GeneratedProperty - > GetFName ( ) ;
2021-04-22 04:57:09 -04:00
2021-10-12 21:21:22 -04:00
// We dont need to handle arrays with a member set, as they use a member get-by-ref
if ( ! TargetPin - > PinType . IsArray ( ) )
{
TargetPin = InstanceAssignmentNode - > FindPinChecked ( FoldedPropertyRecord - > GeneratedProperty - > GetFName ( ) ) ;
}
}
2021-04-22 04:57:09 -04:00
}
2020-09-01 14:07:48 -04:00
if ( TargetPin - > PinType . IsArray ( ) )
{
// Grab the array that we need to set members for
2020-09-09 08:32:25 -04:00
UK2Node_StructMemberGet * FetchArrayNode = InCompilationContext . SpawnIntermediateNode < UK2Node_StructMemberGet > ( InNode , InCompilationContext . GetConsolidatedEventGraph ( ) ) ;
2021-10-12 21:21:22 -04:00
FetchArrayNode - > VariableReference . SetSelfMember ( FoldedPropertyRecord ? MutableDataProperty - > GetFName ( ) : Record . NodeVariableProperty - > GetFName ( ) ) ;
FetchArrayNode - > StructType = FoldedPropertyRecord ? MutableDataProperty - > Struct : Record . NodeVariableProperty - > Struct ;
2020-09-01 14:07:48 -04:00
FetchArrayNode - > AllocatePinsForSingleMemberGet ( PropertyName ) ;
Record . CustomEventNodes . Add ( FetchArrayNode ) ;
UEdGraphPin * ArrayVariableNode = FetchArrayNode - > FindPin ( PropertyName ) ;
if ( SourceInfo - > CopyRecords . Num ( ) > 0 )
{
// Set each element in the array
for ( FPropertyCopyRecord & CopyRecord : SourceInfo - > CopyRecords )
{
int32 ArrayIndex = CopyRecord . DestArrayIndex ;
if ( UEdGraphPin * DestPin = CopyRecord . DestPin )
{
// Create an array element set node
2020-09-09 08:32:25 -04:00
UK2Node_CallArrayFunction * ArrayNode = InCompilationContext . SpawnIntermediateNode < UK2Node_CallArrayFunction > ( InNode , InCompilationContext . GetConsolidatedEventGraph ( ) ) ;
2020-09-01 14:07:48 -04:00
ArrayNode - > FunctionReference . SetExternalMember ( GET_FUNCTION_NAME_CHECKED ( UKismetArrayLibrary , Array_Set ) , UKismetArrayLibrary : : StaticClass ( ) ) ;
ArrayNode - > AllocateDefaultPins ( ) ;
Record . CustomEventNodes . Add ( ArrayNode ) ;
// Connect the execution chain
ExecChain - > MakeLinkTo ( ArrayNode - > GetExecPin ( ) ) ;
ExecChain = ArrayNode - > GetThenPin ( ) ;
// Connect the input array
UEdGraphPin * TargetArrayPin = ArrayNode - > FindPinChecked ( TEXT ( " TargetArray " ) ) ;
TargetArrayPin - > MakeLinkTo ( ArrayVariableNode ) ;
ArrayNode - > PinConnectionListChanged ( TargetArrayPin ) ;
// Set the array index
UEdGraphPin * TargetIndexPin = ArrayNode - > FindPinChecked ( TEXT ( " Index " ) ) ;
TargetIndexPin - > DefaultValue = FString : : FromInt ( ArrayIndex ) ;
// Wire up the data input
UEdGraphPin * TargetItemPin = ArrayNode - > FindPinChecked ( TEXT ( " Item " ) ) ;
TargetItemPin - > CopyPersistentDataFromOldPin ( * DestPin ) ;
2020-09-09 08:32:25 -04:00
InCompilationContext . GetMessageLog ( ) . NotifyIntermediatePinCreation ( TargetItemPin , DestPin ) ;
2020-09-01 14:07:48 -04:00
}
}
}
}
else
{
// Single property
if ( SourceInfo - > CopyRecords . Num ( ) > 0 & & SourceInfo - > CopyRecords [ 0 ] . DestPin ! = nullptr )
{
UEdGraphPin * DestPin = SourceInfo - > CopyRecords [ 0 ] . DestPin ;
2022-03-30 23:02:36 -04:00
PropertiesBeingSet . Add ( PropertyName ) ;
2020-09-01 14:07:48 -04:00
TargetPin - > CopyPersistentDataFromOldPin ( * DestPin ) ;
2020-09-09 08:32:25 -04:00
InCompilationContext . GetMessageLog ( ) . NotifyIntermediatePinCreation ( TargetPin , DestPin ) ;
2020-09-01 14:07:48 -04:00
}
}
}
}
2021-04-22 04:57:09 -04:00
// Remove any unused pins from the assignment nodes to avoid smashing constant values
bool bAnyNodePropertiesSet = false ;
2020-09-01 14:07:48 -04:00
for ( int32 PinIndex = 0 ; PinIndex < AssignmentNode - > ShowPinForProperties . Num ( ) ; + + PinIndex )
{
FOptionalPinFromProperty & TestProperty = AssignmentNode - > ShowPinForProperties [ PinIndex ] ;
TestProperty . bShowPin = PropertiesBeingSet . Contains ( TestProperty . PropertyName ) ;
2021-04-22 04:57:09 -04:00
bAnyNodePropertiesSet | = TestProperty . bShowPin ;
2020-09-01 14:07:48 -04:00
}
2021-04-22 04:57:09 -04:00
if ( bAnyNodePropertiesSet )
{
AssignmentNode - > ReconstructNode ( ) ;
UEdGraphPin * ExecVariablesIn = K2Schema - > FindExecutionPin ( * AssignmentNode , EGPD_Input ) ;
ExecChain - > MakeLinkTo ( ExecVariablesIn ) ;
ExecChain = K2Schema - > FindExecutionPin ( * AssignmentNode , EGPD_Output ) ;
}
if ( InstanceAssignmentNode ! = nullptr )
{
for ( int32 PinIndex = 0 ; PinIndex < InstanceAssignmentNode - > ShowPinForProperties . Num ( ) ; + + PinIndex )
{
FOptionalPinFromProperty & TestProperty = InstanceAssignmentNode - > ShowPinForProperties [ PinIndex ] ;
if ( TestProperty . bShowPin )
{
UEdGraphPin * ExecVariablesIn = K2Schema - > FindExecutionPin ( * InstanceAssignmentNode , EGPD_Input ) ;
ExecChain - > MakeLinkTo ( ExecVariablesIn ) ;
ExecChain = K2Schema - > FindExecutionPin ( * InstanceAssignmentNode , EGPD_Output ) ;
break ;
}
}
}
2020-09-01 14:07:48 -04:00
}
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : RedirectPropertyAccesses ( IAnimBlueprintCompilationContext & InCompilationContext , UAnimGraphNode_Base * InNode , FEvaluationHandlerRecord & InRecord )
{
const FStructProperty * MutableDataProperty = InCompilationContext . GetMutableDataProperty ( ) ;
if ( InCompilationContext . IsAnimGraphNodeFolded ( InNode ) & & MutableDataProperty ! = nullptr )
{
for ( TPair < FName , FAnimNodeSinglePropertyHandler > & NamePropertyPair : InRecord . ServicedProperties )
{
if ( const IAnimBlueprintCompilationContext : : FFoldedPropertyRecord * FoldedPropertyRecord = InCompilationContext . GetFoldedPropertyRecord ( InNode , NamePropertyPair . Key ) )
{
for ( FPropertyCopyRecord & CopyRecord : NamePropertyPair . Value . CopyRecords )
{
if ( CopyRecord . DestPropertyPath . Num ( ) > 1 )
{
// If this record writes to the node, switch it to the mutable data's property instead
if ( CopyRecord . DestPropertyPath [ 0 ] = = InRecord . NodeVariableProperty - > GetName ( ) )
{
CopyRecord . DestPropertyPath [ 0 ] = MutableDataProperty - > GetName ( ) ;
FString DestPropertyPathTail = CopyRecord . DestPropertyPath [ 1 ] ;
FString DestPropertyPathWithoutArray = DestPropertyPathTail ;
FString ArrayIndex ;
int32 ArrayDelim = INDEX_NONE ;
if ( DestPropertyPathTail . FindChar ( TEXT ( ' [ ' ) , ArrayDelim ) )
{
DestPropertyPathWithoutArray = DestPropertyPathTail . Left ( ArrayDelim ) ;
ArrayIndex = DestPropertyPathTail . RightChop ( ArrayDelim ) ;
}
// Switch the destination property from the node's property to the generated one
if ( DestPropertyPathWithoutArray = = FoldedPropertyRecord - > Property - > GetName ( ) )
{
CopyRecord . DestPropertyPath [ 1 ] = FoldedPropertyRecord - > GeneratedProperty - > GetName ( ) + ArrayIndex ;
}
}
}
}
}
}
}
}
void UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : PatchFunctionNameAndCopyRecordsInto ( FExposedValueHandler & Handler ) const
2020-09-01 14:07:48 -04:00
{
Handler . CopyRecords . Empty ( ) ;
2021-05-25 04:35:56 -04:00
for ( const TPair < FName , FAnimNodeSinglePropertyHandler > & ServicedPropPair : ServicedProperties )
2020-09-01 14:07:48 -04:00
{
2021-05-25 04:35:56 -04:00
const FName & PropertyName = ServicedPropPair . Key ;
const FAnimNodeSinglePropertyHandler & PropertyHandler = ServicedPropPair . Value ;
2020-09-01 14:07:48 -04:00
2021-05-25 04:35:56 -04:00
for ( const FPropertyCopyRecord & PropertyCopyRecord : PropertyHandler . CopyRecords )
{
// Only unbatched copies can be processed on a per-node basis
// Skip invalid copy indices as these are usually the result of BP errors/warnings
if ( PropertyCopyRecord . LibraryCompiledHandle . IsValid ( ) & & PropertyCopyRecord . LibraryCompiledHandle . GetBatchId ( ) = = ( int32 ) EAnimPropertyAccessCallSite : : WorkerThread_Unbatched )
2020-09-01 14:07:48 -04:00
{
2021-05-25 04:35:56 -04:00
Handler . CopyRecords . Emplace ( PropertyCopyRecord . LibraryCompiledHandle . GetId ( ) , PropertyCopyRecord . Operation ) ;
2020-09-01 14:07:48 -04:00
}
}
}
2021-05-25 04:35:56 -04:00
if ( ! IsFastPath ( ) )
2020-09-01 14:07:48 -04:00
{
// not all of our pins use copy records so we will need to call our exposed value handler
Handler . BoundFunction = HandlerFunctionName ;
}
}
static UEdGraphPin * FindFirstInputPin ( UEdGraphNode * InNode )
{
const UAnimationGraphSchema * Schema = GetDefault < UAnimationGraphSchema > ( ) ;
for ( UEdGraphPin * Pin : InNode - > Pins )
{
if ( Pin & & Pin - > Direction = = EGPD_Input & & ! Schema - > IsExecPin ( * Pin ) & & ! Schema - > IsSelfPin ( * Pin ) )
{
return Pin ;
}
}
return nullptr ;
}
static bool ForEachInputPin ( UEdGraphNode * InNode , TFunctionRef < bool ( UEdGraphPin * ) > InFunction )
{
const UAnimationGraphSchema * Schema = GetDefault < UAnimationGraphSchema > ( ) ;
bool bResult = false ;
for ( UEdGraphPin * Pin : InNode - > Pins )
{
if ( Pin & & Pin - > Direction = = EGPD_Input & & ! Schema - > IsExecPin ( * Pin ) & & ! Schema - > IsSelfPin ( * Pin ) )
{
bResult | = InFunction ( Pin ) ;
}
}
return bResult ;
}
static UEdGraphNode * FollowKnots ( UEdGraphPin * FromPin , UEdGraphPin * & ToPin )
{
if ( FromPin - > LinkedTo . Num ( ) = = 0 )
{
return nullptr ;
}
UEdGraphPin * LinkedPin = FromPin - > LinkedTo [ 0 ] ;
ToPin = LinkedPin ;
if ( LinkedPin )
{
UEdGraphNode * LinkedNode = LinkedPin - > GetOwningNode ( ) ;
UK2Node_Knot * KnotNode = Cast < UK2Node_Knot > ( LinkedNode ) ;
while ( KnotNode )
{
if ( UEdGraphPin * InputPin = FindFirstInputPin ( KnotNode ) )
{
if ( InputPin - > LinkedTo . Num ( ) > 0 & & InputPin - > LinkedTo [ 0 ] )
{
ToPin = InputPin - > LinkedTo [ 0 ] ;
LinkedNode = InputPin - > LinkedTo [ 0 ] - > GetOwningNode ( ) ;
KnotNode = Cast < UK2Node_Knot > ( LinkedNode ) ;
}
else
{
KnotNode = nullptr ;
}
}
}
return LinkedNode ;
}
return nullptr ;
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : RegisterPin ( UEdGraphPin * DestPin , FProperty * AssociatedProperty , int32 AssociatedPropertyArrayIndex )
2020-09-01 14:07:48 -04:00
{
FAnimNodeSinglePropertyHandler & Handler = ServicedProperties . FindOrAdd ( AssociatedProperty - > GetFName ( ) ) ;
TArray < FString > DestPropertyPath ;
// Prepend the destination property with the node's member property if the property is not on a UClass
if ( Cast < UClass > ( AssociatedProperty - > Owner . ToUObject ( ) ) = = nullptr )
{
DestPropertyPath . Add ( NodeVariableProperty - > GetName ( ) ) ;
}
if ( AssociatedPropertyArrayIndex ! = INDEX_NONE )
{
DestPropertyPath . Add ( FString : : Printf ( TEXT ( " %s[%d] " ) , * AssociatedProperty - > GetName ( ) , AssociatedPropertyArrayIndex ) ) ;
}
else
{
DestPropertyPath . Add ( AssociatedProperty - > GetName ( ) ) ;
}
Handler . CopyRecords . Emplace ( DestPin , AssociatedProperty , AssociatedPropertyArrayIndex , MoveTemp ( DestPropertyPath ) ) ;
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : RegisterPropertyBinding ( FProperty * InProperty , const FAnimGraphNodePropertyBinding & InBinding )
2020-09-01 14:07:48 -04:00
{
FAnimNodeSinglePropertyHandler & Handler = ServicedProperties . FindOrAdd ( InProperty - > GetFName ( ) ) ;
TArray < FString > DestPropertyPath ;
// Prepend the destination property with the node's member property if the property is not on a UClass
if ( Cast < UClass > ( InProperty - > Owner . ToUObject ( ) ) = = nullptr )
{
2021-10-12 21:21:22 -04:00
Handler . bInstanceIsTarget = false ;
2020-09-01 14:07:48 -04:00
DestPropertyPath . Add ( NodeVariableProperty - > GetName ( ) ) ;
}
2021-10-12 21:21:22 -04:00
else
{
Handler . bInstanceIsTarget = true ;
}
2020-09-01 14:07:48 -04:00
2021-05-24 04:47:52 -04:00
if ( InBinding . ArrayIndex ! = INDEX_NONE )
{
DestPropertyPath . Add ( FString : : Printf ( TEXT ( " %s[%d] " ) , * InProperty - > GetName ( ) , InBinding . ArrayIndex ) ) ;
}
else
{
DestPropertyPath . Add ( InProperty - > GetName ( ) ) ;
}
FPropertyCopyRecord & CopyRecord = Handler . CopyRecords . Emplace_GetRef ( InBinding . PropertyPath , DestPropertyPath ) ;
CopyRecord . DestProperty = InProperty ;
CopyRecord . DestArrayIndex = InBinding . ArrayIndex ;
CopyRecord . BindingContextId = InBinding . ContextId ;
2020-09-01 14:07:48 -04:00
}
2021-04-22 04:57:09 -04:00
void UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : BuildFastPathCopyRecords ( IAnimBlueprintPostExpansionStepContext & InCompilationContext )
2020-09-01 14:07:48 -04:00
{
2021-04-22 04:57:09 -04:00
typedef bool ( UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : * GraphCheckerFunc ) ( FCopyRecordGraphCheckContext & , UEdGraphPin * ) ;
2020-09-01 14:07:48 -04:00
GraphCheckerFunc GraphCheckerFuncs [ ] =
{
2021-04-22 04:57:09 -04:00
& UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForSplitPinAccess ,
& UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForVariableGet ,
& UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForLogicalNot ,
& UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForStructMemberAccess ,
& UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForArrayAccess ,
2020-09-01 14:07:48 -04:00
} ;
if ( GetDefault < UEngine > ( ) - > bOptimizeAnimBlueprintMemberVariableAccess )
{
for ( TPair < FName , FAnimNodeSinglePropertyHandler > & ServicedPropPair : ServicedProperties )
{
TArray < FPropertyCopyRecord > AllAdditionalCopyRecords ;
for ( FPropertyCopyRecord & CopyRecord : ServicedPropPair . Value . CopyRecords )
{
if ( CopyRecord . SourcePropertyPath . Num ( ) = = 0 )
{
TArray < FPropertyCopyRecord > AdditionalCopyRecords ;
2020-09-24 00:43:27 -04:00
FCopyRecordGraphCheckContext Context ( CopyRecord , AdditionalCopyRecords , InCompilationContext . GetMessageLog ( ) ) ;
2020-09-01 14:07:48 -04:00
for ( GraphCheckerFunc & CheckFunc : GraphCheckerFuncs )
{
if ( ( this - > * CheckFunc ) ( Context , CopyRecord . DestPin ) )
{
break ;
}
}
if ( AdditionalCopyRecords . Num ( ) > 0 )
{
for ( FPropertyCopyRecord & AdditionalCopyRecord : AdditionalCopyRecords )
{
CheckForMemberOnlyAccess ( AdditionalCopyRecord , AdditionalCopyRecord . DestPin ) ;
}
CopyRecord = AdditionalCopyRecords [ 0 ] ;
for ( int32 AdditionalRecordIndex = 1 ; AdditionalRecordIndex < AdditionalCopyRecords . Num ( ) ; + + AdditionalRecordIndex )
{
AllAdditionalCopyRecords . Add ( AdditionalCopyRecords [ AdditionalRecordIndex ] ) ;
}
}
else
{
CheckForMemberOnlyAccess ( CopyRecord , CopyRecord . DestPin ) ;
}
}
}
// Append any additional copy records
ServicedPropPair . Value . CopyRecords . Append ( AllAdditionalCopyRecords ) ;
}
}
}
static void GetFullyQualifiedPathFromPin ( const UEdGraphPin * Pin , TArray < FString > & OutPath )
{
FString PinName = Pin - > PinName . ToString ( ) ;
while ( Pin - > ParentPin ! = nullptr )
{
PinName [ Pin - > ParentPin - > PinName . GetStringLength ( ) ] = TEXT ( ' . ' ) ;
Pin = Pin - > ParentPin ;
}
UE : : String : : ParseTokens ( PinName , TEXT ( ' . ' ) , [ & OutPath ] ( FStringView InStringView )
{
OutPath . Add ( FString ( InStringView ) ) ;
} ) ;
}
2021-04-22 04:57:09 -04:00
bool UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForVariableGet ( FCopyRecordGraphCheckContext & Context , UEdGraphPin * DestPin )
2020-09-01 14:07:48 -04:00
{
if ( DestPin )
{
UEdGraphPin * SourcePin = nullptr ;
if ( UK2Node_VariableGet * VariableGetNode = Cast < UK2Node_VariableGet > ( FollowKnots ( DestPin , SourcePin ) ) )
{
if ( VariableGetNode & & VariableGetNode - > IsNodePure ( ) & & VariableGetNode - > VariableReference . IsSelfContext ( ) )
{
if ( SourcePin )
{
GetFullyQualifiedPathFromPin ( SourcePin , Context . CopyRecord - > SourcePropertyPath ) ;
return true ;
}
}
}
}
return false ;
}
2021-04-22 04:57:09 -04:00
bool UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForLogicalNot ( FCopyRecordGraphCheckContext & Context , UEdGraphPin * DestPin )
2020-09-01 14:07:48 -04:00
{
if ( DestPin )
{
UEdGraphPin * SourcePin = nullptr ;
UK2Node_CallFunction * CallFunctionNode = Cast < UK2Node_CallFunction > ( FollowKnots ( DestPin , SourcePin ) ) ;
if ( CallFunctionNode & & CallFunctionNode - > FunctionReference . GetMemberName ( ) = = FName ( TEXT ( " Not_PreBool " ) ) )
{
// find and follow input pin
if ( UEdGraphPin * InputPin = FindFirstInputPin ( CallFunctionNode ) )
{
check ( InputPin - > PinType . PinCategory = = UEdGraphSchema_K2 : : PC_Boolean ) ;
if ( CheckForVariableGet ( Context , InputPin ) | | CheckForStructMemberAccess ( Context , InputPin ) | | CheckForArrayAccess ( Context , InputPin ) )
{
check ( Context . CopyRecord - > SourcePropertyPath . Num ( ) > 0 ) ; // this should have been filled in by CheckForVariableGet() or CheckForStructMemberAccess() above
Context . CopyRecord - > Operation = EPostCopyOperation : : LogicalNegateBool ;
return true ;
}
}
}
}
return false ;
}
/** The functions that we can safely native-break */
2021-10-12 21:21:22 -04:00
static const FName NativeBreakFunctionNameAllowList [ ] =
2020-09-01 14:07:48 -04:00
{
FName ( TEXT ( " BreakVector " ) ) ,
FName ( TEXT ( " BreakVector2D " ) ) ,
FName ( TEXT ( " BreakRotator " ) ) ,
} ;
/** Check whether a native break function can be safely used in the fast-path copy system (ie. source and dest data will be the same) */
2021-10-12 21:21:22 -04:00
static bool IsNativeBreakAllowed ( const FName & InFunctionName )
2020-09-01 14:07:48 -04:00
{
2021-10-12 21:21:22 -04:00
for ( const FName & FunctionName : NativeBreakFunctionNameAllowList )
2020-09-01 14:07:48 -04:00
{
if ( InFunctionName = = FunctionName )
{
return true ;
}
}
return false ;
}
/** The functions that we can safely native-make */
2021-10-12 21:21:22 -04:00
static const FName NativeMakeFunctionNameAllowList [ ] =
2020-09-01 14:07:48 -04:00
{
FName ( TEXT ( " MakeVector " ) ) ,
FName ( TEXT ( " MakeVector2D " ) ) ,
FName ( TEXT ( " MakeRotator " ) ) ,
} ;
/** Check whether a native break function can be safely used in the fast-path copy system (ie. source and dest data will be the same) */
2021-10-12 21:21:22 -04:00
static bool IsNativeMakeAllowed ( const FName & InFunctionName )
2020-09-01 14:07:48 -04:00
{
2021-10-12 21:21:22 -04:00
for ( const FName & FunctionName : NativeMakeFunctionNameAllowList )
2020-09-01 14:07:48 -04:00
{
if ( InFunctionName = = FunctionName )
{
return true ;
}
}
return false ;
}
2021-04-22 04:57:09 -04:00
bool UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForStructMemberAccess ( FCopyRecordGraphCheckContext & Context , UEdGraphPin * DestPin )
2020-09-01 14:07:48 -04:00
{
if ( DestPin )
{
UEdGraphPin * SourcePin = nullptr ;
if ( UK2Node_BreakStruct * BreakStructNode = Cast < UK2Node_BreakStruct > ( FollowKnots ( DestPin , SourcePin ) ) )
{
if ( UEdGraphPin * InputPin = FindFirstInputPin ( BreakStructNode ) )
{
if ( CheckForStructMemberAccess ( Context , InputPin ) | | CheckForVariableGet ( Context , InputPin ) | | CheckForArrayAccess ( Context , InputPin ) )
{
check ( Context . CopyRecord - > SourcePropertyPath . Num ( ) > 0 ) ; // this should have been filled in by CheckForVariableGet() above
Context . CopyRecord - > SourcePropertyPath . Add ( SourcePin - > PinName . ToString ( ) ) ;
return true ;
}
}
}
// could be a native break
else if ( UK2Node_CallFunction * NativeBreakNode = Cast < UK2Node_CallFunction > ( FollowKnots ( DestPin , SourcePin ) ) )
{
UFunction * Function = NativeBreakNode - > FunctionReference . ResolveMember < UFunction > ( UKismetMathLibrary : : StaticClass ( ) ) ;
2021-10-12 21:21:22 -04:00
if ( Function & & Function - > HasMetaData ( TEXT ( " NativeBreakFunc " ) ) & & IsNativeBreakAllowed ( Function - > GetFName ( ) ) )
2020-09-01 14:07:48 -04:00
{
if ( UEdGraphPin * InputPin = FindFirstInputPin ( NativeBreakNode ) )
{
if ( CheckForStructMemberAccess ( Context , InputPin ) | | CheckForVariableGet ( Context , InputPin ) | | CheckForArrayAccess ( Context , InputPin ) )
{
check ( Context . CopyRecord - > SourcePropertyPath . Num ( ) > 0 ) ; // this should have been filled in by CheckForVariableGet() above
Context . CopyRecord - > SourcePropertyPath . Add ( SourcePin - > PinName . ToString ( ) ) ;
return true ;
}
}
}
}
}
return false ;
}
2021-04-22 04:57:09 -04:00
bool UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForSplitPinAccess ( FCopyRecordGraphCheckContext & Context , UEdGraphPin * DestPin )
2020-09-01 14:07:48 -04:00
{
if ( DestPin )
{
FPropertyCopyRecord OriginalRecord = * Context . CopyRecord ;
UEdGraphPin * SourcePin = nullptr ;
if ( UK2Node_MakeStruct * MakeStructNode = Cast < UK2Node_MakeStruct > ( FollowKnots ( DestPin , SourcePin ) ) )
{
2020-09-24 00:43:27 -04:00
// Idea here is to account for split pins, so we want to narrow the scope to not also include user-placed makes
UObject * SourceObject = Context . MessageLog . FindSourceObject ( MakeStructNode ) ;
if ( SourceObject & & SourceObject - > IsA < UAnimGraphNode_Base > ( ) )
2020-09-01 14:07:48 -04:00
{
2020-09-24 00:43:27 -04:00
return ForEachInputPin ( MakeStructNode , [ this , & Context , & OriginalRecord ] ( UEdGraphPin * InputPin )
2020-09-01 14:07:48 -04:00
{
Context . CopyRecord - > SourcePropertyPath = OriginalRecord . SourcePropertyPath ;
if ( CheckForStructMemberAccess ( Context , InputPin ) | | CheckForVariableGet ( Context , InputPin ) | | CheckForArrayAccess ( Context , InputPin ) )
{
check ( Context . CopyRecord - > DestPropertyPath . Num ( ) > 0 ) ;
FPropertyCopyRecord RecordCopy = * Context . CopyRecord ;
FPropertyCopyRecord & NewRecord = Context . AdditionalCopyRecords . Add_GetRef ( MoveTemp ( RecordCopy ) ) ;
2020-09-24 00:43:27 -04:00
NewRecord . DestPropertyPath = OriginalRecord . DestPropertyPath ;
2020-09-01 14:07:48 -04:00
NewRecord . DestPropertyPath . Add ( InputPin - > PinName . ToString ( ) ) ;
return true ;
}
return false ;
} ) ;
}
}
2020-09-24 00:43:27 -04:00
else if ( UK2Node_CallFunction * NativeMakeNode = Cast < UK2Node_CallFunction > ( FollowKnots ( DestPin , SourcePin ) ) )
{
UFunction * Function = NativeMakeNode - > FunctionReference . ResolveMember < UFunction > ( UKismetMathLibrary : : StaticClass ( ) ) ;
2021-10-12 21:21:22 -04:00
if ( Function & & Function - > HasMetaData ( TEXT ( " NativeMakeFunc " ) ) & & IsNativeMakeAllowed ( Function - > GetFName ( ) ) )
2020-09-24 00:43:27 -04:00
{
// Idea here is to account for split pins, so we want to narrow the scope to not also include user-placed makes
2021-02-22 11:00:00 -04:00
UObject * SourceObject = Context . MessageLog . FindSourceObject ( NativeMakeNode ) ;
2020-09-24 00:43:27 -04:00
if ( SourceObject & & SourceObject - > IsA < UAnimGraphNode_Base > ( ) )
{
return ForEachInputPin ( NativeMakeNode , [ this , & Context , & OriginalRecord ] ( UEdGraphPin * InputPin )
{
Context . CopyRecord - > SourcePropertyPath = OriginalRecord . SourcePropertyPath ;
if ( CheckForStructMemberAccess ( Context , InputPin ) | | CheckForVariableGet ( Context , InputPin ) | | CheckForArrayAccess ( Context , InputPin ) )
{
check ( Context . CopyRecord - > DestPropertyPath . Num ( ) > 0 ) ;
FPropertyCopyRecord RecordCopy = * Context . CopyRecord ;
FPropertyCopyRecord & NewRecord = Context . AdditionalCopyRecords . Add_GetRef ( MoveTemp ( RecordCopy ) ) ;
NewRecord . DestPropertyPath = OriginalRecord . DestPropertyPath ;
NewRecord . DestPropertyPath . Add ( InputPin - > PinName . ToString ( ) ) ;
return true ;
}
return false ;
} ) ;
}
}
}
2020-09-01 14:07:48 -04:00
}
return false ;
}
2021-04-22 04:57:09 -04:00
bool UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForArrayAccess ( FCopyRecordGraphCheckContext & Context , UEdGraphPin * DestPin )
2020-09-01 14:07:48 -04:00
{
if ( DestPin )
{
UEdGraphPin * SourcePin = nullptr ;
if ( UK2Node_CallArrayFunction * CallArrayFunctionNode = Cast < UK2Node_CallArrayFunction > ( FollowKnots ( DestPin , SourcePin ) ) )
{
if ( CallArrayFunctionNode - > GetTargetFunction ( ) = = UKismetArrayLibrary : : StaticClass ( ) - > FindFunctionByName ( GET_FUNCTION_NAME_CHECKED ( UKismetArrayLibrary , Array_Get ) ) )
{
// Check array index is constant
int32 ArrayIndex = INDEX_NONE ;
if ( UEdGraphPin * IndexPin = CallArrayFunctionNode - > FindPin ( TEXT ( " Index " ) ) )
{
if ( IndexPin - > LinkedTo . Num ( ) > 0 )
{
return false ;
}
ArrayIndex = FCString : : Atoi ( * IndexPin - > DefaultValue ) ;
}
if ( UEdGraphPin * TargetArrayPin = CallArrayFunctionNode - > FindPin ( TEXT ( " TargetArray " ) ) )
{
if ( CheckForVariableGet ( Context , TargetArrayPin ) | | CheckForStructMemberAccess ( Context , TargetArrayPin ) )
{
check ( Context . CopyRecord - > SourcePropertyPath . Num ( ) > 0 ) ; // this should have been filled in by CheckForVariableGet() or CheckForStructMemberAccess() above
Context . CopyRecord - > SourcePropertyPath . Last ( ) . Append ( FString : : Printf ( TEXT ( " [%d] " ) , ArrayIndex ) ) ;
return true ;
}
}
}
}
}
return false ;
}
2021-04-22 04:57:09 -04:00
bool UAnimBlueprintExtension_Base : : FEvaluationHandlerRecord : : CheckForMemberOnlyAccess ( FPropertyCopyRecord & CopyRecord , UEdGraphPin * DestPin )
2020-09-01 14:07:48 -04:00
{
const UAnimationGraphSchema * AnimGraphDefaultSchema = GetDefault < UAnimationGraphSchema > ( ) ;
if ( DestPin )
{
// traverse pins to leaf nodes and check for member access/pure only
TArray < UEdGraphPin * > PinStack ;
PinStack . Add ( DestPin ) ;
while ( PinStack . Num ( ) > 0 )
{
UEdGraphPin * CurrentPin = PinStack . Pop ( false ) ;
for ( auto & LinkedPin : CurrentPin - > LinkedTo )
{
UEdGraphNode * LinkedNode = LinkedPin - > GetOwningNode ( ) ;
if ( LinkedNode )
{
bool bLeafNode = true ;
for ( auto & Pin : LinkedNode - > Pins )
{
if ( Pin ! = LinkedPin & & Pin - > Direction = = EGPD_Input & & ! AnimGraphDefaultSchema - > IsPosePin ( Pin - > PinType ) )
{
bLeafNode = false ;
PinStack . Add ( Pin ) ;
}
}
if ( bLeafNode )
{
if ( UK2Node_VariableGet * LinkedVariableGetNode = Cast < UK2Node_VariableGet > ( LinkedNode ) )
{
if ( ! LinkedVariableGetNode - > IsNodePure ( ) | | ! LinkedVariableGetNode - > VariableReference . IsSelfContext ( ) )
{
// only local variable access is allowed for leaf nodes
CopyRecord . InvalidateFastPath ( ) ;
}
}
else if ( UK2Node_CallFunction * CallFunctionNode = Cast < UK2Node_CallFunction > ( LinkedNode ) )
{
if ( ! CallFunctionNode - > IsNodePure ( ) )
{
// only allow pure function calls
CopyRecord . InvalidateFastPath ( ) ;
}
}
else if ( ! LinkedNode - > IsA < UK2Node_TransitionRuleGetter > ( ) )
{
CopyRecord . InvalidateFastPath ( ) ;
}
}
}
}
}
}
return CopyRecord . IsFastPath ( ) ;
}
# undef LOCTEXT_NAMESPACE