Fix invoking anim events for multi blend animations
This commit is contained in:
@@ -874,6 +874,7 @@ private:
|
|||||||
|
|
||||||
int32 GetRootNodeIndex(Animation* anim);
|
int32 GetRootNodeIndex(Animation* anim);
|
||||||
void ExtractRootMotion(const Animation::NodeToChannel* mapping, int32 rootNodeIndex, Animation* anim, float pos, float prevPos, Transform& rootNode, RootMotionData& rootMotion);
|
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 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, 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);
|
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;
|
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)
|
float GetAnimPos(float& timePos, float startTimePos, bool loop, float length)
|
||||||
{
|
{
|
||||||
// Apply animation offset and looping to calculate the animation sampling position within [0;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
|
// Collect events
|
||||||
if (anim->Events.Count() != 0)
|
ProcessAnimEvents(node, loop, length, startTimePos, oldTimePos, animPos, animPrevPos, anim, speed);
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodes;
|
return nodes;
|
||||||
}
|
}
|
||||||
@@ -267,6 +271,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
|
|||||||
if (animA == nullptr || !animA->IsLoaded() ||
|
if (animA == nullptr || !animA->IsLoaded() ||
|
||||||
animB == nullptr || !animB->IsLoaded())
|
animB == nullptr || !animB->IsLoaded())
|
||||||
return Value::Null;
|
return Value::Null;
|
||||||
|
const float oldTimePos = prevTimePos;
|
||||||
|
|
||||||
// Calculate actual time position within the animation node (defined by length and loop mode)
|
// Calculate actual time position within the animation node (defined by length and loop mode)
|
||||||
const float pos = GetAnimPos(newTimePos, startTimePos, loop, length);
|
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);
|
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;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,6 +348,7 @@ Variant AnimGraphExecutor::SampleAnimationsWithBlend(AnimGraphNode* node, bool l
|
|||||||
animB == nullptr || !animB->IsLoaded() ||
|
animB == nullptr || !animB->IsLoaded() ||
|
||||||
animC == nullptr || !animC->IsLoaded())
|
animC == nullptr || !animC->IsLoaded())
|
||||||
return Value::Null;
|
return Value::Null;
|
||||||
|
const float oldTimePos = prevTimePos;
|
||||||
|
|
||||||
// Calculate actual time position within the animation node (defined by length and loop mode)
|
// Calculate actual time position within the animation node (defined by length and loop mode)
|
||||||
const float pos = GetAnimPos(newTimePos, startTimePos, loop, length);
|
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);
|
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;
|
return nodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user