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; }