| #include "rive/animation/linear_animation_instance.hpp" |
| #include "rive/animation/linear_animation.hpp" |
| #include "rive/animation/loop.hpp" |
| #include <cmath> |
| |
| using namespace rive; |
| |
| LinearAnimationInstance::LinearAnimationInstance(const LinearAnimation* animation, |
| ArtboardInstance* instance) : |
| m_Animation(animation), |
| m_ArtboardInstance(instance), |
| m_Time(animation->enableWorkArea() ? (float)animation->workStart() / animation->fps() : 0), |
| m_TotalTime(0.0f), |
| m_LastTotalTime(0.0f), |
| m_SpilledTime(0.0f), |
| m_Direction(1) {} |
| |
| bool LinearAnimationInstance::advance(float elapsedSeconds) { |
| const LinearAnimation& animation = *m_Animation; |
| m_Time += elapsedSeconds * animation.speed() * m_Direction; |
| m_LastTotalTime = m_TotalTime; |
| m_TotalTime += elapsedSeconds; |
| |
| int fps = animation.fps(); |
| |
| float frames = m_Time * fps; |
| |
| int start = animation.enableWorkArea() ? animation.workStart() : 0; |
| int end = animation.enableWorkArea() ? animation.workEnd() : animation.duration(); |
| int range = end - start; |
| |
| bool keepGoing = true; |
| bool didLoop = false; |
| m_SpilledTime = 0.0f; |
| |
| switch (loop()) { |
| case Loop::oneShot: |
| if (m_Direction == 1 && frames > end) { |
| keepGoing = false; |
| m_SpilledTime = (frames - end) / fps; |
| frames = end; |
| m_Time = frames / fps; |
| didLoop = true; |
| } else if (m_Direction == -1 && frames < start) { |
| keepGoing = false; |
| m_SpilledTime = (start - frames) / fps; |
| frames = start; |
| m_Time = frames / fps; |
| didLoop = true; |
| } |
| break; |
| case Loop::loop: |
| if (m_Direction == 1 && frames >= end) { |
| m_SpilledTime = (frames - end) / fps; |
| frames = m_Time * fps; |
| frames = start + std::fmod(frames - start, range); |
| |
| m_Time = frames / fps; |
| didLoop = true; |
| } else if (m_Direction == -1 && frames <= start) { |
| |
| m_SpilledTime = (start - frames) / fps; |
| frames = m_Time * fps; |
| frames = end - std::abs(std::fmod(start - frames, range)); |
| m_Time = frames / fps; |
| didLoop = true; |
| } |
| break; |
| case Loop::pingPong: |
| while (true) { |
| if (m_Direction == 1 && frames >= end) { |
| m_SpilledTime = (frames - end) / fps; |
| m_Direction = -1; |
| frames = end + (end - frames); |
| m_Time = frames / fps; |
| didLoop = true; |
| } else if (m_Direction == -1 && frames < start) { |
| m_SpilledTime = (start - frames) / fps; |
| m_Direction = 1; |
| frames = start + (start - frames); |
| m_Time = frames / fps; |
| didLoop = true; |
| } else { |
| // we're within the range, we can stop fixing. We do this in |
| // a loop to fix conditions when time has advanced so far |
| // that we've ping-ponged back and forth a few times in a |
| // single frame. We want to accomodate for this in cases |
| // where animations are not advanced on regular intervals. |
| break; |
| } |
| } |
| break; |
| } |
| |
| m_DidLoop = didLoop; |
| return keepGoing; |
| } |
| |
| void LinearAnimationInstance::time(float value) { |
| if (m_Time == value) { |
| return; |
| } |
| m_Time = value; |
| // Make sure to keep last and total in relative lockstep so state machines |
| // can track change even when setting time. |
| auto diff = m_TotalTime - m_LastTotalTime; |
| |
| int start = (m_Animation->enableWorkArea() ? m_Animation->workStart() : 0) * m_Animation->fps(); |
| m_TotalTime = value - start; |
| m_LastTotalTime = m_TotalTime - diff; |
| |
| // leaving this RIGHT now. but is this required? it kinda messes up |
| // playing things backwards and seeking. what purpose does it solve? |
| m_Direction = 1; |
| } |
| |
| uint32_t LinearAnimationInstance::fps() const { return m_Animation->fps(); } |
| |
| uint32_t LinearAnimationInstance::duration() const { return m_Animation->duration(); } |
| |
| float LinearAnimationInstance::speed() const { return m_Animation->speed(); } |
| |
| float LinearAnimationInstance::startSeconds() const { return m_Animation->startSeconds(); } |
| |
| std::string LinearAnimationInstance::name() const { |
| return m_Animation->name(); |
| } |
| |
| // Returns either the animation's default or overridden loop values |
| int LinearAnimationInstance::loopValue() { |
| if (m_LoopValue != -1) { |
| return m_LoopValue; |
| } |
| return m_Animation->loopValue(); |
| } |
| |
| // Override the animation's loop value |
| void LinearAnimationInstance::loopValue(int value) { |
| if (m_LoopValue == value) { |
| return; |
| } |
| if (m_LoopValue == -1 && m_Animation->loopValue() == value) { |
| return; |
| } |
| m_LoopValue = value; |
| } |
| |
| float LinearAnimationInstance::durationSeconds() const { return m_Animation->durationSeconds(); } |