// Copyright Epic Games, Inc. All Rights Reserved.UAnimGraphNode_MirrorPose #include "AnimGraphNode_Mirror.h" #include "Animation/AnimNode_Inertialization.h" #include "Animation/MirrorDataTable.h" #include "ARFilter.h" #include "AssetRegistryModule.h" #include "BlueprintActionFilter.h" #include "BlueprintActionFilter.h" #include "BlueprintActionDatabaseRegistrar.h" #include "BlueprintNodeSpawner.h" #include "Kismet2/CompilerResultsLog.h" #define LOCTEXT_NAMESPACE "AnimGraphNode_MirrorPose" FLinearColor UAnimGraphNode_Mirror::GetNodeTitleColor() const { return FLinearColor(0.7f, 0.7f, 0.7f); } FText UAnimGraphNode_Mirror::GetTooltipText() const { return LOCTEXT("NodeToolTip", "Mirror Pose"); } FText UAnimGraphNode_Mirror::GetNodeTitle(ENodeTitleType::Type TitleType) const { if (Node.GetMirrorDataTable()) { FFormatNamedArguments Args; Args.Add(TEXT("AssetName"), FText::FromName(Node.GetMirrorDataTable()->GetFName())); return FText::Format(LOCTEXT("NodeTitle", "Mirror with {AssetName}"), Args); } else { return LOCTEXT("NodeTitle", "Mirror"); } } FText UAnimGraphNode_Mirror::GetMenuCategory() const { return LOCTEXT("NodeCategory", "Mirroring"); } void UAnimGraphNode_Mirror::GetOutputLinkAttributes(FNodeAttributeArray& OutAttributes) const { if (Node.GetBlendTimeOnMirrorStateChange() > 0.0) { OutAttributes.Add(UE::Anim::IInertializationRequester::Attribute); } } void UAnimGraphNode_Mirror::ValidateAnimNodeDuringCompilation(class USkeleton* ForSkeleton, class FCompilerResultsLog& MessageLog) { Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog); if (Node.GetMirrorDataTable() == nullptr) { MessageLog.Error(TEXT("@@ does not have a mirror data table selected. Please select a table or delete the node."), this); } BindMirrorDataTableChangedDelegate(); } void UAnimGraphNode_Mirror::PreEditChange(FProperty* PropertyThatWillChange) { Super::PreEditChange(PropertyThatWillChange); if (PropertyThatWillChange && PropertyThatWillChange->GetFName() == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_Mirror, MirrorDataTable)) { if (Node.GetMirrorDataTable()) { Node.GetMirrorDataTable()->OnDataTableChanged().RemoveAll(this); } } } void UAnimGraphNode_Mirror::PostPlacedNewNode() { Super::PostPlacedNewNode(); BindMirrorDataTableChangedDelegate(); } void UAnimGraphNode_Mirror::PostLoad() { Super::PostLoad(); BindMirrorDataTableChangedDelegate(); } void UAnimGraphNode_Mirror::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) { const FName PropertyName = (PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName() : NAME_None); if (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_Mirror, MirrorDataTable)) { BindMirrorDataTableChangedDelegate(); } Super::PostEditChangeProperty(PropertyChangedEvent); } void UAnimGraphNode_Mirror::BindMirrorDataTableChangedDelegate() { if (Node.GetMirrorDataTable()) { Node.GetMirrorDataTable()->OnDataTableChanged().RemoveAll(this); Node.GetMirrorDataTable()->OnDataTableChanged().AddUObject(this, &UAnimGraphNode_Mirror::OnMirrorDataTableChanged); } } void UAnimGraphNode_Mirror::OnMirrorDataTableChanged() { if (HasValidBlueprint()) { UBlueprint* BP = GetBlueprint(); BP->Status = BS_Dirty; } } void UAnimGraphNode_Mirror::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { auto LoadedAssetSetup = [](UEdGraphNode* NewNode, bool /*bIsTemplateNode*/, TWeakObjectPtr MirrorDataTablePtr) { UAnimGraphNode_Mirror* MirrorNode = CastChecked(NewNode); MirrorNode->Node.SetMirrorDataTable(MirrorDataTablePtr.Get()); }; auto UnloadedAssetSetup = [](UEdGraphNode* NewNode, bool bIsTemplateNode, const FAssetData AssetData) { UAnimGraphNode_Mirror* MirrorNode = CastChecked(NewNode); if (bIsTemplateNode) { AssetData.GetTagValue("Skeleton", MirrorNode->UnloadedSkeletonName); } else { UMirrorDataTable* MirrorTable = Cast(AssetData.GetAsset()); check(MirrorTable != nullptr); MirrorNode->Node.SetMirrorDataTable(MirrorTable); } }; const UObject* QueryObject = ActionRegistrar.GetActionKeyFilter(); if (QueryObject == nullptr) { FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); // define a filter to help in pulling UMirrrorDataTable asset data from the registry FARFilter Filter; Filter.ClassNames.Add(UMirrorDataTable::StaticClass()->GetFName()); Filter.bRecursiveClasses = true; // Find matching assets and add an entry for each one TArray MirrorDataTableList; AssetRegistryModule.Get().GetAssets(Filter, /*out*/MirrorDataTableList); for (auto AssetIt = MirrorDataTableList.CreateConstIterator(); AssetIt; ++AssetIt) { const FAssetData& Asset = *AssetIt; UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); if (Asset.IsAssetLoaded()) { UMirrorDataTable* MirrorDataTable = Cast(Asset.GetAsset()); if (MirrorDataTable) { NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(LoadedAssetSetup, TWeakObjectPtr(MirrorDataTable)); NodeSpawner->DefaultMenuSignature.MenuName = GetTitleGivenAssetInfo(FText::FromName(MirrorDataTable->GetFName())); } } else { NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(UnloadedAssetSetup, Asset); NodeSpawner->DefaultMenuSignature.MenuName = GetTitleGivenAssetInfo(FText::FromName(Asset.AssetName)); } ActionRegistrar.AddBlueprintAction(Asset, NodeSpawner); } } else if (const UMirrorDataTable* MirrorDataTable = Cast(QueryObject)) { UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); TWeakObjectPtr MirrorDataTablePtr = MakeWeakObjectPtr(const_cast(MirrorDataTable)); NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(LoadedAssetSetup, MirrorDataTablePtr); NodeSpawner->DefaultMenuSignature.MenuName = GetTitleGivenAssetInfo(FText::FromName(MirrorDataTablePtr->GetFName())); NodeSpawner->DefaultMenuSignature.Tooltip = GetTitleGivenAssetInfo(FText::FromString(MirrorDataTablePtr->GetPathName())); ActionRegistrar.AddBlueprintAction(QueryObject, NodeSpawner); } else if (QueryObject == GetClass()) { FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked(TEXT("AssetRegistry")); // define a filter to help in pulling UAnimSequence asset data from the registry FARFilter Filter; Filter.ClassNames.Add(UMirrorDataTable::StaticClass()->GetFName()); Filter.bRecursiveClasses = true; // Find matching assets and add an entry for each one TArray MirrorDataTableList; AssetRegistryModule.Get().GetAssets(Filter, /*out*/MirrorDataTableList); for (auto AssetIt = MirrorDataTableList.CreateConstIterator(); AssetIt; ++AssetIt) { const FAssetData& Asset = *AssetIt; if (Asset.IsAssetLoaded()) { continue; } UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(UnloadedAssetSetup, Asset); NodeSpawner->DefaultMenuSignature.MenuName = GetTitleGivenAssetInfo(FText::FromName(Asset.AssetName)); NodeSpawner->DefaultMenuSignature.Tooltip = GetTitleGivenAssetInfo(FText::FromName(Asset.ObjectPath)); ActionRegistrar.AddBlueprintAction(Asset, NodeSpawner); } } } bool UAnimGraphNode_Mirror::IsActionFilteredOut(class FBlueprintActionFilter const& Filter) { bool bIsFilteredOut = false; FBlueprintActionContext const& FilterContext = Filter.Context; for (UBlueprint* Blueprint : FilterContext.Blueprints) { if (UAnimBlueprint* AnimBlueprint = Cast(Blueprint)) { if (Node.GetMirrorDataTable()) { if (Node.GetMirrorDataTable()->Skeleton != AnimBlueprint->TargetSkeleton) { // Mirror Data Table does not use the same skeleton as the Blueprint, cannot use bIsFilteredOut = true; break; } } else { FAssetData SkeletonData(AnimBlueprint->TargetSkeleton); if (UnloadedSkeletonName != SkeletonData.GetExportTextName()) { bIsFilteredOut = true; break; } } } else { // Not an animation Blueprint, cannot use bIsFilteredOut = true; break; } } return bIsFilteredOut; } FText UAnimGraphNode_Mirror::GetTitleGivenAssetInfo(const FText& AssetName) { FFormatNamedArguments Args; Args.Add(TEXT("AssetName"), AssetName); return FText::Format(LOCTEXT("MirrorNodeTitle", "Mirror with {AssetName}"), Args); } EAnimAssetHandlerType UAnimGraphNode_Mirror::SupportsAssetClass(const UClass* AssetClass) const { if (AssetClass->IsChildOf(UMirrorDataTable::StaticClass())) { return EAnimAssetHandlerType::PrimaryHandler; } else { return EAnimAssetHandlerType::NotSupported; } } #undef LOCTEXT_NAMESPACE