diff --git a/Source/Engine/Animations/Graph/AnimGraph.h b/Source/Engine/Animations/Graph/AnimGraph.h index 0b74b9d6c..72b770664 100644 --- a/Source/Engine/Animations/Graph/AnimGraph.h +++ b/Source/Engine/Animations/Graph/AnimGraph.h @@ -874,6 +874,7 @@ private: int32 GetRootNodeIndex(Animation* anim); void ExtractRootMotion(const Animation::NodeToChannel* mapping, int32 rootNodeIndex, Animation* anim, float pos, float prevPos, Transform& rootNode, RootMotionData& rootMotion); + void ProcessAnimEvents(AnimGraphNode* node, bool loop, float length, float startTimePos, float oldTimePos, float animPos, float animPrevPos, Animation* anim, float speed); Variant SampleAnimation(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* anim, float speed); Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, float speedA, float speedB, float alpha); Variant SampleAnimationsWithBlend(AnimGraphNode* node, bool loop, float length, float startTimePos, float prevTimePos, float& newTimePos, Animation* animA, Animation* animB, Animation* animC, float speedA, float speedB, float speedC, float alphaA, float alphaB, float alphaC); diff --git a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp index d137bd257..6c7d17c28 100644 --- a/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp +++ b/Source/Engine/Animations/Graph/AnimGroup.Animation.cpp @@ -88,6 +88,82 @@ void AnimGraphExecutor::ExtractRootMotion(const Animation::NodeToChannel* mappin rootNode = refPose; } +void AnimGraphExecutor::ProcessAnimEvents(AnimGraphNode* node, bool loop, float length, float startTimePos, float oldTimePos, float animPos, float animPrevPos, Animation* anim, float speed) +{ + if (anim->Events.Count() == 0) + return; + ANIM_GRAPH_PROFILE_EVENT("Events"); + auto& context = Context.Get(); + float eventTimeMin = animPrevPos; + float eventTimeMax = animPos; + if (loop) + { + // Check if animation looped + const float posNotLooped = startTimePos + oldTimePos; + if (posNotLooped < 0.0f || posNotLooped > length) + { + if (context.DeltaTime * speed < 0) + { + // Playback backwards + Swap(eventTimeMin, eventTimeMax); + } + } + } + const float eventTime = animPos / static_cast(anim->Data.FramesPerSecond); + const float eventDeltaTime = (animPos - animPrevPos) / static_cast(anim->Data.FramesPerSecond); + for (const auto& track : anim->Events) + { + for (const auto& k : track.Second.GetKeyframes()) + { + if (!k.Value.Instance) + continue; + const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f; + if (k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration) + { + int32 stateIndex = -1; + if (duration > 1) + { + // Begin for continuous event + for (stateIndex = 0; stateIndex < context.Data->Events.Count(); stateIndex++) + { + const auto& e = context.Data->Events[stateIndex]; + if (e.Instance == k.Value.Instance && e.Node == node) + break; + } + if (stateIndex == context.Data->Events.Count()) + { + auto& e = context.Data->Events.AddOne(); + e.Instance = k.Value.Instance; + e.Anim = anim; + e.Node = node; + ASSERT(k.Value.Instance->Is()); + ((AnimContinuousEvent*)k.Value.Instance)->OnBegin((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime); + } + } + + // Event + k.Value.Instance->OnEvent((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime); + if (stateIndex != -1) + context.Data->Events[stateIndex].Hit = true; + } + else if (duration > 1) + { + // End for continuous event + for (int32 i = 0; i < context.Data->Events.Count(); i++) + { + const auto& e = context.Data->Events[i]; + if (e.Instance == k.Value.Instance && e.Node == node) + { + ((AnimContinuousEvent*)k.Value.Instance)->OnEnd((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime); + context.Data->Events.RemoveAt(i); + break; + } + } + } + } + } +} + float GetAnimPos(float& timePos, float startTimePos, bool loop, float length) { // Apply animation offset and looping to calculate the animation sampling position within [0;length] @@ -184,79 +260,7 @@ Variant AnimGraphExecutor::SampleAnimation(AnimGraphNode* node, bool loop, float } // Collect events - if (anim->Events.Count() != 0) - { - ANIM_GRAPH_PROFILE_EVENT("Events"); - auto& context = Context.Get(); - float eventTimeMin = animPrevPos; - float eventTimeMax = animPos; - if (loop) - { - // Check if animation looped - const float posNotLooped = startTimePos + oldTimePos; - if (posNotLooped < 0.0f || posNotLooped > length) - { - if (context.DeltaTime * speed < 0) - { - // Playback backwards - Swap(eventTimeMin, eventTimeMax); - } - } - } - const float eventTime = animPos / static_cast(anim->Data.FramesPerSecond); - const float eventDeltaTime = (animPos - animPrevPos) / static_cast(anim->Data.FramesPerSecond); - for (const auto& track : anim->Events) - { - for (const auto& k : track.Second.GetKeyframes()) - { - if (!k.Value.Instance) - continue; - const float duration = k.Value.Duration > 1 ? k.Value.Duration : 0.0f; - if (k.Time <= eventTimeMax && eventTimeMin <= k.Time + duration) - { - int32 stateIndex = -1; - if (duration > 1) - { - // Begin for continuous event - for (stateIndex = 0; stateIndex < context.Data->Events.Count(); stateIndex++) - { - const auto& e = context.Data->Events[stateIndex]; - if (e.Instance == k.Value.Instance && e.Node == node) - break; - } - if (stateIndex == context.Data->Events.Count()) - { - auto& e = context.Data->Events.AddOne(); - e.Instance = k.Value.Instance; - e.Anim = anim; - e.Node = node; - ASSERT(k.Value.Instance->Is()); - ((AnimContinuousEvent*)k.Value.Instance)->OnBegin((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime); - } - } - - // Event - k.Value.Instance->OnEvent((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime); - if (stateIndex != -1) - context.Data->Events[stateIndex].Hit = true; - } - else if (duration > 1) - { - // End for continuous event - for (int32 i = 0; i < context.Data->Events.Count(); i++) - { - const auto& e = context.Data->Events[i]; - if (e.Instance == k.Value.Instance && e.Node == node) - { - ((AnimContinuousEvent*)k.Value.Instance)->OnEnd((AnimatedModel*)context.Data->Object, anim, eventTime, eventDeltaTime); - context.Data->Events.RemoveAt(i); - break; - } - } - } - } - } - } + ProcessAnimEvents(node, loop, length, startTimePos, oldTimePos, animPos, animPrevPos, anim, speed); return nodes; } @@ -267,6 +271,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l if (animA == nullptr || !animA->IsLoaded() || animB == nullptr || !animB->IsLoaded()) return Value::Null; + const float oldTimePos = prevTimePos; // Calculate actual time position within the animation node (defined by length and loop mode) const float pos = GetAnimPos(newTimePos, startTimePos, loop, length); @@ -327,6 +332,12 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l RootMotionData::Lerp(rootMotionA, rootMotionB, alpha, nodes->RootMotion); } + // Collect events + if (alpha > 0.5f) + ProcessAnimEvents(node, loop, length, startTimePos, oldTimePos, animPosB, animPrevPosB, animB, speedB); + else + ProcessAnimEvents(node, loop, length, startTimePos, oldTimePos, animPosA, animPrevPosA, animA, speedA); + return nodes; } @@ -337,6 +348,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l animB == nullptr || !animB->IsLoaded() || animC == nullptr || !animC->IsLoaded()) return Value::Null; + const float oldTimePos = prevTimePos; // Calculate actual time position within the animation node (defined by length and loop mode) const float pos = GetAnimPos(newTimePos, startTimePos, loop, length); @@ -436,6 +448,14 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l BlendAdditiveWeightedRotation(nodes->RootMotion.Rotation, rootMotionB.Rotation, alphaC); } + // Collect events + if (alphaC > 0.5f) + ProcessAnimEvents(node, loop, length, startTimePos, oldTimePos, animPosC, animPrevPosC, animC, speedC); + else if (alphaB > 0.5f) + ProcessAnimEvents(node, loop, length, startTimePos, oldTimePos, animPosB, animPrevPosB, animB, speedB); + else + ProcessAnimEvents(node, loop, length, startTimePos, oldTimePos, animPosA, animPrevPosA, animA, speedA); + return nodes; }