void FAnimGraphConnectionDrawingPolicy::BuildExecutionRoadmap()
{
	UAnimBlueprint* TargetBP = CastChecked<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraphChecked(GraphObj));
	UAnimBlueprintGeneratedClass* AnimBlueprintClass = (UAnimBlueprintGeneratedClass*)(*(TargetBP->GeneratedClass));

	if (TargetBP->GetObjectBeingDebugged() == NULL)
	{
		return;
	}

	TMap<UProperty*, UObject*> PropertySourceMap;
	AnimBlueprintClass->GetDebugData().GenerateReversePropertyMap(/*out*/ PropertySourceMap);

	FAnimBlueprintDebugData& DebugInfo = AnimBlueprintClass->GetAnimBlueprintDebugData();
	for (auto VisitIt = DebugInfo.UpdatedNodesThisFrame.CreateIterator(); VisitIt; ++VisitIt)
	{
		const FAnimBlueprintDebugData::FNodeVisit& VisitRecord = *VisitIt;

		if ((VisitRecord.SourceID >= 0) && (VisitRecord.SourceID < AnimBlueprintClass->AnimNodeProperties.Num()) && (VisitRecord.TargetID >= 0) && (VisitRecord.TargetID < AnimBlueprintClass->AnimNodeProperties.Num()))
		{
			if (UAnimGraphNode_Base* SourceNode = Cast<UAnimGraphNode_Base>(PropertySourceMap.FindRef(AnimBlueprintClass->AnimNodeProperties[VisitRecord.SourceID])))
			{
				if (UAnimGraphNode_Base* TargetNode = Cast<UAnimGraphNode_Base>(PropertySourceMap.FindRef(AnimBlueprintClass->AnimNodeProperties[VisitRecord.TargetID])))
				{
					//@TODO: Extend the rendering code to allow using the recorded blend weight instead of faked exec times
					FExecPairingMap& Predecessors = PredecessorNodes.FindOrAdd((UEdGraphNode*)SourceNode);
					FTimePair& Timings = Predecessors.FindOrAdd((UEdGraphNode*)TargetNode);
					Timings.PredExecTime = 0.0;
					Timings.ThisExecTime = VisitRecord.Weight;
				}
			}
		}
	}
}
void UAnimationTransitionSchema::GetGraphDisplayInformation(const UEdGraph& Graph, /*out*/ FGraphDisplayInfo& DisplayInfo) const
{
	DisplayInfo.PlainName = FText::FromString( Graph.GetName() );

	const UAnimStateTransitionNode* TransNode = Cast<const UAnimStateTransitionNode>(Graph.GetOuter());
	if (TransNode == NULL)
	{
		//@TODO: Transition graphs should be created with the transition node as their outer as well!
		UAnimBlueprint* Blueprint = CastChecked<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(&Graph));
		if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Blueprint->GetAnimBlueprintSkeletonClass())
		{
			TransNode = GetTransitionNodeFromGraph(AnimBlueprintClass->GetAnimBlueprintDebugData(), &Graph);
		}
	}

	if (TransNode)
	{
		FFormatNamedArguments Args;
		Args.Add(TEXT("NodeTitle"), TransNode->GetNodeTitle(ENodeTitleType::FullTitle));
		DisplayInfo.PlainName = FText::Format( NSLOCTEXT("Animation", "TransitionRuleGraphTitle", "{NodeTitle} (rule)"), Args );
	}

	DisplayInfo.DisplayName = DisplayInfo.PlainName;
}
Exemplo n.º 3
0
void UAnimInstance::UpdateAnimation(float DeltaSeconds)
{
#if WITH_EDITORONLY_DATA
	if (GIsEditor)
	{
		// Reset the anim graph visualization
		if (RootNode != NULL)
		{
			if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Cast<UAnimBlueprintGeneratedClass>(GetClass()))
			{
				UAnimBlueprint* AnimBP = CastChecked<UAnimBlueprint>(AnimBlueprintClass->ClassGeneratedBy);

				if (AnimBP->GetObjectBeingDebugged() == this)
				{
					AnimBlueprintClass->GetAnimBlueprintDebugData().ResetNodeVisitSites();
				}
			}
		}

		// Update the lifetimer and see if we should use the snapshot instead
		CurrentLifeTimerScrubPosition += DeltaSeconds;
		LifeTimer = FMath::Max<double>(CurrentLifeTimerScrubPosition, LifeTimer);

		if (UpdateSnapshotAndSkipRemainingUpdate())
		{
			return;
		}
	}
