Playable as a high-level api
diff --git a/include/rive/playable.hpp b/include/rive/playable.hpp new file mode 100644 index 0000000..2b834a7 --- /dev/null +++ b/include/rive/playable.hpp
@@ -0,0 +1,70 @@ +#ifndef _RIVE_PLAYABLE_HPP_ +#define _RIVE_PLAYABLE_HPP_ + +#include "rive/math/aabb.hpp" +#include "rive/span.hpp" + +namespace rive { + class Factory; + class FileAssetResolver; + class Renderer; + + class ArtboardInstance; + class File; + class LinearAnimationInstance; + class StateMachineInstance; + + class SMIInput; + class SMIBool; + class SMINumber; + class SMITrigger; + + class Playable { + private: + std::unique_ptr<File> m_File; + std::unique_ptr<ArtboardInstance> m_ABI; + std::unique_ptr<LinearAnimationInstance> m_LAI; + std::unique_ptr<StateMachineInstance> m_SMI; + + Playable(); + + public: + static std::unique_ptr<Playable> import(Span<const uint8_t>, + Factory*, + FileAssetResolver* = nullptr); + + static std::unique_ptr<Playable> + animationAt(File*, size_t artboardIndex, size_t animationIndex); + + static std::unique_ptr<Playable> + stateMachineAt(File*, size_t artboardIndex, size_t machineIndex); + + ~Playable(); + + AABB bounds() const; + + // returns -1 for continuous + float durationSeconds() const; + + // returns true if draw() should be called + bool advance(float elapsedSeconds); + + void draw(Renderer*); + + enum class PointerState { + kDown, // pointer changed from 'up' to 'down' + kUp, // pointer changed from 'down' to 'up' + kMoved, // pointer position changed + }; + void pointerEvent(Vec2D, PointerState); + + size_t inputCount() const; + SMIInput* inputAt(size_t index) const; + SMIBool* boolNamed(const std::string&) const; + SMINumber* numberNamed(const std::string&) const; + SMITrigger* triggerNamed(const std::string&) const; + }; + +} // namespace rive + +#endif
diff --git a/src/playable.cpp b/src/playable.cpp new file mode 100644 index 0000000..7c2e072 --- /dev/null +++ b/src/playable.cpp
@@ -0,0 +1,126 @@ +#include "rive/artboard.hpp" +#include "rive/file.hpp" +#include "rive/file_asset_resolver.hpp" +#include "rive/playable.hpp" +#include "rive/animation/linear_animation_instance.hpp" +#include "rive/animation/state_machine_instance.hpp" + +using namespace rive; + +Playable::Playable() {} + +Playable::~Playable() {} + +std::unique_ptr<Playable> Playable::animationAt(File* file, + size_t artboardIndex, + size_t animationIndex) { + auto abi = file->artboardAt(artboardIndex); + if (abi) { + auto lai = abi->animationAt(animationIndex); + if (lai) { + std::unique_ptr<Playable> play(new Playable); + play->m_ABI = std::move(abi); + play->m_LAI = std::move(lai); + return play; + } + } + return nullptr; +} + +std::unique_ptr<Playable> Playable::stateMachineAt(File* file, + size_t artboardIndex, + size_t machineIndex) { + auto abi = file->artboardAt(artboardIndex); + if (abi) { + auto smi = abi->stateMachineAt(machineIndex); + if (smi) { + std::unique_ptr<Playable> play(new Playable); + play->m_ABI = std::move(abi); + play->m_SMI = std::move(smi); + return play; + } + } + return nullptr; +} + +std::unique_ptr<Playable> Playable::import(Span<const uint8_t> bytes, + Factory* factory, + FileAssetResolver* resolver) +{ + auto file = File::import(bytes, factory, nullptr, resolver); + if (!file) { + return nullptr; + } + + auto play = Playable::stateMachineAt(file.get(), 0, 0); + if (!play) { + play = Playable::animationAt(file.get(), 0, 0); + } + if (play) { + play->m_File = std::move(file); + } + return play; +} + +AABB Playable::bounds() const { + return m_ABI->bounds(); +} + +float Playable::durationSeconds() const { + return m_SMI ? -1 : m_LAI->durationSeconds(); +} + +// returns true if draw() should be called +bool Playable::advance(float elapsedSeconds) { + bool needsRedraw; + if (m_SMI) { + needsRedraw = m_SMI->advance(elapsedSeconds); + } else { + needsRedraw = m_LAI->advance(elapsedSeconds); + m_LAI->apply(); + } + return needsRedraw; +} + +void Playable::draw(Renderer* renderer) { + m_ABI->draw(renderer); +} + +void Playable::pointerEvent(Vec2D pos, PointerState state) { + if (m_SMI) { + switch (state) { + case PointerState::kDown: + m_SMI->pointerDown(pos); + break; + case PointerState::kMoved: + m_SMI->pointerMove(pos); + break; + case PointerState::kUp: + m_SMI->pointerUp(pos); + break; + } + } + // else the animation just ignores the pointer events +} + +// StateMachine Inputs + +size_t Playable::inputCount() const { + return m_SMI ? m_SMI->inputCount() : 0; +} + +SMIInput* Playable::inputAt(size_t index) const { + return m_SMI ? m_SMI->input(index) : nullptr; +} + +SMIBool* Playable::boolNamed(const std::string& name) const { + return m_SMI ? m_SMI->getBool(name) : nullptr; +} + +SMINumber* Playable::numberNamed(const std::string& name) const { + return m_SMI ? m_SMI->getNumber(name) : nullptr; +} + +SMITrigger* Playable::triggerNamed(const std::string& name) const { + return m_SMI ? m_SMI->getTrigger(name) : nullptr; +}