// 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 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& Binding, float DeltaTime, bool bDispatchEvents) const { TTraitBinding TimelineTrait; ensure(Binding.GetStackInterfaceSuper(TimelineTrait)); TTraitBinding 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 NotifySourceTrait; ensure(Binding.GetStackInterfaceSuper(NotifySourceTrait)); const FTimelineDelta Delta = TimelineTrait.GetDelta(Context); // Query for notifies TArray Notifies; NotifySourceTrait.GetNotifies(Context, PreAdvanceState.GetPosition(), Delta.GetDeltaTime(), PreAdvanceState.IsLooping(), Notifies); FInstanceData* InstanceData = Binding.GetInstanceData(); InstanceData->DeltaTime = Delta.GetDeltaTime(); for(const FAnimNotifyEventReference& NotifyData : Notifies) { if(const FAnimNotifyEvent* NotifyEvent = NotifyData.GetNotify()) { if (UAnimNotifyState* Notify = Cast(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().NotifyEvent == NotifyEvent; })) { FoundInstance->GetMutable().CurrentTime = NotifyData.GetCurrentAnimationTime(); } else { FInstancedStruct& Struct = InstanceData->EvaluationNotifies.AddDefaulted_GetRef(); Struct.InitializeAs(*HandlerType); FEvaluationNotify_BaseInstance& Instance = Struct.GetMutable(); 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& Binding) const { IEvaluate::PostEvaluate(Context, Binding); const FSharedData* SharedData = Binding.GetSharedData(); check(SharedData); FInstanceData* InstanceData = Binding.GetInstanceData(); check(InstanceData); InstanceData->HostObject = Context.GetHostObject(); // Get Root motion provider { TTraitBinding 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()->EvaluationNotifies) { FEvaluationNotify_BaseInstance& NotifyInstance = NotifyInstanceData.Get(); 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(); 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().bActive == false; }); } }