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