From 78672988b26d793c93e3f7c31329e715b956c680 Mon Sep 17 00:00:00 2001 From: bodymovin Date: Wed, 22 May 2024 21:40:09 +0000 Subject: [PATCH] fix for unexpected triggered events fixes #7226 This PR fixes reports of events when animations are playing backward. It considers both the timeline speed and the state machine state speed to determine direction. It also handles not reporting an event twice if it comes from a "ping pong" loop. It also adds a small performance improvement, skipping the binary search if possible. Diffs= 265c00985 fix for unexpected triggered events (#7227) Co-authored-by: hernan --- .rive_head | 2 +- include/rive/animation/keyed_object.hpp | 2 +- include/rive/animation/keyed_property.hpp | 2 +- include/rive/animation/linear_animation.hpp | 5 ++- .../animation/linear_animation_instance.hpp | 1 + src/animation/keyed_object.cpp | 4 +- src/animation/keyed_property.cpp | 37 ++++++++++++++++--- src/animation/linear_animation.cpp | 22 +++++++---- src/animation/linear_animation_instance.cpp | 20 ++++++++-- 9 files changed, 71 insertions(+), 24 deletions(-) diff --git a/.rive_head b/.rive_head index dd5879ce..a329f6d8 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -8a538c243fcc5b72159388a7e63669f1e6998dda +265c009852e86492deb69277ad18aa2c394c93e5 diff --git a/include/rive/animation/keyed_object.hpp b/include/rive/animation/keyed_object.hpp index e11306c7..860e11fc 100644 --- a/include/rive/animation/keyed_object.hpp +++ b/include/rive/animation/keyed_object.hpp @@ -19,7 +19,7 @@ class KeyedObject : public KeyedObjectBase void reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const; + bool isAtStartFrame) const; void apply(Artboard* coreContext, float time, float mix); StatusCode import(ImportStack& importStack) override; diff --git a/include/rive/animation/keyed_property.hpp b/include/rive/animation/keyed_property.hpp index c45f3bd1..1ef73ce5 100644 --- a/include/rive/animation/keyed_property.hpp +++ b/include/rive/animation/keyed_property.hpp @@ -20,7 +20,7 @@ class KeyedProperty : public KeyedPropertyBase uint32_t objectId, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const; + bool isAtStartFrame) const; /// Apply interpolating key frames. void apply(Core* object, float time, float mix); diff --git a/include/rive/animation/linear_animation.hpp b/include/rive/animation/linear_animation.hpp index 2efcfeb7..f7aa60ea 100644 --- a/include/rive/animation/linear_animation.hpp +++ b/include/rive/animation/linear_animation.hpp @@ -35,6 +35,7 @@ class LinearAnimation : public LinearAnimationBase /// Returns the start time/ end time of the animation in seconds, considering speed float startTime() const; + float startTime(float multiplier) const; float endTime() const; /// Convert a global clock to local seconds (takes into consideration @@ -49,7 +50,9 @@ class LinearAnimation : public LinearAnimationBase void reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, - float secondsTo) const; + float secondsTo, + float speedDirection, + bool fromPong) const; }; } // namespace rive diff --git a/include/rive/animation/linear_animation_instance.hpp b/include/rive/animation/linear_animation_instance.hpp index 4919bd7f..316b0c7c 100644 --- a/include/rive/animation/linear_animation_instance.hpp +++ b/include/rive/animation/linear_animation_instance.hpp @@ -101,6 +101,7 @@ class LinearAnimationInstance : public Scene private: const LinearAnimation* m_animation = nullptr; float m_time; + float m_speedDirection; float m_totalTime; float m_lastTotalTime; float m_spilledTime; diff --git a/src/animation/keyed_object.cpp b/src/animation/keyed_object.cpp index bb54f886..0bb8e63a 100644 --- a/src/animation/keyed_object.cpp +++ b/src/animation/keyed_object.cpp @@ -52,7 +52,7 @@ StatusCode KeyedObject::onAddedClean(CoreContext* context) void KeyedObject::reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const + bool isAtStartFrame) const { for (const std::unique_ptr& property : m_keyedProperties) { @@ -64,7 +64,7 @@ void KeyedObject::reportKeyedCallbacks(KeyedCallbackReporter* reporter, objectId(), secondsFrom, secondsTo, - secondsFromExactOffset); + isAtStartFrame); } } diff --git a/src/animation/keyed_property.cpp b/src/animation/keyed_property.cpp index 750e976d..7156fb89 100644 --- a/src/animation/keyed_property.cpp +++ b/src/animation/keyed_property.cpp @@ -18,13 +18,18 @@ void KeyedProperty::addKeyFrame(std::unique_ptr keyframe) int KeyedProperty::closestFrameIndex(float seconds, int exactOffset) const { - int idx = 0; int mid = 0; float closestSeconds = 0; int start = 0; auto numKeyFrames = static_cast(m_keyFrames.size()); int end = numKeyFrames - 1; + // If it's the last keyframe, we skip the binary search + if (seconds > m_keyFrames[end]->seconds()) + { + return end + 1; + } + while (start <= end) { mid = (start + end) >> 1; @@ -41,19 +46,39 @@ int KeyedProperty::closestFrameIndex(float seconds, int exactOffset) const { return mid + exactOffset; } - idx = start; } - return idx; + return start; } void KeyedProperty::reportKeyedCallbacks(KeyedCallbackReporter* reporter, uint32_t objectId, float secondsFrom, float secondsTo, - int secondsFromExactOffset) const + bool isAtStartFrame) const { - int idx = closestFrameIndex(secondsFrom, secondsFromExactOffset); - int idxTo = closestFrameIndex(secondsTo, 1); + if (secondsFrom == secondsTo) + { + return; + } + bool isForward = secondsFrom <= secondsTo; + int fromExactOffset = 0; + int toExactOffset = isForward ? 1 : 0; + if (isForward) + { + if (!isAtStartFrame) + { + fromExactOffset = 1; + } + } + else + { + if (isAtStartFrame) + { + fromExactOffset = 1; + } + } + int idx = closestFrameIndex(secondsFrom, fromExactOffset); + int idxTo = closestFrameIndex(secondsTo, toExactOffset); if (idxTo < idx) { diff --git a/src/animation/linear_animation.cpp b/src/animation/linear_animation.cpp index c2ade3b5..21d795d9 100644 --- a/src/animation/linear_animation.cpp +++ b/src/animation/linear_animation.cpp @@ -86,6 +86,10 @@ float LinearAnimation::endSeconds() const } float LinearAnimation::startTime() const { return (speed() >= 0) ? startSeconds() : endSeconds(); } +float LinearAnimation::startTime(float multiplier) const +{ + return ((speed() * multiplier) >= 0) ? startSeconds() : endSeconds(); +} float LinearAnimation::endTime() const { return (speed() >= 0) ? endSeconds() : startSeconds(); } float LinearAnimation::durationSeconds() const { return std::abs(endSeconds() - startSeconds()); } @@ -119,16 +123,18 @@ float LinearAnimation::globalToLocalSeconds(float seconds) const void LinearAnimation::reportKeyedCallbacks(KeyedCallbackReporter* reporter, float secondsFrom, - float secondsTo) const + float secondsTo, + float speedDirection, + bool fromPong) const { - int secondsFromExactOffset = - startTime() == secondsFrom && - (speed() >= 0 ? secondsFrom < secondsTo : secondsFrom < secondsTo) - ? 0 - : 1; + float startingTime = startTime(speedDirection); + bool isAtStartFrame = startingTime == secondsFrom; - for (const auto& object : m_KeyedObjects) + if (!isAtStartFrame || !fromPong) { - object->reportKeyedCallbacks(reporter, secondsFrom, secondsTo, secondsFromExactOffset); + for (const auto& object : m_KeyedObjects) + { + object->reportKeyedCallbacks(reporter, secondsFrom, secondsTo, isAtStartFrame); + } } } \ No newline at end of file diff --git a/src/animation/linear_animation_instance.cpp b/src/animation/linear_animation_instance.cpp index ff7fe55a..f40f8afe 100644 --- a/src/animation/linear_animation_instance.cpp +++ b/src/animation/linear_animation_instance.cpp @@ -13,6 +13,7 @@ LinearAnimationInstance::LinearAnimationInstance(const LinearAnimation* animatio Scene(instance), m_animation((assert(animation != nullptr), animation)), m_time((speedMultiplier >= 0) ? animation->startTime() : animation->endTime()), + m_speedDirection((speedMultiplier >= 0) ? 1 : -1), m_totalTime(0.0f), m_lastTotalTime(0.0f), m_spilledTime(0.0f), @@ -23,6 +24,7 @@ LinearAnimationInstance::LinearAnimationInstance(LinearAnimationInstance const& Scene(lhs), m_animation(lhs.m_animation), m_time(lhs.m_time), + m_speedDirection(lhs.m_speedDirection), m_totalTime(lhs.m_totalTime), m_lastTotalTime(lhs.m_lastTotalTime), m_spilledTime(lhs.m_spilledTime), @@ -64,7 +66,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte m_time += deltaSeconds; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, lastTime, m_time); + animation.reportKeyedCallbacks(reporter, lastTime, m_time, m_speedDirection, false); } int fps = animation.fps(); @@ -107,7 +109,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte didLoop = true; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, 0.0f, m_time); + animation.reportKeyedCallbacks(reporter, 0.0f, m_time, m_speedDirection, false); } } else if (direction == -1 && frames <= start) @@ -119,11 +121,16 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte didLoop = true; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, end / (float)fps, m_time); + animation.reportKeyedCallbacks(reporter, + end / (float)fps, + m_time, + m_speedDirection, + false); } } break; case Loop::pingPong: + bool fromPong = true; while (true) { if (direction == 1 && frames >= end) @@ -153,8 +160,13 @@ bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporte didLoop = true; if (reporter != nullptr) { - animation.reportKeyedCallbacks(reporter, lastTime, m_time); + animation.reportKeyedCallbacks(reporter, + lastTime, + m_time, + m_speedDirection, + fromPong); } + fromPong = !fromPong; } break; }