You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
Added a new animation graph node that hosts its own UBlendSpaceBase. Modified UBlendSpaceBase to allow for pose links to be evaluated as the sample points. The new blend space graphs can be spawned from existing UBlendSpace and UBlendSpace1D assets, or they can be created from scratch, or they can be converted from existing blendspace player nodes via the context menu. Fixed anim node conversion functions so that their transactions work correctly. Updated FBlueprintEditorUtils::IsGraphNameUnique to allow it to work with any object as the outer, not just UBlueprint. UBlueprint still has a special case for functions and events. This is to support GenerateUniqueGraphName within a scope (e.g. an outer graph). Formalized the concept of 'node sub-graphs' (as well as the composite node pattern a little). Previously a number of known node types that contained sub-graphs (e.g. UK2Node_Composite) had special case logic for dealing with node/graph deletion etc. Now any node can opt into this behaviour via the GetSubGraphs() override. Added status bar readouts for the blendspace grid, so we dont have to stuff the prompts into the tooltip any more. Moved anim BP related APIs out of FBlueprintEditor. They are always used via FAnimationBlueprintEditor. Refactored graph title bar widget creation out into a function to allow other document tab factories to create it. Altered breadcrumb trail click callbacks and SMyBlueprint::ExecuteAction to always JumpToHyperLink rather than calling OpenDocument directly. This allows unknown (to FBlueprintEditor) document types that reference objects to be correctly jumped to using the breadcrumb trail. Derived asset editors (i.e. FAnimationBlueprintEditor) can intercept the JumpToHyperlink call to ensure that the correct document is presented (i.e. the correct tab payload is generated). Instead of making yet another bunch of duplicated code for handling the various alpha blend options, refactored this into FAnimGraphNodeAlphaOptions (for editor code) and FAnimNodeAlphaOptions (for runtime code). Added OnCopyTermDefaultsToDefaultObject for per-node copying of default values from editor node to runtime node, rather than another special-case in the compiler. #rb Jurre.deBaare,Phillip.Kavan [CL 15177316 by Thomas Sarkanen in ue5-main branch]
774 lines
24 KiB
C++
774 lines
24 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
/*=============================================================================
|
|
AnimStateTransitionNode.cpp
|
|
=============================================================================*/
|
|
|
|
#include "AnimStateTransitionNode.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Animation/AnimInstance.h"
|
|
#include "AnimationTransitionGraph.h"
|
|
#include "AnimationTransitionSchema.h"
|
|
#include "AnimationCustomTransitionGraph.h"
|
|
#include "AnimationCustomTransitionSchema.h"
|
|
#include "AnimGraphNode_TransitionResult.h"
|
|
#include "Kismet2/CompilerResultsLog.h"
|
|
#include "EdGraphUtilities.h"
|
|
#include "Kismet2/Kismet2NameValidators.h"
|
|
#include "ScopedTransaction.h"
|
|
#include "Animation/BlendProfile.h"
|
|
#include "UObject/UE5MainStreamObjectVersion.h"
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// IAnimStateTransitionNodeSharedDataHelper
|
|
|
|
#define LOCTEXT_NAMESPACE "A3Nodes"
|
|
|
|
class ANIMGRAPH_API IAnimStateTransitionNodeSharedDataHelper
|
|
{
|
|
public:
|
|
void UpdateSharedData(UAnimStateTransitionNode* Node, TSharedPtr<INameValidatorInterface> NameValidator);
|
|
void MakeSureGuidExists(UAnimStateTransitionNode* Node);
|
|
|
|
protected:
|
|
virtual bool CheckIfNodesShouldShareData(const UAnimStateTransitionNode* NodeA, const UAnimStateTransitionNode* NodeB) = 0;
|
|
virtual bool CheckIfHasDataToShare(const UAnimStateTransitionNode* Node) = 0;
|
|
virtual void ShareData(UAnimStateTransitionNode* NodeWhoWantsToShare, const UAnimStateTransitionNode* ShareFrom) = 0;
|
|
virtual FString& AccessShareDataName(UAnimStateTransitionNode* Node) = 0;
|
|
virtual FGuid& AccessShareDataGuid(UAnimStateTransitionNode* Node) = 0;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FAnimStateTransitionNodeSharedRulesHelper
|
|
|
|
class ANIMGRAPH_API FAnimStateTransitionNodeSharedRulesHelper : public IAnimStateTransitionNodeSharedDataHelper
|
|
{
|
|
protected:
|
|
virtual bool CheckIfNodesShouldShareData(const UAnimStateTransitionNode* NodeA, const UAnimStateTransitionNode* NodeB) override;
|
|
virtual bool CheckIfHasDataToShare(const UAnimStateTransitionNode* Node) override;
|
|
virtual void ShareData(UAnimStateTransitionNode* NodeWhoWantsToShare, const UAnimStateTransitionNode* ShareFrom) override;
|
|
virtual FString& AccessShareDataName(UAnimStateTransitionNode* Node) override;
|
|
virtual FGuid& AccessShareDataGuid(UAnimStateTransitionNode* Node) override;
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FAnimStateTransitionNodeSharedCrossfadeHelper
|
|
|
|
class ANIMGRAPH_API FAnimStateTransitionNodeSharedCrossfadeHelper : public IAnimStateTransitionNodeSharedDataHelper
|
|
{
|
|
protected:
|
|
virtual bool CheckIfNodesShouldShareData(const UAnimStateTransitionNode* NodeA, const UAnimStateTransitionNode* NodeB) override;
|
|
virtual bool CheckIfHasDataToShare(const UAnimStateTransitionNode* Node) override;
|
|
virtual void ShareData(UAnimStateTransitionNode* NodeWhoWantsToShare, const UAnimStateTransitionNode* ShareFrom) override;
|
|
virtual FString& AccessShareDataName(UAnimStateTransitionNode* Node) override;
|
|
virtual FGuid& AccessShareDataGuid(UAnimStateTransitionNode* Node) override;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////
|
|
// UAnimStateTransitionNode
|
|
|
|
UAnimStateTransitionNode::UAnimStateTransitionNode(const FObjectInitializer& ObjectInitializer)
|
|
: Super(ObjectInitializer)
|
|
{
|
|
|
|
CrossfadeDuration = 0.2f;
|
|
BlendMode = EAlphaBlendOption::HermiteCubic;
|
|
bSharedRules = false;
|
|
SharedRulesGuid.Invalidate();
|
|
bSharedCrossfade = false;
|
|
SharedCrossfadeIdx = INDEX_NONE;
|
|
SharedCrossfadeGuid.Invalidate();
|
|
Bidirectional = false;
|
|
PriorityOrder = 1;
|
|
LogicType = ETransitionLogicType::TLT_StandardBlend;
|
|
}
|
|
|
|
void UAnimStateTransitionNode::AllocateDefaultPins()
|
|
{
|
|
UEdGraphPin* Inputs = CreatePin(EGPD_Input, TEXT("Transition"), TEXT("In"));
|
|
Inputs->bHidden = true;
|
|
UEdGraphPin* Outputs = CreatePin(EGPD_Output, TEXT("Transition"), TEXT("Out"));
|
|
Outputs->bHidden = true;
|
|
}
|
|
|
|
void UAnimStateTransitionNode::PostPlacedNewNode()
|
|
{
|
|
CreateBoundGraph();
|
|
}
|
|
|
|
void UAnimStateTransitionNode::PostLoad()
|
|
{
|
|
Super::PostLoad();
|
|
|
|
// make sure we have guid for shared rules
|
|
if (bSharedRules && !SharedRulesGuid.IsValid())
|
|
{
|
|
FAnimStateTransitionNodeSharedRulesHelper().MakeSureGuidExists(this);
|
|
}
|
|
|
|
// make sure we have guid for shared crossfade
|
|
if (bSharedCrossfade && !SharedCrossfadeGuid.IsValid())
|
|
{
|
|
FAnimStateTransitionNodeSharedCrossfadeHelper().MakeSureGuidExists(this);
|
|
}
|
|
|
|
if(GetLinkerUE4Version() < VER_UE4_ADDED_NON_LINEAR_TRANSITION_BLENDS)
|
|
{
|
|
switch(CrossfadeMode_DEPRECATED)
|
|
{
|
|
case ETransitionBlendMode::TBM_Linear:
|
|
BlendMode = EAlphaBlendOption::Linear;
|
|
break;
|
|
case ETransitionBlendMode::TBM_Cubic:
|
|
// Old cubic was actually an in/out hermite polynomial (FMath::SmoothStep)
|
|
BlendMode = EAlphaBlendOption::HermiteCubic;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(GetLinkerCustomVersion(FAnimPhysObjectVersion::GUID) < FAnimPhysObjectVersion::FixupBadBlendProfileReferences)
|
|
{
|
|
ValidateBlendProfile();
|
|
}
|
|
}
|
|
|
|
bool UAnimStateTransitionNode::ValidateBlendProfile()
|
|
{
|
|
if(BlendProfile)
|
|
{
|
|
// validate the skeleton of our blend profile
|
|
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
|
|
UAnimBlueprint* AnimBP = CastChecked<UAnimBlueprint>(Blueprint);
|
|
|
|
if(AnimBP->TargetSkeleton && !AnimBP->TargetSkeleton->BlendProfiles.Contains(BlendProfile))
|
|
{
|
|
BlendProfile = nullptr;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void UAnimStateTransitionNode::PostPasteNode()
|
|
{
|
|
if (bSharedRules)
|
|
{
|
|
FAnimStateTransitionNodeSharedRulesHelper().UpdateSharedData(this, MakeShareable(new FAnimStateTransitionNodeSharedRulesNameValidator(this)));
|
|
}
|
|
|
|
if (bSharedCrossfade)
|
|
{
|
|
FAnimStateTransitionNodeSharedCrossfadeHelper().UpdateSharedData(this, MakeShareable(new FAnimStateTransitionNodeSharedCrossfadeNameValidator(this)));
|
|
}
|
|
|
|
if (BoundGraph == NULL)
|
|
{
|
|
// fail-safe, create empty transition graph
|
|
CreateBoundGraph();
|
|
}
|
|
|
|
for (UEdGraphNode* GraphNode : BoundGraph->Nodes)
|
|
{
|
|
GraphNode->CreateNewGuid();
|
|
GraphNode->PostPasteNode();
|
|
GraphNode->ReconstructNode();
|
|
}
|
|
|
|
if(CustomTransitionGraph)
|
|
{
|
|
// Needs to be added to the parent graph
|
|
UEdGraph* ParentGraph = GetGraph();
|
|
|
|
if(ParentGraph->SubGraphs.Find(CustomTransitionGraph) == INDEX_NONE)
|
|
{
|
|
ParentGraph->SubGraphs.Add(CustomTransitionGraph);
|
|
}
|
|
|
|
// Transactional flag is lost in copy/paste, restore it.
|
|
CustomTransitionGraph->SetFlags(RF_Transactional);
|
|
}
|
|
|
|
ValidateBlendProfile();
|
|
|
|
Super::PostPasteNode();
|
|
|
|
// We don't want to paste nodes in that aren't fully linked (transition nodes have fixed pins as they
|
|
// really describe the connection between two other nodes). If we find one missing link, get rid of the node.
|
|
for(UEdGraphPin* Pin : Pins)
|
|
{
|
|
if(Pin->LinkedTo.Num() == 0)
|
|
{
|
|
DestroyNode();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
FText UAnimStateTransitionNode::GetNodeTitle(ENodeTitleType::Type TitleType) const
|
|
{
|
|
UAnimStateNodeBase* PrevState = GetPreviousState();
|
|
UAnimStateNodeBase* NextState = GetNextState();
|
|
|
|
if (!SharedRulesName.IsEmpty())
|
|
{
|
|
return FText::FromString(SharedRulesName);
|
|
}
|
|
else if ((PrevState != NULL) && (NextState != NULL))
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("PrevState"), FText::FromString(PrevState->GetStateName()));
|
|
Args.Add(TEXT("NextState"), FText::FromString(NextState->GetStateName()));
|
|
|
|
return FText::Format(LOCTEXT("PrevStateToNewState", "{PrevState} to {NextState}"), Args);
|
|
}
|
|
else
|
|
{
|
|
FFormatNamedArguments Args;
|
|
Args.Add(TEXT("BoundGraph"), (BoundGraph != NULL) ? FText::FromString(BoundGraph->GetName()) : LOCTEXT("Null", "(null)") );
|
|
// @TODO: FText::Format() is slow, and we could benefit from caching
|
|
// this off like we do for a lot of other nodes (but we have to
|
|
// make sure to invalidate the cached string at the appropriate
|
|
// times).
|
|
return FText::Format(LOCTEXT("TransitioNState", "Trans {BoundGraph}}"), Args);
|
|
}
|
|
}
|
|
|
|
FText UAnimStateTransitionNode::GetTooltipText() const
|
|
{
|
|
return LOCTEXT("StateTransitionTooltip", "This is a state transition");
|
|
}
|
|
|
|
UAnimStateNodeBase* UAnimStateTransitionNode::GetPreviousState() const
|
|
{
|
|
if (Pins[0]->LinkedTo.Num() > 0)
|
|
{
|
|
return Cast<UAnimStateNodeBase>(Pins[0]->LinkedTo[0]->GetOwningNode());
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
UAnimStateNodeBase* UAnimStateTransitionNode::GetNextState() const
|
|
{
|
|
if (Pins[1]->LinkedTo.Num() > 0)
|
|
{
|
|
return Cast<UAnimStateNodeBase>(Pins[1]->LinkedTo[0]->GetOwningNode());
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
FLinearColor UAnimStateTransitionNode::GetNodeTitleColor() const
|
|
{
|
|
return FColorList::Red;
|
|
}
|
|
|
|
void UAnimStateTransitionNode::PinConnectionListChanged(UEdGraphPin* Pin)
|
|
{
|
|
if (Pin->LinkedTo.Num() == 0)
|
|
{
|
|
// Commit suicide; transitions must always have an input and output connection
|
|
Modify();
|
|
|
|
// Our parent graph will have our graph in SubGraphs so needs to be modified to record that.
|
|
if(UEdGraph* ParentGraph = GetGraph())
|
|
{
|
|
ParentGraph->Modify();
|
|
}
|
|
|
|
DestroyNode();
|
|
}
|
|
}
|
|
|
|
void UAnimStateTransitionNode::CreateConnections(UAnimStateNodeBase* PreviousState, UAnimStateNodeBase* NextState)
|
|
{
|
|
// Previous to this
|
|
Pins[0]->Modify();
|
|
Pins[0]->LinkedTo.Empty();
|
|
|
|
PreviousState->GetOutputPin()->Modify();
|
|
Pins[0]->MakeLinkTo(PreviousState->GetOutputPin());
|
|
|
|
// This to next
|
|
Pins[1]->Modify();
|
|
Pins[1]->LinkedTo.Empty();
|
|
|
|
NextState->GetInputPin()->Modify();
|
|
Pins[1]->MakeLinkTo(NextState->GetInputPin());
|
|
}
|
|
|
|
void UAnimStateTransitionNode::PrepareForCopying()
|
|
{
|
|
Super::PrepareForCopying();
|
|
// move bound graph node here, so during copying it will be referenced
|
|
// for shared nodes at least one of them has to be referencing it, so we will be fine
|
|
BoundGraph->Rename(NULL, this, REN_DoNotDirty | REN_DontCreateRedirectors);
|
|
}
|
|
|
|
void UAnimStateTransitionNode::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
|
{
|
|
FName PropertyName = (PropertyChangedEvent.Property != NULL) ? PropertyChangedEvent.Property->GetFName() : NAME_None;
|
|
|
|
if (PropertyName == GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, CrossfadeDuration) ||
|
|
PropertyName == GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, BlendMode) ||
|
|
PropertyName == GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, CustomBlendCurve) ||
|
|
PropertyName == GET_MEMBER_NAME_CHECKED(UAnimStateTransitionNode, BlendProfile))
|
|
{
|
|
PropagateCrossfadeSettings();
|
|
}
|
|
|
|
if (PropertyName == FName(TEXT("LogicType")) )
|
|
{
|
|
if ((LogicType == ETransitionLogicType::TLT_Custom) && (CustomTransitionGraph == NULL))
|
|
{
|
|
CreateCustomTransitionGraph();
|
|
}
|
|
else if (CustomTransitionGraph != NULL)
|
|
{
|
|
// UAnimationCustomTransitionSchema::HandleGraphBeingDeleted resets logic type, so we'll need to restore it after RemoveGraph
|
|
const TEnumAsByte<ETransitionLogicType::Type> DesiredLogicType = LogicType;
|
|
|
|
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
|
|
FBlueprintEditorUtils::RemoveGraph(Blueprint, CustomTransitionGraph);
|
|
CustomTransitionGraph = NULL;
|
|
|
|
LogicType = DesiredLogicType;
|
|
}
|
|
}
|
|
|
|
Super::PostEditChangeProperty(PropertyChangedEvent);
|
|
}
|
|
|
|
FString UAnimStateTransitionNode::GetStateName() const
|
|
{
|
|
return (BoundGraph != NULL) ? *(BoundGraph->GetName()) : TEXT("(null)");
|
|
}
|
|
|
|
void UAnimStateTransitionNode::MakeRulesShareable(FString ShareName)
|
|
{
|
|
bSharedRules = true;
|
|
SharedRulesName = ShareName;
|
|
SharedRulesGuid = FGuid::NewGuid();
|
|
}
|
|
|
|
void UAnimStateTransitionNode::MakeCrossfadeShareable(FString ShareName)
|
|
{
|
|
// Give us a unique idx. This remaps every SharedCrossfadeIdx in the graph (in case some were deleted)
|
|
UEdGraph* CurrentGraph = GetGraph();
|
|
|
|
SharedCrossfadeIdx = INDEX_NONE;
|
|
TArray<int32> Remap;
|
|
for (int32 idx=0; idx < CurrentGraph->Nodes.Num(); idx++)
|
|
{
|
|
if (UAnimStateTransitionNode* Node = Cast<UAnimStateTransitionNode>(CurrentGraph->Nodes[idx]))
|
|
{
|
|
if (Node->SharedCrossfadeIdx != INDEX_NONE || Node == this)
|
|
{
|
|
Node->SharedCrossfadeIdx = Remap.AddUnique(Node->SharedCrossfadeIdx)+1; // Remaps existing index to lowest index available
|
|
}
|
|
}
|
|
}
|
|
|
|
bSharedCrossfade = true;
|
|
SharedCrossfadeName = ShareName;
|
|
SharedCrossfadeGuid = FGuid::NewGuid();
|
|
}
|
|
|
|
void UAnimStateTransitionNode::UnshareRules()
|
|
{
|
|
bSharedRules = false;
|
|
SharedRulesName.Empty();
|
|
SharedRulesGuid.Invalidate();
|
|
|
|
if ((BoundGraph == NULL) || IsBoundGraphShared())
|
|
{
|
|
BoundGraph = NULL;
|
|
CreateBoundGraph();
|
|
}
|
|
}
|
|
|
|
void UAnimStateTransitionNode::UnshareCrossade()
|
|
{
|
|
bSharedCrossfade = false;
|
|
SharedCrossfadeIdx = INDEX_NONE;
|
|
SharedCrossfadeName.Empty();
|
|
SharedCrossfadeGuid.Invalidate();
|
|
}
|
|
|
|
void UAnimStateTransitionNode::UseSharedRules(const UAnimStateTransitionNode* Node)
|
|
{
|
|
if(Node == this || Node == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("UseSharedRules", "Use Shared Rules"));
|
|
|
|
Modify();
|
|
|
|
UEdGraph* CurrentGraph = GetGraph();
|
|
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraphChecked(CurrentGraph);
|
|
|
|
UEdGraph* GraphToDelete = NULL;
|
|
if ((BoundGraph != NULL) && !IsBoundGraphShared())
|
|
{
|
|
GraphToDelete = BoundGraph;
|
|
}
|
|
|
|
BoundGraph = Node->BoundGraph;
|
|
bSharedRules = Node->bSharedRules;
|
|
SharedRulesName = Node->SharedRulesName;
|
|
SharedColor = Node->SharedColor;
|
|
SharedRulesGuid = Node->SharedRulesGuid;
|
|
|
|
if (GraphToDelete != NULL)
|
|
{
|
|
FBlueprintEditorUtils::RemoveGraph(Blueprint, GraphToDelete);
|
|
}
|
|
|
|
// If this node has shared crossfade settings, and we currently dont... share with it automatically.
|
|
// We'll see if this is actually helpful or just confusing. I think it might be a common operation
|
|
// and this avoid having to manually select to share the rules and then share the crossfade settings.
|
|
if ((SharedCrossfadeIdx == INDEX_NONE) && (Node->SharedCrossfadeIdx != INDEX_NONE))
|
|
{
|
|
UseSharedCrossfade(Node);
|
|
}
|
|
}
|
|
|
|
void UAnimStateTransitionNode::UseSharedCrossfade(const UAnimStateTransitionNode* Node)
|
|
{
|
|
if(Node == this || Node == nullptr)
|
|
{
|
|
return;
|
|
}
|
|
|
|
FScopedTransaction Transaction(LOCTEXT("UseSharedCrossfade", "Use Shared Crossfade"));
|
|
|
|
Modify();
|
|
|
|
bSharedCrossfade = Node->bSharedCrossfade;
|
|
SharedCrossfadeName = Node->SharedCrossfadeName;
|
|
SharedCrossfadeGuid = Node->SharedCrossfadeGuid;
|
|
CopyCrossfadeSettings(Node);
|
|
}
|
|
|
|
void UAnimStateTransitionNode::CopyCrossfadeSettings(const UAnimStateTransitionNode* SrcNode)
|
|
{
|
|
CrossfadeDuration = SrcNode->CrossfadeDuration;
|
|
CrossfadeMode_DEPRECATED = SrcNode->CrossfadeMode_DEPRECATED;
|
|
BlendMode = SrcNode->BlendMode;
|
|
CustomBlendCurve = SrcNode->CustomBlendCurve;
|
|
BlendProfile = SrcNode->BlendProfile;
|
|
SharedCrossfadeIdx = SrcNode->SharedCrossfadeIdx;
|
|
SharedCrossfadeName = SrcNode->SharedCrossfadeName;
|
|
SharedCrossfadeGuid = SrcNode->SharedCrossfadeGuid;
|
|
}
|
|
|
|
void UAnimStateTransitionNode::PropagateCrossfadeSettings()
|
|
{
|
|
UEdGraph* CurrentGraph = GetGraph();
|
|
for (int32 idx = 0; idx < CurrentGraph->Nodes.Num(); idx++)
|
|
{
|
|
if (UAnimStateTransitionNode* Node = Cast<UAnimStateTransitionNode>(CurrentGraph->Nodes[idx]))
|
|
{
|
|
if (Node->SharedCrossfadeIdx != INDEX_NONE && Node->SharedCrossfadeGuid == SharedCrossfadeGuid)
|
|
{
|
|
Node->Modify();
|
|
Node->CopyCrossfadeSettings(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UAnimStateTransitionNode::IsReverseTrans(const UAnimStateNodeBase* Node)
|
|
{
|
|
return (Bidirectional && GetNextState() == Node);
|
|
}
|
|
|
|
void UAnimStateTransitionNode::CreateBoundGraph()
|
|
{
|
|
// Create a new animation graph
|
|
check(BoundGraph == NULL);
|
|
BoundGraph = FBlueprintEditorUtils::CreateNewGraph(this, NAME_None, UAnimationTransitionGraph::StaticClass(), UAnimationTransitionSchema::StaticClass());
|
|
check(BoundGraph);
|
|
|
|
// Find an interesting name
|
|
FEdGraphUtilities::RenameGraphToNameOrCloseToName(BoundGraph, TEXT("Transition"));
|
|
|
|
// Initialize the anim graph
|
|
const UEdGraphSchema* Schema = BoundGraph->GetSchema();
|
|
Schema->CreateDefaultNodesForGraph(*BoundGraph);
|
|
|
|
// Add the new graph as a child of our parent graph
|
|
UEdGraph* ParentGraph = GetGraph();
|
|
|
|
if(ParentGraph->SubGraphs.Find(BoundGraph) == INDEX_NONE)
|
|
{
|
|
ParentGraph->SubGraphs.Add(BoundGraph);
|
|
}
|
|
}
|
|
|
|
void UAnimStateTransitionNode::CreateCustomTransitionGraph()
|
|
{
|
|
// Create a new animation graph
|
|
check(CustomTransitionGraph == NULL);
|
|
CustomTransitionGraph = FBlueprintEditorUtils::CreateNewGraph(
|
|
this,
|
|
NAME_None,
|
|
UAnimationCustomTransitionGraph::StaticClass(),
|
|
UAnimationCustomTransitionSchema::StaticClass());
|
|
check(CustomTransitionGraph);
|
|
|
|
// Find an interesting name
|
|
FEdGraphUtilities::RenameGraphToNameOrCloseToName(CustomTransitionGraph, TEXT("CustomTransition"));
|
|
|
|
// Initialize the anim graph
|
|
const UEdGraphSchema* Schema = CustomTransitionGraph->GetSchema();
|
|
Schema->CreateDefaultNodesForGraph(*CustomTransitionGraph);
|
|
|
|
// Add the new graph as a child of our parent graph
|
|
UEdGraph* ParentGraph = GetGraph();
|
|
|
|
if(ParentGraph->SubGraphs.Find(CustomTransitionGraph) == INDEX_NONE)
|
|
{
|
|
ParentGraph->Modify();
|
|
ParentGraph->SubGraphs.Add(CustomTransitionGraph);
|
|
}
|
|
}
|
|
|
|
void UAnimStateTransitionNode::Serialize(FArchive& Ar)
|
|
{
|
|
Super::Serialize(Ar);
|
|
Ar.UsingCustomVersion(FAnimPhysObjectVersion::GUID);
|
|
Ar.UsingCustomVersion(FUE5MainStreamObjectVersion::GUID);
|
|
}
|
|
|
|
void UAnimStateTransitionNode::DestroyNode()
|
|
{
|
|
// BoundGraph may be shared with another graph, if so, don't remove it here
|
|
UEdGraph* GraphToRemove = IsBoundGraphShared() ? NULL : GetBoundGraph();
|
|
|
|
BoundGraph = NULL;
|
|
Super::DestroyNode();
|
|
|
|
if (GraphToRemove)
|
|
{
|
|
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
|
|
FBlueprintEditorUtils::RemoveGraph(Blueprint, GraphToRemove, EGraphRemoveFlags::Recompile);
|
|
}
|
|
|
|
if (CustomTransitionGraph)
|
|
{
|
|
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
|
|
FBlueprintEditorUtils::RemoveGraph(Blueprint, CustomTransitionGraph, EGraphRemoveFlags::Recompile);
|
|
}
|
|
}
|
|
|
|
/** Returns true if this nodes BoundGraph is shared with another node in the parent graph */
|
|
bool UAnimStateTransitionNode::IsBoundGraphShared() const
|
|
{
|
|
if (BoundGraph)
|
|
{
|
|
//@TODO: O(N) search
|
|
UEdGraph* ParentGraph = GetGraph();
|
|
for (int32 NodeIdx = 0; NodeIdx < ParentGraph->Nodes.Num(); NodeIdx++)
|
|
{
|
|
UAnimStateNodeBase* AnimNode = Cast<UAnimStateNodeBase>(ParentGraph->Nodes[NodeIdx]);
|
|
if ((AnimNode != NULL) && (AnimNode != this) && (AnimNode->GetBoundGraph() == BoundGraph))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void UAnimStateTransitionNode::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const
|
|
{
|
|
Super::ValidateNodeDuringCompilation(MessageLog);
|
|
|
|
if (UAnimationTransitionGraph* TransGraph = Cast<UAnimationTransitionGraph>(BoundGraph))
|
|
{
|
|
UAnimGraphNode_TransitionResult* ResultNode = TransGraph->GetResultNode();
|
|
check(ResultNode);
|
|
|
|
if (ResultNode->PropertyBindings.Num() > 0 && ResultNode->PropertyBindings.CreateIterator()->Value.bIsBound)
|
|
{
|
|
// Rule is bound so nothing more to check
|
|
}
|
|
else if (ResultNode->Pins.Num() > 0)
|
|
{
|
|
UEdGraphPin* BoolResultPin = ResultNode->Pins[0];
|
|
if (BoolResultPin && (BoolResultPin->LinkedTo.Num() == 0) && (BoolResultPin->DefaultValue.ToBool() == false))
|
|
{
|
|
// check for native transition rule before warning
|
|
bool bHasNative = false;
|
|
UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForNodeChecked(this);
|
|
if(Blueprint && Blueprint->ParentClass)
|
|
{
|
|
UAnimInstance* AnimInstance = CastChecked<UAnimInstance>(Blueprint->ParentClass->GetDefaultObject());
|
|
if(AnimInstance)
|
|
{
|
|
UEdGraph* ParentGraph = GetGraph();
|
|
UAnimStateNodeBase* PrevState = GetPreviousState();
|
|
UAnimStateNodeBase* NextState = GetNextState();
|
|
if(PrevState != nullptr && NextState != nullptr && ParentGraph != nullptr)
|
|
{
|
|
FName FunctionName;
|
|
bHasNative = AnimInstance->HasNativeTransitionBinding(ParentGraph->GetFName(), FName(*PrevState->GetStateName()), FName(*NextState->GetStateName()), FunctionName);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bHasNative && !bAutomaticRuleBasedOnSequencePlayerInState)
|
|
{
|
|
MessageLog.Warning(TEXT("@@ will never be taken, please connect something to @@"), this, BoolResultPin);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
MessageLog.Error(TEXT("@@ contains an invalid or NULL BoundGraph. Please delete and recreate the transition."), this);
|
|
}
|
|
}
|
|
|
|
TArray<UEdGraph*> UAnimStateTransitionNode::GetSubGraphs() const
|
|
{
|
|
TArray<UEdGraph*> SubGraphs;
|
|
if(!IsBoundGraphShared())
|
|
{
|
|
SubGraphs.Add(BoundGraph);
|
|
}
|
|
SubGraphs.Add(CustomTransitionGraph);
|
|
return SubGraphs;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// IAnimStateTransitionNodeSharedDataHelper
|
|
|
|
void IAnimStateTransitionNodeSharedDataHelper::UpdateSharedData(UAnimStateTransitionNode* Node, TSharedPtr<INameValidatorInterface> NameValidator)
|
|
{
|
|
// get all other transition nodes
|
|
TArray<UAnimStateTransitionNode*> TransitionNodes;
|
|
|
|
UEdGraph* ParentGraph = Node->GetGraph();
|
|
ParentGraph->GetNodesOfClass(TransitionNodes);
|
|
|
|
// check if there is other node that can provide us with data
|
|
for (TArray<UAnimStateTransitionNode*>::TIterator It(TransitionNodes); It; ++ It)
|
|
{
|
|
UAnimStateTransitionNode* OtherNode = *It;
|
|
if (OtherNode != Node &&
|
|
CheckIfHasDataToShare(OtherNode) &&
|
|
CheckIfNodesShouldShareData(Node, OtherNode))
|
|
{
|
|
// use shared data of that node (to make sure everything is linked up properly)
|
|
ShareData(Node, OtherNode);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check if our shared rule name is original
|
|
if (NameValidator->FindValidString(AccessShareDataName(Node)) != EValidatorResult::Ok)
|
|
{
|
|
// rename all shared rules name in nodes that should share same name
|
|
for (TArray<UAnimStateTransitionNode*>::TIterator It(TransitionNodes); It; ++ It)
|
|
{
|
|
UAnimStateTransitionNode* OtherNode = *It;
|
|
if (OtherNode != Node &&
|
|
CheckIfNodesShouldShareData(Node, OtherNode))
|
|
{
|
|
AccessShareDataName(OtherNode) = AccessShareDataName(Node);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IAnimStateTransitionNodeSharedDataHelper::MakeSureGuidExists(UAnimStateTransitionNode* Node)
|
|
{
|
|
UEdGraph* CurrentGraph = Node->GetGraph();
|
|
for (int32 idx=0; idx < CurrentGraph->Nodes.Num(); idx++)
|
|
{
|
|
if (UAnimStateTransitionNode* OtherNode = Cast<UAnimStateTransitionNode>(CurrentGraph->Nodes[idx]))
|
|
{
|
|
if (OtherNode != Node &&
|
|
CheckIfNodesShouldShareData(Node, OtherNode))
|
|
{
|
|
AccessShareDataName(Node) = AccessShareDataName(OtherNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (! AccessShareDataGuid(Node).IsValid())
|
|
{
|
|
AccessShareDataGuid(Node) = FGuid::NewGuid();
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FAnimStateTransitionNodeSharedRulesHelper
|
|
|
|
bool FAnimStateTransitionNodeSharedRulesHelper::CheckIfNodesShouldShareData(const UAnimStateTransitionNode* NodeA, const UAnimStateTransitionNode* NodeB)
|
|
{
|
|
return NodeA->bSharedRules && NodeB->bSharedRules && NodeA->SharedRulesGuid == NodeB->SharedRulesGuid;
|
|
}
|
|
|
|
bool FAnimStateTransitionNodeSharedRulesHelper::CheckIfHasDataToShare(const UAnimStateTransitionNode* Node)
|
|
{
|
|
return Node->BoundGraph != NULL;
|
|
}
|
|
|
|
void FAnimStateTransitionNodeSharedRulesHelper::ShareData(UAnimStateTransitionNode* NodeWhoWantsToShare, const UAnimStateTransitionNode* ShareFrom)
|
|
{
|
|
NodeWhoWantsToShare->UseSharedRules(ShareFrom);
|
|
}
|
|
|
|
FString& FAnimStateTransitionNodeSharedRulesHelper::AccessShareDataName(UAnimStateTransitionNode* Node)
|
|
{
|
|
return Node->SharedRulesName;
|
|
}
|
|
|
|
FGuid& FAnimStateTransitionNodeSharedRulesHelper::AccessShareDataGuid(UAnimStateTransitionNode* Node)
|
|
{
|
|
return Node->SharedRulesGuid;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FAnimStateTransitionNodeSharedCrossfadeHelper
|
|
|
|
bool FAnimStateTransitionNodeSharedCrossfadeHelper::CheckIfNodesShouldShareData(const UAnimStateTransitionNode* NodeA, const UAnimStateTransitionNode* NodeB)
|
|
{
|
|
return NodeA->bSharedCrossfade && NodeB->bSharedCrossfade && NodeA->SharedCrossfadeGuid == NodeB->SharedCrossfadeGuid;
|
|
}
|
|
|
|
bool FAnimStateTransitionNodeSharedCrossfadeHelper::CheckIfHasDataToShare(const UAnimStateTransitionNode* Node)
|
|
{
|
|
return Node->SharedCrossfadeIdx != INDEX_NONE;
|
|
}
|
|
|
|
void FAnimStateTransitionNodeSharedCrossfadeHelper::ShareData(UAnimStateTransitionNode* NodeWhoWantsToShare, const UAnimStateTransitionNode* ShareFrom)
|
|
{
|
|
NodeWhoWantsToShare->UseSharedCrossfade(ShareFrom);
|
|
}
|
|
|
|
FString& FAnimStateTransitionNodeSharedCrossfadeHelper::AccessShareDataName(UAnimStateTransitionNode* Node)
|
|
{
|
|
return Node->SharedCrossfadeName;
|
|
}
|
|
|
|
FGuid& FAnimStateTransitionNodeSharedCrossfadeHelper::AccessShareDataGuid(UAnimStateTransitionNode* Node)
|
|
{
|
|
return Node->SharedCrossfadeGuid;
|
|
}
|
|
|
|
#undef LOCTEXT_NAMESPACE
|