Files
UnrealEngine/Engine/Source/Runtime/AnimGraphRuntime/Private/AnimSequencerInstanceProxy.cpp
2025-05-18 13:04:45 +08:00

333 lines
12 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimSequencerInstanceProxy.h"
#include "AnimSequencerInstance.h"
#include "Components/SkeletalMeshComponent.h"
#include UE_INLINE_GENERATED_CPP_BY_NAME(AnimSequencerInstanceProxy)
void FAnimSequencerInstanceProxy::Initialize(UAnimInstance* InAnimInstance)
{
FAnimInstanceProxy::Initialize(InAnimInstance);
ConstructNodes();
FullBodyBlendNode.bAdditiveNode = false;
FullBodyBlendNode.bNormalizeAlpha = true;
AdditiveBlendNode.bAdditiveNode = true;
AdditiveBlendNode.bNormalizeAlpha = false;
FullBodyBlendNode.ResetPoses();
AdditiveBlendNode.ResetPoses();
SnapshotNode.SnapshotName = UAnimSequencerInstance::SequencerPoseName;
ClearSequencePlayerAndMirrorMaps();
UpdateCounter.Reset();
RootMotionOverride.Reset();
}
bool FAnimSequencerInstanceProxy::Evaluate(FPoseContext& Output)
{
SequencerRootNode.Evaluate_AnyThread(Output);
if (RootMotionOverride.IsSet())
{
if (!RootMotionOverride.GetValue().bBlendFirstChildOfRoot)
{
for (const FCompactPoseBoneIndex BoneIndex : Output.Pose.ForEachBoneIndex())
{
if (BoneIndex.IsRootBone())
{
Output.Pose[BoneIndex] = RootMotionOverride.GetValue().RootMotion;
break;
}
}
}
else if (RootMotionOverride.GetValue().ChildBoneIndex != INDEX_NONE)
{
FCompactPoseBoneIndex PoseIndex = Output.Pose.GetBoneContainer().GetCompactPoseIndexFromSkeletonIndex(RootMotionOverride.GetValue().ChildBoneIndex);
if (PoseIndex.IsValid())
{
Output.Pose[PoseIndex] = RootMotionOverride.GetValue().RootMotion;
}
}
}
RootBoneTransform.Reset();
if (SwapRootBone != ESwapRootBone::SwapRootBone_None)
{
for (const FCompactPoseBoneIndex BoneIndex : Output.Pose.ForEachBoneIndex())
{
if (BoneIndex.IsRootBone())
{
RootBoneTransform = Output.Pose[BoneIndex];
Output.Pose[BoneIndex] = FTransform::Identity;
break;
}
}
}
return true;
}
void FAnimSequencerInstanceProxy::PostEvaluate(UAnimInstance* InAnimInstance)
{
if (GetSkelMeshComponent() && SwapRootBone != ESwapRootBone::SwapRootBone_None)
{
if (RootBoneTransform.IsSet())
{
FTransform RelativeTransform = RootBoneTransform.GetValue();
if (InitialTransform.IsSet())
{
RelativeTransform = RootBoneTransform.GetValue() * InitialTransform.GetValue();
}
if (SwapRootBone == ESwapRootBone::SwapRootBone_Component)
{
GetSkelMeshComponent()->SetRelativeLocationAndRotation(RelativeTransform.GetLocation(), RelativeTransform.GetRotation().Rotator());
}
else if (SwapRootBone == ESwapRootBone::SwapRootBone_Actor)
{
AActor* Actor = GetSkelMeshComponent()->GetOwner();
if (Actor && Actor->GetRootComponent())
{
Actor->GetRootComponent()->SetRelativeLocationAndRotation(RelativeTransform.GetLocation(), RelativeTransform.GetRotation().Rotator());
}
}
}
}
}
void FAnimSequencerInstanceProxy::UpdateAnimationNode(const FAnimationUpdateContext& InContext)
{
UpdateCounter.Increment();
SequencerRootNode.Update_AnyThread(InContext);
}
void FAnimSequencerInstanceProxy::ConstructNodes()
{
// construct node link node for full body and additive to apply additive node
SequencerRootNode.Base.SetLinkNode(&FullBodyBlendNode);
SequencerRootNode.Additive.SetLinkNode(&AdditiveBlendNode);
}
void FAnimSequencerInstanceProxy::AddReferencedObjects(UAnimInstance* InAnimInstance, FReferenceCollector& Collector)
{
Super::AddReferencedObjects(InAnimInstance, Collector);
for (const TPair<uint32, FSequencerPlayerBase*>& IndexPlayerPair : SequencerToPlayerMap)
{
if(IndexPlayerPair.Value->IsOfType<FSequencerPlayerAnimSequence>())
{
FSequencerPlayerAnimSequence* SequencerPlayerAnimSequence = static_cast<FSequencerPlayerAnimSequence*>(IndexPlayerPair.Value);
Collector.AddPropertyReferencesWithStructARO(FAnimNode_SequenceEvaluator_Standalone::StaticStruct(), &SequencerPlayerAnimSequence->PlayerNode);
}
}
for (const TPair<uint32, FAnimNode_Mirror_Standalone*>& IndexMirrorPair : SequencerToMirrorMap)
{
Collector.AddPropertyReferencesWithStructARO(FAnimNode_Mirror_Standalone::StaticStruct(), IndexMirrorPair.Value);
}
}
void FAnimSequencerInstanceProxy::ClearSequencePlayerAndMirrorMaps()
{
for (TPair<uint32, FSequencerPlayerBase*>& Iter : SequencerToPlayerMap)
{
delete Iter.Value;
}
SequencerToPlayerMap.Empty();
for (TPair<uint32, FAnimNode_Mirror_Standalone*>& Iter : SequencerToMirrorMap)
{
delete Iter.Value;
}
SequencerToMirrorMap.Empty();
}
void FAnimSequencerInstanceProxy::ResetPose()
{
SequencerRootNode.Base.SetLinkNode(&SnapshotNode);
//force evaluation?
}
void FAnimSequencerInstanceProxy::ResetNodes()
{
FMemory::Memzero(FullBodyBlendNode.DesiredAlphas.GetData(), FullBodyBlendNode.DesiredAlphas.GetAllocatedSize());
FMemory::Memzero(AdditiveBlendNode.DesiredAlphas.GetData(), AdditiveBlendNode.DesiredAlphas.GetAllocatedSize());
}
FAnimSequencerInstanceProxy::~FAnimSequencerInstanceProxy()
{
ClearSequencePlayerAndMirrorMaps();
}
void FAnimSequencerInstanceProxy::InitAnimTrack(UAnimSequenceBase* InAnimSequence, uint32 SequenceId)
{
if (InAnimSequence != nullptr)
{
FSequencerPlayerAnimSequence* PlayerState = FindPlayer<FSequencerPlayerAnimSequence>(SequenceId);
if (PlayerState == nullptr)
{
const bool bIsAdditive = InAnimSequence->IsValidAdditive();
FAnimNode_MultiWayBlend& BlendNode = (bIsAdditive) ? AdditiveBlendNode : FullBodyBlendNode;
// you shouldn't allow additive animation to be added here, but if it changes type after
// you'll see this warning coming up
if (bIsAdditive && InAnimSequence->GetAdditiveAnimType() == AAT_RotationOffsetMeshSpace)
{
// this doesn't work
UE_LOG(LogAnimation, Warning, TEXT("ERROR: Animation [%s] in Sequencer has Mesh Space additive animation. No support on mesh space additive animation. "), *GetNameSafe(InAnimSequence));
}
const int32 PoseIndex = BlendNode.AddPose() - 1;
// add the new entry to map
FSequencerPlayerAnimSequence* NewPlayerState = new FSequencerPlayerAnimSequence();
NewPlayerState->PoseIndex = PoseIndex;
NewPlayerState->bAdditive = bIsAdditive;
SequencerToPlayerMap.Add(SequenceId, NewPlayerState);
// link player to mirror node,
FAnimNode_Mirror_Standalone* NewMirrorNode = new FAnimNode_Mirror_Standalone();
NewMirrorNode->SetMirror(false);
NewMirrorNode->SetSourceLinkNode(&NewPlayerState->PlayerNode);
SequencerToMirrorMap.Add(SequenceId, NewMirrorNode);
// link mirror to blendnode, this will let you trigger notifies and so on
NewPlayerState->PlayerNode.SetTeleportToExplicitTime(false);
BlendNode.Poses[PoseIndex].SetLinkNode(NewMirrorNode);
// set player state
PlayerState = NewPlayerState;
}
// now set animation data to player
PlayerState->PlayerNode.SetSequence(InAnimSequence);
PlayerState->PlayerNode.SetExplicitTime(0.f);
// initialize player
PlayerState->PlayerNode.Initialize_AnyThread(FAnimationInitializeContext(this));
FAnimNode_Mirror_Standalone* Mirror = SequencerToMirrorMap.FindRef(SequenceId);
if (Mirror)
{
Mirror->Initialize_AnyThread(FAnimationInitializeContext(this));
Mirror->CacheBones_AnyThread(FAnimationCacheBonesContext(this));
}
}
}
/*
// this isn't used yet. If we want to optimize it, we could do this way, but right now the way sequencer updates, we don't have a good point
// where we could just clear one sequence id. We just clear all the weights before update.
// once they go out of range, they don't get called anymore, so there is no good point of tearing down
// there is multiple tear down point but we couldn't find where only happens once activated and once getting out
// because sequencer finds the nearest point, not exact point, it doesn't have good point of tearing down
void FAnimSequencerInstanceProxy::TermAnimTrack(int32 SequenceId)
{
FSequencerPlayerState* PlayerState = FindPlayer(SequenceId);
if (PlayerState)
{
FAnimNode_MultiWayBlend& BlendNode = (PlayerState->bAdditive) ? AdditiveBlendNode : FullBodyBlendNode;
// remove the pose from blend node
BlendNode.Poses.RemoveAt(PlayerState->PoseIndex);
BlendNode.DesiredAlphas.RemoveAt(PlayerState->PoseIndex);
// remove from Sequence Map
SequencerToPlayerMap.Remove(SequenceId);
}
}*/
void FAnimSequencerInstanceProxy::UpdateAnimTrack(UAnimSequenceBase* InAnimSequence, uint32 SequenceId, float InPosition, float Weight, bool bFireNotifies)
{
UpdateAnimTrack(InAnimSequence, SequenceId, TOptional<FRootMotionOverride>(), TOptional<float>(), InPosition, Weight, bFireNotifies, nullptr);
}
void FAnimSequencerInstanceProxy::UpdateAnimTrack(UAnimSequenceBase* InAnimSequence, uint32 SequenceId, TOptional<float> InFromPosition, float InToPosition, float Weight, bool bFireNotifies)
{
UpdateAnimTrack(InAnimSequence, SequenceId, TOptional<FRootMotionOverride>(), InFromPosition, InToPosition, Weight, bFireNotifies, nullptr);
}
void FAnimSequencerInstanceProxy::UpdateAnimTrackWithRootMotion(UAnimSequenceBase* InAnimSequence, int32 SequenceId, const TOptional<FRootMotionOverride>& RootMotion, float InFromPosition, float InToPosition, float Weight, bool bFireNotifies)
{
UpdateAnimTrack(InAnimSequence, SequenceId, RootMotion, InFromPosition, InToPosition, Weight, bFireNotifies, nullptr);
}
void FAnimSequencerInstanceProxy::UpdateAnimTrackWithRootMotion(UAnimSequenceBase* InAnimSequence, int32 SequenceId, const TOptional<FRootMotionOverride>& RootMotion, float InFromPosition, float InToPosition, float Weight, bool bFireNotifies, UMirrorDataTable* InMirrorDataTable)
{
UpdateAnimTrack(InAnimSequence, SequenceId, RootMotion, InFromPosition, InToPosition, Weight, bFireNotifies, InMirrorDataTable);
}
void FAnimSequencerInstanceProxy::UpdateAnimTrackWithRootMotion(const FAnimSequencerData& InAnimSequencerData)
{
SwapRootBone = InAnimSequencerData.SwapRootBone;
InitialTransform = InAnimSequencerData.InitialTransform;
UpdateAnimTrack(InAnimSequencerData.AnimSequence, InAnimSequencerData.SequenceId, InAnimSequencerData.RootMotion, InAnimSequencerData.FromPosition, InAnimSequencerData.ToPosition, InAnimSequencerData.Weight, InAnimSequencerData.bFireNotifies, InAnimSequencerData.MirrorDataTable);
}
void FAnimSequencerInstanceProxy::UpdateAnimTrack(UAnimSequenceBase* InAnimSequence, uint32 SequenceId, const TOptional<FRootMotionOverride>& InRootMotionOverride, TOptional<float> InFromPosition, float InToPosition, float Weight, bool bFireNotifies, UMirrorDataTable* InMirrorDataTable)
{
EnsureAnimTrack(InAnimSequence, SequenceId);
FSequencerPlayerAnimSequence* PlayerState = FindPlayer<FSequencerPlayerAnimSequence>(SequenceId);
PlayerState->PlayerNode.SetExplicitTime(InToPosition);
if (InFromPosition.IsSet())
{
// Set the internal time accumulator at the "from" time so that the player node will correctly evaluate the
// desired "from/to" range. We also disable the reinitialization code so it doesn't mess up that time we
// just set.
PlayerState->PlayerNode.SetExplicitPreviousTime(InFromPosition.GetValue());
PlayerState->PlayerNode.SetReinitializationBehavior(ESequenceEvalReinit::NoReset);
}
FAnimNode_Mirror_Standalone* MirrorNode = SequencerToMirrorMap.FindRef(SequenceId);
if (MirrorNode)
{
MirrorNode->SetMirror(InMirrorDataTable != nullptr);
UMirrorDataTable* OldMirrorDataTable = MirrorNode->GetMirrorDataTable();
MirrorNode->SetMirrorDataTable(InMirrorDataTable);
if (InMirrorDataTable && OldMirrorDataTable != InMirrorDataTable)
{
MirrorNode->CacheBones_AnyThread(FAnimationCacheBonesContext(this));
}
}
// if no fire notifies, we can teleport to explicit time
PlayerState->PlayerNode.SetTeleportToExplicitTime(!bFireNotifies);
// if moving to 0.f, we mark this to teleport. Otherwise, do not use explicit time
FAnimNode_MultiWayBlend& BlendNode = (PlayerState->bAdditive) ? AdditiveBlendNode : FullBodyBlendNode;
BlendNode.DesiredAlphas[PlayerState->PoseIndex] = Weight;
// if additive, apply alpha value correctlyeTick
// this will be used when apply additive is blending correct total alpha to additive
if (PlayerState->bAdditive)
{
SequencerRootNode.Alpha = BlendNode.GetTotalAlpha();
}
RootMotionOverride = InRootMotionOverride;
}
void FAnimSequencerInstanceProxy::EnsureAnimTrack(UAnimSequenceBase* InAnimSequence, uint32 SequenceId)
{
FSequencerPlayerAnimSequence* PlayerState = FindPlayer<FSequencerPlayerAnimSequence>(SequenceId);
if (!PlayerState)
{
InitAnimTrack(InAnimSequence, SequenceId);
}
else if (PlayerState->PlayerNode.GetSequence() != InAnimSequence)
{
PlayerState->PlayerNode.SetSequence(InAnimSequence);
}
}