Files
UnrealEngineUWP/Engine/Source/Editor/AnimGraph/Private/AnimGraphNode_RotationOffsetBlendSpace.cpp
benoit gadreau 5bac2b1cb2 fixed crash when validating nodes: template anim BPs don't have any skeleton assigned
#rb thomas.sarkanen
[FYI] kiaran.ritchie
#jira UE-142972
#preflight 6214d1cc9e2201e21406a740
#lockdown tbd

#ROBOMERGE-AUTHOR: benoit.gadreau
#ROBOMERGE-SOURCE: CL 19071009 in //UE5/Release-5.0/... via CL 19090369
#ROBOMERGE-BOT: UE5 (Release-Engine-Staging -> Main) (v921-19075845)

[CL 19134147 by benoit gadreau in ue5-main branch]
2022-02-24 19:44:00 -05:00

358 lines
13 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimGraphNode_RotationOffsetBlendSpace.h"
#include "UObject/UObjectHash.h"
#include "UObject/UObjectIterator.h"
#include "ToolMenus.h"
#include "GraphEditorActions.h"
#include "Kismet2/CompilerResultsLog.h"
#include "BlueprintNodeSpawner.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "Animation/AnimationSettings.h"
#include "Animation/AimOffsetBlendSpace.h"
#include "Animation/AimOffsetBlendSpace1D.h"
#include "DetailLayoutBuilder.h"
#include "ScopedTransaction.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "AnimGraphCommands.h"
#include "BlueprintNodeTemplateCache.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "IAnimBlueprintNodeOverrideAssetsContext.h"
/////////////////////////////////////////////////////
// UAnimGraphNode_RotationOffsetBlendSpace
#define LOCTEXT_NAMESPACE "UAnimGraphNode_RotationOffsetBlendSpace"
UAnimGraphNode_RotationOffsetBlendSpace::UAnimGraphNode_RotationOffsetBlendSpace(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
}
FText UAnimGraphNode_RotationOffsetBlendSpace::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
UBlendSpace* BlendSpaceToCheck = Node.GetBlendSpace();
UEdGraphPin* BlendSpacePin = FindPin(GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, BlendSpace));
if (BlendSpacePin != nullptr && BlendSpaceToCheck == nullptr)
{
BlendSpaceToCheck = Cast<UBlendSpace>(BlendSpacePin->DefaultObject);
}
if (BlendSpaceToCheck == nullptr)
{
if (TitleType == ENodeTitleType::ListView || TitleType == ENodeTitleType::MenuTitle)
{
return LOCTEXT("RotationOffsetBlend_NONE_ListTitle", "AimOffset Player '(None)'");
}
else
{
return LOCTEXT("RotationOffsetBlend_NONE_Title", "(None)\nAimOffset Player");
}
}
// @TODO: the bone can be altered in the property editor, so we have to
// choose to mark this dirty when that happens for this to properly work
else //if (!CachedNodeTitles.IsTitleCached(TitleType, this))
{
const FText BlendSpaceName = FText::FromString(BlendSpaceToCheck->GetName());
FFormatNamedArguments Args;
Args.Add(TEXT("BlendSpaceName"), BlendSpaceName);
// FText::Format() is slow, so we cache this to save on performance
if (TitleType == ENodeTitleType::ListView || TitleType == ENodeTitleType::MenuTitle)
{
CachedNodeTitles.SetCachedTitle(TitleType, FText::Format(LOCTEXT("AimOffsetListTitle", "AimOffset Player '{BlendSpaceName}'"), Args), this);
}
else
{
CachedNodeTitles.SetCachedTitle(TitleType, FText::Format(LOCTEXT("AimOffsetFullTitle", "{BlendSpaceName}\nAimOffset Player"), Args), this);
}
}
return CachedNodeTitles[TitleType];
}
void UAnimGraphNode_RotationOffsetBlendSpace::GetMenuActions(FBlueprintActionDatabaseRegistrar& InActionRegistrar) const
{
GetMenuActionsHelper(
InActionRegistrar,
GetClass(),
{ UAimOffsetBlendSpace::StaticClass(), UAimOffsetBlendSpace1D::StaticClass() },
{ },
[](const FAssetData& InAssetData, UClass* InClass)
{
if(InAssetData.IsValid())
{
return FText::Format(LOCTEXT("MenuDescFormat", "AimOffset Player '{0}'"), FText::FromName(InAssetData.AssetName));
}
else
{
return LOCTEXT("MenuDesc", "AimOffset Player");
}
},
[](const FAssetData& InAssetData, UClass* InClass)
{
if(InAssetData.IsValid())
{
return FText::Format(LOCTEXT("MenuDescTooltipFormat", "AimOffset Player\n'{0}'"), FText::FromName(InAssetData.ObjectPath));
}
else
{
return LOCTEXT("MenuDescTooltip", "AimOffset Player");
}
},
[](UEdGraphNode* InNewNode, bool bInIsTemplateNode, const FAssetData InAssetData)
{
UAnimGraphNode_AssetPlayerBase::SetupNewNode(InNewNode, bInIsTemplateNode, InAssetData);
});
}
FBlueprintNodeSignature UAnimGraphNode_RotationOffsetBlendSpace::GetSignature() const
{
FBlueprintNodeSignature NodeSignature = Super::GetSignature();
NodeSignature.AddSubObject(Node.GetBlendSpace());
return NodeSignature;
}
void UAnimGraphNode_RotationOffsetBlendSpace::SetAnimationAsset(UAnimationAsset* Asset)
{
if (UBlendSpace* BlendSpace = Cast<UBlendSpace>(Asset))
{
Node.SetBlendSpace(BlendSpace);
}
}
void UAnimGraphNode_RotationOffsetBlendSpace::OnOverrideAssets(IAnimBlueprintNodeOverrideAssetsContext& InContext) const
{
if(InContext.GetAssets().Num() > 0)
{
if (UBlendSpace* BlendSpace = Cast<UBlendSpace>(InContext.GetAssets()[0]))
{
FAnimNode_BlendSpacePlayer& AnimNode = InContext.GetAnimNode<FAnimNode_BlendSpacePlayer>();
AnimNode.SetBlendSpace(BlendSpace);
}
}
}
void UAnimGraphNode_RotationOffsetBlendSpace::ValidateAnimNodeDuringCompilation(class USkeleton* ForSkeleton, class FCompilerResultsLog& MessageLog)
{
Super::ValidateAnimNodeDuringCompilation(ForSkeleton, MessageLog);
UBlendSpace* BlendSpaceToCheck = Node.GetBlendSpace();
UEdGraphPin* BlendSpacePin = FindPin(GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, BlendSpace));
if (BlendSpacePin != nullptr && BlendSpaceToCheck == nullptr)
{
BlendSpaceToCheck = Cast<UBlendSpace>(BlendSpacePin->DefaultObject);
}
if (BlendSpaceToCheck == NULL)
{
// we may have a connected node
if (BlendSpacePin == nullptr || BlendSpacePin->LinkedTo.Num() == 0)
{
MessageLog.Error(TEXT("@@ references an unknown blend space"), this);
}
}
else if (Cast<UAimOffsetBlendSpace>(BlendSpaceToCheck) == NULL &&
Cast<UAimOffsetBlendSpace1D>(BlendSpaceToCheck) == NULL)
{
MessageLog.Error(TEXT("@@ references an invalid blend space (one that is not an aim offset)"), this);
}
else if (ForSkeleton)
{
USkeleton* BlendSpaceSkeleton = BlendSpaceToCheck->GetSkeleton();
if (BlendSpaceSkeleton && // if blend space doesn't have skeleton, it might be due to blend space not loaded yet, @todo: wait with anim blueprint compilation until all assets are loaded?
!ForSkeleton->IsCompatible(BlendSpaceSkeleton))
{
MessageLog.Error(TEXT("@@ references blendspace that uses an incompatible skeleton @@"), this, BlendSpaceSkeleton);
}
}
if (UAnimationSettings::Get()->bEnablePerformanceLog)
{
if (Node.LODThreshold < 0)
{
MessageLog.Warning(TEXT("@@ contains no LOD Threshold."), this);
}
}
}
void UAnimGraphNode_RotationOffsetBlendSpace::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
{
if (!Context->bIsDebugging)
{
// add an option to convert to single frame
{
FToolMenuSection& Section = Menu->AddSection("AnimGraphNodeBlendSpacePlayer", LOCTEXT("BlendSpaceHeading", "Blend Space"));
Section.AddMenuEntry(FAnimGraphCommands::Get().OpenRelatedAsset);
Section.AddMenuEntry(FAnimGraphCommands::Get().ConvertToAimOffsetLookAt);
Section.AddMenuEntry(FAnimGraphCommands::Get().ConvertToAimOffsetGraph);
}
}
}
void UAnimGraphNode_RotationOffsetBlendSpace::GetAllAnimationSequencesReferred(TArray<UAnimationAsset*>& AnimationAssets) const
{
if(Node.GetBlendSpace())
{
HandleAnimReferenceCollection(Node.BlendSpace, AnimationAssets);
}
}
void UAnimGraphNode_RotationOffsetBlendSpace::ReplaceReferredAnimations(const TMap<UAnimationAsset*, UAnimationAsset*>& AnimAssetReplacementMap)
{
HandleAnimReferenceReplacement(Node.BlendSpace, AnimAssetReplacementMap);
}
EAnimAssetHandlerType UAnimGraphNode_RotationOffsetBlendSpace::SupportsAssetClass(const UClass* AssetClass) const
{
if (AssetClass->IsChildOf(UBlendSpace::StaticClass()) && IsAimOffsetBlendSpace(AssetClass))
{
return EAnimAssetHandlerType::PrimaryHandler;
}
else
{
return EAnimAssetHandlerType::NotSupported;
}
}
void UAnimGraphNode_RotationOffsetBlendSpace::CustomizePinData(UEdGraphPin* Pin, FName SourcePropertyName, int32 ArrayIndex) const
{
Super::CustomizePinData(Pin, SourcePropertyName, ArrayIndex);
if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, Alpha))
{
Pin->bHidden = (Node.AlphaInputType != EAnimAlphaInputType::Float);
if (!Pin->bHidden)
{
Pin->PinFriendlyName = Node.AlphaScaleBias.GetFriendlyName(Node.AlphaScaleBiasClamp.GetFriendlyName(Pin->PinFriendlyName));
}
}
if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, bAlphaBoolEnabled))
{
Pin->bHidden = (Node.AlphaInputType != EAnimAlphaInputType::Bool);
}
if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaCurveName))
{
Pin->bHidden = (Node.AlphaInputType != EAnimAlphaInputType::Curve);
if (!Pin->bHidden)
{
Pin->PinFriendlyName = Node.AlphaScaleBiasClamp.GetFriendlyName(Pin->PinFriendlyName);
}
}
}
void UAnimGraphNode_RotationOffsetBlendSpace::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
{
const FName PropertyName = (PropertyChangedEvent.Property ? PropertyChangedEvent.Property->GetFName() : NAME_None);
// Reconstruct node to show updates to PinFriendlyNames.
if ((PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaScaleBias))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, bMapRange))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputRange, Min))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputRange, Max))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, Scale))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, Bias))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, bClampResult))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, ClampMin))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, ClampMax))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, bInterpResult))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, InterpSpeedIncreasing))
|| (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FInputScaleBiasClamp, InterpSpeedDecreasing)))
{
ReconstructNode();
}
if (PropertyName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaInputType))
{
FScopedTransaction Transaction(LOCTEXT("ChangeAlphaInputType", "Change Alpha Input Type"));
Modify();
// Break links to pins going away
for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex)
{
UEdGraphPin* Pin = Pins[PinIndex];
if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, Alpha))
{
if (Node.AlphaInputType != EAnimAlphaInputType::Float)
{
Pin->BreakAllPinLinks();
PropertyBindings.Remove(Pin->PinName);
}
}
else if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, bAlphaBoolEnabled))
{
if (Node.AlphaInputType != EAnimAlphaInputType::Bool)
{
Pin->BreakAllPinLinks();
PropertyBindings.Remove(Pin->PinName);
}
}
else if (Pin->PinName == GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaCurveName))
{
if (Node.AlphaInputType != EAnimAlphaInputType::Curve)
{
Pin->BreakAllPinLinks();
PropertyBindings.Remove(Pin->PinName);
}
}
}
ReconstructNode();
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}
Super::PostEditChangeProperty(PropertyChangedEvent);
}
void UAnimGraphNode_RotationOffsetBlendSpace::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
{
Super::CustomizeDetails(DetailBuilder);
TSharedRef<IPropertyHandle> NodeHandle = DetailBuilder.GetProperty(FName(TEXT("Node")), GetClass());
if (Node.AlphaInputType != EAnimAlphaInputType::Bool)
{
DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimNode_RotationOffsetBlendSpace, bAlphaBoolEnabled)));
DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaBoolBlend)));
}
if (Node.AlphaInputType != EAnimAlphaInputType::Float)
{
DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimNode_RotationOffsetBlendSpace, Alpha)));
DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaScaleBias)));
}
if (Node.AlphaInputType != EAnimAlphaInputType::Curve)
{
DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaCurveName)));
}
if ((Node.AlphaInputType != EAnimAlphaInputType::Float)
&& (Node.AlphaInputType != EAnimAlphaInputType::Curve))
{
DetailBuilder.HideProperty(NodeHandle->GetChildHandle(GET_MEMBER_NAME_CHECKED(FAnimNode_RotationOffsetBlendSpace, AlphaScaleBiasClamp)));
}
}
UAnimationAsset* UAnimGraphNode_RotationOffsetBlendSpace::GetAnimationAsset() const
{
UBlendSpace* BlendSpace = Node.GetBlendSpace();
UEdGraphPin* BlendSpacePin = FindPin(GET_MEMBER_NAME_STRING_CHECKED(FAnimNode_RotationOffsetBlendSpace, BlendSpace));
if (BlendSpacePin != nullptr && BlendSpace == nullptr)
{
BlendSpace = Cast<UBlendSpace>(BlendSpacePin->DefaultObject);
}
return BlendSpace;
}
#undef LOCTEXT_NAMESPACE