Files
UnrealEngineUWP/Engine/Source/Editor/AnimGraph/Private/AnimGraphNode_BlendSpaceGraphBase.cpp
Thomas Sarkanen d9c2b172f7 Skeleton compatibility improvements
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]
2022-11-01 06:25:59 -04:00

592 lines
20 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_BlendSpaceGraphBase.h"
#include "GraphEditorActions.h"
#include "Kismet2/CompilerResultsLog.h"
#include "BlueprintNodeSpawner.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "Animation/AimOffsetBlendSpace.h"
#include "Animation/AimOffsetBlendSpace1D.h"
#include "Animation/AnimSync.h"
#include "IAnimBlueprintCopyTermDefaultsContext.h"
#include "IAnimBlueprintGeneratedClassCompiledData.h"
#include "AnimationGraph.h"
#include "AnimationGraphSchema.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "BlendSpaceGraph.h"
#include "BlueprintEditorModule.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "DetailLayoutBuilder.h"
#include "DetailCategoryBuilder.h"
#include "EdGraphUtilities.h"
#include "IAnimBlueprintCompilationContext.h"
#include "AnimationBlendSpaceSampleGraph.h"
#include "AnimBlueprintExtension.h"
#include "AnimGraphNode_BlendSpaceSampleResult.h"
#include "AnimGraphNode_SequencePlayer.h"
#include "Kismet2/Kismet2NameValidators.h"
#include "AnimNodes/AnimNode_BlendSpaceGraphBase.h"
#include "BlueprintEditor.h"
#include "Animation/AnimSequence.h"
#include "AnimBlueprintExtension_BlendSpaceGraph.h"
#include "BlueprintNodeTemplateCache.h"
#define LOCTEXT_NAMESPACE "UAnimGraphNode_BlendSpaceGraphBase"
UAnimGraphNode_BlendSpaceGraphBase::UAnimGraphNode_BlendSpaceGraphBase()
{
bCanRenameNode = true;
}
FText UAnimGraphNode_BlendSpaceGraphBase::GetMenuCategory() const
{
return LOCTEXT("BlendSpaceCategory_Label", "Animation|Blend Spaces");
}
FLinearColor UAnimGraphNode_BlendSpaceGraphBase::GetNodeTitleColor() const
{
return FLinearColor(0.8f, 0.8f, 0.8f);
}
FSlateIcon UAnimGraphNode_BlendSpaceGraphBase::GetIconAndTint(FLinearColor& OutColor) const
{
return FSlateIcon(FAppStyle::GetAppStyleSetName(), "ClassIcon.BlendSpace");
}
FText UAnimGraphNode_BlendSpaceGraphBase::GetTooltipText() const
{
bool const bIsTemplateNode = GetGraph() == nullptr || FBlueprintNodeTemplateCache::IsTemplateOuter(GetGraph());
if(bIsTemplateNode)
{
return FText::GetEmpty();
}
else
{
return GetNodeTitle(ENodeTitleType::ListView);
}
}
UAnimGraphNode_Base* UAnimGraphNode_BlendSpaceGraphBase::ExpandGraphAndProcessNodes(UEdGraph* SourceGraph, UAnimGraphNode_Base* SourceRootNode, IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
{
// Clone the nodes from the source graph
// Note that we outer this graph to the ConsolidatedEventGraph to allow ExpansionStep to
// correctly retrieve the context for any expanded function calls (custom make/break structs etc.)
UEdGraph* ClonedGraph = FEdGraphUtilities::CloneGraph(SourceGraph, InCompilationContext.GetConsolidatedEventGraph(), &InCompilationContext.GetMessageLog(), true);
// Grab all the animation nodes and find the corresponding root node in the cloned set
UAnimGraphNode_Base* TargetRootNode = nullptr;
TArray<UAnimGraphNode_Base*> AnimNodeList;
for (auto NodeIt = ClonedGraph->Nodes.CreateIterator(); NodeIt; ++NodeIt)
{
UEdGraphNode* ClonedNode = *NodeIt;
if (UAnimGraphNode_Base* TestNode = Cast<UAnimGraphNode_Base>(ClonedNode))
{
AnimNodeList.Add(TestNode);
//@TODO: There ought to be a better way to determine this
if (InCompilationContext.GetMessageLog().FindSourceObject(TestNode) == InCompilationContext.GetMessageLog().FindSourceObject(SourceRootNode))
{
TargetRootNode = TestNode;
}
}
}
check(TargetRootNode);
// Run another expansion pass to catch the graph we just added (this is slightly wasteful)
InCompilationContext.ExpansionStep(ClonedGraph, false);
// Validate graph now we have expanded/pruned
InCompilationContext.ValidateGraphIsWellFormed(ClonedGraph);
// Move the cloned nodes into the consolidated event graph
const bool bIsLoading = InCompilationContext.GetBlueprint()->bIsRegeneratingOnLoad || IsAsyncLoading();
const bool bIsCompiling = InCompilationContext.GetBlueprint()->bBeingCompiled;
ClonedGraph->MoveNodesToAnotherGraph(InCompilationContext.GetConsolidatedEventGraph(), bIsLoading, bIsCompiling);
// Process any animation nodes
{
TArray<UAnimGraphNode_Base*> RootSet;
RootSet.Add(TargetRootNode);
InCompilationContext.PruneIsolatedAnimationNodes(RootSet, AnimNodeList);
InCompilationContext.ProcessAnimationNodes(AnimNodeList);
}
// Returns the processed cloned version of SourceRootNode
return TargetRootNode;
}
void UAnimGraphNode_BlendSpaceGraphBase::OnProcessDuringCompilation(IAnimBlueprintCompilationContext& InCompilationContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
{
FStructProperty* NodeProperty = GetFNodeProperty();
FArrayProperty* PoseLinksProperty = CastFieldChecked<FArrayProperty>(NodeProperty->Struct->FindPropertyByName(GET_MEMBER_NAME_CHECKED(FAnimNode_BlendSpaceGraphBase, SamplePoseLinks)));
// Resize pose links to match graphs
FAnimNode_BlendSpaceGraphBase* AnimNode = NodeProperty->ContainerPtrToValuePtr<FAnimNode_BlendSpaceGraphBase>(this);
AnimNode->SamplePoseLinks.SetNum(Graphs.Num());
for(int32 PoseIndex = 0; PoseIndex < Graphs.Num(); ++PoseIndex)
{
UAnimationBlendSpaceSampleGraph* SampleGraph = CastChecked<UAnimationBlendSpaceSampleGraph>(Graphs[PoseIndex]);
UAnimGraphNode_Base* RootNode = ExpandGraphAndProcessNodes(SampleGraph, SampleGraph->ResultNode, InCompilationContext, OutCompiledData);
InCompilationContext.AddPoseLinkMappingRecord(FPoseLinkMappingRecord::MakeFromArrayEntry(this, RootNode, PoseLinksProperty, PoseIndex));
}
}
void UAnimGraphNode_BlendSpaceGraphBase::OnCopyTermDefaultsToDefaultObject(IAnimBlueprintCopyTermDefaultsContext& InCompilationContext, IAnimBlueprintNodeCopyTermDefaultsContext& InPerNodeContext, IAnimBlueprintGeneratedClassCompiledData& OutCompiledData)
{
FAnimNode_BlendSpaceGraphBase* DestinationNode = reinterpret_cast<FAnimNode_BlendSpaceGraphBase*>(InPerNodeContext.GetDestinationPtr());
UAnimBlueprintExtension_BlendSpaceGraph* Extension = UAnimBlueprintExtension::GetExtension<UAnimBlueprintExtension_BlendSpaceGraph>(GetAnimBlueprint());
DestinationNode->BlendSpace = Extension->AddBlendSpace(BlendSpace);
}
void UAnimGraphNode_BlendSpaceGraphBase::SetupFromAsset(const FAssetData& InAssetData, bool bInIsTemplateNode)
{
if(InAssetData.IsValid())
{
InAssetData.GetTagValue("Skeleton", SkeletonName);
if(SkeletonName == TEXT("None"))
{
SkeletonName.Empty();
}
if(!bInIsTemplateNode)
{
UBlendSpace* Asset = CastChecked<UBlendSpace>(InAssetData.GetAsset());
UAnimBlueprint* AnimBlueprint = GetAnimBlueprint();
const UAnimationGraphSchema* AnimationGraphSchema = GetDefault<UAnimationGraphSchema>();
BlendSpaceGraph = CastChecked<UBlendSpaceGraph>(FBlueprintEditorUtils::CreateNewGraph(this, Asset->GetFName(), UBlendSpaceGraph::StaticClass(), UEdGraphSchema::StaticClass()));
BlendSpaceGraph->BlendSpace = BlendSpace = DuplicateObject(Asset, BlendSpaceGraph);
BlendSpaceGraph->BlendSpace->ClearFlags(RF_Public | RF_Standalone | RF_Transient);
BlendSpaceGraph->BlendSpace->SetFlags(RF_Transactional);
BlendSpaceGraph->BlendSpace->ResetSkeleton(nullptr);
BlendSpaceGraph->BlendSpace->EmptyMetaData();
BlendSpaceGraph->BlendSpace->RemoveUserDataOfClass(UAssetUserData::StaticClass());
BlendSpaceGraph->bAllowDeletion = false;
BlendSpaceGraph->bAllowRenaming = true;
// Add the new graph as a child of our parent graph
UEdGraph* ParentGraph = GetGraph();
if(ParentGraph->SubGraphs.Find(BlendSpaceGraph) == INDEX_NONE)
{
ParentGraph->Modify();
ParentGraph->SubGraphs.Add(BlendSpaceGraph);
}
for(int32 SampleIndex = 0; SampleIndex < BlendSpace->SampleData.Num(); ++SampleIndex)
{
FBlendSample& Sample = BlendSpace->SampleData[SampleIndex];
FName SampleName = Sample.Animation != nullptr ? Sample.Animation->GetFName() : FName("Sample", SampleIndex);
UAnimationBlendSpaceSampleGraph* SampleGraph = AddGraph(SampleName, Sample.Animation);
// Clear the animation now we have created the point
Sample.Animation = nullptr;
}
}
}
}
void UAnimGraphNode_BlendSpaceGraphBase::SetupFromClass(TSubclassOf<UBlendSpace> InBlendSpaceClass, bool bInIsTemplateNode)
{
if(bInIsTemplateNode)
{
BlendSpaceClass = InBlendSpaceClass;
}
else
{
BlendSpaceGraph = CastChecked<UBlendSpaceGraph>(FBlueprintEditorUtils::CreateNewGraph(this, InBlendSpaceClass.Get()->GetFName(), UBlendSpaceGraph::StaticClass(), UEdGraphSchema::StaticClass()));
BlendSpaceGraph->BlendSpace = BlendSpace = NewObject<UBlendSpace>(BlendSpaceGraph, InBlendSpaceClass.Get());
BlendSpaceGraph->BlendSpace->ClearFlags(RF_Public | RF_Standalone | RF_Transient);
BlendSpaceGraph->BlendSpace->SetFlags(RF_Transactional);
BlendSpaceGraph->bAllowDeletion = false;
BlendSpaceGraph->bAllowRenaming = true;
// Add the new graph as a child of our parent graph
UEdGraph* ParentGraph = GetGraph();
if(ParentGraph->SubGraphs.Find(BlendSpaceGraph) == INDEX_NONE)
{
ParentGraph->Modify();
ParentGraph->SubGraphs.Add(BlendSpaceGraph);
}
}
}
UObject* UAnimGraphNode_BlendSpaceGraphBase::GetJumpTargetForDoubleClick() const
{
return BlendSpaceGraph;
}
void UAnimGraphNode_BlendSpaceGraphBase::JumpToDefinition() const
{
TSharedPtr<IBlueprintEditor> BlueprintEditor = FKismetEditorUtilities::GetIBlueprintEditorForObject(BlendSpace, true);
if(BlueprintEditor.IsValid())
{
BlueprintEditor->JumpToHyperlink(BlendSpaceGraph, false);
}
}
void UAnimGraphNode_BlendSpaceGraphBase::CustomizeDetails(IDetailLayoutBuilder& InDetailBuilder)
{
if (BlendSpace)
{
IDetailCategoryBuilder& BlendSpaceCategory = InDetailBuilder.EditCategory("Blend Space");
IDetailPropertyRow* BlendSpaceRow = BlendSpaceCategory.AddExternalObjects( { BlendSpace } );
BlendSpaceRow->ShouldAutoExpand(true);
TSharedRef<IPropertyHandle> XHandle = InDetailBuilder.GetProperty(TEXT("Node.X"), GetClass());
XHandle->SetPropertyDisplayName(FText::FromString(BlendSpace->GetBlendParameter(0).DisplayName));
TSharedRef<IPropertyHandle> YHandle = InDetailBuilder.GetProperty(TEXT("Node.Y"), GetClass());
if (BlendSpace->IsA<UBlendSpace1D>())
{
InDetailBuilder.HideProperty(YHandle);
}
else
{
YHandle->SetPropertyDisplayName(FText::FromString(BlendSpace->GetBlendParameter(1).DisplayName));
}
}
}
TArray<UEdGraph*> UAnimGraphNode_BlendSpaceGraphBase::GetSubGraphs() const
{
return TArray<UEdGraph*>( { BlendSpaceGraph } );
}
void UAnimGraphNode_BlendSpaceGraphBase::DestroyNode()
{
UBlueprint* Blueprint = GetBlueprint();
for (UEdGraph* SampleGraph : Graphs)
{
SampleGraph->Modify();
FBlueprintEditorUtils::RemoveGraph(Blueprint, SampleGraph);
}
UEdGraph* GraphToRemove = BlendSpaceGraph;
BlendSpaceGraph = nullptr;
Graphs.Empty();
Super::DestroyNode();
if (GraphToRemove)
{
GraphToRemove->Modify();
FBlueprintEditorUtils::RemoveGraph(Blueprint, GraphToRemove, EGraphRemoveFlags::Recompile);
}
}
void UAnimGraphNode_BlendSpaceGraphBase::PreloadRequiredAssets()
{
PreloadObject(BlendSpace);
Super::PreloadRequiredAssets();
}
TSharedPtr<INameValidatorInterface> UAnimGraphNode_BlendSpaceGraphBase::MakeNameValidator() const
{
class FNameValidator : public FStringSetNameValidator
{
public:
FNameValidator(const UAnimGraphNode_BlendSpaceGraphBase* InBlendSpaceGraphNode)
: FStringSetNameValidator(FString())
{
TArray<UAnimGraphNode_BlendSpaceGraphBase*> Nodes;
UAnimationGraph* AnimGraph = CastChecked<UAnimationGraph>(InBlendSpaceGraphNode->GetOuter());
AnimGraph->GetNodesOfClass<UAnimGraphNode_BlendSpaceGraphBase>(Nodes);
for (UAnimGraphNode_BlendSpaceGraphBase* Node : Nodes)
{
if (Node != InBlendSpaceGraphNode)
{
Names.Add(Node->GetBlendSpaceGraphName());
}
}
}
};
return MakeShared<FNameValidator>(this);
}
void UAnimGraphNode_BlendSpaceGraphBase::OnRenameNode(const FString& NewName)
{
FBlueprintEditorUtils::RenameGraph(BlendSpaceGraph, NewName);
}
FString UAnimGraphNode_BlendSpaceGraphBase::GetBlendSpaceGraphName() const
{
return (BlendSpaceGraph != nullptr) ? *(BlendSpaceGraph->GetName()) : TEXT("(null)");
}
FString UAnimGraphNode_BlendSpaceGraphBase::GetBlendSpaceName() const
{
return (BlendSpace != nullptr) ? *(BlendSpace->GetName()) : TEXT("(null)");
}
void UAnimGraphNode_BlendSpaceGraphBase::PostPasteNode()
{
Super::PostPasteNode();
if(BlendSpaceGraph != nullptr)
{
// Add the new graph as a child of our parent graph
UEdGraph* ParentGraph = GetGraph();
if(ParentGraph->SubGraphs.Find(BlendSpaceGraph) == INDEX_NONE)
{
ParentGraph->SubGraphs.Add(BlendSpaceGraph);
}
// Refresh sample graphs
for (UEdGraph* SampleGraph : Graphs)
{
for(UEdGraphNode* GraphNode : SampleGraph->Nodes)
{
GraphNode->CreateNewGuid();
GraphNode->PostPasteNode();
GraphNode->ReconstructNode();
}
}
// Find an interesting name
TSharedPtr<INameValidatorInterface> NameValidator = FNameValidatorFactory::MakeValidator(this);
FBlueprintEditorUtils::RenameGraphWithSuggestion(BlendSpaceGraph, NameValidator, BlendSpaceGraph->GetName());
// Restore transactional flag that is lost during copy/paste process
BlendSpaceGraph->SetFlags(RF_Transactional);
}
}
void UAnimGraphNode_BlendSpaceGraphBase::PostPlacedNewNode()
{
Super::PostPlacedNewNode();
// Create a new graph & blendspace if we havent been set up already
if(BlendSpaceGraph == nullptr)
{
check(BlendSpace == nullptr);
BlendSpaceGraph = CastChecked<UBlendSpaceGraph>(FBlueprintEditorUtils::CreateNewGraph(this, NAME_None, UBlendSpaceGraph::StaticClass(), UEdGraphSchema::StaticClass()));
BlendSpaceGraph->BlendSpace = BlendSpace = NewObject<UBlendSpace>();
// Find an interesting name
TSharedPtr<INameValidatorInterface> NameValidator = FNameValidatorFactory::MakeValidator(this);
FBlueprintEditorUtils::RenameGraphWithSuggestion(BlendSpaceGraph, NameValidator, TEXT("New Blend Space"));
// Add the new graph as a child of our parent graph
UEdGraph* ParentGraph = GetGraph();
if(ParentGraph->SubGraphs.Find(BlendSpaceGraph) == INDEX_NONE)
{
ParentGraph->Modify();
ParentGraph->SubGraphs.Add(BlendSpaceGraph);
}
}
}
void UAnimGraphNode_BlendSpaceGraphBase::CustomizePinData(UEdGraphPin* Pin, FName SourcePropertyName, int32 ArrayIndex) const
{
if (BlendSpace != nullptr)
{
if (SourcePropertyName == TEXT("X"))
{
Pin->PinFriendlyName = FText::FromString(BlendSpace->GetBlendParameter(0).DisplayName);
}
else if (SourcePropertyName == TEXT("Y"))
{
Pin->PinFriendlyName = FText::FromString(BlendSpace->GetBlendParameter(1).DisplayName);
Pin->bHidden = BlendSpace->IsA<UBlendSpace1D>() ? 1 : 0;
}
else if (SourcePropertyName == TEXT("Z"))
{
Pin->PinFriendlyName = FText::FromString(BlendSpace->GetBlendParameter(2).DisplayName);
}
}
}
void UAnimGraphNode_BlendSpaceGraphBase::PostProcessPinName(const UEdGraphPin* Pin, FString& DisplayName) const
{
if(Pin->Direction == EGPD_Input)
{
if(BlendSpace != nullptr)
{
if(Pin->PinName == TEXT("X"))
{
DisplayName = BlendSpace->GetBlendParameter(0).DisplayName;
}
else if(Pin->PinName == TEXT("Y"))
{
DisplayName = BlendSpace->GetBlendParameter(1).DisplayName;
}
else if(Pin->PinName == TEXT("Z"))
{
DisplayName = BlendSpace->GetBlendParameter(2).DisplayName;
}
}
}
Super::PostProcessPinName(Pin, DisplayName);
}
int32 UAnimGraphNode_BlendSpaceGraphBase::GetSampleIndex(const UEdGraph* Graph) const
{
for (int32 Index = 0 ; Index != Graphs.Num() ; ++Index)
{
if (Graphs[Index] == Graph)
{
return Index;
}
}
return INDEX_NONE;
}
UAnimationBlendSpaceSampleGraph* UAnimGraphNode_BlendSpaceGraphBase::AddGraph(FName InSampleName, UAnimSequence* InSequence)
{
UAnimationBlendSpaceSampleGraph* NewGraph = AddGraphInternal(InSampleName, InSequence);
Graphs.Add(NewGraph);
return NewGraph;
}
UAnimationBlendSpaceSampleGraph* UAnimGraphNode_BlendSpaceGraphBase::AddGraphInternal(FName InSampleName, UAnimSequence* InSequence)
{
const UAnimationGraphSchema* AnimationGraphSchema = GetDefault<UAnimationGraphSchema>();
Modify();
const FName NewGraphName = FBlueprintEditorUtils::GenerateUniqueGraphName(BlendSpaceGraph, InSampleName.ToString());
UAnimationBlendSpaceSampleGraph* SampleGraph = CastChecked<UAnimationBlendSpaceSampleGraph>(FBlueprintEditorUtils::CreateNewGraph(BlendSpaceGraph, NewGraphName, UAnimationBlendSpaceSampleGraph::StaticClass(), UAnimationGraphSchema::StaticClass()));
FGraphNodeCreator<UAnimGraphNode_BlendSpaceSampleResult> ResultNodeCreator(*SampleGraph);
UAnimGraphNode_BlendSpaceSampleResult* ResultSinkNode = ResultNodeCreator.CreateNode();
ResultNodeCreator.Finalize();
AnimationGraphSchema->SetNodeMetaData(ResultSinkNode, FNodeMetadata::DefaultGraphNode);
SampleGraph->ResultNode = ResultSinkNode;
SampleGraph->bAllowDeletion = false;
SampleGraph->bAllowRenaming = true;
if(InSequence != nullptr)
{
// Attach an asset player if a valid animation is supplied
FGraphNodeCreator<UAnimGraphNode_SequencePlayer> SequencePlayerNodeCreator(*SampleGraph);
UAnimGraphNode_SequencePlayer* SequencePlayer = SequencePlayerNodeCreator.CreateNode();
SequencePlayer->SetAnimationAsset(InSequence);
SequencePlayer->Node.SetGroupMethod(EAnimSyncMethod::Graph);
SequencePlayerNodeCreator.Finalize();
// Offset node in X
SequencePlayer->NodePosX = SampleGraph->ResultNode->NodePosX - 400;
UEdGraphPin* OutputPin = SequencePlayer->FindPinChecked(TEXT("Pose"), EGPD_Output);
UEdGraphPin* InputPin = SampleGraph->ResultNode->FindPinChecked(TEXT("Result"), EGPD_Input);
OutputPin->MakeLinkTo(InputPin);
}
BlendSpaceGraph->Modify();
BlendSpaceGraph->SubGraphs.Add(SampleGraph);
return SampleGraph;
}
void UAnimGraphNode_BlendSpaceGraphBase::RemoveGraph(int32 InSampleIndex)
{
Modify();
check(Graphs.IsValidIndex(InSampleIndex));
UEdGraph* GraphToRemove = Graphs[InSampleIndex];
TSharedPtr<FBlueprintEditor> BlueprintEditor = StaticCastSharedPtr<FBlueprintEditor>(FKismetEditorUtilities::GetIBlueprintEditorForObject(GraphToRemove, false));
check(BlueprintEditor.IsValid());
// 'Asset' blendspace sample deletion uses a RemoveAtSwap, so we must mirror it here to maintain the same indices
Graphs.RemoveAtSwap(InSampleIndex);
FBlueprintEditorUtils::RemoveGraph(GetAnimBlueprint(), GraphToRemove);
BlueprintEditor->CloseDocumentTab(GraphToRemove);
}
void UAnimGraphNode_BlendSpaceGraphBase::ReplaceGraph(int32 InSampleIndex, UAnimSequence* InSequence)
{
Modify();
check(Graphs.IsValidIndex(InSampleIndex));
UEdGraph* GraphToRemove = Graphs[InSampleIndex];
FName GraphName = GraphToRemove->GetFName();
TSharedPtr<FBlueprintEditor> BlueprintEditor = StaticCastSharedPtr<FBlueprintEditor>(FKismetEditorUtilities::GetIBlueprintEditorForObject(GraphToRemove, false));
check(BlueprintEditor.IsValid());
FBlueprintEditorUtils::RemoveGraph(GetAnimBlueprint(), GraphToRemove);
BlueprintEditor->CloseDocumentTab(GraphToRemove);
Graphs[InSampleIndex] = AddGraphInternal(GraphName, InSequence);
}
FName UAnimGraphNode_BlendSpaceGraphBase::GetSyncGroupName() const
{
FStructProperty* NodeProperty = GetFNodeProperty();
const FAnimNode_BlendSpaceGraphBase* AnimNode = NodeProperty->ContainerPtrToValuePtr<FAnimNode_BlendSpaceGraphBase>(this);
return AnimNode->GroupName;
}
void UAnimGraphNode_BlendSpaceGraphBase::SetSyncGroupName(FName InName)
{
FStructProperty* NodeProperty = GetFNodeProperty();
FAnimNode_BlendSpaceGraphBase* AnimNode = NodeProperty->ContainerPtrToValuePtr<FAnimNode_BlendSpaceGraphBase>(this);
AnimNode->GroupName = InName;
}
void UAnimGraphNode_BlendSpaceGraphBase::GetInputLinkAttributes(FNodeAttributeArray& OutAttributes) const
{
if(GetSyncGroupName() != NAME_None)
{
OutAttributes.Add(UE::Anim::FAnimSync::Attribute);
}
}
void UAnimGraphNode_BlendSpaceGraphBase::GetRequiredExtensions(TArray<TSubclassOf<UAnimBlueprintExtension>>& OutExtensions) const
{
OutExtensions.Add(UAnimBlueprintExtension_BlendSpaceGraph::StaticClass());
}
bool UAnimGraphNode_BlendSpaceGraphBase::IsActionFilteredOut(class FBlueprintActionFilter const& Filter)
{
bool bIsFilteredOut = false;
FBlueprintActionContext const& FilterContext = Filter.Context;
for (UBlueprint* Blueprint : FilterContext.Blueprints)
{
if (UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(Blueprint))
{
if(!SkeletonName.IsEmpty())
{
if(AnimBlueprint->TargetSkeleton == nullptr || !AnimBlueprint->TargetSkeleton->IsCompatibleForEditor(SkeletonName))
{
bIsFilteredOut = true;
break;
}
}
}
else
{
// Not an animation Blueprint, cannot use
bIsFilteredOut = true;
break;
}
}
return bIsFilteredOut;
}
#undef LOCTEXT_NAMESPACE