Fix invoking anim events for multi blend animations

This commit is contained in:
Wojtek Figat
2022-07-29 20:25:13 +02:00
parent db24ea950e
commit 3a39dad30f
2 changed files with 94 additions and 73 deletions

View File

@@ -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);

View File

@@ -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<float>(anim->Data.FramesPerSecond);
const float eventDeltaTime = (animPos - animPrevPos) / static_cast<float>(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>());
((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<float>(anim->Data.FramesPerSecond);
const float eventDeltaTime = (animPos - animPrevPos) / static_cast<float>(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>());
((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;
}