Fix invoking anim events for multi blend animations
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user