Files
2025-05-18 13:04:45 +08:00

209 lines
7.1 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "AnimNext/EvaluationNotifiesTrait.h"
#include "Animation/AnimNotifies/AnimNotifyState.h"
#include "TraitInterfaces/INotifySource.h"
#include "TraitInterfaces/ITimeline.h"
#include "TraitCore/NodeInstance.h"
static const FName RootBoneTransformName = "RootBoneTransform";
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FEvaluationNotifiesTrait
namespace UE::AnimNext
{
TMap<UClass*, UScriptStruct*> FEvaluationNotifiesTrait::NotifyEvaluationHandlerMap;
AUTO_REGISTER_ANIM_TRAIT(FEvaluationNotifiesTrait)
#define TRAIT_INTERFACE_ENUMERATOR(GeneratorMacro) \
GeneratorMacro(ITimelinePlayer) \
GeneratorMacro(IUpdate) \
GeneratorMacro(IEvaluate) \
// Trait required interfaces implementation boilerplate
#define TRAIT_REQUIRED_INTERFACE_ENUMERATOR(GeneratorMacroRequired) \
GeneratorMacroRequired(ITimelinePlayer) \
GeneratorMacroRequired(ITimeline) \
GeneratorMacroRequired(INotifySource) \
// Trait implementation boilerplate
GENERATE_ANIM_TRAIT_IMPLEMENTATION(FEvaluationNotifiesTrait, TRAIT_INTERFACE_ENUMERATOR, NULL_ANIM_TRAIT_INTERFACE_ENUMERATOR, NULL_ANIM_TRAIT_EVENT_ENUMERATOR)
#undef TRAIT_INTERFACE_ENUMERATOR
void FEvaluationNotifiesTrait::AdvanceBy(FExecutionContext& Context, const TTraitBinding<ITimelinePlayer>& Binding, float DeltaTime, bool bDispatchEvents) const
{
TTraitBinding<ITimeline> TimelineTrait;
ensure(Binding.GetStackInterfaceSuper(TimelineTrait));
TTraitBinding<ITimelinePlayer> TimelinePlayerTrait;
ensure(Binding.GetStackInterfaceSuper(TimelinePlayerTrait));
// Get current state from the stack, advance time, then get the delta state
const FTimelineState PreAdvanceState = TimelineTrait.GetState(Context);
TimelinePlayerTrait.AdvanceBy(Context, DeltaTime, bDispatchEvents);
if (!bDispatchEvents)
{
return;
}
TTraitBinding<INotifySource> NotifySourceTrait;
ensure(Binding.GetStackInterfaceSuper(NotifySourceTrait));
const FTimelineDelta Delta = TimelineTrait.GetDelta(Context);
// Query for notifies
TArray<FAnimNotifyEventReference> Notifies;
NotifySourceTrait.GetNotifies(Context, PreAdvanceState.GetPosition(), Delta.GetDeltaTime(), PreAdvanceState.IsLooping(), Notifies);
FInstanceData* InstanceData = Binding.GetInstanceData<FInstanceData>();
InstanceData->DeltaTime = Delta.GetDeltaTime();
for(const FAnimNotifyEventReference& NotifyData : Notifies)
{
if(const FAnimNotifyEvent* NotifyEvent = NotifyData.GetNotify())
{
if (UAnimNotifyState* Notify = Cast<UAnimNotifyState>(NotifyEvent->NotifyStateClass))
{
if (UScriptStruct** HandlerType = NotifyEvaluationHandlerMap.Find(Notify->GetClass()))
{
if (FInstancedStruct* FoundInstance = InstanceData->EvaluationNotifies.FindByPredicate([NotifyEvent](const FStructView ExistingInstance)
{
// check if we already have an instance for this event
return ExistingInstance.Get<FEvaluationNotify_BaseInstance>().NotifyEvent == NotifyEvent;
}))
{
FoundInstance->GetMutable<FEvaluationNotify_BaseInstance>().CurrentTime = NotifyData.GetCurrentAnimationTime();
}
else
{
FInstancedStruct& Struct = InstanceData->EvaluationNotifies.AddDefaulted_GetRef();
Struct.InitializeAs(*HandlerType);
FEvaluationNotify_BaseInstance& Instance = Struct.GetMutable<FEvaluationNotify_BaseInstance>();
Instance.NotifyEvent = NotifyEvent;
Instance.AnimNotify = Notify;
Instance.StartTime = NotifyEvent->GetTriggerTime();
Instance.EndTime = NotifyEvent->GetEndTriggerTime();
Instance.CurrentTime = NotifyData.GetCurrentAnimationTime();
}
}
}
}
}
}
void FEvaluationNotifiesTrait::PostEvaluate(FEvaluateTraversalContext& Context, const TTraitBinding<IEvaluate>& Binding) const
{
IEvaluate::PostEvaluate(Context, Binding);
const FSharedData* SharedData = Binding.GetSharedData<FSharedData>();
check(SharedData);
FInstanceData* InstanceData = Binding.GetInstanceData<FInstanceData>();
check(InstanceData);
InstanceData->HostObject = Context.GetHostObject();
// Get Root motion provider
{
TTraitBinding<IAttributeProvider> AttributeTrait;
if (!Binding.GetStackInterface(AttributeTrait))
{
// UE_LOG(LogAnimNextWarping, Error, TEXT("FSteeringTrait::PostEvaluate, missing IAttributeProvider"));
return;
}
InstanceData->OnExtractRootMotionAttribute = AttributeTrait.GetOnExtractRootMotionAttribute(Context);
}
InstanceData->DataInterface = &Binding.GetTraitPtr().GetNodeInstance()->GetOwner();
InstanceData->DataInterface->GetVariable(RootBoneTransformName, InstanceData->RootBoneTransform);
Context.AppendTask(FAnimNextEvaluationNotifiesTask::Make(InstanceData, SharedData));
}
void FEvaluationNotifiesTrait::RegisterEvaluationHandler(UClass* NotifyType, UScriptStruct* HandlerType)
{
NotifyEvaluationHandlerMap.Add(NotifyType, HandlerType);
}
void FEvaluationNotifiesTrait::UnregisterEvaluationHandler(UClass* NotifyType)
{
NotifyEvaluationHandlerMap.Remove(NotifyType);
}
void FEvaluationNotifiesTrait::FInstanceData::Destruct(const FExecutionContext& Context, const FTraitBinding& Binding)
{
for (FStructView NotifyInstanceData : Binding.GetInstanceData<FInstanceData>()->EvaluationNotifies)
{
FEvaluationNotify_BaseInstance& NotifyInstance = NotifyInstanceData.Get<FEvaluationNotify_BaseInstance>();
if (NotifyInstance.bActive)
{
NotifyInstance.bActive = false;
NotifyInstance.End(*this);
}
}
FTrait::FInstanceData::Destruct(Context, Binding);
}
} // namespace UE::AnimNext
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// FAnimNextEvaluationNotifiesTask
FAnimNextEvaluationNotifiesTask FAnimNextEvaluationNotifiesTask::Make(UE::AnimNext::FEvaluationNotifiesTrait::FInstanceData* InstanceData, const UE::AnimNext::FEvaluationNotifiesTrait::FSharedData* SharedData)
{
FAnimNextEvaluationNotifiesTask Task;
Task.InstanceData = InstanceData;
Task.SharedData = SharedData;
return Task;
}
void FAnimNextEvaluationNotifiesTask::Execute(UE::AnimNext::FEvaluationVM& VM) const
{
using namespace UE::AnimNext;
// Execute EvaluationNotifies
if (InstanceData->DeltaTime > 0)
{
for(auto& NotifyInstanceData : InstanceData->EvaluationNotifies)
{
FEvaluationNotify_BaseInstance& NotifyInstance = NotifyInstanceData.GetMutable<FEvaluationNotify_BaseInstance>();
if (NotifyInstance.StartTime <= NotifyInstance.CurrentTime && NotifyInstance.EndTime >= NotifyInstance.CurrentTime)
{
if (!NotifyInstance.bActive)
{
NotifyInstance.bActive = true;
NotifyInstance.Start();
}
NotifyInstance.Update(*InstanceData, VM);
}
else
{
if (NotifyInstance.bActive)
{
NotifyInstance.bActive = false;
NotifyInstance.End(*InstanceData);
}
}
NotifyInstance.CurrentTime += InstanceData->DeltaTime;
}
InstanceData->EvaluationNotifies.RemoveAll([](const FInstancedStruct& Data)
{
return Data.Get<FEvaluationNotify_BaseInstance>().bActive == false;
});
}
}