Add common base-class for playable instances
diff --git a/include/rive/animation/linear_animation_instance.hpp b/include/rive/animation/linear_animation_instance.hpp index dcbdc67..71b0218 100644 --- a/include/rive/animation/linear_animation_instance.hpp +++ b/include/rive/animation/linear_animation_instance.hpp
@@ -1,14 +1,15 @@ #ifndef _RIVE_LINEAR_ANIMATION_INSTANCE_HPP_ #define _RIVE_LINEAR_ANIMATION_INSTANCE_HPP_ + #include "rive/artboard.hpp" +#include "rive/scene.hpp" namespace rive { class LinearAnimation; - class LinearAnimationInstance { + class LinearAnimationInstance : public Scene { private: const LinearAnimation* m_Animation = nullptr; - ArtboardInstance* m_ArtboardInstance; float m_Time; float m_TotalTime; float m_LastTotalTime; @@ -17,13 +18,18 @@ bool m_DidLoop; int m_LoopValue = -1; + protected: + bool isTranslucent() const override; + bool advanceAndApply(float seconds) override; + std::string name() const override; + public: - LinearAnimationInstance(const LinearAnimation*, ArtboardInstance* instance); + LinearAnimationInstance(const LinearAnimation*, ArtboardInstance*); // Advance the animation by the specified time. Returns true if the // animation will continue to animate after this advance. bool advance(float seconds); - + // Returns a pointer to the instance's animation const LinearAnimation* animation() const { return m_Animation; } @@ -61,18 +67,17 @@ float totalTime() const { return m_TotalTime; } float lastTotalTime() const { return m_LastTotalTime; } float spilledTime() const { return m_SpilledTime; } - float durationSeconds() const; + float durationSeconds() const override; // Forwarded from animation uint32_t fps() const; uint32_t duration() const; float speed() const; float startSeconds() const; - std::string name() const; // Returns either the animation's default or overridden loop values - Loop loop() { return (Loop)loopValue(); } - int loopValue(); + Loop loop() const override { return (Loop)loopValue(); } + int loopValue() const; // Override the animation's default loop void loopValue(int value); };
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp index 57b3e64..0506d23 100644 --- a/include/rive/animation/state_machine_instance.hpp +++ b/include/rive/animation/state_machine_instance.hpp
@@ -6,6 +6,7 @@ #include <vector> #include "rive/animation/linear_animation_instance.hpp" #include "rive/event_type.hpp" +#include "rive/scene.hpp" namespace rive { class StateMachine; @@ -19,12 +20,11 @@ class StateMachineLayerInstance; class HitShape; - class StateMachineInstance { + class StateMachineInstance : public Scene { friend class SMIInput; private: const StateMachine* m_Machine; - ArtboardInstance* m_ArtboardInstance; bool m_NeedsAdvance = false; std::vector<SMIInput*> m_InputInstances; // we own each pointer @@ -40,9 +40,18 @@ template <typename SMType, typename InstType> InstType* getNamedInput(const std::string& name) const; + protected: + float durationSeconds() const override { return -1; } + bool advanceAndApply(float secs) override; + Loop loop() const override { return Loop::oneShot; } + // For now play it safe -- unless we can inspect all of our + // possible states and animations... + bool isTranslucent() const override { return true; } + std::string name() const override; + public: StateMachineInstance(const StateMachine* machine, ArtboardInstance* instance); - ~StateMachineInstance(); + ~StateMachineInstance() override; // Advance the state machine by the specified time. Returns true if the // state machine will continue to animate after this advance. @@ -54,14 +63,11 @@ // Returns a pointer to the instance's stateMachine const StateMachine* stateMachine() const { return m_Machine; } - size_t inputCount() const { return m_InputInstances.size(); } - SMIInput* input(size_t index) const; - - SMIBool* getBool(const std::string& name) const; - SMINumber* getNumber(const std::string& name) const; - SMITrigger* getTrigger(const std::string& name) const; - - std::string name() const; + size_t inputCount() const override { return m_InputInstances.size(); } + SMIInput* input(size_t index) const override; + SMIBool* getBool(const std::string& name) const override; + SMINumber* getNumber(const std::string& name) const override; + SMITrigger* getTrigger(const std::string& name) const override; const size_t currentAnimationCount() const; const LinearAnimationInstance* currentAnimationByIndex(size_t index) const; @@ -75,9 +81,9 @@ // the empty string. const LayerState* stateChangedByIndex(size_t index) const; - void pointerMove(Vec2D position); - void pointerDown(Vec2D position); - void pointerUp(Vec2D position); + void pointerMove(Vec2D position) override; + void pointerDown(Vec2D position) override; + void pointerUp(Vec2D position) override; }; } // namespace rive #endif
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 2306890..17daac9 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -119,6 +119,8 @@ const std::vector<Core*>& objects() const { return m_Objects; } AABB bounds() const; + + // Can we hide these from the public? (they use playable) bool isTranslucent(const LinearAnimation*) const; bool isTranslucent(const LinearAnimationInstance*) const;
diff --git a/include/rive/scene.hpp b/include/rive/scene.hpp new file mode 100644 index 0000000..90de466 --- /dev/null +++ b/include/rive/scene.hpp
@@ -0,0 +1,58 @@ +#ifndef _RIVE_SCENE_HPP_ +#define _RIVE_SCENE_HPP_ + +#include "rive/animation/loop.hpp" +#include "rive/math/aabb.hpp" +#include "rive/math/vec2d.hpp" +#include <string> + +namespace rive { + class ArtboardInstance; + class Renderer; + + class SMIInput; + class SMIBool; + class SMINumber; + class SMITrigger; + + class Scene { + protected: + ArtboardInstance* m_ArtboardInstance; + + Scene(ArtboardInstance*); + + public: + virtual ~Scene() {} + + float width() const; + float height() const; + AABB bounds() const { return {0, 0, this->width(), this->height()}; } + + virtual std::string name() const = 0; + + // Returns onShot if this has no looping (e.g. a statemachine) + virtual Loop loop() const = 0; + // Returns true iff the Scene is known to not be fully opaque + virtual bool isTranslucent() const = 0; + // returns -1 for continuous + virtual float durationSeconds() const = 0; + + // returns true if draw() should be called + virtual bool advanceAndApply(float elapsedSeconds) = 0; + + void draw(Renderer*); + + virtual void pointerDown(Vec2D); + virtual void pointerMove(Vec2D); + virtual void pointerUp(Vec2D); + + virtual size_t inputCount() const; + virtual SMIInput* input(size_t index) const; + virtual SMIBool* getBool(const std::string&) const; + virtual SMINumber* getNumber(const std::string&) const; + virtual SMITrigger* getTrigger(const std::string&) const; + }; + +} // namespace rive + +#endif
diff --git a/skia/viewer/src/main.cpp b/skia/viewer/src/main.cpp index 8568137..a0ff9a7 100644 --- a/skia/viewer/src/main.cpp +++ b/skia/viewer/src/main.cpp
@@ -22,6 +22,7 @@ #include "rive/artboard.hpp" #include "rive/file.hpp" #include "rive/layout.hpp" +#include "rive/playable.hpp" #include "rive/math/aabb.hpp" #include "skia_factory.hpp" #include "skia_renderer.hpp" @@ -37,8 +38,7 @@ std::string filename; std::unique_ptr<rive::File> currentFile; std::unique_ptr<rive::ArtboardInstance> artboardInstance; -std::unique_ptr<rive::StateMachineInstance> stateMachineInstance; -std::unique_ptr<rive::LinearAnimationInstance> animationInstance; +std::unique_ptr<rive::Playable> currentPlayable; // ImGui wants raw pointers to names, but our public API returns // names as strings (by value), so we cache these names each time we @@ -77,8 +77,7 @@ fprintf(stderr, "failed to import file\n"); return; } - animationInstance = nullptr; - stateMachineInstance = nullptr; + currentPlayable = nullptr; artboardInstance = nullptr; currentFile = std::move(file); @@ -87,7 +86,8 @@ loadNames(artboardInstance.get()); if (index >= 0 && index < artboardInstance->stateMachineCount()) { - stateMachineInstance = artboardInstance->stateMachineAt(index); + currentPlayable = artboardInstance->stateMachineAt(index); + currentPlayable->inputCount(); } } @@ -101,8 +101,7 @@ fprintf(stderr, "failed to import file\n"); return; } - animationInstance = nullptr; - stateMachineInstance = nullptr; + currentPlayable = nullptr; artboardInstance = nullptr; currentFile = std::move(file); @@ -111,7 +110,8 @@ loadNames(artboardInstance.get()); if (index >= 0 && index < artboardInstance->animationCount()) { - animationInstance = artboardInstance->animationAt(index); + currentPlayable = artboardInstance->animationAt(index); + currentPlayable->inputCount(); } } @@ -121,18 +121,18 @@ float xscale, yscale; glfwGetWindowContentScale(window, &xscale, &yscale); lastWorldMouse = gInverseViewTransform * rive::Vec2D(x * xscale, y * yscale); - if (stateMachineInstance != nullptr) { - stateMachineInstance->pointerMove(lastWorldMouse); + if (currentPlayable) { + currentPlayable->pointerMove(lastWorldMouse); } } void glfwMouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { - if (stateMachineInstance != nullptr) { + if (currentPlayable) { switch (action) { case GLFW_PRESS: - stateMachineInstance->pointerDown(lastWorldMouse); + currentPlayable->pointerDown(lastWorldMouse); break; case GLFW_RELEASE: - stateMachineInstance->pointerUp(lastWorldMouse); + currentPlayable->pointerUp(lastWorldMouse); break; } } @@ -271,14 +271,8 @@ paint.setColor(SK_ColorDKGRAY); canvas->drawPaint(paint); - if (artboardInstance != nullptr) { - if (animationInstance != nullptr) { - animationInstance->advance(elapsed); - animationInstance->apply(); - } else if (stateMachineInstance != nullptr) { - stateMachineInstance->advance(elapsed); - } - artboardInstance->advance(elapsed); + if (currentPlayable) { + currentPlayable->advanceAndApply(elapsed); rive::SkiaRenderer renderer(canvas); renderer.save(); @@ -286,16 +280,15 @@ auto viewTransform = rive::computeAlignment(rive::Fit::contain, rive::Alignment::center, rive::AABB(0, 0, width, height), - artboardInstance->bounds()); + currentPlayable->bounds()); renderer.transform(viewTransform); // Store the inverse view so we can later go from screen to world. gInverseViewTransform = viewTransform.invertOrIdentity(); // post_mouse_event(artboard.get(), canvas->getTotalMatrix()); - artboardInstance->draw(&renderer); + currentPlayable->draw(&renderer); renderer.restore(); } - context->flush(); ImGui_ImplOpenGL3_NewFrame(); @@ -332,13 +325,13 @@ animationIndex = -1; initStateMachine(stateMachineIndex); } - if (stateMachineInstance != nullptr) { + if (currentPlayable != nullptr) { ImGui::Columns(2); ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() * 0.6666); - for (int i = 0; i < stateMachineInstance->inputCount(); i++) { - auto inputInstance = stateMachineInstance->input(i); + for (int i = 0; i < currentPlayable->inputCount(); i++) { + auto inputInstance = currentPlayable->input(i); if (inputInstance->input()->is<rive::StateMachineNumber>()) { // ImGui requires names as id's, use ## to hide the
diff --git a/src/animation/linear_animation_instance.cpp b/src/animation/linear_animation_instance.cpp index 1c8badb..9819657 100644 --- a/src/animation/linear_animation_instance.cpp +++ b/src/animation/linear_animation_instance.cpp
@@ -7,14 +7,21 @@ LinearAnimationInstance::LinearAnimationInstance(const LinearAnimation* animation, ArtboardInstance* instance) : + Scene(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::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; @@ -126,8 +133,12 @@ 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() { +int LinearAnimationInstance::loopValue() const { if (m_LoopValue != -1) { return m_LoopValue; } @@ -145,4 +156,4 @@ m_LoopValue = value; } -float LinearAnimationInstance::durationSeconds() const { return m_Animation->durationSeconds(); } \ No newline at end of file +float LinearAnimationInstance::durationSeconds() const { return m_Animation->durationSeconds(); }
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index fee23a4..0447d10 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp
@@ -284,9 +284,10 @@ } StateMachineInstance::StateMachineInstance(const StateMachine* machine, - ArtboardInstance* instance) : - m_Machine(machine), m_ArtboardInstance(instance) { - assert(instance->isInstance()); + ArtboardInstance* instance) + : Scene(instance) + , m_Machine(machine) +{ const auto count = machine->inputCount(); m_InputInstances.resize(count); for (size_t i = 0; i < count; i++) { @@ -368,6 +369,12 @@ return m_NeedsAdvance; } +bool StateMachineInstance::advanceAndApply(float seconds) { + bool more = this->advance(seconds); + m_ArtboardInstance->advance(seconds); + return more; +} + void StateMachineInstance::markNeedsAdvance() { m_NeedsAdvance = true; } bool StateMachineInstance::needsAdvance() const { return m_NeedsAdvance; }
diff --git a/src/scene.cpp b/src/scene.cpp new file mode 100644 index 0000000..d46302d --- /dev/null +++ b/src/scene.cpp
@@ -0,0 +1,30 @@ +#include "rive/artboard.hpp" +#include "rive/scene.hpp" + +using namespace rive; + +Scene::Scene(ArtboardInstance* abi) : m_ArtboardInstance(abi) { + assert(m_ArtboardInstance->isInstance()); +} + +float Scene::width() const { + return m_ArtboardInstance->width(); +} + +float Scene::height() const { + return m_ArtboardInstance->height(); +} + +void Scene::draw(Renderer* renderer) { + m_ArtboardInstance->draw(renderer); +} + +void Scene::pointerDown(Vec2D) {} +void Scene::pointerMove(Vec2D) {} +void Scene::pointerUp(Vec2D) {} + +size_t Scene::inputCount() const { return 0; } +SMIInput* Scene::input(size_t index) const { return nullptr; } +SMIBool* Scene::getBool(const std::string&) const { return nullptr; } +SMINumber* Scene::getNumber(const std::string&) const { return nullptr; } +SMITrigger* Scene::getTrigger(const std::string&) const { return nullptr; }