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; }
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; } } } } } }