Files
UnrealEngineUWP/Engine/Source/Editor/AnimGraph/Private/AnimGraphNode_AssetPlayerBase.cpp
Thomas Sarkanen b85a3cea8c Animation sync node and anim graph attributes
Refactored tick record sync into utility structure - FAnimSync. This improves the odd API around tick records, better encapsulating functionality and leaving less for the caller to get wrong. This will also eventually allow this to be refactored out into a scriptable pipeline stage.
Added sync node and scoped sync message for new 'graph based sync'. Nodes that subscribe to graph-based-sync determine their sync group based on the scope that they are in.
Removed 4.26-style sync scopes - pushed all syncing up to the main anim instance. Linked anim instances no longer sync their own tick records.
To make graph based sync more useful, surfaced graph attributes and their visualizations as labels on pins and parallel wires to visualize flow.
This involves statically determining the attribute flow of the graph at compile time. Added a new compiler handler to deal with this new debug data.
Updated a lot of nodes to specify their attributes so graph flow can be correctly visualized.
Added tracing of attributes and sync records and visualization of traced records when debugging the anim graph.

#rb Jurre.deBaare, Martin.Wilson

[CL 14998555 by Thomas Sarkanen in ue5-main branch]
2021-01-06 09:11:59 -04:00

186 lines
6.0 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_AssetPlayerBase.h"
#include "EdGraphSchema_K2.h"
#include "Animation/AnimComposite.h"
#include "Animation/BlendSpaceBase.h"
#include "Animation/AimOffsetBlendSpace.h"
#include "Animation/AimOffsetBlendSpace1D.h"
#include "AnimGraphNode_SequencePlayer.h"
#include "AnimGraphNode_SequenceEvaluator.h"
#include "AnimGraphNode_RotationOffsetBlendSpace.h"
#include "AnimGraphNode_BlendSpacePlayer.h"
#include "AnimGraphNode_BlendSpaceEvaluator.h"
#include "Animation/PoseAsset.h"
#include "AnimGraphNode_PoseBlendNode.h"
#include "AnimGraphNode_PoseByName.h"
#include "AnimGraphNode_PoseDriver.h"
#include "UObject/UObjectIterator.h"
#include "Animation/AnimLayerInterface.h"
#include "IAnimBlueprintCompilerHandlerCollection.h"
#include "AnimBlueprintCompilerHandler_Base.h"
#include "IAnimBlueprintGeneratedClassCompiledData.h"
#include "IAnimBlueprintCompilationContext.h"
#include "Animation/AnimSync.h"
#include "Animation/AnimAttributes.h"
#include "UObject/UE5MainStreamObjectVersion.h"
#define LOCTEXT_NAMESPACE "UAnimGraphNode_AssetPlayerBase"
void UAnimGraphNode_AssetPlayerBase::Serialize(FArchive& Ar)
{
Super::Serialize(Ar);
Ar.UsingCustomVersion(FUE5MainStreamObjectVersion::GUID);
if(Ar.CustomVer(FUE5MainStreamObjectVersion::GUID) < FUE5MainStreamObjectVersion::AnimSyncGroupsExplicitSyncMethod)
{
if(SyncGroup.GroupName != NAME_None)
{
SyncGroup.Method = EAnimSyncMethod::SyncGroup;
}
}
}
void UAnimGraphNode_AssetPlayerBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
{
Super::PostEditChangeProperty(PropertyChangedEvent);
if(PropertyChangedEvent.GetPropertyName() == GET_MEMBER_NAME_CHECKED(FAnimationGroupReference, Method))
{
if(SyncGroup.Method != EAnimSyncMethod::SyncGroup)
{
SyncGroup.GroupName = NAME_None;
SyncGroup.GroupRole = EAnimGroupRole::CanBeLeader;
}
}
}
void UAnimGraphNode_AssetPlayerBase::PinConnectionListChanged(UEdGraphPin* Pin)
{
Super::PinConnectionListChanged(Pin);
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object)
{
// recache visualization now an asset pin's connection is changed
if (const UEdGraphSchema* Schema = GetSchema())
{
Schema->ForceVisualizationCacheClear();
}
}
}
void UAnimGraphNode_AssetPlayerBase::PinDefaultValueChanged(UEdGraphPin* Pin)
{
Super::PinDefaultValueChanged(Pin);
if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Object)
{
// recache visualization now an asset pin's default value has changed
if (const UEdGraphSchema* Schema = GetSchema())
{
Schema->ForceVisualizationCacheClear();
}
}
}
void UAnimGraphNode_AssetPlayerBase::OnProcessDuringCompilation(IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
{
UBlueprint* Blueprint = GetBlueprint();
// Process Asset Player nodes to, if necessary cache off their node index for retrieval at runtime (used for evaluating Automatic Rule Transitions when using Layer nodes)
auto ProcessGraph = [this, &OutCompiledData](UEdGraph* Graph)
{
// Make sure we do not process the default AnimGraph
static const FName DefaultAnimGraphName("AnimGraph");
if (Graph->GetFName() != DefaultAnimGraphName)
{
FString GraphName = Graph->GetName();
// Also make sure we do not process any empty stub graphs
if (!GraphName.Contains(ANIM_FUNC_DECORATOR))
{
if (Graph->Nodes.ContainsByPredicate([this, &OutCompiledData](UEdGraphNode* Node) { return Node->NodeGuid == NodeGuid; }))
{
if (int32* IndexPtr = OutCompiledData.GetAnimBlueprintDebugData().NodeGuidToIndexMap.Find(NodeGuid))
{
FGraphAssetPlayerInformation& Info = OutCompiledData.GetGraphAssetPlayerInformation().FindOrAdd(FName(*GraphName));
Info.PlayerNodeIndices.AddUnique(*IndexPtr);
}
}
}
}
};
// Check for any definition of a layer graph
for (UEdGraph* Graph : Blueprint->FunctionGraphs)
{
ProcessGraph(Graph);
}
// Check for any implemented AnimLayer interface graphs
for (FBPInterfaceDescription& InterfaceDesc : Blueprint->ImplementedInterfaces)
{
// Only process Anim Layer interfaces
if (InterfaceDesc.Interface->IsChildOf<UAnimLayerInterface>())
{
for (UEdGraph* Graph : InterfaceDesc.Graphs)
{
ProcessGraph(Graph);
}
}
}
}
void UAnimGraphNode_AssetPlayerBase::ValidateAnimNodeDuringCompilation(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog)
{
Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog);
if(SyncGroup.Method == EAnimSyncMethod::SyncGroup && SyncGroup.GroupName == NAME_None)
{
MessageLog.Error(*LOCTEXT("NoSyncGroupSupplied", "Node @@ is set to use sync groups, but no sync group has been supplied").ToString(), this);
}
}
void UAnimGraphNode_AssetPlayerBase::GetOutputLinkAttributes(FNodeAttributeArray& OutAttributes) const
{
OutAttributes.Add(UE::Anim::FAttributes::Curves);
OutAttributes.Add(UE::Anim::FAttributes::Attributes);
if(SyncGroup.Method == EAnimSyncMethod::Graph)
{
OutAttributes.Add(UE::Anim::FAnimSync::Attribute);
}
}
UClass* GetNodeClassForAsset(const UClass* AssetClass)
{
UClass* NodeClass = nullptr;
// Iterate over all classes..
for (TObjectIterator<UClass> ClassIt; ClassIt; ++ClassIt)
{
UClass *Class = *ClassIt;
// Look for AnimGraphNode classes
if (Class->IsChildOf(UAnimGraphNode_Base::StaticClass()))
{
// See if this node is the 'primary handler' for this asset type
const UAnimGraphNode_Base* NodeCDO = Class->GetDefaultObject<UAnimGraphNode_Base>();
if (NodeCDO->SupportsAssetClass(AssetClass) == EAnimAssetHandlerType::PrimaryHandler)
{
NodeClass = Class;
break;
}
}
}
return NodeClass;
}
bool SupportNodeClassForAsset(const UClass* AssetClass, UClass* NodeClass)
{
// Get node CDO
const UAnimGraphNode_Base* NodeCDO = NodeClass->GetDefaultObject<UAnimGraphNode_Base>();
// See if this node supports this asset type (primary or not)
return (NodeCDO->SupportsAssetClass(AssetClass) != EAnimAssetHandlerType::NotSupported);
}
#undef LOCTEXT_NAMESPACE