You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Skeleton compatibility is now bi-directional. Specifying a compatible skeleton A -> B now implies B -> A. Skeleton compatibility is now an editor-only concern. The runtime will attempt to do the 'best it can' via name -> name mappings. Only the editor will prevent assigning incompatible skeletons in (e.g.) asset pickers etc. Skeleton compatibility checks in editor can now be disabled in the editor preferences (and each asset picker now has a checkbox option in its view settings that allows for quick access to this). Moves FSkeletonRemapping to its own file (which is now private). Skeleton remappings are now generated on demand on worker threads just before animation decompression and stored in a registry, guarded by FRWScopeLock for thread-safety. Fixed some anim BP compiler edge cases where asset references on pins were not getting preloaded correctly, causing skeletons to be erroneously reported as missing. Exposed the current asset registry filter in SAssetView so that menu extensions can access it (and use it to provide context) #jira UE-166054 #jira UE-167355 #rb Jurre.deBaare,John.vanderBerg #preflight 635902602e6690262afa86f9 [CL 22878911 by Thomas Sarkanen in ue5-main branch]
555 lines
19 KiB
C++
555 lines
19 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "AnimGraphNode_AssetPlayerBase.h"
|
|
#include "EdGraphSchema_K2.h"
|
|
#include "Animation/AnimComposite.h"
|
|
#include "BlueprintActionFilter.h"
|
|
#include "BlueprintNodeSpawner.h"
|
|
#include "BlueprintAssetNodeSpawner.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
#include "Animation/AnimLayerInterface.h"
|
|
#include "IAnimBlueprintGeneratedClassCompiledData.h"
|
|
#include "IAnimBlueprintCompilationContext.h"
|
|
#include "Animation/AnimSync.h"
|
|
#include "Animation/AnimAttributes.h"
|
|
#include "AssetRegistry/AssetRegistryModule.h"
|
|
#include "UObject/UE5MainStreamObjectVersion.h"
|
|
#include "BlueprintActionDatabaseRegistrar.h"
|
|
#include "BlueprintNodeTemplateCache.h"
|
|
#include "Animation/AnimNode_AssetPlayerBase.h"
|
|
|
|
#define LOCTEXT_NAMESPACE "UAnimGraphNode_AssetPlayerBase"
|
|
|
|
void UAnimGraphNode_AssetPlayerBase::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
|
|
Ar.UsingCustomVersion(FUE5MainStreamObjectVersion::GUID);
|
|
|
|
if(Ar.IsLoading() && Ar.CustomVer(FUE5MainStreamObjectVersion::GUID) < FUE5MainStreamObjectVersion::AnimSyncGroupsExplicitSyncMethod)
|
|
{
|
|
if(SyncGroup_DEPRECATED.GroupName != NAME_None)
|
|
{
|
|
SyncGroup_DEPRECATED.Method = EAnimSyncMethod::SyncGroup;
|
|
}
|
|
}
|
|
|
|
if(Ar.IsLoading() && Ar.CustomVer(FUE5MainStreamObjectVersion::GUID) < FUE5MainStreamObjectVersion::AnimNodeConstantDataRefactorPhase0)
|
|
{
|
|
FStructProperty* NodeProperty = GetFNodeProperty();
|
|
if(NodeProperty->Struct->IsChildOf(FAnimNode_AssetPlayerBase::StaticStruct()))
|
|
{
|
|
FAnimNode_AssetPlayerBase* Node = NodeProperty->ContainerPtrToValuePtr<FAnimNode_AssetPlayerBase>(this);
|
|
Node->SetGroupName(SyncGroup_DEPRECATED.GroupName);
|
|
Node->SetGroupRole(SyncGroup_DEPRECATED.GroupRole);
|
|
Node->SetGroupMethod(SyncGroup_DEPRECATED.Method);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_AssetPlayerBase::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
|
|
FStructProperty* NodeProperty = GetFNodeProperty();
|
|
if(NodeProperty->Struct->IsChildOf(FAnimNode_AssetPlayerBase::StaticStruct()))
|
|
{
|
|
if(PropertyChangedEvent.GetPropertyName() == TEXT("Method"))
|
|
{
|
|
FAnimNode_AssetPlayerBase* Node = NodeProperty->ContainerPtrToValuePtr<FAnimNode_AssetPlayerBase>(this);
|
|
if(Node->GetGroupMethod() != EAnimSyncMethod::SyncGroup)
|
|
{
|
|
Node->SetGroupName(NAME_None);
|
|
Node->SetGroupRole(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();
|
|
}
|
|
}
|
|
}
|
|
|
|
FText UAnimGraphNode_AssetPlayerBase::GetTooltipText() const
|
|
{
|
|
bool const bIsTemplateNode = GetGraph() == nullptr || FBlueprintNodeTemplateCache::IsTemplateOuter(GetGraph());
|
|
if(bIsTemplateNode)
|
|
{
|
|
return FText::GetEmpty();
|
|
}
|
|
else
|
|
{
|
|
// FText::Format() is slow, so we utilize the cached list title
|
|
return GetNodeTitle(ENodeTitleType::ListView);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
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 && 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);
|
|
|
|
FStructProperty* NodeProperty = GetFNodeProperty();
|
|
if(NodeProperty->Struct->IsChildOf(FAnimNode_AssetPlayerBase::StaticStruct()))
|
|
{
|
|
FAnimNode_AssetPlayerBase* Node = NodeProperty->ContainerPtrToValuePtr<FAnimNode_AssetPlayerBase>(this);
|
|
|
|
const FName GroupName = Node->GetGroupName();
|
|
if(Node->GetGroupMethod() == EAnimSyncMethod::SyncGroup && GroupName == NAME_None)
|
|
{
|
|
MessageLog.Error(*LOCTEXT("NoSyncGroupSupplied", "Node @@ is set to use sync groups, but no sync group has been supplied").ToString(), this);
|
|
}
|
|
else if(Node->GetGroupMethod() != EAnimSyncMethod::SyncGroup && GroupName != NAME_None)
|
|
{
|
|
FText const ErrorFormat = LOCTEXT("InvalidSyncGroupSupplied", "Node @@ is set to not use named sync groups, but a sync group {0} is set.");
|
|
MessageLog.Error( *FText::Format(ErrorFormat, FText::FromName(GroupName)).ToString(), this );
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_AssetPlayerBase::GetOutputLinkAttributes(FNodeAttributeArray& OutAttributes) const
|
|
{
|
|
OutAttributes.Add(UE::Anim::FAttributes::Curves);
|
|
OutAttributes.Add(UE::Anim::FAttributes::Attributes);
|
|
|
|
FStructProperty* NodeProperty = GetFNodeProperty();
|
|
if(NodeProperty->Struct->IsChildOf(FAnimNode_AssetPlayerBase::StaticStruct()))
|
|
{
|
|
const FAnimNode_AssetPlayerBase* Node = NodeProperty->ContainerPtrToValuePtr<FAnimNode_AssetPlayerBase>(this);
|
|
if(Node->GetGroupMethod() == 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);
|
|
}
|
|
|
|
void UAnimGraphNode_AssetPlayerBase::SetupNewNode(UEdGraphNode* InNewNode, bool bInIsTemplateNode, const FAssetData InAssetData)
|
|
{
|
|
UAnimGraphNode_AssetPlayerBase* GraphNode = CastChecked<UAnimGraphNode_AssetPlayerBase>(InNewNode);
|
|
|
|
if(InAssetData.IsValid())
|
|
{
|
|
InAssetData.GetTagValue("Skeleton", GraphNode->UnloadedSkeletonName);
|
|
if(GraphNode->UnloadedSkeletonName == TEXT("None"))
|
|
{
|
|
GraphNode->UnloadedSkeletonName.Empty();
|
|
}
|
|
|
|
if(!bInIsTemplateNode)
|
|
{
|
|
GraphNode->SetAnimationAsset(CastChecked<UAnimationAsset>(InAssetData.GetAsset()));
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_AssetPlayerBase::GetMenuActionsHelper(
|
|
FBlueprintActionDatabaseRegistrar& InActionRegistrar,
|
|
TSubclassOf<UAnimGraphNode_Base> InNodeClass,
|
|
const TArray<TSubclassOf<UObject>>& InAssetTypes,
|
|
const TArray<TSubclassOf<UObject>>& InExcludedAssetTypes,
|
|
const TFunctionRef<FText(const FAssetData&, UClass*)>& InMenuNameFunction,
|
|
const TFunctionRef<FText(const FAssetData&, UClass*)>& InMenuTooltipFunction,
|
|
const TFunction<void(UEdGraphNode*, bool, const FAssetData)>& InSetupNewNodeFromAssetFunction,
|
|
const TFunction<void(UEdGraphNode*, bool, TSubclassOf<UObject>)>& InSetupNewNodeFromClassFunction,
|
|
const TFunction<FText(const FAssetData&)>& InMenuCategoryFunction)
|
|
{
|
|
auto MakeActionFromAsset = [&InActionRegistrar, &InMenuNameFunction, &InMenuTooltipFunction, InSetupNewNodeFromAssetFunction, InNodeClass, InMenuCategoryFunction](const FAssetData& InAssetData)
|
|
{
|
|
auto AssetSetup = [InSetupNewNodeFromAssetFunction, InAssetData, InMenuCategoryFunction](UEdGraphNode* InNewNode, bool bInIsTemplateNode)
|
|
{
|
|
InSetupNewNodeFromAssetFunction(InNewNode, bInIsTemplateNode, InAssetData);
|
|
};
|
|
|
|
UBlueprintAssetNodeSpawner* NodeSpawner = UBlueprintAssetNodeSpawner::Create(InNodeClass.Get(), InAssetData);
|
|
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda(AssetSetup);
|
|
NodeSpawner->DefaultMenuSignature.MenuName = InMenuNameFunction(InAssetData, InAssetData.GetClass());
|
|
NodeSpawner->DefaultMenuSignature.Category = InMenuCategoryFunction != nullptr ? InMenuCategoryFunction(InAssetData) : FText::GetEmpty();
|
|
NodeSpawner->DefaultMenuSignature.Tooltip = InMenuTooltipFunction(InAssetData, InAssetData.GetClass());
|
|
InActionRegistrar.AddBlueprintAction(InAssetData, NodeSpawner);
|
|
};
|
|
|
|
auto MakeActionFromClass = [&InActionRegistrar, &InMenuNameFunction, &InMenuTooltipFunction, InSetupNewNodeFromClassFunction, InNodeClass](const TSubclassOf<UObject>& InAssetClass)
|
|
{
|
|
if(InSetupNewNodeFromClassFunction != nullptr)
|
|
{
|
|
auto AssetSetup = [InSetupNewNodeFromClassFunction, InAssetClass](UEdGraphNode* InNewNode, bool bInIsTemplateNode)
|
|
{
|
|
InSetupNewNodeFromClassFunction(InNewNode, bInIsTemplateNode, InAssetClass);
|
|
};
|
|
|
|
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(InNodeClass.Get());
|
|
NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateLambda(AssetSetup);
|
|
NodeSpawner->DefaultMenuSignature.MenuName = InMenuNameFunction(FAssetData(), InAssetClass.Get());
|
|
NodeSpawner->DefaultMenuSignature.Tooltip = InMenuTooltipFunction(FAssetData(), InAssetClass.Get());
|
|
InActionRegistrar.AddBlueprintAction(InAssetClass, NodeSpawner);
|
|
}
|
|
};
|
|
|
|
const UObject* QueryObject = InActionRegistrar.GetActionKeyFilter();
|
|
bool bIsObjectOfAssetType = false;
|
|
for(const TSubclassOf<UObject>& AssetType : InAssetTypes)
|
|
{
|
|
if(QueryObject && QueryObject->IsA(AssetType.Get()))
|
|
{
|
|
bIsObjectOfAssetType = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (QueryObject == nullptr || QueryObject == InNodeClass.Get())
|
|
{
|
|
FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
|
|
|
FARFilter Filter;
|
|
for(const TSubclassOf<UObject>& AssetType : InAssetTypes)
|
|
{
|
|
Filter.ClassPaths.Add(AssetType.Get()->GetClassPathName());
|
|
}
|
|
for(const TSubclassOf<UObject>& ExcludedAssetType : InExcludedAssetTypes)
|
|
{
|
|
Filter.RecursiveClassPathsExclusionSet.Add(ExcludedAssetType.Get()->GetClassPathName());
|
|
}
|
|
Filter.bRecursiveClasses = true;
|
|
|
|
TArray<FAssetData> Assets;
|
|
AssetRegistryModule.Get().GetAssets(Filter, Assets);
|
|
|
|
for (const FAssetData& AssetData : Assets)
|
|
{
|
|
if(AssetData.IsUAsset())
|
|
{
|
|
MakeActionFromAsset(AssetData);
|
|
}
|
|
}
|
|
|
|
if(InSetupNewNodeFromClassFunction != nullptr)
|
|
{
|
|
// Add 'class' nodes
|
|
for(const TSubclassOf<UObject>& AssetType : InAssetTypes)
|
|
{
|
|
MakeActionFromClass(AssetType);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Add 'empty' asset node
|
|
MakeActionFromAsset(FAssetData());
|
|
}
|
|
}
|
|
else if (bIsObjectOfAssetType)
|
|
{
|
|
MakeActionFromAsset(FAssetData(QueryObject));
|
|
}
|
|
}
|
|
|
|
bool UAnimGraphNode_AssetPlayerBase::IsActionFilteredOut(class FBlueprintActionFilter const& Filter)
|
|
{
|
|
bool bIsFilteredOut = false;
|
|
FBlueprintActionContext const& FilterContext = Filter.Context;
|
|
|
|
for (UBlueprint* Blueprint : FilterContext.Blueprints)
|
|
{
|
|
UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Blueprint);
|
|
if (AnimBlueprint)
|
|
{
|
|
UAnimationAsset* Asset = GetAnimationAsset();
|
|
if(Asset)
|
|
{
|
|
if (AnimBlueprint->TargetSkeleton == nullptr || !AnimBlueprint->TargetSkeleton->IsCompatibleForEditor(Asset->GetSkeleton()))
|
|
{
|
|
// Asset does not use a compatible skeleton with the Blueprint, cannot use
|
|
bIsFilteredOut = true;
|
|
break;
|
|
}
|
|
}
|
|
else if(!UnloadedSkeletonName.IsEmpty())
|
|
{
|
|
if(AnimBlueprint->TargetSkeleton == nullptr || !AnimBlueprint->TargetSkeleton->IsCompatibleForEditor(UnloadedSkeletonName))
|
|
{
|
|
bIsFilteredOut = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not an animation Blueprint, cannot use
|
|
bIsFilteredOut = true;
|
|
break;
|
|
}
|
|
}
|
|
return bIsFilteredOut;
|
|
}
|
|
|
|
FText UAnimGraphNode_AssetPlayerBase::GetNodeTitleHelper(ENodeTitleType::Type InTitleType, UEdGraphPin* InAssetPin, const FText& InAssetDesc, const TFunction<FText(UAnimationAsset*)> InPostFixFunctionRef) const
|
|
{
|
|
UAnimationAsset* Asset = GetAnimationAsset();
|
|
if (Asset == nullptr)
|
|
{
|
|
// Check for bindings
|
|
bool bHasBinding = false;
|
|
if(InAssetPin != nullptr)
|
|
{
|
|
if (PropertyBindings.Find(InAssetPin->GetFName()) != nullptr)
|
|
{
|
|
bHasBinding = true;
|
|
}
|
|
}
|
|
|
|
// Also check for links
|
|
if (bHasBinding || (InAssetPin && InAssetPin->LinkedTo.Num() > 0))
|
|
{
|
|
return InAssetDesc;
|
|
}
|
|
// check for a default value on the pin
|
|
else if (InAssetPin && InAssetPin->DefaultObject != nullptr)
|
|
{
|
|
return GetNodeTitleForAsset(InTitleType, CastChecked<UAnimationAsset>(InAssetPin->DefaultObject), InAssetDesc, InPostFixFunctionRef);
|
|
}
|
|
else
|
|
{
|
|
return InAssetDesc;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return GetNodeTitleForAsset(InTitleType, Asset, InAssetDesc, InPostFixFunctionRef);
|
|
}
|
|
}
|
|
|
|
FText UAnimGraphNode_AssetPlayerBase::GetNodeTitleForAsset(ENodeTitleType::Type InTitleType, UAnimationAsset* InAsset, const FText& InAssetDesc, const TFunction<FText(UAnimationAsset*)> InPostFixFunctionRef) const
|
|
{
|
|
UAnimationAsset* Asset = GetAnimationAsset();
|
|
check(Asset);
|
|
const FText AssetName = FText::FromString(Asset->GetName());
|
|
|
|
if (InTitleType == ENodeTitleType::ListView || InTitleType == ENodeTitleType::MenuTitle)
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("AssetName"), AssetName);
|
|
Args.Add(TEXT("AssetDesc"), InAssetDesc);
|
|
|
|
if(InPostFixFunctionRef != nullptr)
|
|
{
|
|
const FText PostFix = InPostFixFunctionRef(InAsset);
|
|
if(!PostFix.IsEmpty())
|
|
{
|
|
Args.Add(TEXT("PostFix"), PostFix);
|
|
static const FTextFormat FormatWithPostFix(LOCTEXT("AssetPlayerTitlewithPostFix", "{AssetDesc} {PostFix} '{AssetName}'"));
|
|
return FText::Format(FormatWithPostFix, Args);
|
|
}
|
|
}
|
|
|
|
static const FTextFormat FormatWithoutPostFix(LOCTEXT("AssetPlayerTitle", "{AssetDesc} '{AssetName}'"));
|
|
return FText::Format(FormatWithoutPostFix, Args);
|
|
}
|
|
else
|
|
{
|
|
FFormatNamedArguments TitleArgs;
|
|
TitleArgs.Add(TEXT("AssetName"), AssetName);
|
|
TitleArgs.Add(TEXT("AssetDesc"), InAssetDesc);
|
|
FText Title = FText::Format(LOCTEXT("AssetPlayerFullTitle", "{AssetName}\n{AssetDesc}"), TitleArgs);
|
|
|
|
if (InTitleType == ENodeTitleType::FullTitle)
|
|
{
|
|
FStructProperty* NodeProperty = GetFNodeProperty();
|
|
if(NodeProperty->Struct->IsChildOf(FAnimNode_AssetPlayerBase::StaticStruct()))
|
|
{
|
|
const FAnimNode_AssetPlayerBase* Node = NodeProperty->ContainerPtrToValuePtr<FAnimNode_AssetPlayerBase>(this);
|
|
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("Title"), Title);
|
|
|
|
if(Node->GetGroupMethod() == EAnimSyncMethod::SyncGroup)
|
|
{
|
|
Args.Add(TEXT("SyncGroupName"), FText::FromName(Node->GetGroupName()));
|
|
static const FTextFormat FormatAssetPlayerNodeSyncGroupSubtitle(LOCTEXT("AssetPlayerNodeSyncGroupSubtitle", "{Title}\nSync group {SyncGroupName}"));
|
|
Title = FText::Format(FormatAssetPlayerNodeSyncGroupSubtitle, Args);
|
|
}
|
|
else if(Node->GetGroupMethod() == EAnimSyncMethod::Graph)
|
|
{
|
|
static const FTextFormat FormatAssetPlayerNodeGraphSyncGroupSubtitle(LOCTEXT("AssetPlayerNodeGraphSyncGroupSubtitle", "{Title}\nGraph sync group"));
|
|
Title = FText::Format(FormatAssetPlayerNodeGraphSyncGroupSubtitle, Args);
|
|
|
|
UObject* ObjectBeingDebugged = GetAnimBlueprint()->GetObjectBeingDebugged();
|
|
UAnimBlueprintGeneratedClass* GeneratedClass = GetAnimBlueprint()->GetAnimBlueprintGeneratedClass();
|
|
if (ObjectBeingDebugged && GeneratedClass)
|
|
{
|
|
int32 NodeIndex = GeneratedClass->GetNodeIndexFromGuid(NodeGuid);
|
|
if(NodeIndex != INDEX_NONE)
|
|
{
|
|
if(const FName* SyncGroupNamePtr = GeneratedClass->GetAnimBlueprintDebugData().NodeSyncsThisFrame.Find(NodeIndex))
|
|
{
|
|
Args.Add(TEXT("SyncGroupName"), FText::FromName(*SyncGroupNamePtr));
|
|
static const FTextFormat FormatAssetPlayerNodeDynamicGraphSyncGroupSubtitle(LOCTEXT("AssetPlayerNodeDynamicGraphSyncGroupSubtitle", "{Title}\nGraph sync group {SyncGroupName}"));
|
|
Title = FText::Format(FormatAssetPlayerNodeDynamicGraphSyncGroupSubtitle, Args);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return Title;
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_AssetPlayerBase::ValidateAnimNodeDuringCompilationHelper(USkeleton* ForSkeleton, FCompilerResultsLog& MessageLog, UAnimationAsset* InAsset, TSubclassOf<UAnimationAsset> InAssetType, UEdGraphPin* InExposedPin, FName InPropertyName)
|
|
{
|
|
UAnimationAsset* AssetToCheck = InAsset;
|
|
if(InExposedPin != nullptr && AssetToCheck == nullptr)
|
|
{
|
|
AssetToCheck = Cast<UAnimationAsset>(InExposedPin->DefaultObject);
|
|
}
|
|
|
|
if(AssetToCheck == nullptr)
|
|
{
|
|
if(!GetAnimBlueprint()->bIsTemplate)
|
|
{
|
|
// Check for bindings
|
|
bool bHasBinding = false;
|
|
bool bAlwaysDynamic = false;
|
|
if(InExposedPin != nullptr)
|
|
{
|
|
if(PropertyBindings.Contains(InExposedPin->GetFName()))
|
|
{
|
|
bHasBinding = true;
|
|
}
|
|
}
|
|
|
|
if(AlwaysDynamicProperties.Contains(InPropertyName))
|
|
{
|
|
bAlwaysDynamic = true;
|
|
}
|
|
|
|
// we may have a connected node or binding
|
|
if(!bAlwaysDynamic)
|
|
{
|
|
if (InExposedPin == nullptr || (InExposedPin->LinkedTo.Num() == 0 && !bHasBinding))
|
|
{
|
|
MessageLog.Error(*FText::Format(LOCTEXT("MissingAssetFormat", "@@ references an unknown {0}"), InAssetType->GetDisplayNameText()).ToString(), this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
USkeleton* AssetSkeleton = AssetToCheck->GetSkeleton();
|
|
|
|
// if asset doesn't have a skeleton, it might be due to the asset no being not loaded yet
|
|
if(AssetSkeleton == nullptr)
|
|
{
|
|
MessageLog.Error(*FText::Format(LOCTEXT("MissingSkeletonFormat", "@@ references {0} that uses a missing skeleton @@"), InAssetType->GetDisplayNameText()).ToString(), this, AssetSkeleton);
|
|
}
|
|
}
|
|
}
|
|
|
|
void UAnimGraphNode_AssetPlayerBase::PreloadRequiredAssetsHelper(UAnimationAsset* InAsset, UEdGraphPin* InExposedPin)
|
|
{
|
|
UAnimationAsset* AssetToLoad = InAsset;
|
|
if(InExposedPin != nullptr && AssetToLoad == nullptr)
|
|
{
|
|
AssetToLoad = Cast<UAnimationAsset>(InExposedPin->DefaultObject);
|
|
}
|
|
|
|
PreloadObject(AssetToLoad);
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE |