Files
UnrealEngineUWP/Engine/Source/Editor/AnimGraph/Private/AnimationGraphSchema.cpp
Marc Audy 26836a22cd Copying //UE4/Dev-Framework to Dev-Main (//UE4/Dev-Main) @ 2868852
#lockdown Nick.Penwarden

==========================
MAJOR FEATURES + CHANGES
==========================

Change 2821607 on 2016/01/08 by Mieszko.Zielinski

	Added a way to limit amount of information logged by vlog by discarding logs from classes from outside of class whitelist #UE4

	This feature was followed by refactoring of functions taking FVisualLogEntry pointers to use references instead.

	#rb Lukasz.Furman
	#codereview John.Abercrombie

Change 2828384 on 2016/01/14 by Mieszko.Zielinski

	Back out of visual log refactor done as part of CL#2821607 #UE4

Change 2855722 on 2016/02/04 by Zak.Middleton

	#ue4 - Expose PrimitiveComponent GetCollisionEnabled(), IsCollisionEnabled(), IsQueryCollisionEnabled(), IsPhysicsCollisionEnabled() functions to blueprints.

	#rb Daniel.Broder, Ori.Cohen
	#codereview Bob.Tellez
	#jira UE-26411

Change 2855737 on 2016/02/04 by James.Golding

	Add test AssetUserData C++ class to QAGame

Change 2855739 on 2016/02/04 by James.Golding

	PR #1858 : Add abstract and editinlinenew to AssetUserData class, so derived classes can be added to assets via details panel
	https://github.com/EpicGames/UnrealEngine/pull/1858
	#github 1858
	#jira UE-24494
	#rb thomas.sarkanen

Change 2855842 on 2016/02/04 by James.Golding

	UE-23968 : Add clamping to Friction, Restitution, Density and RaiseMassToPower properties of PhysicalMaterial
	#codereview ori.cohen
	#rb thomas.sarkanen

Change 2855843 on 2016/02/04 by James.Golding

	PR #1984 : Add 'AssetUserData' support to AnimationAsset/Texture
	https://github.com/EpicGames/UnrealEngine/pull/1984
	#github 1984
	#jira UE-25913
	#rb thomas.sarkanen

Change 2855844 on 2016/02/04 by James.Golding

	UE-23176 Fix incorrect clamping in GenerateKDopAsSimpleCollision() which could cause degenerate hulls
	Also don't 'nudge' kdops if there are other primitives present, will offset from mesh we were fitting around
	#rb thomas.sarkanen

Change 2855850 on 2016/02/04 by Benn.Gallagher

	Update required LOD bones when associating a cloth chunk that uses bones that aren't weighted to that LOD
	#rb Martin.Wilson

Change 2856409 on 2016/02/04 by Zak.Middleton

	#ue4 - Change LogSpawn warnings to normal logs for cases that are common and valid during gameplay (failed due to collision at the target location, or failed because it is destroyed in the process of spawning, ie projectile spawned in a target).

	#codereview Jeff.Farris

Change 2857018 on 2016/02/05 by Benn.Gallagher

	Fixes for various anim graph related subgraph issues/crashes
	#rb Ben.Cosh

Change 2857193 on 2016/02/05 by Lukasz.Furman

	extended crowd agent interface to handle avoidance groups
	#ue4 UE-24823
	#1896

Change 2857200 on 2016/02/05 by Lukasz.Furman

	fixed memory leak in CrowdManager
	#ue UE-26516
	#2026
	#codereview Mieszko.Zielinski

Change 2857417 on 2016/02/05 by Lina.Halper

	Add keyword for animation functions

Change 2858854 on 2016/02/08 by Benn.Gallagher

	Fixed pose evaluator customizations being processed at the wrong time which caused properties to always be re-added even if removed from the details panel, causing duplicated entries.
	#jira UE-8421
	#rb Lina.Halper

Change 2858855 on 2016/02/08 by Benn.Gallagher

	Fixed blendspace players only reporting normalized time when using a time getter in an anim graph transition.
	#rb Lina.Halper