#endif

	AnimNotifies.Empty();
	MorphTargetCurves.Empty();

	ClearSlotNodeWeights();

	//Track material params we set last time round so we can clear them if they aren't set again.
	MaterialParamatersToClear.Empty();
	for( auto Iter = MaterialParameterCurves.CreateConstIterator(); Iter; ++Iter )
	{
		if(Iter.Value() > 0.0f)
		{
			MaterialParamatersToClear.Add(Iter.Key());
		}
	}
	MaterialParameterCurves.Empty();
	VertexAnims.Empty();

	// Reset the player tick list (but keep it presized)
	UngroupedActivePlayers.Empty(UngroupedActivePlayers.Num());
	for (int32 GroupIndex = 0; GroupIndex < SyncGroups.Num(); ++GroupIndex)
	{
		SyncGroups[GroupIndex].Reset();
	}

	NativeUpdateAnimation(DeltaSeconds);
	BlueprintUpdateAnimation(DeltaSeconds);

	// update weight before all nodes update comes in
	Montage_UpdateWeight(DeltaSeconds);

	// Update the anim graph
	if (RootNode != NULL)
	{
		IncrementContextCounter();
		FAnimationUpdateContext UpdateContext(this, DeltaSeconds);
		RootNode->Update(UpdateContext);
	}

	// curve values can be used during update state, so we need to clear the array before ticking each elements
	// where we collect new items
	EventCurves.Empty();

	// Handle all players inside sync groups
	for (int32 GroupIndex = 0; GroupIndex < SyncGroups.Num(); ++GroupIndex)
	{
		FAnimGroupInstance& SyncGroup = SyncGroups[GroupIndex];

		if (SyncGroup.ActivePlayers.Num() > 0)
		{
			const int32 GroupLeaderIndex = FMath::Max(SyncGroup.GroupLeaderIndex, 0);

			// Tick the group leader
			FAnimAssetTickContext TickContext(DeltaSeconds);
			FAnimTickRecord& GroupLeader = SyncGroup.ActivePlayers[GroupLeaderIndex];
			GroupLeader.SourceAsset->TickAssetPlayerInstance(GroupLeader, this, TickContext);

			// Update everything else to follow the leader
			if (SyncGroup.ActivePlayers.Num() > 1)
			{
				TickContext.ConvertToFollower();

				for (int32 TickIndex = 0; TickIndex < SyncGroup.ActivePlayers.Num(); ++TickIndex)
				{
					if (TickIndex != GroupLeaderIndex)
					{
						const FAnimTickRecord& AssetPlayer = SyncGroup.ActivePlayers[TickIndex];
						AssetPlayer.SourceAsset->TickAssetPlayerInstance(AssetPlayer, this, TickContext);
					}
				}
			}
		}
	}

	// Handle the remaining ungrouped animation players
	for (int32 TickIndex = 0; TickIndex < UngroupedActivePlayers.Num(); ++TickIndex)
	{
		const FAnimTickRecord& AssetPlayerToTick = UngroupedActivePlayers[TickIndex];
		FAnimAssetTickContext TickContext(DeltaSeconds);
		AssetPlayerToTick.SourceAsset->TickAssetPlayerInstance(AssetPlayerToTick, this, TickContext);
	}

	// update montage should run in game thread
	// if we do multi threading, make sure this stays in game thread
	Montage_Advance(DeltaSeconds);

	// now trigger Notifies
	TriggerAnimNotifies(DeltaSeconds);

	// Add 0.0 curves to clear parameters that we have previously set but didn't set this tick.
	//   - Make a copy of MaterialParametersToClear as it will be modified by AddCurveValue
	TArray<FName> ParamsToClearCopy = MaterialParamatersToClear;
	for (int i = 0; i < ParamsToClearCopy.Num(); ++i)
	{
		AddCurveValue(ParamsToClearCopy[i], 0.0f, ACF_DrivesMaterial);
	}


