Add support for nested state machines and nested hit tests.
diff --git a/include/rive/animation/nested_state_machine.hpp b/include/rive/animation/nested_state_machine.hpp index aa30f02..125e83f 100644 --- a/include/rive/animation/nested_state_machine.hpp +++ b/include/rive/animation/nested_state_machine.hpp
@@ -1,14 +1,26 @@ #ifndef _RIVE_NESTED_STATE_MACHINE_HPP_ #define _RIVE_NESTED_STATE_MACHINE_HPP_ #include "rive/generated/animation/nested_state_machine_base.hpp" -#include <stdio.h> +#include "rive/math/vec2d.hpp" +#include <memory> + namespace rive { class ArtboardInstance; - + class StateMachineInstance; class NestedStateMachine : public NestedStateMachineBase { + private: + std::unique_ptr<StateMachineInstance> m_StateMachineInstance; + public: + NestedStateMachine(); + ~NestedStateMachine() override; void advance(float elapsedSeconds) override; void initializeAnimation(ArtboardInstance*) override; + StateMachineInstance* stateMachineInstance(); + + void pointerMove(Vec2D position); + void pointerDown(Vec2D position); + void pointerUp(Vec2D position); }; } // namespace rive
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp index 6f47a50..5b05aa6 100644 --- a/include/rive/animation/state_machine_instance.hpp +++ b/include/rive/animation/state_machine_instance.hpp
@@ -19,6 +19,7 @@ class Shape; class StateMachineLayerInstance; class HitShape; + class NestedArtboard; class StateMachineInstance : public Scene { friend class SMIInput; @@ -27,13 +28,15 @@ const StateMachine* m_Machine; bool m_NeedsAdvance = false; - std::vector<SMIInput*> m_InputInstances; // we own each pointer + std::vector<SMIInput*> m_InputInstances; // we own each pointer size_t m_LayerCount; StateMachineLayerInstance* m_Layers; void markNeedsAdvance(); std::vector<std::unique_ptr<HitShape>> m_HitShapes; + std::vector<NestedArtboard*> m_HitNestedArtboards; + /// Provide a hitEvent if you want to process a down or an up for the pointer position too. void processEvent(Vec2D position, EventType hitEvent = EventType::updateHover);
diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 34def2f..f7cb07b 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp
@@ -99,6 +99,7 @@ CommandPath* backgroundPath() const { return m_BackgroundPath.get(); } const std::vector<Core*>& objects() const { return m_Objects; } + const std::vector<NestedArtboard*> nestedArtboards() const { return m_NestedArtboards; } AABB bounds() const;
diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp index 71e1c13..1fdc746 100644 --- a/include/rive/nested_artboard.hpp +++ b/include/rive/nested_artboard.hpp
@@ -29,6 +29,15 @@ Core* clone() const override; bool advance(float elapsedSeconds); void update(ComponentDirt value) override; + + bool hasNestedStateMachines() const; + const std::vector<NestedAnimation*>& nestedAnimations() const; + + /// Convert a world space (relative to the artboard that this + /// NestedArtboard is a child of) to the local space of the Artboard + /// nested within. Returns true when the conversion succeeds, and false + /// when one is not possible. + bool worldToLocal(Vec2D world, Vec2D* local); }; } // namespace rive
diff --git a/src/animation/nested_state_machine.cpp b/src/animation/nested_state_machine.cpp index 139572a..c453867 100644 --- a/src/animation/nested_state_machine.cpp +++ b/src/animation/nested_state_machine.cpp
@@ -1,7 +1,39 @@ #include "rive/animation/nested_state_machine.hpp" +#include "rive/animation/state_machine_instance.hpp" using namespace rive; -void NestedStateMachine::advance(float elapsedSeconds) {} +NestedStateMachine::NestedStateMachine() {} +NestedStateMachine::~NestedStateMachine() {} -void NestedStateMachine::initializeAnimation(ArtboardInstance*) {} \ No newline at end of file +void NestedStateMachine::advance(float elapsedSeconds) { + if (m_StateMachineInstance != nullptr) { + m_StateMachineInstance->advance(elapsedSeconds); + } +} + +void NestedStateMachine::initializeAnimation(ArtboardInstance* artboard) { + m_StateMachineInstance = artboard->stateMachineAt(animationId()); +} + +StateMachineInstance* NestedStateMachine::stateMachineInstance() { + return m_StateMachineInstance.get(); +} + +void NestedStateMachine::pointerMove(Vec2D position) { + if (m_StateMachineInstance != nullptr) { + m_StateMachineInstance->pointerMove(position); + } +} + +void NestedStateMachine::pointerDown(Vec2D position) { + if (m_StateMachineInstance != nullptr) { + m_StateMachineInstance->pointerDown(position); + } +} + +void NestedStateMachine::pointerUp(Vec2D position) { + if (m_StateMachineInstance != nullptr) { + m_StateMachineInstance->pointerUp(position); + } +} \ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index e6d2a60..bbf40dd 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp
@@ -17,6 +17,9 @@ #include "rive/shapes/shape.hpp" #include "rive/math/aabb.hpp" #include "rive/math/hit_test.hpp" +#include "rive/nested_artboard.hpp" +#include "rive/nested_animation.hpp" +#include "rive/animation/nested_state_machine.hpp" #include <unordered_map> using namespace rive; @@ -237,8 +240,10 @@ } // namespace rive void StateMachineInstance::processEvent(Vec2D position, EventType hitEvent) { - position -= Vec2D(m_ArtboardInstance->originX() * m_ArtboardInstance->width(), - m_ArtboardInstance->originY() * m_ArtboardInstance->height()); + if (m_ArtboardInstance->frameOrigin()) { + position -= Vec2D(m_ArtboardInstance->originX() * m_ArtboardInstance->width(), + m_ArtboardInstance->originY() * m_ArtboardInstance->height()); + } const float hitRadius = 2; auto hitArea = AABB(position.x - hitRadius, @@ -275,6 +280,41 @@ } } } + + // TODO: store a hittable abstraction for HitShape and NestedArtboard that + // can be sorted by drawOrder so they can be iterated in one loop and early + // out if any hit stops propagation (also require the ability to mark a hit + // as able to stop propagation) + for (auto nestedArtboard : m_HitNestedArtboards) { + Vec2D nestedPosition; + if (!nestedArtboard->worldToLocal(position, &nestedPosition)) { + // Mounted artboard isn't ready or has a 0 scale transform. + continue; + } + // var nestedPosition = nestedArtboard.worldToLocal(position); + // if (nestedPosition == null) { + // // Mounted artboard isn't ready or has a 0 scale transform. + // continue; + // } + for (auto nestedAnimation : nestedArtboard->nestedAnimations()) { + if (nestedAnimation->is<NestedStateMachine>()) { + auto nestedStateMachine = nestedAnimation->as<NestedStateMachine>(); + switch (hitEvent) { + case EventType::down: + nestedStateMachine->pointerDown(nestedPosition); + break; + case EventType::up: + nestedStateMachine->pointerUp(nestedPosition); + break; + case EventType::updateHover: + nestedStateMachine->pointerMove(nestedPosition); + break; + default: + break; + } + } + } + } } void StateMachineInstance::pointerMove(Vec2D position) { @@ -343,6 +383,12 @@ hitShape->events.push_back(event); } } + + for (auto nestedArtboard : instance->nestedArtboards()) { + if (nestedArtboard->hasNestedStateMachines()) { + m_HitNestedArtboards.push_back(nestedArtboard); + } + } } StateMachineInstance::~StateMachineInstance() {
diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp index 6bd13d8..0e26962 100644 --- a/src/nested_artboard.cpp +++ b/src/nested_artboard.cpp
@@ -4,6 +4,8 @@ #include "rive/importers/import_stack.hpp" #include "rive/importers/backboard_importer.hpp" #include "rive/nested_animation.hpp" +#include "rive/animation/nested_state_machine.hpp" +#include <cassert> using namespace rive; @@ -24,6 +26,7 @@ assert(artboard != nullptr); m_Artboard = artboard; + m_Artboard->frameOrigin(false); m_Instance = nullptr; if (artboard->isInstance()) { m_Instance.reset(static_cast<ArtboardInstance*>(artboard)); // take ownership @@ -45,7 +48,7 @@ // transformations. renderer->save(); } - renderer->transform(worldTransform() * makeTranslate(m_Artboard)); + renderer->transform(worldTransform()); m_Artboard->draw(renderer); renderer->restore(); } @@ -110,3 +113,31 @@ m_Artboard->opacity(renderOpacity()); } } + +bool NestedArtboard::hasNestedStateMachines() const { + for (auto animation : m_NestedAnimations) { + if (animation->is<NestedStateMachine>()) { + return true; + } + } + return false; +} + +const std::vector<NestedAnimation*>& NestedArtboard::nestedAnimations() const { + return m_NestedAnimations; +} + +bool NestedArtboard::worldToLocal(Vec2D world, Vec2D* local) { + assert(local != nullptr); + if (m_Artboard == nullptr) { + return false; + } + Mat2D toMountedArtboard; + if (!worldTransform().invert(&toMountedArtboard)) { + return false; + } + + *local = toMountedArtboard * world; + + return true; +} \ No newline at end of file