blob: 51b0caf661f1c7f3f02a099333dbe2fabccf079c [file] [log] [blame]
#include "rive/animation/linear_animation_instance.hpp"
#include "rive/animation/linear_animation.hpp"
#include "rive/animation/loop.hpp"
#include "rive/rive_counter.hpp"
#include <cmath>
using namespace rive;
LinearAnimationInstance::LinearAnimationInstance(const LinearAnimation* animation,
ArtboardInstance* instance) :
Scene(instance),
m_Animation(animation),
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) {
Counter::update(Counter::kLinearAnimationInstance, +1);
}
LinearAnimationInstance::LinearAnimationInstance(LinearAnimationInstance const& lhs) :
Scene(lhs),
m_Animation(lhs.m_Animation),
m_Time(lhs.m_Time),
m_TotalTime(lhs.m_TotalTime),
m_LastTotalTime(lhs.m_LastTotalTime),
m_SpilledTime(lhs.m_SpilledTime),
m_Direction(lhs.m_Direction),
m_DidLoop(lhs.m_DidLoop),
m_LoopValue(lhs.m_LoopValue) {
Counter::update(Counter::kLinearAnimationInstance, +1);
}
LinearAnimationInstance::~LinearAnimationInstance() {
Counter::update(Counter::kLinearAnimationInstance, -1);
}
bool LinearAnimationInstance::advanceAndApply(float seconds) {
bool more = this->advance(seconds);
this->apply();
m_ArtboardInstance->advance(seconds);
return more;
}
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 = (float)end;
m_Time = frames / fps;
didLoop = true;
} else if (m_Direction == -1 && frames < start) {
keepGoing = false;
m_SpilledTime = (start - frames) / fps;
frames = (float)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, (float)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, (float)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(); }
bool LinearAnimationInstance::isTranslucent() const {
return m_ArtboardInstance->isTranslucent(this);
}
// Returns either the animation's default or overridden loop values
int LinearAnimationInstance::loopValue() const {
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(); }