Change 2859159 on 2016/02/08 by Aaron.McLeran

	UE-25586 Fix for reporting audio memory usage

	- Realtime decompressed sounds that have CachedRealtimeFirstBuffers need to report the compressed asset size

Change 2859192 on 2016/02/08 by Laurent.Delayen

	Removed check disallowing cooked additive animations from using AnimationRelative and AnimationScale retargeting modes (on the root bone or editor).
	Skip AnimationRelative retargeting for baked additive animations since it gets cancelled out during the additive creation process.

	#rb martin.wilson
	#codereview lina.halper

Change 2859238 on 2016/02/08 by Lina.Halper

	Git pull request #1895

Change 2859239 on 2016/02/08 by Lina.Halper

	Git pull request #1813

Change 2859453 on 2016/02/08 by Aaron.McLeran

[CL 2868856 by Marc Audy in Main branch]
2016-02-16 13:35:21 -05:00

470 lines
16 KiB
C++

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.
/*=============================================================================
AnimationGraphSchema.cpp
=============================================================================*/
#include "AnimGraphPrivatePCH.h"
#include "BlueprintUtilities.h"
#include "GraphEditorActions.h"
#include "ScopedTransaction.h"
#include "AssetData.h"
#include "AnimationGraphSchema.h"
#include "K2Node_TransitionRuleGetter.h"
#include "AnimStateNode.h"
#include "AnimGraphNode_BlendSpacePlayer.h"
#include "AnimGraphNode_ComponentToLocalSpace.h"
#include "AnimGraphNode_LocalToComponentSpace.h"
#include "AnimGraphNode_Root.h"
#include "AnimGraphNode_RotationOffsetBlendSpace.h"
#include "AnimGraphNode_SequencePlayer.h"
#define LOCTEXT_NAMESPACE "AnimationGraphSchema"
/////////////////////////////////////////////////////
// UAnimationGraphSchema
UAnimationGraphSchema::UAnimationGraphSchema(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
{
PN_SequenceName = TEXT("Sequence");
NAME_NeverAsPin = TEXT("NeverAsPin");
NAME_PinHiddenByDefault = TEXT("PinHiddenByDefault");
NAME_PinShownByDefault = TEXT("PinShownByDefault");
NAME_AlwaysAsPin = TEXT("AlwaysAsPin");
NAME_OnEvaluate = TEXT("OnEvaluate");
NAME_CustomizeProperty = TEXT("CustomizeProperty");
DefaultEvaluationHandlerName = TEXT("EvaluateGraphExposedInputs");
}
FLinearColor UAnimationGraphSchema::GetPinTypeColor(const FEdGraphPinType& PinType) const
{
const bool bAdditive = PinType.PinSubCategory == TEXT("Additive");
if (UAnimationGraphSchema::IsLocalSpacePosePin(PinType))
{
if (bAdditive)
{
return FLinearColor(0.12, 0.60, 0.10);
}
else
{
return FLinearColor::White;
}
}
else if (UAnimationGraphSchema::IsComponentSpacePosePin(PinType))
{
//@TODO: Pick better colors
if (bAdditive)
{
return FLinearColor(0.12, 0.60, 0.60);
}
else
{
return FLinearColor(0.20f, 0.50f, 1.00f);
}
}
return Super::GetPinTypeColor(PinType);
}
EGraphType UAnimationGraphSchema::GetGraphType(const UEdGraph* TestEdGraph) const
{
return GT_Animation;
}
void UAnimationGraphSchema::CreateDefaultNodesForGraph(UEdGraph& Graph) const
{
// Create the result node
FGraphNodeCreator<UAnimGraphNode_Root> NodeCreator(Graph);
UAnimGraphNode_Root* ResultSinkNode = NodeCreator.CreateNode();
NodeCreator.Finalize();
SetNodeMetaData(ResultSinkNode, FNodeMetadata::DefaultGraphNode);
}
void UAnimationGraphSchema::HandleGraphBeingDeleted(UEdGraph& GraphBeingRemoved) const
{
if (UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(&GraphBeingRemoved))
{
// Look for state nodes that reference this graph
TArray<UAnimStateNodeBase*> StateNodes;
FBlueprintEditorUtils::GetAllNodesOfClassEx<UAnimStateNode>(Blueprint, StateNodes);
TSet<UAnimStateNodeBase*> NodesToDelete;
for (int32 i = 0; i < StateNodes.Num(); ++i)
{
UAnimStateNodeBase* StateNode = StateNodes[i];
if (StateNode->GetBoundGraph() == &GraphBeingRemoved)
{
NodesToDelete.Add(StateNode);
}
}
// Delete the node that owns us
ensure(NodesToDelete.Num() <= 1);
for (TSet<UAnimStateNodeBase*>::TIterator It(NodesToDelete); It; ++It)
{
UAnimStateNodeBase* NodeToDelete = *It;
FBlueprintEditorUtils::RemoveNode(Blueprint, NodeToDelete, true);
// Prevent re-entrancy here
NodeToDelete->ClearBoundGraph();
}
}
}
bool UAnimationGraphSchema::IsPosePin(const FEdGraphPinType& PinType)
{
return IsLocalSpacePosePin(PinType) || IsComponentSpacePosePin(PinType);
}
bool UAnimationGraphSchema::IsLocalSpacePosePin(const FEdGraphPinType& PinType)
{
const UAnimationGraphSchema* Schema = GetDefault<UAnimationGraphSchema>();
UScriptStruct* PoseLinkStruct = FPoseLink::StaticStruct();
return (PinType.PinCategory == Schema->PC_Struct) && (PinType.PinSubCategoryObject == PoseLinkStruct);
}
bool UAnimationGraphSchema::IsComponentSpacePosePin(const FEdGraphPinType& PinType)
{
const UAnimationGraphSchema* Schema = GetDefault<UAnimationGraphSchema>();
UScriptStruct* ComponentSpacePoseLinkStruct = FComponentSpacePoseLink::StaticStruct();
return (PinType.PinCategory == Schema->PC_Struct) && (PinType.PinSubCategoryObject == ComponentSpacePoseLinkStruct);
}
const FPinConnectionResponse UAnimationGraphSchema::DetermineConnectionResponseOfCompatibleTypedPins(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UEdGraphPin* InputPin, const UEdGraphPin* OutputPin) const
{
// Enforce a tree hierarchy; where poses can only have one output (parent) connection
if (IsPosePin(OutputPin->PinType) && IsPosePin(InputPin->PinType))
{
if ((OutputPin->LinkedTo.Num() > 0) || (InputPin->LinkedTo.Num() > 0))
{
const ECanCreateConnectionResponse ReplyBreakOutputs = CONNECT_RESPONSE_BREAK_OTHERS_AB;
return FPinConnectionResponse(ReplyBreakOutputs, TEXT("Replace existing connections"));
}
}
// Fall back to standard K2 rules
return Super::DetermineConnectionResponseOfCompatibleTypedPins(PinA, PinB, InputPin, OutputPin);
}
bool UAnimationGraphSchema::ArePinsCompatible(const UEdGraphPin* PinA, const UEdGraphPin* PinB, const UClass* CallingContext, bool bIgnoreArray) const
{
// both are pose pin, but doesn't match type, then return false;
if (IsPosePin(PinA->PinType) && IsPosePin(PinB->PinType) && IsLocalSpacePosePin(PinA->PinType) != IsLocalSpacePosePin(PinB->PinType))
{
return false;
}
return Super::ArePinsCompatible(PinA, PinB, CallingContext, bIgnoreArray);
}
bool UAnimationGraphSchema::DoesSupportAnimNotifyActions() const
{
// Don't offer notify items in anim graph
return false;
}
bool UAnimationGraphSchema::SearchForAutocastFunction(const UEdGraphPin* OutputPin, const UEdGraphPin* InputPin, FName& TargetFunction, /*out*/ UClass*& FunctionOwner) const
{
if (IsComponentSpacePosePin(OutputPin->PinType) && IsLocalSpacePosePin(InputPin->PinType))
{
// Insert a Component To LocalSpace conversion
return true;
}
else if (IsLocalSpacePosePin(OutputPin->PinType) && IsComponentSpacePosePin(InputPin->PinType))
{
// Insert a Local To ComponentSpace conversion
return true;
}
else
{
return Super::SearchForAutocastFunction(OutputPin, InputPin, TargetFunction, FunctionOwner);
}
}
bool UAnimationGraphSchema::CreateAutomaticConversionNodeAndConnections(UEdGraphPin* PinA, UEdGraphPin* PinB) const
{
// Determine which pin is an input and which pin is an output
UEdGraphPin* InputPin = NULL;
UEdGraphPin* OutputPin = NULL;
if (!CategorizePinsByDirection(PinA, PinB, /*out*/ InputPin, /*out*/ OutputPin))
{
return false;
}
// Look for animation specific conversion operations
UK2Node* TemplateNode = NULL;
if (IsComponentSpacePosePin(OutputPin->PinType) && IsLocalSpacePosePin(InputPin->PinType))
{
TemplateNode = NewObject<UAnimGraphNode_ComponentToLocalSpace>();
}
else if (IsLocalSpacePosePin(OutputPin->PinType) && IsComponentSpacePosePin(InputPin->PinType))
{
TemplateNode = NewObject<UAnimGraphNode_LocalToComponentSpace>();
}
// Spawn the conversion node if it's specific to animation
if (TemplateNode != NULL)
{
UEdGraph* Graph = InputPin->GetOwningNode()->GetGraph();
FVector2D AverageLocation = CalculateAveragePositionBetweenNodes(InputPin, OutputPin);
UK2Node* ConversionNode = FEdGraphSchemaAction_K2NewNode::SpawnNodeFromTemplate<UK2Node>(Graph, TemplateNode, AverageLocation);
AutowireConversionNode(InputPin, OutputPin, ConversionNode);
return true;
}
else
{
// Give the regular conversions a shot
return Super::CreateAutomaticConversionNodeAndConnections(PinA, PinB);
}
}
bool IsAimOffsetBlendSpace(UBlendSpaceBase* BlendSpace)
{
return BlendSpace->IsA(UAimOffsetBlendSpace::StaticClass()) ||
BlendSpace->IsA(UAimOffsetBlendSpace1D::StaticClass());
}
void UAnimationGraphSchema::SpawnNodeFromAsset(UAnimationAsset* Asset, const FVector2D& GraphPosition, UEdGraph* Graph, UEdGraphPin* PinIfAvailable)
{
check(Graph);
check(Graph->GetSchema()->IsA(UAnimationGraphSchema::StaticClass()));
check(Asset);
UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(Graph));
const bool bSkelMatch = (AnimBlueprint != NULL) && (AnimBlueprint->TargetSkeleton == Asset->GetSkeleton());
const bool bTypeMatch = (PinIfAvailable == NULL) || UAnimationGraphSchema::IsLocalSpacePosePin(PinIfAvailable->PinType);
const bool bDirectionMatch = (PinIfAvailable == NULL) || (PinIfAvailable->Direction == EGPD_Input);
if (bSkelMatch && bTypeMatch && bDirectionMatch)
{
FEdGraphSchemaAction_K2NewNode Action;
if (UAnimSequence* Sequence = Cast<UAnimSequence>(Asset))
{
UAnimGraphNode_SequencePlayer* PlayerNode = NewObject<UAnimGraphNode_SequencePlayer>();
PlayerNode->Node.Sequence = Sequence;
Action.NodeTemplate = PlayerNode;
}
else if (UBlendSpaceBase* BlendSpace = Cast<UBlendSpaceBase>(Asset))
{
if (IsAimOffsetBlendSpace(BlendSpace))
{
UAnimGraphNode_RotationOffsetBlendSpace* PlayerNode = NewObject<UAnimGraphNode_RotationOffsetBlendSpace>();
PlayerNode->Node.BlendSpace = BlendSpace;
Action.NodeTemplate = PlayerNode;
}
else
{
UAnimGraphNode_BlendSpacePlayer* PlayerNode = NewObject<UAnimGraphNode_BlendSpacePlayer>();
PlayerNode->Node.BlendSpace = BlendSpace;
Action.NodeTemplate = PlayerNode;
}
}
else if (UAnimComposite* Composite = Cast<UAnimComposite>(Asset))
{
UAnimGraphNode_SequencePlayer* PlayerNode = NewObject<UAnimGraphNode_SequencePlayer>();
PlayerNode->Node.Sequence = Composite;
Action.NodeTemplate = PlayerNode;
}
else
{
//unknown type
return;
}
Action.PerformAction(Graph, PinIfAvailable, GraphPosition);
}
}
void UAnimationGraphSchema::UpdateNodeWithAsset(UK2Node* K2Node, UAnimationAsset* Asset)
{
if (Asset != NULL)
{
if (UAnimGraphNode_SequencePlayer* SequencePlayerNode = Cast<UAnimGraphNode_SequencePlayer>(K2Node))
{
if (UAnimSequence* Sequence = Cast<UAnimSequence>(Asset))
{
// Skeleton matches, and it's a sequence player; replace the existing sequence with the dragged one
SequencePlayerNode->Node.Sequence = Sequence;
}
}
else if (UBlendSpace* BlendSpace = Cast<UBlendSpace>(Asset))
{
if (IsAimOffsetBlendSpace(BlendSpace))
{
if (UAnimGraphNode_RotationOffsetBlendSpace* RotationOffsetNode = Cast<UAnimGraphNode_RotationOffsetBlendSpace>(K2Node))
{
// Skeleton matches, and it's a blendspace player; replace the existing blendspace with the dragged one
RotationOffsetNode->Node.BlendSpace = BlendSpace;
}
}
else
{
if (UAnimGraphNode_BlendSpacePlayer* BlendSpacePlayerNode = Cast<UAnimGraphNode_BlendSpacePlayer>(K2Node))
{
// Skeleton matches, and it's a blendspace player; replace the existing blendspace with the dragged one
BlendSpacePlayerNode->Node.BlendSpace = BlendSpace;
}
}
}
}
}
void UAnimationGraphSchema::DroppedAssetsOnGraph( const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraph* Graph ) const
{
UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets);
if ((Asset != NULL) && (Graph != NULL))
{
SpawnNodeFromAsset(Asset, GraphPosition, Graph, NULL);
}
}
void UAnimationGraphSchema::DroppedAssetsOnNode(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphNode* Node) const
{
UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets);
UK2Node* K2Node = Cast<UK2Node>(Node);
if ((Asset != NULL) && (K2Node!= NULL))
{
UpdateNodeWithAsset(K2Node, Asset);
}
}
void UAnimationGraphSchema::DroppedAssetsOnPin(const TArray<FAssetData>& Assets, const FVector2D& GraphPosition, UEdGraphPin* Pin) const
{
UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets);
if ((Asset != NULL) && (Pin != NULL))
{
SpawnNodeFromAsset(Asset, GraphPosition, Pin->GetOwningNode()->GetGraph(), Pin);
}
}
void UAnimationGraphSchema::GetAssetsNodeHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphNode* HoverNode, FString& OutTooltipText, bool& OutOkIcon) const
{
UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets);
if ((Asset == NULL) || (HoverNode == NULL))
{
OutTooltipText = TEXT("");
OutOkIcon = false;
return;
}
const UK2Node* PlayerNodeUnderCursor = NULL;
if (Asset->IsA(UAnimSequence::StaticClass()))
{
PlayerNodeUnderCursor = Cast<const UAnimGraphNode_SequencePlayer>(HoverNode);
}
else if (Asset->IsA(UBlendSpace::StaticClass()))
{
UBlendSpace* BlendSpace = CastChecked<UBlendSpace>(Asset);
if (IsAimOffsetBlendSpace(BlendSpace))
{
PlayerNodeUnderCursor = Cast<const UAnimGraphNode_RotationOffsetBlendSpace>(HoverNode);
}
else
{
PlayerNodeUnderCursor = Cast<const UAnimGraphNode_BlendSpacePlayer>(HoverNode);
}
}
// this one only should happen when there is an Anim Blueprint
UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForNode(HoverNode));
const bool bSkelMatch = (AnimBlueprint != NULL) && (AnimBlueprint->TargetSkeleton == Asset->GetSkeleton());
if (!bSkelMatch)
{
OutOkIcon = false;
OutTooltipText = FString::Printf(TEXT("Skeletons are not compatible"));
}
else if (PlayerNodeUnderCursor != NULL)
{
OutOkIcon = true;
OutTooltipText = FString::Printf(TEXT("Change node to play %s"), *(Asset->GetName()));
}
else
{
OutOkIcon = false;
OutTooltipText = FString::Printf(TEXT("Cannot replace '%s' with a sequence player"), *(HoverNode->GetName()));
}
}
void UAnimationGraphSchema::GetAssetsPinHoverMessage(const TArray<FAssetData>& Assets, const UEdGraphPin* HoverPin, FString& OutTooltipText, bool& OutOkIcon) const
{
UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets);
if ((Asset == NULL) || (HoverPin == NULL))
{
OutTooltipText = TEXT("");
OutOkIcon = false;
return;
}
// this one only should happen when there is an Anim Blueprint
UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForNode(HoverPin->GetOwningNode()));
const bool bSkelMatch = (AnimBlueprint != NULL) && (AnimBlueprint->TargetSkeleton == Asset->GetSkeleton());
const bool bTypeMatch = UAnimationGraphSchema::IsLocalSpacePosePin(HoverPin->PinType);
const bool bDirectionMatch = HoverPin->Direction == EGPD_Input;
if (bSkelMatch && bTypeMatch && bDirectionMatch)
{
OutOkIcon = true;
OutTooltipText = FString::Printf(TEXT("Play %s and feed to %s"), *(Asset->GetName()), *HoverPin->PinName);
}
else
{
OutOkIcon = false;
OutTooltipText = FString::Printf(TEXT("Type or direction mismatch; must be wired to a pose input"));
}
}
void UAnimationGraphSchema::GetAssetsGraphHoverMessage(const TArray<FAssetData>& Assets, const UEdGraph* HoverGraph, FString& OutTooltipText, bool& OutOkIcon) const
{
UAnimationAsset* Asset = FAssetData::GetFirstAsset<UAnimationAsset>(Assets);
if (Asset)
{
UAnimBlueprint* AnimBlueprint = Cast<UAnimBlueprint>(FBlueprintEditorUtils::FindBlueprintForGraph(HoverGraph));
const bool bSkelMatch = (AnimBlueprint != NULL) && (AnimBlueprint->TargetSkeleton == Asset->GetSkeleton());
if (!bSkelMatch)
{
OutOkIcon = false;
OutTooltipText = FString::Printf(TEXT("Skeletons are not compatible"));
}
}
}
void UAnimationGraphSchema::GetContextMenuActions(const UEdGraph* CurrentGraph, const UEdGraphNode* InGraphNode, const UEdGraphPin* InGraphPin, FMenuBuilder* MenuBuilder, bool bIsDebugging) const
{
Super::GetContextMenuActions(CurrentGraph, InGraphNode, InGraphPin, MenuBuilder, bIsDebugging);
}
FText UAnimationGraphSchema::GetPinDisplayName(const UEdGraphPin* Pin) const
{
check(Pin != NULL);
FText DisplayName = Super::GetPinDisplayName(Pin);
if (UAnimGraphNode_Base* Node = Cast<UAnimGraphNode_Base>(Pin->GetOwningNode()))
{
FString ProcessedDisplayName = DisplayName.ToString();
Node->PostProcessPinName(Pin, ProcessedDisplayName);
DisplayName = FText::FromString(ProcessedDisplayName);
}
return DisplayName;
}
#undef LOCTEXT_NAMESPACE