#if WITH_EDITOR && 0
	{
		// Take a snapshot if the scrub control is locked to the end, we are playing, and we are the one being debugged
		if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Cast<UAnimBlueprintGeneratedClass>(GetClass()))
		{
			if (UAnimBlueprint* Blueprint = Cast<UAnimBlueprint>(AnimBlueprintClass->ClassGeneratedBy))
			{
				if (Blueprint->GetObjectBeingDebugged() == this)
				{
					if ((CurrentLifeTimerScrubPosition == LifeTimer) && (DeltaSeconds > 0.0f))
					{
						AnimBlueprintClass->GetAnimBlueprintDebugData().TakeSnapshot(this);
					}
				}
			}
		}
	}
#endif
}
void UAnimGraphNode_BlendSpacePlayer::BakeDataDuringCompilation(class FCompilerResultsLog& MessageLog)
{
	UAnimBlueprint* AnimBlueprint = GetAnimBlueprint();
	Node.GroupIndex = AnimBlueprint->FindOrAddGroup(SyncGroup.GroupName);
	Node.GroupRole = SyncGroup.GroupRole;
}
bool UK2Node_TransitionRuleGetter::IsActionFilteredOut(class FBlueprintActionFilter const& Filter)
{
	if(Filter.Context.Graphs.Num())
	{
		const UEdGraphSchema* Schema = Filter.Context.Graphs[0]->GetSchema();
		if(Cast<UAnimationGraphSchema>(Schema))
		{
			if(GetterType == ETransitionGetter::ArbitraryState_GetBlendWeight)
			{
				// only show the transition nodes if the associated state node is in every graph:
				if( AssociatedStateNode )
				{
					for( auto Blueprint : Filter.Context.Blueprints )
					{
						TArray<UAnimStateNode*> States;
						FBlueprintEditorUtils::GetAllNodesOfClass(Blueprint, /*out*/ States);

						if( !States.Contains( AssociatedStateNode ) )
						{
							return true;
						}
					}
					return false;
				}
			}
			return true;
		}
		else if(Cast<UAnimationTransitionSchema>(Schema))
		{
			// Non-sequence specific TransitionRuleGetter nodes have no associated nodes assigned, they are always allowed in AnimationTransitionSchema graphs
			if( AssociatedStateNode == nullptr && AssociatedAnimAssetPlayerNode == nullptr)
			{
				return false;
			}
			else if(AssociatedAnimAssetPlayerNode)
			{
				if(Filter.Context.Blueprints.Num())
				{
					UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Filter.Context.Blueprints[0]);
					check(AnimBlueprint);

					if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = AnimBlueprint->GetAnimBlueprintSkeletonClass())
					{
						// Local function to find the transition node using the context graph and AnimBlueprintDebugData
						auto GetTransitionNodeFromGraphLambda = [](const FAnimBlueprintDebugData& DebugData, const UEdGraph* Graph) -> UAnimStateTransitionNode*
						{
							if (const TWeakObjectPtr<UAnimStateTransitionNode>* TransNodePtr = DebugData.TransitionGraphToNodeMap.Find(Graph))
							{
								return TransNodePtr->Get();
							}

							if (const TWeakObjectPtr<UAnimStateTransitionNode>* TransNodePtr = DebugData.TransitionBlendGraphToNodeMap.Find(Graph))
							{
								return TransNodePtr->Get();
							}

							return NULL;
						};

						// Check if the TransitionNode can be found in the AninBlueprint's debug data
						if (UAnimStateTransitionNode* TransNode = GetTransitionNodeFromGraph(AnimBlueprintClass->GetAnimBlueprintDebugData(), Filter.Context.Graphs[0]))
						{
							if (UAnimStateNode* SourceStateNode = Cast<UAnimStateNode>(TransNode->GetPreviousState()))
							{
								// Check if the AnimAssetPlayerNode's graph is the state machine's bound graph
								if(AssociatedAnimAssetPlayerNode->GetGraph() == SourceStateNode->BoundGraph)
								{
									return false;
								}
							}
						}
					}
				}
			}
		}
	}
	return true;
}
void UAnimationTransitionSchema::GetSourceStateActions(FGraphContextMenuBuilder& ContextMenuBuilder) const
{
	if ((ContextMenuBuilder.FromPin == NULL) || ((ContextMenuBuilder.FromPin->Direction == EGPD_Input) && (ContextMenuBuilder.FromPin->PinType.PinCategory == PC_Float)))
	{
		// Find the source state associated with this transition
		UAnimBlueprint* Blueprint = CastChecked<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(ContextMenuBuilder.CurrentGraph));

		if (UAnimBlueprintGeneratedClass* AnimBlueprintClass = Blueprint->GetAnimBlueprintSkeletonClass())
		{
			if (UAnimStateTransitionNode* TransNode = GetTransitionNodeFromGraph(AnimBlueprintClass->GetAnimBlueprintDebugData(), ContextMenuBuilder.CurrentGraph))
			{
				if (UAnimStateNode* SourceStateNode = Cast<UAnimStateNode>(TransNode->GetPreviousState()))
				{
					// Offer options from the source state

					// Sequence player positions
					ETransitionGetter::Type SequenceSpecificGetters[] =
					{
						ETransitionGetter::AnimationAsset_GetCurrentTime,
						ETransitionGetter::AnimationAsset_GetLength,
						ETransitionGetter::AnimationAsset_GetCurrentTimeFraction,
						ETransitionGetter::AnimationAsset_GetTimeFromEnd,
						ETransitionGetter::AnimationAsset_GetTimeFromEndFraction
					};

					TArray<UK2Node*> AssetPlayers;
					SourceStateNode->BoundGraph->GetNodesOfClassEx<UAnimGraphNode_Base, UK2Node>(/*out*/ AssetPlayers);

					const FString Category_AssetPlayer(TEXT("Asset Player"));

					for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(SequenceSpecificGetters); ++TypeIndex)
					{
						for (auto NodeIt = AssetPlayers.CreateConstIterator(); NodeIt; ++NodeIt)
						{
							UAnimGraphNode_Base* AnimNode = CastChecked<UAnimGraphNode_Base>(*NodeIt);
							
							if (AnimNode->DoesSupportTimeForTransitionGetter())
							{
								UK2Node_TransitionRuleGetter* NodeTemplate = ContextMenuBuilder.CreateTemplateNode<UK2Node_TransitionRuleGetter>();

								FString AssetName;

								UAnimationAsset * AnimAsset = AnimNode->GetAnimationAsset();
								if (AnimAsset)
								{
									NodeTemplate->AssociatedAnimAssetPlayerNode = AnimNode;
									AssetName = AnimAsset->GetName();
								}

								NodeTemplate->GetterType = SequenceSpecificGetters[TypeIndex];

								FFormatNamedArguments Args;
								Args.Add(TEXT("NodeName"), UK2Node_TransitionRuleGetter::GetFriendlyName(NodeTemplate->GetterType));
								Args.Add(TEXT("AssetName"), FText::FromString(AssetName));
								FText Title = FText::Format(LOCTEXT("TransitionFor", "{NodeName} for '{AssetName}'"), Args);

								TSharedPtr<FEdGraphSchemaAction_K2NewNode> Action = FK2ActionMenuBuilder::AddNewNodeAction(ContextMenuBuilder, Category_AssetPlayer, Title, NodeTemplate->GetTooltipText().ToString(), 0, NodeTemplate->GetKeywords());
								Action->NodeTemplate = NodeTemplate;
							}
						}
					}

					// Non-sequence specific ones
					ETransitionGetter::Type NonSpecificGetters[] =
					{
						ETransitionGetter::CurrentTransitionDuration,
						ETransitionGetter::CurrentState_ElapsedTime,
						ETransitionGetter::CurrentState_GetBlendWeight
					};

					for (int32 TypeIndex = 0; TypeIndex < ARRAY_COUNT(NonSpecificGetters); ++TypeIndex)
					{
						FString Category_Transition(TEXT("Transition"));

						UK2Node_TransitionRuleGetter* NodeTemplate = ContextMenuBuilder.CreateTemplateNode<UK2Node_TransitionRuleGetter>();
						NodeTemplate->GetterType = NonSpecificGetters[TypeIndex];

						FText Title = UK2Node_TransitionRuleGetter::GetFriendlyName(NodeTemplate->GetterType);

						TSharedPtr<FEdGraphSchemaAction_K2NewNode> Action = FK2ActionMenuBuilder::AddNewNodeAction(ContextMenuBuilder, Category_Transition, Title, NodeTemplate->GetTooltipText().ToString(), 0, NodeTemplate->GetKeywords());
						Action->NodeTemplate = NodeTemplate;
					}
				}
			}
		}
	}
}