Manual Code Sync: 50/257050/1 accepted/tizen/unified/20210419.153502 submit/tizen/20210419.031955
authorHermet Park <chuneon.park@samsung.com>
Mon, 19 Apr 2021 02:30:02 +0000 (11:30 +0900)
committerHermet Park <chuneon.park@samsung.com>
Mon, 19 Apr 2021 02:30:43 +0000 (11:30 +0900)
Author: Hermet Park <chuneon.park@samsung.com>
Date:   Thu Apr 15 16:52:28 2021 +0900

    up-to-date submodule (rive-cpp)

Change-Id: Ibd0aed1e5cd249874f859bc0c19b58df7d4fc396

85 files changed:
meson.build
submodule/.clang-format
submodule/.gitignore
submodule/dev/core_generator/lib/src/definition.dart
submodule/dev/core_generator/lib/src/field_type.dart
submodule/dev/core_generator/lib/src/field_types/string_field_type.dart
submodule/dev/defs/animation/state_machine_double.json [deleted file]
submodule/dev/defs/animation/state_machine_number.json [new file with mode: 0644]
submodule/dev/defs/animation/state_transition.json
submodule/dev/defs/animation/transition_double_condition.json [deleted file]
submodule/dev/defs/animation/transition_number_condition.json [new file with mode: 0644]
submodule/include/animation/animation_state.hpp
submodule/include/animation/layer_state.hpp
submodule/include/animation/linear_animation.hpp
submodule/include/animation/linear_animation_instance.hpp
submodule/include/animation/state_machine.hpp
submodule/include/animation/state_machine_double.hpp [deleted file]
submodule/include/animation/state_machine_input.hpp
submodule/include/animation/state_machine_input_instance.hpp [new file with mode: 0644]
submodule/include/animation/state_machine_instance.hpp [new file with mode: 0644]
submodule/include/animation/state_machine_layer.hpp
submodule/include/animation/state_machine_number.hpp [new file with mode: 0644]
submodule/include/animation/state_machine_trigger.hpp
submodule/include/animation/state_transition.hpp
submodule/include/animation/state_transition_flags.hpp [new file with mode: 0644]
submodule/include/animation/transition_bool_condition.hpp
submodule/include/animation/transition_condition.hpp
submodule/include/animation/transition_condition_op.hpp [new file with mode: 0644]
submodule/include/animation/transition_double_condition.hpp [deleted file]
submodule/include/animation/transition_number_condition.hpp [new file with mode: 0644]
submodule/include/animation/transition_trigger_condition.hpp
submodule/include/animation/transition_value_condition.hpp
submodule/include/artboard.hpp
submodule/include/core/reader.h
submodule/include/file.hpp
submodule/include/generated/animation/animation_base.hpp
submodule/include/generated/animation/state_machine_component_base.hpp
submodule/include/generated/animation/state_machine_double_base.hpp [deleted file]
submodule/include/generated/animation/state_machine_number_base.hpp [new file with mode: 0644]
submodule/include/generated/animation/state_transition_base.hpp
submodule/include/generated/animation/transition_double_condition_base.hpp [deleted file]
submodule/include/generated/animation/transition_number_condition_base.hpp [new file with mode: 0644]
submodule/include/generated/component_base.hpp
submodule/include/generated/core_registry.hpp
submodule/include/importers/artboard_importer.hpp
submodule/include/importers/import_stack.hpp
submodule/include/importers/keyed_property_importer.hpp
submodule/include/importers/layer_state_importer.hpp [new file with mode: 0644]
submodule/include/importers/state_machine_importer.hpp [new file with mode: 0644]
submodule/include/importers/state_machine_layer_importer.hpp [new file with mode: 0644]
submodule/include/importers/state_transition_importer.hpp [new file with mode: 0644]
submodule/skia/dependencies/make_gl3w.sh [new file with mode: 0755]
submodule/skia/dependencies/make_imgui.sh [new file with mode: 0755]
submodule/skia/dependencies/make_viewer_dependencies.sh [new file with mode: 0755]
submodule/skia/viewer/build/premake5.lua
submodule/skia/viewer/src/main.cpp
submodule/src/animation/animation_state.cpp [new file with mode: 0644]
submodule/src/animation/keyed_property.cpp
submodule/src/animation/layer_state.cpp
submodule/src/animation/linear_animation.cpp
submodule/src/animation/linear_animation_instance.cpp
submodule/src/animation/state_machine.cpp [new file with mode: 0644]
submodule/src/animation/state_machine_input.cpp
submodule/src/animation/state_machine_input_instance.cpp [new file with mode: 0644]
submodule/src/animation/state_machine_instance.cpp [new file with mode: 0644]
submodule/src/animation/state_machine_layer.cpp
submodule/src/animation/state_machine_trigger.cpp [deleted file]
submodule/src/animation/state_transition.cpp
submodule/src/animation/transition_bool_condition.cpp [new file with mode: 0644]
submodule/src/animation/transition_condition.cpp
submodule/src/animation/transition_number_condition.cpp [new file with mode: 0644]
submodule/src/animation/transition_trigger_condition.cpp [new file with mode: 0644]
submodule/src/artboard.cpp
submodule/src/file.cpp
submodule/src/importers/artboard_importer.cpp
submodule/src/importers/keyed_property_importer.cpp
submodule/src/importers/layer_state_importer.cpp [new file with mode: 0644]
submodule/src/importers/state_machine_importer.cpp [new file with mode: 0644]
submodule/src/importers/state_machine_layer_importer.cpp [new file with mode: 0644]
submodule/src/importers/state_transition_importer.cpp [new file with mode: 0644]
submodule/src/shapes/path.cpp
submodule/src/shapes/polygon.cpp
submodule/test/assets/rocket.riv [new file with mode: 0644]
submodule/test/file_test.cpp
submodule/test/state_machine_test.cpp [new file with mode: 0644]

index 03a37b59c554796495b4ba87305726a4f7384fce..42de241a76afcf69e85668ab03a13b025ae38b03 100644 (file)
@@ -53,7 +53,6 @@ install_headers([
    'submodule/include/animation/state_machine.hpp',
    'submodule/include/animation/state_machine_bool.hpp',
    'submodule/include/animation/state_machine_component.hpp',
-   'submodule/include/animation/state_machine_double.hpp',
    'submodule/include/animation/state_machine_input.hpp',
    'submodule/include/animation/state_machine_layer.hpp',
    'submodule/include/animation/state_machine_layer_component.hpp',
@@ -61,7 +60,6 @@ install_headers([
    'submodule/include/animation/state_transition.hpp',
    'submodule/include/animation/transition_bool_condition.hpp',
    'submodule/include/animation/transition_condition.hpp',
-   'submodule/include/animation/transition_double_condition.hpp',
    'submodule/include/animation/transition_trigger_condition.hpp',
    'submodule/include/animation/transition_value_condition.hpp'
 ], subdir: 'rive/animation')
@@ -130,7 +128,6 @@ install_headers([
    'submodule/include/generated/animation/state_machine_base.hpp',
    'submodule/include/generated/animation/state_machine_bool_base.hpp',
    'submodule/include/generated/animation/state_machine_component_base.hpp',
-   'submodule/include/generated/animation/state_machine_double_base.hpp',
    'submodule/include/generated/animation/state_machine_input_base.hpp',
    'submodule/include/generated/animation/state_machine_layer_base.hpp',
    'submodule/include/generated/animation/state_machine_layer_component_base.hpp',
@@ -138,7 +135,6 @@ install_headers([
    'submodule/include/generated/animation/state_transition_base.hpp',
    'submodule/include/generated/animation/transition_bool_condition_base.hpp',
    'submodule/include/generated/animation/transition_condition_base.hpp',
-   'submodule/include/generated/animation/transition_double_condition_base.hpp',
    'submodule/include/generated/animation/transition_trigger_condition_base.hpp',
    'submodule/include/generated/animation/transition_value_condition_base.hpp'
 ], subdir: 'rive/generated/animation')
@@ -244,11 +240,16 @@ rive_cpp_src = [
    'submodule/src/animation/linear_animation.cpp',
    'submodule/src/animation/linear_animation_instance.cpp',
    'submodule/src/animation/layer_state.cpp',
+   'submodule/src/animation/state_machine.cpp',
    'submodule/src/animation/state_machine_input.cpp',
+   'submodule/src/animation/state_machine_input_instance.cpp',
+   'submodule/src/animation/state_machine_instance.cpp',
    'submodule/src/animation/state_machine_layer.cpp',
-   'submodule/src/animation/state_machine_trigger.cpp',
    'submodule/src/animation/state_transition.cpp',
+   'submodule/src/animation/transition_bool_condition.cpp',
    'submodule/src/animation/transition_condition.cpp',
+   'submodule/src/animation/transition_number_condition.cpp',
+   'submodule/src/animation/transition_trigger_condition.cpp',
    'submodule/src/core/binary_reader.cpp',
    'submodule/src/core/field_types/core_bool_type.cpp',
    'submodule/src/core/field_types/core_color_type.cpp',
@@ -258,7 +259,11 @@ rive_cpp_src = [
    'submodule/src/importers/artboard_importer.cpp',
    'submodule/src/importers/keyed_object_importer.cpp',
    'submodule/src/importers/keyed_property_importer.cpp',
+   'submodule/src/importers/layer_state_importer.cpp',
    'submodule/src/importers/linear_animation_importer.cpp',
+   'submodule/src/importers/state_machine_importer.cpp',
+   'submodule/src/importers/state_machine_layer_importer.cpp',
+   'submodule/src/importers/state_transition_importer.cpp',
    'submodule/src/shapes/clipping_shape.cpp',
    'submodule/src/shapes/cubic_mirrored_vertex.cpp',
    'submodule/src/shapes/metrics_path.cpp',
index 54be8d55d2c734ffeaadf18ec981f1d89f346f4b..fe1a732d2c974683c3e101267eed65c9807658e4 100644 (file)
@@ -17,6 +17,7 @@ BinPackArguments: false
 BinPackParameters: false
 AlignAfterOpenBracket: Align
 BreakBeforeBraces: Custom
+SortIncludes: false
 BraceWrapping:
   AfterEnum: true
   AfterClass: true
index dbee1ff5bb748754f2c44c6d242c5da062fceaf1..faa7c3c0d1c78020aa8bc183230ed3abef5cddae 100644 (file)
@@ -66,3 +66,6 @@ skia/dependencies/x264
 skia/renderer/build/bin
 skia/**/build/bin
 /skia/dependencies/glfw
+/skia/dependencies/gl3w
+/skia/dependencies/imgui
+/skia/viewer/imgui.ini
index b8bbeb9c6b57a32eddb598e812bb4895aa63e251..d1a2042c2bb2e459c6e0aade675da8763250917a 100644 (file)
@@ -173,7 +173,7 @@ class Definition {
       code.writeln('public:');
       for (final property in properties) {
         code.writeln(
-            'inline ${property.type.cppName} ${property.name}() const ' +
+            'inline ${property.type.cppGetterName} ${property.name}() const ' +
                 (property.isGetOverride ? 'override' : '') +
                 '{ return m_${property.capitalizedName}; }');
 
index c04e77aacc47dfa55359bf2cee3ee0b72fcde630..c5dd856dd138cbd98b80174a454db2fb057fc6f7 100644 (file)
@@ -11,6 +11,7 @@ abstract class FieldType {
   String _cppName;
   final String include;
   String get cppName => _cppName;
+  String get cppGetterName => _cppName;
 
   String _runtimeCoreType;
   String get runtimeCoreType => _runtimeCoreType;
index c73ac480d96e96d1ba762f5de66458d294e9bd33..db4842fe0f639e8f3810428dcb0af2939aa06619 100644 (file)
@@ -7,6 +7,9 @@ class StringFieldType extends FieldType {
   @override
   String get defaultValue => '""';
 
+  @override
+  String get cppGetterName => 'const std::string&';
+
   @override
   String convertCpp(String value) {
     var result = value;
diff --git a/submodule/dev/defs/animation/state_machine_double.json b/submodule/dev/defs/animation/state_machine_double.json
deleted file mode 100644 (file)
index 7ea0953..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "name": "StateMachineDouble",
-  "key": {
-    "int": 56,
-    "string": "statemachinedouble"
-  },
-  "extends": "animation/state_machine_input.json",
-  "properties": {
-    "value": {
-      "type": "double",
-      "initialValue": "0",
-      "key": {
-        "int": 140,
-        "string": "value"
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/state_machine_number.json b/submodule/dev/defs/animation/state_machine_number.json
new file mode 100644 (file)
index 0000000..c9f9d2e
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "name": "StateMachineNumber",
+  "key": {
+    "int": 56,
+    "string": "statemachinenumber"
+  },
+  "extends": "animation/state_machine_input.json",
+  "properties": {
+    "value": {
+      "type": "double",
+      "initialValue": "0",
+      "key": {
+        "int": 140,
+        "string": "value"
+      }
+    }
+  }
+}
\ No newline at end of file
index 7553d35cde2987c410181a0e6199cddd74242b88..668273f7225c29f325f7fccb71b2d51589489da8 100644 (file)
@@ -42,7 +42,7 @@
         "int": 158,
         "string": "duration"
       },
-      "description": "Duration of the trasition (mix time) in milliseconds."
+      "description": "Duration of the trasition (mix time) in milliseconds or percentage (0-100) based on flags."
     },
     "transitionOrder": {
       "type": "FractionalIndex",
       },
       "description": "Order value for sorting transitions in states.",
       "runtime": false
+    },
+    "exitTime": {
+      "type": "uint",
+      "initialValue": "0",
+      "key": {
+        "int": 160,
+        "string": "exittime"
+      },
+      "description": "Duration in milliseconds that must elapse before allowing the state to change. If the flags mark this property as being percentage based, the value is in 0-100% of the outgoing animation's duration"
     }
   }
 }
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/transition_double_condition.json b/submodule/dev/defs/animation/transition_double_condition.json
deleted file mode 100644 (file)
index 7fa83ad..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-  "name": "TransitionDoubleCondition",
-  "key": {
-    "int": 70,
-    "string": "transitiondoublecondition"
-  },
-  "extends": "animation/transition_value_condition.json",
-  "properties": {
-    "value": {
-      "type": "double",
-      "initialValue": "0",
-      "key": {
-        "int": 157,
-        "string": "value"
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/submodule/dev/defs/animation/transition_number_condition.json b/submodule/dev/defs/animation/transition_number_condition.json
new file mode 100644 (file)
index 0000000..b581107
--- /dev/null
@@ -0,0 +1,18 @@
+{
+  "name": "TransitionNumberCondition",
+  "key": {
+    "int": 70,
+    "string": "transitionnumbercondition"
+  },
+  "extends": "animation/transition_value_condition.json",
+  "properties": {
+    "value": {
+      "type": "double",
+      "initialValue": "0",
+      "key": {
+        "int": 157,
+        "string": "value"
+      }
+    }
+  }
+}
\ No newline at end of file
index 91fc866cdc52c6ef65e9c3e8d54374ab7b2a3abc..93a539eb05eec991096621a66e3668324fc4bdd2 100644 (file)
@@ -4,9 +4,19 @@
 #include <stdio.h>
 namespace rive
 {
+       class LinearAnimation;
+       class Artboard;
+       class StateMachineLayerImporter;
+
        class AnimationState : public AnimationStateBase
        {
+               friend class StateMachineLayerImporter;
+
+       private:
+               LinearAnimation* m_Animation;
+
        public:
+               const LinearAnimation* animation() const { return m_Animation; }
        };
 } // namespace rive
 
index 3bda97d2c36412f22fb4c6122fca91f04a42bdc5..0e0f13ccc984c9f746565a5c3f801ba52fbb0790 100644 (file)
@@ -2,13 +2,39 @@
 #define _RIVE_LAYER_STATE_HPP_
 #include "generated/animation/layer_state_base.hpp"
 #include <stdio.h>
+#include <vector>
+
 namespace rive
 {
+       class StateTransition;
+       class LayerStateImporter;
+       class StateMachineLayerImporter;
+
        class LayerState : public LayerStateBase
        {
+               friend class LayerStateImporter;
+               friend class StateMachineLayerImporter;
+
+       private:
+               std::vector<StateTransition*> m_Transitions;
+               void addTransition(StateTransition* transition);
+
        public:
+               ~LayerState();
                StatusCode onAddedDirty(CoreContext* context) override;
                StatusCode onAddedClean(CoreContext* context) override;
+
+               StatusCode import(ImportStack& importStack) override;
+
+               size_t transitionCount() const { return m_Transitions.size(); }
+               StateTransition* transition(size_t index) const
+               {
+                       if (index < m_Transitions.size())
+                       {
+                               return m_Transitions[index];
+                       }
+                       return nullptr;
+               }
        };
 } // namespace rive
 
index f6eb78ca182a81228f612a624dfb1f0e122ca351..d64c986f746185e429046444bb96dd1690a6663b 100644 (file)
@@ -18,12 +18,16 @@ namespace rive
                StatusCode onAddedDirty(CoreContext* context) override;
                StatusCode onAddedClean(CoreContext* context) override;
                void addKeyedObject(KeyedObject* object);
-               void apply(Artboard* artboard, float time, float mix = 1.0f);
+               void apply(Artboard* artboard, float time, float mix = 1.0f) const;
 
                Loop loop() const { return (Loop)loopValue(); }
 
                StatusCode import(ImportStack& importStack) override;
 
+               float startSeconds() const;
+               float endSeconds() const;
+               float durationSeconds() const;
+
 #ifdef TESTING
                size_t numKeyedObjects() { return m_KeyedObjects.size(); }
 #endif
index c892f5c164a44c77bb9e162fe00b7746e8a12b49..78da2ec4713228e476afef5d51213d9b554e50f2 100644 (file)
@@ -1,6 +1,6 @@
 #ifndef _RIVE_LINEAR_ANIMATION_INSTANCE_HPP_
 #define _RIVE_LINEAR_ANIMATION_INSTANCE_HPP_
-#include "animation/linear_animation.hpp"
+#include "artboard.hpp"
 
 namespace rive
 {
@@ -9,20 +9,23 @@ namespace rive
        class LinearAnimationInstance
        {
        private:
-               LinearAnimation* m_Animation = nullptr;
+               const LinearAnimation* m_Animation = nullptr;
                float m_Time;
+               float m_TotalTime;
+               float m_LastTotalTime;
+               float m_SpilledTime;
                int m_Direction;
                bool m_DidLoop;
 
        public:
-               LinearAnimationInstance(LinearAnimation* animation);
+               LinearAnimationInstance(const LinearAnimation* animation);
 
                // 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
-               LinearAnimation* animation() const { return m_Animation; }
+               const LinearAnimation* animation() const { return m_Animation; }
 
                // Returns the current point in time at which this instance has advance
                // to
@@ -41,7 +44,11 @@ namespace rive
 
                // Set when the animation is advanced, true if the animation has stopped
                // (oneShot), reached the end (loop), or changed direction (pingPong)
-               bool didLoop() const { return m_DidLoop; } 
+               bool didLoop() const { return m_DidLoop; }
+
+               float totalTime() const { return m_TotalTime; }
+               float lastTotalTime() const { return m_LastTotalTime; }
+               float spilledTime() const { return m_SpilledTime; }
        };
 } // namespace rive
 #endif
\ No newline at end of file
index e532c3f60ac5bfe51698c9ad22e34c943c4ff4ee..e5089a3090a561e540b1cbb979c20ca96307e48d 100644 (file)
@@ -2,11 +2,38 @@
 #define _RIVE_STATE_MACHINE_HPP_
 #include "generated/animation/state_machine_base.hpp"
 #include <stdio.h>
+#include <vector>
+
 namespace rive
 {
+       class StateMachineLayer;
+       class StateMachineInput;
+       class StateMachineImporter;
        class StateMachine : public StateMachineBase
        {
+               friend class StateMachineImporter;
+
+       private:
+               std::vector<StateMachineLayer*> m_Layers;
+               std::vector<StateMachineInput*> m_Inputs;
+
+               void addLayer(StateMachineLayer* layer);
+               void addInput(StateMachineInput* input);
+
        public:
+               ~StateMachine();
+               StatusCode import(ImportStack& importStack) override;
+
+               size_t layerCount() const { return m_Layers.size(); }
+               size_t inputCount() const { return m_Inputs.size(); }
+
+               const StateMachineInput* input(std::string name) const;
+               const StateMachineInput* input(size_t index) const;
+               const StateMachineLayer* layer(std::string name) const;
+               const StateMachineLayer* layer(size_t index) const;
+
+               StatusCode onAddedDirty(CoreContext* context) override;
+               StatusCode onAddedClean(CoreContext* context) override;
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/state_machine_double.hpp b/submodule/include/animation/state_machine_double.hpp
deleted file mode 100644 (file)
index 4a1a748..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef _RIVE_STATE_MACHINE_DOUBLE_HPP_
-#define _RIVE_STATE_MACHINE_DOUBLE_HPP_
-#include "generated/animation/state_machine_double_base.hpp"
-#include <stdio.h>
-namespace rive
-{
-       class StateMachineDouble : public StateMachineDoubleBase
-       {
-       public:
-       };
-} // namespace rive
-
-#endif
\ No newline at end of file
index 4b483bafe4f42ca33fcf5a64547824131bbb814a..ffa8ebe710e3443b25a58ecbc3f1685811ada196 100644 (file)
@@ -9,6 +9,7 @@ namespace rive
        public:
                StatusCode onAddedDirty(CoreContext* context) override;
                StatusCode onAddedClean(CoreContext* context) override;
+               StatusCode import(ImportStack& importStack) override;
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/state_machine_input_instance.hpp b/submodule/include/animation/state_machine_input_instance.hpp
new file mode 100644 (file)
index 0000000..d9849ad
--- /dev/null
@@ -0,0 +1,86 @@
+#ifndef _RIVE_STATE_MACHINE_INPUT_INSTANCE_HPP_
+#define _RIVE_STATE_MACHINE_INPUT_INSTANCE_HPP_
+
+#include <string>
+#include <stdint.h>
+
+namespace rive
+{
+       class StateMachineInstance;
+       class StateMachineInput;
+       class StateMachineBool;
+       class StateMachineNumber;
+       class StateMachineTrigger;
+       class TransitionTriggerCondition;
+
+       class SMIInput
+       {
+               friend class StateMachineInstance;
+
+       private:
+               StateMachineInstance* m_MachineInstance;
+               const StateMachineInput* m_Input;
+
+               virtual void advanced() {}
+
+       protected:
+               void valueChanged();
+
+               SMIInput(const StateMachineInput* input,
+                        StateMachineInstance* machineInstance);
+
+       public:
+               virtual ~SMIInput() {}
+               const StateMachineInput* input() const { return m_Input; }
+               
+               const std::string& name() const;
+               uint16_t inputCoreType() const;
+       };
+
+       class SMIBool : public SMIInput
+       {
+               friend class StateMachineInstance;
+
+       private:
+               bool m_Value;
+
+               SMIBool(const StateMachineBool* input,
+                       StateMachineInstance* machineInstance);
+
+       public:
+               bool value() const { return m_Value; }
+               void value(bool newValue);
+       };
+
+       class SMINumber : public SMIInput
+       {
+               friend class StateMachineInstance;
+
+       private:
+               float m_Value;
+
+               SMINumber(const StateMachineNumber* input,
+                         StateMachineInstance* machineInstance);
+
+       public:
+               float value() const { return m_Value; }
+               void value(float newValue);
+       };
+
+       class SMITrigger : public SMIInput
+       {
+               friend class StateMachineInstance;
+               friend class TransitionTriggerCondition;
+
+       private:
+               bool m_Fired = false;
+
+               SMITrigger(const StateMachineTrigger* input,
+                          StateMachineInstance* machineInstance);
+               void advanced() override { m_Fired = false; }
+
+       public:
+               void fire();
+       };
+} // namespace rive
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/state_machine_instance.hpp b/submodule/include/animation/state_machine_instance.hpp
new file mode 100644 (file)
index 0000000..bcfbc6d
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _RIVE_STATE_MACHINE_INSTANCE_HPP_
+#define _RIVE_STATE_MACHINE_INSTANCE_HPP_
+
+#include <string>
+#include <stddef.h>
+
+namespace rive
+{
+       class StateMachine;
+       class SMIInput;
+       class Artboard;
+       class SMIBool;
+       class SMINumber;
+       class SMITrigger;
+
+       class StateMachineLayerInstance;
+
+       class StateMachineInstance
+       {
+               friend class SMIInput;
+
+       private:
+               StateMachine* m_Machine;
+               bool m_NeedsAdvance = false;
+
+               size_t m_InputCount;
+               SMIInput** m_InputInstances;
+               unsigned int m_LayerCount;
+               StateMachineLayerInstance* m_Layers;
+
+               void markNeedsAdvance();
+
+       public:
+               StateMachineInstance(StateMachine* machine);
+               ~StateMachineInstance();
+
+               // Advance the state machine by the specified time. Returns true if the
+               // state machine will continue to animate after this advance.
+               bool advance(float seconds);
+
+               // Applies the animations of the StateMachine to an artboard.
+               void apply(Artboard* artboard) const;
+
+               // Returns true when the StateMachineInstance has more data to process.
+               bool needsAdvance() const;
+
+               size_t inputCount() const { return m_InputCount; }
+               SMIInput* input(size_t index) const;
+
+               SMIBool* getBool(std::string name) const;
+               SMINumber* getNumber(std::string name) const;
+               SMITrigger* getTrigger(std::string name) const;
+       };
+} // namespace rive
+#endif
\ No newline at end of file
index 1345b43cedb05539232ee929eec5e0842f7d46a9..c83b788c9d8ab0e05a6efac93ad3142ccddfda88 100644 (file)
@@ -2,13 +2,49 @@
 #define _RIVE_STATE_MACHINE_LAYER_HPP_
 #include "generated/animation/state_machine_layer_base.hpp"
 #include <stdio.h>
+#include <vector>
+
 namespace rive
 {
+       class LayerState;
+       class StateMachineLayerImporter;
+       class AnyState;
+       class EntryState;
+       class ExitState;
        class StateMachineLayer : public StateMachineLayerBase
        {
+               friend class StateMachineLayerImporter;
+
+       private:
+               std::vector<LayerState*> m_States;
+               AnyState* m_Any = nullptr;
+               EntryState* m_Entry = nullptr;
+               ExitState* m_Exit = nullptr;
+
+               void addState(LayerState* state);
+
        public:
+               ~StateMachineLayer();
                StatusCode onAddedDirty(CoreContext* context) override;
                StatusCode onAddedClean(CoreContext* context) override;
+
+               StatusCode import(ImportStack& importStack) override;
+
+               const AnyState* anyState() const { return m_Any; }
+               const EntryState* entryState() const { return m_Entry; }
+               const ExitState* exitState() const { return m_Exit; }
+
+#ifdef TESTING
+               size_t stateCount() const { return m_States.size(); }
+               LayerState* state(size_t index) const
+               {
+                       if (index < m_States.size())
+                       {
+                               return m_States[index];
+                       }
+                       return nullptr;
+               }
+#endif
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/state_machine_number.hpp b/submodule/include/animation/state_machine_number.hpp
new file mode 100644 (file)
index 0000000..c390ee0
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef _RIVE_STATE_MACHINE_NUMBER_HPP_
+#define _RIVE_STATE_MACHINE_NUMBER_HPP_
+#include "generated/animation/state_machine_number_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+       class StateMachineNumber : public StateMachineNumberBase
+       {
+       public:
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
index a6fb10f46b908f4d1e43d909b3939b97176e2170..c2f671d5f6533e4bd4349319082a5fef8340dc7a 100644 (file)
@@ -6,12 +6,6 @@ namespace rive
 {
        class StateMachineTrigger : public StateMachineTriggerBase
        {
-       private: 
-               bool m_Fired = false;
-               void reset();
-
-       public:
-               void fire();
        };
 } // namespace rive
 
index 22e74237e44ef3e9db92a8b5650002777db56cb7..140cab803bb32921f71fdc64cda1f7f98aa7b2e5 100644 (file)
@@ -1,14 +1,84 @@
 #ifndef _RIVE_STATE_TRANSITION_HPP_
 #define _RIVE_STATE_TRANSITION_HPP_
+#include "animation/state_transition_flags.hpp"
 #include "generated/animation/state_transition_base.hpp"
 #include <stdio.h>
+#include <vector>
+
 namespace rive
 {
+       class LayerState;
+       class StateMachineLayerImporter;
+       class StateTransitionImporter;
+       class TransitionCondition;
        class StateTransition : public StateTransitionBase
        {
+               friend class StateMachineLayerImporter;
+               friend class StateTransitionImporter;
+
+       private:
+               StateTransitionFlags transitionFlags() const
+               {
+                       return static_cast<StateTransitionFlags>(flags());
+               }
+               LayerState* m_StateTo = nullptr;
+
+               std::vector<TransitionCondition*> m_Conditions;
+               void addCondition(TransitionCondition* condition);
+
        public:
+               ~StateTransition();
+               const LayerState* stateTo() const { return m_StateTo; }
+
                StatusCode onAddedDirty(CoreContext* context) override;
                StatusCode onAddedClean(CoreContext* context) override;
+
+               /// Whether the transition is marked disabled (usually done in the
+               /// editor).
+               bool isDisabled() const
+               {
+                       return (transitionFlags() & StateTransitionFlags::Disabled) ==
+                              StateTransitionFlags::Disabled;
+               }
+
+               /// Whether the animation is held at exit or if it keeps advancing
+               /// during mixing.
+               bool pauseOnExit() const
+               {
+                       return (transitionFlags() & StateTransitionFlags::PauseOnExit) ==
+                              StateTransitionFlags::PauseOnExit;
+               }
+
+               /// Whether exit time is enabled. All other conditions still apply, the
+               /// exit time is effectively an AND with the rest of the conditions.
+               bool enableExitTime() const
+               {
+                       return (transitionFlags() & StateTransitionFlags::EnableExitTime) ==
+                              StateTransitionFlags::EnableExitTime;
+               }
+
+               StatusCode import(ImportStack& importStack) override;
+
+               size_t conditionCount() const { return m_Conditions.size(); }
+               TransitionCondition* condition(size_t index) const
+               {
+                       if (index < m_Conditions.size())
+                       {
+                               return m_Conditions[index];
+                       }
+                       return nullptr;
+               }
+
+               /// The amount of time to mix the outgoing animation onto the incoming
+               /// one when changing state. Only applies when going out from an
+               /// AnimationState.
+               float mixTime(const LayerState* stateFrom) const;
+
+               /// Exit time in seconds. Specify relativeToWorkArea to use the work
+               /// area start as the origin. Otherwise time 0 of the animation is the
+               /// origin.
+               float exitTimeSeconds(const LayerState* stateFrom,
+                                     bool relativeToWorkArea) const;
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/state_transition_flags.hpp b/submodule/include/animation/state_transition_flags.hpp
new file mode 100644 (file)
index 0000000..a37a718
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef _RIVE_STATE_TRANSITION_FLAGS_HPP_
+#define _RIVE_STATE_TRANSITION_FLAGS_HPP_
+
+#include <type_traits>
+
+namespace rive
+{
+       enum class StateTransitionFlags : unsigned char
+       {
+               None = 0,
+
+               /// Whether the transition is disabled.
+               Disabled = 1 << 0,
+
+               /// Whether the transition duration is a percentage or time in ms.
+               DurationIsPercentage = 1 << 1,
+
+               /// Whether exit time is enabled.
+               EnableExitTime = 1 << 2,
+
+               /// Whether the exit time is a percentage or time in ms.
+               ExitTimeIsPercentage = 1 << 3,
+
+               /// Whether the animation is held at exit or if it keeps advancing
+               /// during mixing.
+               PauseOnExit = 1 << 4
+
+       };
+
+       inline constexpr StateTransitionFlags operator&(StateTransitionFlags lhs,
+                                                       StateTransitionFlags rhs)
+       {
+               return static_cast<StateTransitionFlags>(
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(lhs) &
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(rhs));
+       }
+
+       inline constexpr StateTransitionFlags operator^(StateTransitionFlags lhs,
+                                                       StateTransitionFlags rhs)
+       {
+               return static_cast<StateTransitionFlags>(
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(lhs) ^
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(rhs));
+       }
+
+       inline constexpr StateTransitionFlags operator|(StateTransitionFlags lhs,
+                                                       StateTransitionFlags rhs)
+       {
+               return static_cast<StateTransitionFlags>(
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(lhs) |
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(rhs));
+       }
+
+       inline constexpr StateTransitionFlags operator~(StateTransitionFlags rhs)
+       {
+               return static_cast<StateTransitionFlags>(
+                   ~static_cast<std::underlying_type<StateTransitionFlags>::type>(
+                       rhs));
+       }
+
+       inline StateTransitionFlags& operator|=(StateTransitionFlags& lhs,
+                                               StateTransitionFlags rhs)
+       {
+               lhs = static_cast<StateTransitionFlags>(
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(lhs) |
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(rhs));
+
+               return lhs;
+       }
+
+       inline StateTransitionFlags& operator&=(StateTransitionFlags& lhs,
+                                               StateTransitionFlags rhs)
+       {
+               lhs = static_cast<StateTransitionFlags>(
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(lhs) &
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(rhs));
+
+               return lhs;
+       }
+
+       inline StateTransitionFlags& operator^=(StateTransitionFlags& lhs,
+                                               StateTransitionFlags rhs)
+       {
+               lhs = static_cast<StateTransitionFlags>(
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(lhs) ^
+                   static_cast<std::underlying_type<StateTransitionFlags>::type>(rhs));
+
+               return lhs;
+       }
+} // namespace rive
+#endif
\ No newline at end of file
index 9062454fdb5bc9cf6ca9694c7a7553b5f5b28747..40a1f27fac418a3f1b0ca62e10ac6df93e0c56b3 100644 (file)
@@ -7,6 +7,10 @@ namespace rive
        class TransitionBoolCondition : public TransitionBoolConditionBase
        {
        public:
+               bool evaluate(const SMIInput* inputInstance) const override;
+
+       protected:
+               bool validateInputType(const StateMachineInput* input) const override;
        };
 } // namespace rive
 
index df8af6e09a2df6f5633b87722f6ee7871a775ab0..b970cbd53a4f9381ab6b3a9eb79f7aaedd76d765 100644 (file)
@@ -1,14 +1,30 @@
 #ifndef _RIVE_TRANSITION_CONDITION_HPP_
 #define _RIVE_TRANSITION_CONDITION_HPP_
 #include "generated/animation/transition_condition_base.hpp"
-#include <stdio.h>
+
 namespace rive
 {
+       class StateMachineInput;
+       class SMIInput;
+
        class TransitionCondition : public TransitionConditionBase
        {
        public:
                StatusCode onAddedDirty(CoreContext* context) override;
                StatusCode onAddedClean(CoreContext* context) override;
+
+               StatusCode import(ImportStack& importStack) override;
+
+               virtual bool evaluate(const SMIInput* inputInstance) const
+               {
+                       return true;
+               }
+
+       protected:
+               virtual bool validateInputType(const StateMachineInput* input) const
+               {
+                       return true;
+               }
        };
 } // namespace rive
 
diff --git a/submodule/include/animation/transition_condition_op.hpp b/submodule/include/animation/transition_condition_op.hpp
new file mode 100644 (file)
index 0000000..70e8e76
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _RIVE_TRANSITION_CONDITION_OP_HPP_
+#define _RIVE_TRANSITION_CONDITION_OP_HPP_
+
+namespace rive
+{
+       enum class TransitionConditionOp : int
+       {
+               equal = 0,
+               notEqual = 1,
+               lessThanOrEqual = 2,
+               greaterThanOrEqual = 3,
+               lessThan = 4,
+               greaterThan = 5
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
diff --git a/submodule/include/animation/transition_double_condition.hpp b/submodule/include/animation/transition_double_condition.hpp
deleted file mode 100644 (file)
index c6f8a7b..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#ifndef _RIVE_TRANSITION_DOUBLE_CONDITION_HPP_
-#define _RIVE_TRANSITION_DOUBLE_CONDITION_HPP_
-#include "generated/animation/transition_double_condition_base.hpp"
-#include <stdio.h>
-namespace rive
-{
-       class TransitionDoubleCondition : public TransitionDoubleConditionBase
-       {
-       public:
-       };
-} // namespace rive
-
-#endif
\ No newline at end of file
diff --git a/submodule/include/animation/transition_number_condition.hpp b/submodule/include/animation/transition_number_condition.hpp
new file mode 100644 (file)
index 0000000..99d49fc
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef _RIVE_TRANSITION_NUMBER_CONDITION_HPP_
+#define _RIVE_TRANSITION_NUMBER_CONDITION_HPP_
+#include "generated/animation/transition_number_condition_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+       class TransitionNumberCondition : public TransitionNumberConditionBase
+       {
+       protected:
+               bool validateInputType(const StateMachineInput* input) const override;
+
+       public:
+               bool evaluate(const SMIInput* inputInstance) const override;
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
index a820a6d6cb0909676c93e0502832b1909ffe85c7..72f26bde1c3eb0cf2c1bf4d6b778822731f9a097 100644 (file)
@@ -7,6 +7,10 @@ namespace rive
        class TransitionTriggerCondition : public TransitionTriggerConditionBase
        {
        public:
+               bool evaluate(const SMIInput* inputInstance) const override;
+
+       protected:
+               bool validateInputType(const StateMachineInput* input) const override;
        };
 } // namespace rive
 
index 28c8c54c78ff1637550b07582cd6bba9e98eb854..52ab1cca18f2106369601897b5a5446e0059f04d 100644 (file)
@@ -1,12 +1,17 @@
 #ifndef _RIVE_TRANSITION_VALUE_CONDITION_HPP_
 #define _RIVE_TRANSITION_VALUE_CONDITION_HPP_
 #include "generated/animation/transition_value_condition_base.hpp"
-#include <stdio.h>
+#include "animation/transition_condition_op.hpp"
+
 namespace rive
 {
        class TransitionValueCondition : public TransitionValueConditionBase
        {
        public:
+               TransitionConditionOp op() const
+               {
+                       return (TransitionConditionOp)opValue();
+               }
        };
 } // namespace rive
 
index dc4c4bddf8aeede4c3ccfeb68546df099fe4f7a9..3c8201dc6baf20441f9c3c14bf767c791f213135 100644 (file)
@@ -14,12 +14,14 @@ namespace rive
        class Drawable;
        class Node;
        class DrawTarget;
+       class ArtboardImporter;
 
        class Artboard : public ArtboardBase,
                         public CoreContext,
                         public ShapePaintContainer
        {
                friend class File;
+               friend class ArtboardImporter;
 
        private:
                std::vector<Core*> m_Objects;
@@ -36,13 +38,17 @@ namespace rive
                void sortDependencies();
                void sortDrawOrder();
 
+#ifdef TESTING
        public:
-               ~Artboard();
-               StatusCode initialize();
+#endif
                void addObject(Core* object);
                void addAnimation(LinearAnimation* object);
                void addStateMachine(StateMachine* object);
 
+       public:
+               ~Artboard();
+               StatusCode initialize();
+
                Core* resolve(int id) const override;
 
                StatusCode onAddedClean(CoreContext* context) override
@@ -79,15 +85,15 @@ namespace rive
                        return nullptr;
                }
 
-               LinearAnimation* firstAnimation();
-               LinearAnimation* animation(std::string name);
-               LinearAnimation* animation(size_t index);
-               size_t animationCount() { return m_Animations.size(); }
+               LinearAnimation* firstAnimation() const;
+               LinearAnimation* animation(std::string name) const;
+               LinearAnimation* animation(size_t index) const;
+               size_t animationCount() const { return m_Animations.size(); }
 
-               StateMachine* firstStateMachine();
-               StateMachine* stateMachine(std::string name);
-               StateMachine* stateMachine(size_t index);
-               size_t stateMachineCount() { return m_StateMachines.size(); }
+               StateMachine* firstStateMachine() const;
+               StateMachine* stateMachine(std::string name) const;
+               StateMachine* stateMachine(size_t index) const;
+               size_t stateMachineCount() const { return m_StateMachines.size(); }
        };
 } // namespace rive
 
index 4527a33b716e43a46653f32bb6b3b8283756aa25..8bf67f22c0dd74d7c70d02b74018fdd90235ac8e 100644 (file)
@@ -64,7 +64,7 @@ inline size_t
 decode_double(const uint8_t* buf, const uint8_t* buf_end, double* r)
 {
        // Return zero bytes read on buffer overflow
-       if (buf_end - buf < sizeof(double))
+       if (buf_end - buf < ((unsigned) sizeof(double)))
        {
                return 0;
        }
@@ -92,7 +92,7 @@ decode_double(const uint8_t* buf, const uint8_t* buf_end, double* r)
 inline size_t decode_float(const uint8_t* buf, const uint8_t* buf_end, float* r)
 {
        // Return zero bytes read on buffer overflow
-       if (buf_end - buf < sizeof(float))
+       if (buf_end - buf < (unsigned) sizeof(float))
        {
                return 0;
        }
@@ -114,7 +114,7 @@ inline size_t
 decode_uint_8(const uint8_t* buf, const uint8_t* buf_end, uint8_t* r)
 {
        // Return zero bytes read on buffer overflow
-       if (buf_end - buf < sizeof(uint8_t))
+       if (buf_end - buf < (unsigned) sizeof(uint8_t))
        {
                return 0;
        }
@@ -128,7 +128,7 @@ inline size_t
 decode_uint_32(const uint8_t* buf, const uint8_t* buf_end, uint32_t* r)
 {
        // Return zero bytes read on buffer overflow
-       if (buf_end - buf < sizeof(uint32_t))
+       if (buf_end - buf < (unsigned) sizeof(uint32_t))
        {
                return 0;
        }
@@ -142,4 +142,4 @@ decode_uint_32(const uint8_t* buf, const uint8_t* buf_end, uint32_t* r)
                memcpy(r, buf, sizeof(uint32_t));
        }
        return sizeof(uint32_t);
-}
\ No newline at end of file
+}
index a0831532ca31d36f552c4690f2ef54013e0c4c9d..e5fdb54b63cce0513c2b30c3e5220a709947d268 100644 (file)
@@ -64,9 +64,16 @@ namespace rive
                Artboard* artboard() const;
 
                /// @returns the named artboard. If no artboard is found with that name,
-               /// the null pointer is returned
+               /// the null pointer is returned.
                Artboard* artboard(std::string name) const;
 
+               /// @returns the artboard at the specified index, or the nullptr if the
+               /// index is out of range.
+               Artboard* artboard(size_t index) const;
+
+               /// @returns the number of artboards in the file.
+               size_t artboardCount() const { return m_Artboards.size(); }
+
        private:
                ImportResult read(BinaryReader& reader, const RuntimeHeader& header);
        };
index 6d96ebb40e30997609aa52ff875e0fdc75245ee4..8faa68eea5f88fe86cf1f318c794a9cb0fb36d14 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef _RIVE_ANIMATION_BASE_HPP_
 #define _RIVE_ANIMATION_BASE_HPP_
+#include <string>
 #include "core.hpp"
 #include "core/field_types/core_string_type.hpp"
-#include <string>
 namespace rive
 {
        class AnimationBase : public Core
@@ -33,7 +33,7 @@ namespace rive
        private:
                std::string m_Name = "";
        public:
-               inline std::string name() const { return m_Name; }
+               inline const std::string& name() const { return m_Name; }
                void name(std::string value)
                {
                        if (m_Name == value)
index eacfe670cfe83e903b91496ddf1c0bc15f9da0fe..f8f8ae02b91539ca7f5ff951b1afce911dd7c656 100644 (file)
@@ -1,8 +1,8 @@
 #ifndef _RIVE_STATE_MACHINE_COMPONENT_BASE_HPP_
 #define _RIVE_STATE_MACHINE_COMPONENT_BASE_HPP_
+#include <string>
 #include "core.hpp"
 #include "core/field_types/core_string_type.hpp"
-#include <string>
 namespace rive
 {
        class StateMachineComponentBase : public Core
@@ -33,7 +33,7 @@ namespace rive
        private:
                std::string m_Name = "";
        public:
-               inline std::string name() const { return m_Name; }
+               inline const std::string& name() const { return m_Name; }
                void name(std::string value)
                {
                        if (m_Name == value)
diff --git a/submodule/include/generated/animation/state_machine_double_base.hpp b/submodule/include/generated/animation/state_machine_double_base.hpp
deleted file mode 100644 (file)
index 87528b5..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef _RIVE_STATE_MACHINE_DOUBLE_BASE_HPP_
-#define _RIVE_STATE_MACHINE_DOUBLE_BASE_HPP_
-#include "animation/state_machine_input.hpp"
-#include "core/field_types/core_double_type.hpp"
-namespace rive
-{
-       class StateMachineDoubleBase : public StateMachineInput
-       {
-       protected:
-               typedef StateMachineInput Super;
-
-       public:
-               static const uint16_t typeKey = 56;
-
-               /// Helper to quickly determine if a core object extends another without
-               /// RTTI at runtime.
-               bool isTypeOf(uint16_t typeKey) const override
-               {
-                       switch (typeKey)
-                       {
-                               case StateMachineDoubleBase::typeKey:
-                               case StateMachineInputBase::typeKey:
-                               case StateMachineComponentBase::typeKey:
-                                       return true;
-                               default:
-                                       return false;
-                       }
-               }
-
-               uint16_t coreType() const override { return typeKey; }
-
-               static const uint16_t valuePropertyKey = 140;
-
-       private:
-               float m_Value = 0.0f;
-       public:
-               inline float value() const { return m_Value; }
-               void value(float value)
-               {
-                       if (m_Value == value)
-                       {
-                               return;
-                       }
-                       m_Value = value;
-                       valueChanged();
-               }
-
-               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
-               {
-                       switch (propertyKey)
-                       {
-                               case valuePropertyKey:
-                                       m_Value = CoreDoubleType::deserialize(reader);
-                                       return true;
-                       }
-                       return StateMachineInput::deserialize(propertyKey, reader);
-               }
-
-       protected:
-               virtual void valueChanged() {}
-       };
-} // namespace rive
-
-#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/state_machine_number_base.hpp b/submodule/include/generated/animation/state_machine_number_base.hpp
new file mode 100644 (file)
index 0000000..199e5c3
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef _RIVE_STATE_MACHINE_NUMBER_BASE_HPP_
+#define _RIVE_STATE_MACHINE_NUMBER_BASE_HPP_
+#include "animation/state_machine_input.hpp"
+#include "core/field_types/core_double_type.hpp"
+namespace rive
+{
+       class StateMachineNumberBase : public StateMachineInput
+       {
+       protected:
+               typedef StateMachineInput Super;
+
+       public:
+               static const uint16_t typeKey = 56;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case StateMachineNumberBase::typeKey:
+                               case StateMachineInputBase::typeKey:
+                               case StateMachineComponentBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+               static const uint16_t valuePropertyKey = 140;
+
+       private:
+               float m_Value = 0.0f;
+       public:
+               inline float value() const { return m_Value; }
+               void value(float value)
+               {
+                       if (m_Value == value)
+                       {
+                               return;
+                       }
+                       m_Value = value;
+                       valueChanged();
+               }
+
+               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+               {
+                       switch (propertyKey)
+                       {
+                               case valuePropertyKey:
+                                       m_Value = CoreDoubleType::deserialize(reader);
+                                       return true;
+                       }
+                       return StateMachineInput::deserialize(propertyKey, reader);
+               }
+
+       protected:
+               virtual void valueChanged() {}
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
index 11aca403ecd0ee8514330c2004d542acb1e12ef1..09d7083e8cfb428859306f03496b00a46543333a 100644 (file)
@@ -31,11 +31,13 @@ namespace rive
                static const uint16_t stateToIdPropertyKey = 151;
                static const uint16_t flagsPropertyKey = 152;
                static const uint16_t durationPropertyKey = 158;
+               static const uint16_t exitTimePropertyKey = 160;
 
        private:
                int m_StateToId = -1;
                int m_Flags = 0;
                int m_Duration = 0;
+               int m_ExitTime = 0;
        public:
                inline int stateToId() const { return m_StateToId; }
                void stateToId(int value)
@@ -70,6 +72,17 @@ namespace rive
                        durationChanged();
                }
 
+               inline int exitTime() const { return m_ExitTime; }
+               void exitTime(int value)
+               {
+                       if (m_ExitTime == value)
+                       {
+                               return;
+                       }
+                       m_ExitTime = value;
+                       exitTimeChanged();
+               }
+
                bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
                {
                        switch (propertyKey)
@@ -83,6 +96,9 @@ namespace rive
                                case durationPropertyKey:
                                        m_Duration = CoreUintType::deserialize(reader);
                                        return true;
+                               case exitTimePropertyKey:
+                                       m_ExitTime = CoreUintType::deserialize(reader);
+                                       return true;
                        }
                        return StateMachineLayerComponent::deserialize(propertyKey, reader);
                }
@@ -91,6 +107,7 @@ namespace rive
                virtual void stateToIdChanged() {}
                virtual void flagsChanged() {}
                virtual void durationChanged() {}
+               virtual void exitTimeChanged() {}
        };
 } // namespace rive
 
diff --git a/submodule/include/generated/animation/transition_double_condition_base.hpp b/submodule/include/generated/animation/transition_double_condition_base.hpp
deleted file mode 100644 (file)
index 991b4cd..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-#ifndef _RIVE_TRANSITION_DOUBLE_CONDITION_BASE_HPP_
-#define _RIVE_TRANSITION_DOUBLE_CONDITION_BASE_HPP_
-#include "animation/transition_value_condition.hpp"
-#include "core/field_types/core_double_type.hpp"
-namespace rive
-{
-       class TransitionDoubleConditionBase : public TransitionValueCondition
-       {
-       protected:
-               typedef TransitionValueCondition Super;
-
-       public:
-               static const uint16_t typeKey = 70;
-
-               /// Helper to quickly determine if a core object extends another without
-               /// RTTI at runtime.
-               bool isTypeOf(uint16_t typeKey) const override
-               {
-                       switch (typeKey)
-                       {
-                               case TransitionDoubleConditionBase::typeKey:
-                               case TransitionValueConditionBase::typeKey:
-                               case TransitionConditionBase::typeKey:
-                                       return true;
-                               default:
-                                       return false;
-                       }
-               }
-
-               uint16_t coreType() const override { return typeKey; }
-
-               static const uint16_t valuePropertyKey = 157;
-
-       private:
-               float m_Value = 0.0f;
-       public:
-               inline float value() const { return m_Value; }
-               void value(float value)
-               {
-                       if (m_Value == value)
-                       {
-                               return;
-                       }
-                       m_Value = value;
-                       valueChanged();
-               }
-
-               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
-               {
-                       switch (propertyKey)
-                       {
-                               case valuePropertyKey:
-                                       m_Value = CoreDoubleType::deserialize(reader);
-                                       return true;
-                       }
-                       return TransitionValueCondition::deserialize(propertyKey, reader);
-               }
-
-       protected:
-               virtual void valueChanged() {}
-       };
-} // namespace rive
-
-#endif
\ No newline at end of file
diff --git a/submodule/include/generated/animation/transition_number_condition_base.hpp b/submodule/include/generated/animation/transition_number_condition_base.hpp
new file mode 100644 (file)
index 0000000..e2a7800
--- /dev/null
@@ -0,0 +1,64 @@
+#ifndef _RIVE_TRANSITION_NUMBER_CONDITION_BASE_HPP_
+#define _RIVE_TRANSITION_NUMBER_CONDITION_BASE_HPP_
+#include "animation/transition_value_condition.hpp"
+#include "core/field_types/core_double_type.hpp"
+namespace rive
+{
+       class TransitionNumberConditionBase : public TransitionValueCondition
+       {
+       protected:
+               typedef TransitionValueCondition Super;
+
+       public:
+               static const uint16_t typeKey = 70;
+
+               /// Helper to quickly determine if a core object extends another without
+               /// RTTI at runtime.
+               bool isTypeOf(uint16_t typeKey) const override
+               {
+                       switch (typeKey)
+                       {
+                               case TransitionNumberConditionBase::typeKey:
+                               case TransitionValueConditionBase::typeKey:
+                               case TransitionConditionBase::typeKey:
+                                       return true;
+                               default:
+                                       return false;
+                       }
+               }
+
+               uint16_t coreType() const override { return typeKey; }
+
+               static const uint16_t valuePropertyKey = 157;
+
+       private:
+               float m_Value = 0.0f;
+       public:
+               inline float value() const { return m_Value; }
+               void value(float value)
+               {
+                       if (m_Value == value)
+                       {
+                               return;
+                       }
+                       m_Value = value;
+                       valueChanged();
+               }
+
+               bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+               {
+                       switch (propertyKey)
+                       {
+                               case valuePropertyKey:
+                                       m_Value = CoreDoubleType::deserialize(reader);
+                                       return true;
+                       }
+                       return TransitionValueCondition::deserialize(propertyKey, reader);
+               }
+
+       protected:
+               virtual void valueChanged() {}
+       };
+} // namespace rive
+
+#endif
\ No newline at end of file
index a4b317628d3eb40dc5d872086aff920b09a3cbcd..49681b4a0ac0ab487ddd9ee262e72a746a7384bc 100644 (file)
@@ -1,9 +1,9 @@
 #ifndef _RIVE_COMPONENT_BASE_HPP_
 #define _RIVE_COMPONENT_BASE_HPP_
+#include <string>
 #include "core.hpp"
 #include "core/field_types/core_string_type.hpp"
 #include "core/field_types/core_uint_type.hpp"
-#include <string>
 namespace rive
 {
        class ComponentBase : public Core
@@ -36,7 +36,7 @@ namespace rive
                std::string m_Name = "";
                int m_ParentId = 0;
        public:
-               inline std::string name() const { return m_Name; }
+               inline const std::string& name() const { return m_Name; }
                void name(std::string value)
                {
                        if (m_Name == value)
index 00b0e911eb881c4594f949c1e81d13073e4f5c34..e090b44129f1664f3e849b5d718180d00ebfc1f9 100644 (file)
 #include "animation/state_machine.hpp"
 #include "animation/state_machine_bool.hpp"
 #include "animation/state_machine_component.hpp"
-#include "animation/state_machine_double.hpp"
 #include "animation/state_machine_input.hpp"
 #include "animation/state_machine_layer.hpp"
 #include "animation/state_machine_layer_component.hpp"
+#include "animation/state_machine_number.hpp"
 #include "animation/state_machine_trigger.hpp"
 #include "animation/state_transition.hpp"
 #include "animation/transition_bool_condition.hpp"
 #include "animation/transition_condition.hpp"
-#include "animation/transition_double_condition.hpp"
+#include "animation/transition_number_condition.hpp"
 #include "animation/transition_trigger_condition.hpp"
 #include "animation/transition_value_condition.hpp"
 #include "artboard.hpp"
@@ -83,14 +83,16 @@ namespace rive
                                        return new AnimationState();
                                case KeyedObjectBase::typeKey:
                                        return new KeyedObject();
+                               case StateMachineNumberBase::typeKey:
+                                       return new StateMachineNumber();
                                case TransitionTriggerConditionBase::typeKey:
                                        return new TransitionTriggerCondition();
                                case KeyedPropertyBase::typeKey:
                                        return new KeyedProperty();
-                               case StateMachineDoubleBase::typeKey:
-                                       return new StateMachineDouble();
                                case KeyFrameIdBase::typeKey:
                                        return new KeyFrameId();
+                               case TransitionNumberConditionBase::typeKey:
+                                       return new TransitionNumberCondition();
                                case AnyStateBase::typeKey:
                                        return new AnyState();
                                case StateMachineLayerBase::typeKey:
@@ -99,8 +101,6 @@ namespace rive
                                        return new Animation();
                                case CubicInterpolatorBase::typeKey:
                                        return new CubicInterpolator();
-                               case TransitionDoubleConditionBase::typeKey:
-                                       return new TransitionDoubleCondition();
                                case StateTransitionBase::typeKey:
                                        return new StateTransition();
                                case KeyFrameDoubleBase::typeKey:
@@ -246,6 +246,9 @@ namespace rive
                                case StateTransitionBase::durationPropertyKey:
                                        object->as<StateTransitionBase>()->duration(value);
                                        break;
+                               case StateTransitionBase::exitTimePropertyKey:
+                                       object->as<StateTransitionBase>()->exitTime(value);
+                                       break;
                                case LinearAnimationBase::fpsPropertyKey:
                                        object->as<LinearAnimationBase>()->fps(value);
                                        break;
@@ -321,8 +324,11 @@ namespace rive
                {
                        switch (propertyKey)
                        {
-                               case StateMachineDoubleBase::valuePropertyKey:
-                                       object->as<StateMachineDoubleBase>()->value(value);
+                               case StateMachineNumberBase::valuePropertyKey:
+                                       object->as<StateMachineNumberBase>()->value(value);
+                                       break;
+                               case TransitionNumberConditionBase::valuePropertyKey:
+                                       object->as<TransitionNumberConditionBase>()->value(value);
                                        break;
                                case CubicInterpolatorBase::x1PropertyKey:
                                        object->as<CubicInterpolatorBase>()->x1(value);
@@ -336,9 +342,6 @@ namespace rive
                                case CubicInterpolatorBase::y2PropertyKey:
                                        object->as<CubicInterpolatorBase>()->y2(value);
                                        break;
-                               case TransitionDoubleConditionBase::valuePropertyKey:
-                                       object->as<TransitionDoubleConditionBase>()->value(value);
-                                       break;
                                case KeyFrameDoubleBase::valuePropertyKey:
                                        object->as<KeyFrameDoubleBase>()->value(value);
                                        break;
@@ -602,6 +605,8 @@ namespace rive
                                        return object->as<StateTransitionBase>()->flags();
                                case StateTransitionBase::durationPropertyKey:
                                        return object->as<StateTransitionBase>()->duration();
+                               case StateTransitionBase::exitTimePropertyKey:
+                                       return object->as<StateTransitionBase>()->exitTime();
                                case LinearAnimationBase::fpsPropertyKey:
                                        return object->as<LinearAnimationBase>()->fps();
                                case LinearAnimationBase::durationPropertyKey:
@@ -655,8 +660,10 @@ namespace rive
                {
                        switch (propertyKey)
                        {
-                               case StateMachineDoubleBase::valuePropertyKey:
-                                       return object->as<StateMachineDoubleBase>()->value();
+                               case StateMachineNumberBase::valuePropertyKey:
+                                       return object->as<StateMachineNumberBase>()->value();
+                               case TransitionNumberConditionBase::valuePropertyKey:
+                                       return object->as<TransitionNumberConditionBase>()->value();
                                case CubicInterpolatorBase::x1PropertyKey:
                                        return object->as<CubicInterpolatorBase>()->x1();
                                case CubicInterpolatorBase::y1PropertyKey:
@@ -665,8 +672,6 @@ namespace rive
                                        return object->as<CubicInterpolatorBase>()->x2();
                                case CubicInterpolatorBase::y2PropertyKey:
                                        return object->as<CubicInterpolatorBase>()->y2();
-                               case TransitionDoubleConditionBase::valuePropertyKey:
-                                       return object->as<TransitionDoubleConditionBase>()->value();
                                case KeyFrameDoubleBase::valuePropertyKey:
                                        return object->as<KeyFrameDoubleBase>()->value();
                                case LinearAnimationBase::speedPropertyKey:
@@ -843,6 +848,7 @@ namespace rive
                                case StateTransitionBase::stateToIdPropertyKey:
                                case StateTransitionBase::flagsPropertyKey:
                                case StateTransitionBase::durationPropertyKey:
+                               case StateTransitionBase::exitTimePropertyKey:
                                case LinearAnimationBase::fpsPropertyKey:
                                case LinearAnimationBase::durationPropertyKey:
                                case LinearAnimationBase::loopValuePropertyKey:
@@ -867,12 +873,12 @@ namespace rive
                                case CubicWeightBase::outValuesPropertyKey:
                                case CubicWeightBase::outIndicesPropertyKey:
                                        return CoreUintType::id;
-                               case StateMachineDoubleBase::valuePropertyKey:
+                               case StateMachineNumberBase::valuePropertyKey:
+                               case TransitionNumberConditionBase::valuePropertyKey:
                                case CubicInterpolatorBase::x1PropertyKey:
                                case CubicInterpolatorBase::y1PropertyKey:
                                case CubicInterpolatorBase::x2PropertyKey:
                                case CubicInterpolatorBase::y2PropertyKey:
-                               case TransitionDoubleConditionBase::valuePropertyKey:
                                case KeyFrameDoubleBase::valuePropertyKey:
                                case LinearAnimationBase::speedPropertyKey:
                                case LinearGradientBase::startXPropertyKey:
index ee7ad8ba34de52e5d318d4fa2a10bef3eb594828..9db5f784815829d83bcebae50fd702e38b63e709 100644 (file)
@@ -5,10 +5,10 @@
 
 namespace rive
 {
-    class Core;
-    class Artboard;
-    class LinearAnimation;
-    class StateMachine;
+       class Core;
+       class Artboard;
+       class LinearAnimation;
+       class StateMachine;
        class ArtboardImporter : public ImportStackObject
        {
        private:
@@ -16,10 +16,13 @@ namespace rive
 
        public:
                ArtboardImporter(Artboard* artboard);
-        void addComponent(Core* object);
-        void addAnimation(LinearAnimation* animation);
-        void addStateMachine(StateMachine* stateMachine);
+               void addComponent(Core* object);
+               void addAnimation(LinearAnimation* animation);
+               void addStateMachine(StateMachine* stateMachine);
                StatusCode resolve() override;
+               const Artboard* artboard() const { return m_Artboard; }
+
+               bool readNullObject() override;
        };
 } // namespace rive
 #endif
index c380745bf5953d6ca7deb427a750c8cbba38ca3f..f31a49efce937af0fbcecf8798d10f94be95f23f 100644 (file)
@@ -1,7 +1,10 @@
 #ifndef _RIVE_IMPORT_STACK_HPP_
 #define _RIVE_IMPORT_STACK_HPP_
 #include "status_code.hpp"
+#include <algorithm>
 #include <unordered_map>
+#include <vector>
+#include <algorithm>
 
 namespace rive
 {
@@ -10,12 +13,14 @@ namespace rive
        public:
                virtual ~ImportStackObject() {}
                virtual StatusCode resolve() { return StatusCode::Ok; }
+               virtual bool readNullObject() { return false; }
        };
 
        class ImportStack
        {
        private:
                std::unordered_map<uint16_t, ImportStackObject*> m_Latests;
+               std::vector<ImportStackObject*> m_LastAdded;
 
        public:
                template <typename T = ImportStackObject> T* latest(uint16_t coreType)
@@ -35,6 +40,15 @@ namespace rive
                        if (itr != m_Latests.end())
                        {
                                auto stackObject = itr->second;
+
+                               // Remove it from latests.
+                               auto itr = std::find(
+                                   m_LastAdded.begin(), m_LastAdded.end(), stackObject);
+                               if (itr != m_LastAdded.end())
+                               {
+                                       m_LastAdded.erase(itr);
+                               }
+
                                StatusCode code = stackObject->resolve();
                                delete stackObject;
                                if (code != StatusCode::Ok)
@@ -52,6 +66,7 @@ namespace rive
                        else
                        {
                                m_Latests[coreType] = object;
+                               m_LastAdded.push_back(object);
                        }
                        return StatusCode::Ok;
                }
@@ -76,6 +91,19 @@ namespace rive
                                delete pair.second;
                        }
                }
+
+               bool readNullObject()
+               {
+                       for (auto itr = m_LastAdded.rbegin(); itr != m_LastAdded.rend();
+                            itr++)
+                       {
+                               if ((*itr)->readNullObject())
+                               {
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
        };
 } // namespace rive
 #endif
\ No newline at end of file
index 2a14a902bc6c8dbcc21aa8706c3aa0795841a4cc..286772de4c6d7cea3dd0fc9d6d903c6145bcd73c 100644 (file)
@@ -19,6 +19,7 @@ namespace rive
                KeyedPropertyImporter(LinearAnimation* animation,
                                      KeyedProperty* keyedProperty);
                void addKeyFrame(KeyFrame* keyFrame);
+               bool readNullObject() override;
        };
 } // namespace rive
 #endif
diff --git a/submodule/include/importers/layer_state_importer.hpp b/submodule/include/importers/layer_state_importer.hpp
new file mode 100644 (file)
index 0000000..e88dfd3
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _RIVE_LAYER_STATE_IMPORTER_HPP_
+#define _RIVE_LAYER_STATE_IMPORTER_HPP_
+
+#include "importers/import_stack.hpp"
+
+namespace rive
+{
+       class LayerState;
+       class StateTransition;
+
+       class LayerStateImporter : public ImportStackObject
+       {
+       private:
+               LayerState* m_State;
+
+       public:
+               LayerStateImporter(LayerState* state);
+               void addTransition(StateTransition* transition);
+               StatusCode resolve() override;
+       };
+} // namespace rive
+#endif
diff --git a/submodule/include/importers/state_machine_importer.hpp b/submodule/include/importers/state_machine_importer.hpp
new file mode 100644 (file)
index 0000000..7ab487b
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef _RIVE_STATE_MACHINE_IMPORTER_HPP_
+#define _RIVE_STATE_MACHINE_IMPORTER_HPP_
+
+#include "importers/import_stack.hpp"
+
+namespace rive
+{
+       class StateMachineInput;
+       class StateMachineLayer;
+       class StateMachine;
+       class StateMachineImporter : public ImportStackObject
+       {
+       private:
+               StateMachine* m_StateMachine;
+
+       public:
+               StateMachineImporter(StateMachine* machine);
+               const StateMachine* stateMachine() const { return m_StateMachine; }
+               void addLayer(StateMachineLayer* layer);
+               void addInput(StateMachineInput* input);
+               StatusCode resolve() override;
+               bool readNullObject() override;
+       };
+} // namespace rive
+#endif
diff --git a/submodule/include/importers/state_machine_layer_importer.hpp b/submodule/include/importers/state_machine_layer_importer.hpp
new file mode 100644 (file)
index 0000000..56a9b95
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef _RIVE_STATE_MACHINE_LAYER_IMPORTER_HPP_
+#define _RIVE_STATE_MACHINE_LAYER_IMPORTER_HPP_
+
+#include "importers/import_stack.hpp"
+
+namespace rive
+{
+       class StateMachineLayer;
+       class LayerState;
+       class ArtboardImporter;
+
+       class StateMachineLayerImporter : public ImportStackObject
+       {
+       private:
+               StateMachineLayer* m_Layer;
+               ArtboardImporter* m_ArtboardImporter;
+
+       public:
+               StateMachineLayerImporter(StateMachineLayer* layer,
+                                         ArtboardImporter* artboardImporter);
+               void addState(LayerState* state);
+               StatusCode resolve() override;
+               bool readNullObject() override;
+       };
+} // namespace rive
+#endif
diff --git a/submodule/include/importers/state_transition_importer.hpp b/submodule/include/importers/state_transition_importer.hpp
new file mode 100644 (file)
index 0000000..1779f5c
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef _RIVE_TRANSITION_IMPORTER_HPP_
+#define _RIVE_TRANSITION_IMPORTER_HPP_
+
+#include "importers/import_stack.hpp"
+
+namespace rive
+{
+       class StateTransition;
+       class TransitionCondition;
+
+       class StateTransitionImporter : public ImportStackObject
+       {
+       private:
+               StateTransition* m_Transition;
+
+       public:
+               StateTransitionImporter(StateTransition* transition);
+               void addCondition(TransitionCondition* condition);
+               StatusCode resolve() override;
+       };
+} // namespace rive
+#endif
diff --git a/submodule/skia/dependencies/make_gl3w.sh b/submodule/skia/dependencies/make_gl3w.sh
new file mode 100755 (executable)
index 0000000..cbcf32b
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -e
+
+# GL3W requires CMake
+
+GL3W_REPO=https://github.com/skaslev/gl3w
+GL3W_STABLE_BRANCH=master
+
+if [ ! -d gl3w ]; then
+       echo "Cloning gl3w."
+    git clone $GL3W_REPO
+fi
+
+cd gl3w && git checkout $GL3W_STABLE_BRANCH && git fetch && git pull
+
+mkdir -p build
+cd build
+cmake ../
+make
\ No newline at end of file
diff --git a/submodule/skia/dependencies/make_imgui.sh b/submodule/skia/dependencies/make_imgui.sh
new file mode 100755 (executable)
index 0000000..944b91a
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+
+set -e
+
+IMGUI_REPO=https://github.com/ocornut/imgui
+IMGUI_STABLE_BRANCH=master
+
+if [ ! -d imgui ]; then
+       echo "Cloning ImGui."
+    git clone $IMGUI_REPO
+fi
+
+cd imgui && git checkout $IMGUI_STABLE_BRANCH && git fetch && git pull
diff --git a/submodule/skia/dependencies/make_viewer_dependencies.sh b/submodule/skia/dependencies/make_viewer_dependencies.sh
new file mode 100755 (executable)
index 0000000..4ccf061
--- /dev/null
@@ -0,0 +1,4 @@
+./make_skia.sh
+./make_glfw.sh
+./make_gl3w.sh
+./make_imgui.sh
\ No newline at end of file
index 7cdfc27a69640414b56643641340ff40dec6527c..028e18609b466148b5397d87b336566969343012 100644 (file)
@@ -18,13 +18,18 @@ objdir "obj/%{cfg.buildcfg}"
 includedirs {"../include", "../../../include", "../../renderer/include", "../../dependencies/glfw/include",
              "../../dependencies/skia", "../../dependencies/skia/include/core",
              "../../dependencies/skia/include/effects", "../../dependencies/skia/include/gpu",
-             "../../dependencies/skia/include/config"}
+             "../../dependencies/skia/include/config", "../../dependencies/imgui", "../../dependencies",
+             "../../dependencies/gl3w/build/include"}
 
 links {"Cocoa.framework", "IOKit.framework", "CoreVideo.framework", "rive", "skia", "rive_skia_renderer", "glfw3"}
 libdirs {"../../../build/bin/%{cfg.buildcfg}", "../../dependencies/glfw_build/src",
          "../../dependencies/skia/out/Static", "../../renderer/build/bin/%{cfg.buildcfg}"}
 
-files {"../src/**.cpp"}
+files {"../src/**.cpp", "../../dependencies/gl3w/build/src/gl3w.c",
+       "../../dependencies/imgui/backends/imgui_impl_glfw.cpp",
+       "../../dependencies/imgui/backends/imgui_impl_opengl3.cpp", "../../dependencies/imgui/imgui_widgets.cpp",
+       "../../dependencies/imgui/imgui.cpp", "../../dependencies/imgui/imgui_tables.cpp",
+       "../../dependencies/imgui/imgui_draw.cpp"}
 
 buildoptions {"-Wall", "-fno-exceptions", "-fno-rtti"}
 
@@ -34,7 +39,7 @@ symbols "On"
 
 filter "configurations:release"
 defines {"RELEASE"}
-defines { "NDEBUG" }
+defines {"NDEBUG"}
 optimize "On"
 
 -- Clean Function --
index 97446282f74c39d293024714cdf0d60f3566d6d7..e41ec5d819aa0c27ef3f1876fe171e976162228e 100644 (file)
@@ -1,3 +1,7 @@
+
+// Makes ure gl3w is included before glfw3
+#include "GL/gl3w.h"
+
 #define SK_GL
 #include "GLFW/glfw3.h"
 
 #include "gl/GrGLInterface.h"
 
 #include "animation/linear_animation_instance.hpp"
+#include "animation/state_machine_instance.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/state_machine_bool.hpp"
+#include "animation/state_machine_trigger.hpp"
 #include "artboard.hpp"
 #include "file.hpp"
 #include "layout.hpp"
 #include "math/aabb.hpp"
 #include "skia_renderer.hpp"
 
+#include "imgui/backends/imgui_impl_glfw.h"
+#include "imgui/backends/imgui_impl_opengl3.h"
+
 #include <cmath>
 #include <stdio.h>
 
+std::string filename;
 rive::File* currentFile = nullptr;
 rive::Artboard* artboard = nullptr;
+rive::StateMachineInstance* stateMachineInstance = nullptr;
 rive::LinearAnimationInstance* animationInstance = nullptr;
+uint8_t* fileBytes = nullptr;
+unsigned int fileBytesLength = 0;
 
-void glfwErrorCallback(int error, const char* description)
-{
-       puts(description);
-}
+int animationIndex = 0;
+int stateMachineIndex = -1;
 
-void glfwDropCallback(GLFWwindow* window, int count, const char** paths)
+void initStateMachine(int index)
 {
-       // Just get the last dropped file for now...
-       auto filename = paths[count - 1];
-       FILE* fp = fopen(filename, "r");
-       fseek(fp, 0, SEEK_END);
-       auto length = ftell(fp);
-       fseek(fp, 0, SEEK_SET);
-       uint8_t* bytes = new uint8_t[length];
-       if (fread(bytes, 1, length, fp) != length)
+       stateMachineIndex = index;
+       animationIndex = -1;
+       assert(fileBytes != nullptr);
+       auto reader = rive::BinaryReader(fileBytes, fileBytesLength);
+       rive::File* file = nullptr;
+       auto result = rive::File::import(reader, &file);
+       if (result != rive::ImportResult::success)
        {
-               delete[] bytes;
-               fprintf(stderr, "failed to read all of %s\n", filename);
+               delete[] fileBytes;
+               fprintf(stderr, "failed to import file\n");
                return;
        }
-       auto reader = rive::BinaryReader(bytes, length);
+       artboard = file->artboard();
+       artboard->advance(0.0f);
+
+       delete animationInstance;
+       delete stateMachineInstance;
+       delete currentFile;
+       animationInstance = nullptr;
+       stateMachineInstance = nullptr;
+
+       auto stateMachine = index >= 0 && index < artboard->stateMachineCount()
+                               ? artboard->stateMachine(index)
+                               : nullptr;
+       if (stateMachine != nullptr)
+       {
+               stateMachineInstance = new rive::StateMachineInstance(stateMachine);
+       }
+
+       currentFile = file;
+}
+
+void initAnimation(int index)
+{
+       animationIndex = index;
+       stateMachineIndex = -1;
+       assert(fileBytes != nullptr);
+       auto reader = rive::BinaryReader(fileBytes, fileBytesLength);
        rive::File* file = nullptr;
        auto result = rive::File::import(reader, &file);
        if (result != rive::ImportResult::success)
        {
-               delete[] bytes;
-               fprintf(stderr, "failed to import %s\n", filename);
+               delete[] fileBytes;
+               fprintf(stderr, "failed to import file\n");
                return;
        }
        artboard = file->artboard();
        artboard->advance(0.0f);
 
        delete animationInstance;
+       delete stateMachineInstance;
        delete currentFile;
+       animationInstance = nullptr;
+       stateMachineInstance = nullptr;
 
-       auto animation = artboard->firstAnimation();
+       auto animation = index >= 0 && index < artboard->animationCount()
+                            ? artboard->animation(index)
+                            : nullptr;
        if (animation != nullptr)
        {
                animationInstance = new rive::LinearAnimationInstance(animation);
        }
-       else
-       {
-               animationInstance = nullptr;
-       }
 
        currentFile = file;
-       delete[] bytes;
+}
+
+void glfwErrorCallback(int error, const char* description)
+{
+       puts(description);
+}
+
+void glfwDropCallback(GLFWwindow* window, int count, const char** paths)
+{
+       // Just get the last dropped file for now...
+       filename = paths[count - 1];
+
+       FILE* fp = fopen(filename.c_str(), "r");
+       fseek(fp, 0, SEEK_END);
+       fileBytesLength = ftell(fp);
+       fseek(fp, 0, SEEK_SET);
+       delete[] fileBytes;
+       fileBytes = new uint8_t[fileBytesLength];
+       if (fread(fileBytes, 1, fileBytesLength, fp) != fileBytesLength)
+       {
+               delete[] fileBytes;
+               fprintf(stderr, "failed to read all of %s\n", filename.c_str());
+               return;
+       }
+       initAnimation(0);
 }
 
 int main()
@@ -81,8 +144,8 @@ int main()
        }
        glfwSetErrorCallback(glfwErrorCallback);
 
-       glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
-       glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
+       glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
+       glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
        GLFWwindow* window = glfwCreateWindow(1280, 720, "Rive Viewer", NULL, NULL);
        if (window == nullptr)
        {
@@ -93,9 +156,25 @@ int main()
 
        glfwSetDropCallback(window, glfwDropCallback);
        glfwMakeContextCurrent(window);
+       if (gl3wInit() != 0)
+       {
+               fprintf(stderr, "Failed to make initialize gl3w.\n");
+               glfwTerminate();
+               return 1;
+       }
        // Enable VSYNC.
        glfwSwapInterval(1);
 
+       // Setup ImGui
+       ImGui::CreateContext();
+       ImGuiIO& io = ImGui::GetIO();
+       (void)io;
+
+       ImGui::StyleColorsDark();
+       ImGui_ImplGlfw_InitForOpenGL(window, true);
+       ImGui_ImplOpenGL3_Init("#version 150");
+       io.Fonts->AddFontDefault();
+
        // Setup Skia
        GrContextOptions options;
        sk_sp<GrContext> context = GrContext::MakeGL(nullptr, options);
@@ -172,6 +251,11 @@ int main()
                                animationInstance->advance(elapsed);
                                animationInstance->apply(artboard);
                        }
+                       else if (stateMachineInstance != nullptr)
+                       {
+                               stateMachineInstance->advance(elapsed);
+                               stateMachineInstance->apply(artboard);
+                       }
                        artboard->advance(elapsed);
 
                        rive::SkiaRenderer renderer(canvas);
@@ -186,16 +270,128 @@ int main()
 
                context->flush();
 
+               ImGui_ImplOpenGL3_NewFrame();
+               ImGui_ImplGlfw_NewFrame();
+               ImGui::NewFrame();
+
+               if (artboard != nullptr)
+               {
+                       ImGui::Begin(filename.c_str(), nullptr);
+                       if (ImGui::ListBox(
+                               "Animations",
+                               &animationIndex,
+                               [](void* data, int index, const char** name) {
+                                       const char* animationName =
+                                           artboard->animation(index)->name().c_str();
+                                       *name = animationName;
+                                       return true;
+                               },
+                               artboard,
+                               artboard->animationCount(),
+                               4))
+                       {
+                               stateMachineIndex = -1;
+                               initAnimation(animationIndex);
+                       }
+                       if (ImGui::ListBox(
+                               "State Machines",
+                               &stateMachineIndex,
+                               [](void* data, int index, const char** name) {
+                                       const char* machineName =
+                                           artboard->stateMachine(index)->name().c_str();
+                                       *name = machineName;
+                                       return true;
+                               },
+                               artboard,
+                               artboard->stateMachineCount(),
+                               4))
+                       {
+                               animationIndex = -1;
+                               initStateMachine(stateMachineIndex);
+                       }
+                       if (stateMachineInstance != nullptr)
+                       {
+
+                               ImGui::Columns(2);
+                               ImGui::SetColumnWidth(0, ImGui::GetWindowWidth() * 0.6666);
+
+                               for (int i = 0; i < stateMachineInstance->inputCount(); i++)
+                               {
+                                       auto inputInstance = stateMachineInstance->input(i);
+
+                                       if (inputInstance->input()->is<rive::StateMachineNumber>())
+                                       {
+                                               // ImGui requires names as id's, use ## to hide the
+                                               // label but still give it an id.
+                                               char label[256];
+                                               snprintf(label, 256, "##%u", i);
+
+                                               auto number =
+                                                   static_cast<rive::SMINumber*>(inputInstance);
+                                               float v = number->value();
+                                               ImGui::InputFloat(label, &v, 1.0f, 2.0f, "%.3f");
+                                               number->value(v);
+                                               ImGui::NextColumn();
+                                       }
+                                       else if (inputInstance->input()
+                                                    ->is<rive::StateMachineTrigger>())
+                                       {
+                                               // ImGui requires names as id's, use ## to hide the
+                                               // label but still give it an id.
+                                               char label[256];
+                                               snprintf(label, 256, "Fire##%u", i);
+                                               if (ImGui::Button(label))
+                                               {
+                                                       auto trigger =
+                                                           static_cast<rive::SMITrigger*>(inputInstance);
+                                                       trigger->fire();
+                                               }
+                                               ImGui::NextColumn();
+                                       }
+                                       else if (inputInstance->input()
+                                                    ->is<rive::StateMachineBool>())
+                                       {
+                                               // ImGui requires names as id's, use ## to hide the
+                                               // label but still give it an id.
+                                               char label[256];
+                                               snprintf(label, 256, "##%u", i);
+                                               auto boolInput =
+                                                   static_cast<rive::SMIBool*>(inputInstance);
+                                               bool value = boolInput->value();
+
+                                               ImGui::Checkbox(label, &value);
+                                               boolInput->value(value);
+                                               ImGui::NextColumn();
+                                       }
+                                       ImGui::Text("%s", inputInstance->input()->name().c_str());
+                                       ImGui::NextColumn();
+                               }
+
+                               ImGui::Columns(1);
+                       }
+                       ImGui::End();
+               }
+               else
+               {
+                       ImGui::Text("Drop a .riv file to preview.");
+               }
+
+               ImGui::Render();
+               ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
+
                glfwSwapBuffers(window);
                glfwPollEvents();
        }
 
        delete currentFile;
+       delete[] fileBytes;
 
        // Cleanup Skia.
        delete surface;
        context = nullptr;
 
+       ImGui_ImplGlfw_Shutdown();
+
        // Cleanup GLFW.
        glfwDestroyWindow(window);
        glfwTerminate();
diff --git a/submodule/src/animation/animation_state.cpp b/submodule/src/animation/animation_state.cpp
new file mode 100644 (file)
index 0000000..997e93d
--- /dev/null
@@ -0,0 +1,6 @@
+#include "animation/animation_state.hpp"
+#include "animation/linear_animation.hpp"
+#include "core_context.hpp"
+#include "artboard.hpp"
+
+using namespace rive;
index a3129462d79327ef180a5705c92c09bb459cffb8..ab7c9b52fd37bf89fc58d375cb9dc748bae71d58 100644 (file)
@@ -27,8 +27,8 @@ void KeyedProperty::apply(Core* object, float seconds, float mix)
        int mid = 0;
        float closestSeconds = 0.0f;
        int start = 0;
-       auto numKeyFrames = m_KeyFrames.size();
-       int end = (int)numKeyFrames - 1;
+       auto numKeyFrames = static_cast<int>(m_KeyFrames.size());
+       int end = numKeyFrames - 1;
        while (start <= end)
        {
                mid = (start + end) >> 1;
@@ -120,4 +120,4 @@ StatusCode KeyedProperty::import(ImportStack& importStack)
        }
        importer->addKeyedProperty(this);
        return Super::import(importStack);
-}
\ No newline at end of file
+}
index e4be101f950e03bef1043c13d764abcf6b668102..e21d33ebe04371219f33daa835e5e524d389b77c 100644 (file)
@@ -1,15 +1,61 @@
 #include "animation/layer_state.hpp"
-using namespace rive;
 #include "animation/transition_bool_condition.hpp"
+#include "importers/import_stack.hpp"
+#include "importers/state_machine_layer_importer.hpp"
+#include "generated/animation/state_machine_layer_base.hpp"
+#include "animation/state_transition.hpp"
+
+using namespace rive;
 
 using namespace rive;
 
+LayerState::~LayerState()
+{
+       for (auto transition : m_Transitions)
+       {
+               delete transition;
+       }
+}
+
 StatusCode LayerState::onAddedDirty(CoreContext* context)
 {
+       StatusCode code;
+       for (auto transition : m_Transitions)
+       {
+               if ((code = transition->onAddedDirty(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
        return StatusCode::Ok;
 }
 
 StatusCode LayerState::onAddedClean(CoreContext* context)
 {
+       StatusCode code;
+       for (auto transition : m_Transitions)
+       {
+               if ((code = transition->onAddedClean(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
        return StatusCode::Ok;
+}
+
+StatusCode LayerState::import(ImportStack& importStack)
+{
+       auto layerImporter = importStack.latest<StateMachineLayerImporter>(
+           StateMachineLayerBase::typeKey);
+       if (layerImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+       layerImporter->addState(this);
+       return Super::import(importStack);
+}
+
+void LayerState::addTransition(StateTransition* transition)
+{
+       m_Transitions.push_back(transition);
 }
\ No newline at end of file
index c3d8fb0e30c498b0194001f8a8ffda856aa4364b..44df90aa99ed0c83cb23948b57bd475cadcf6b07 100644 (file)
@@ -45,7 +45,7 @@ void LinearAnimation::addKeyedObject(KeyedObject* object)
        m_KeyedObjects.push_back(object);
 }
 
-void LinearAnimation::apply(Artboard* artboard, float time, float mix)
+void LinearAnimation::apply(Artboard* artboard, float time, float mix) const
 {
        for (auto object : m_KeyedObjects)
        {
@@ -63,4 +63,17 @@ StatusCode LinearAnimation::import(ImportStack& importStack)
        }
        artboardImporter->addAnimation(this);
        return Super::import(importStack);
+}
+
+float LinearAnimation::startSeconds() const
+{
+       return (enableWorkArea() ? workStart() : 0) / (float)fps();
+}
+float LinearAnimation::endSeconds() const
+{
+       return (enableWorkArea() ? workEnd() : duration()) / (float)fps();
+}
+float LinearAnimation::durationSeconds() const
+{
+       return endSeconds() - startSeconds();
 }
\ No newline at end of file
index 1ab0301d2f54ccdbe7b670196e7c2c667fd31cf1..179bcc1983f67edd623afef3716a9eb9de5df694 100644 (file)
@@ -1,20 +1,29 @@
 #include "animation/linear_animation_instance.hpp"
+#include "animation/linear_animation.hpp"
 #include "animation/loop.hpp"
 #include <cmath>
 
 using namespace rive;
 
-LinearAnimationInstance::LinearAnimationInstance(LinearAnimation* animation) :
+LinearAnimationInstance::LinearAnimationInstance(
+    const LinearAnimation* animation) :
     m_Animation(animation),
-    m_Time(animation->enableWorkArea() ? (float) animation->workStart() / animation->fps() : 0),
+    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::advance(float elapsedSeconds)
 {
-       LinearAnimation& animation = *m_Animation;
+       const LinearAnimation& animation = *m_Animation;
        m_Time += elapsedSeconds * animation.speed() * m_Direction;
+       m_LastTotalTime = m_TotalTime;
+       m_TotalTime += elapsedSeconds;
 
        int fps = animation.fps();
 
@@ -27,6 +36,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds)
 
        bool keepGoing = true;
        bool didLoop = false;
+       m_SpilledTime = 0.0f;
 
        switch (animation.loop())
        {
@@ -34,6 +44,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds)
                        if (frames > end)
                        {
                                keepGoing = false;
+                               m_SpilledTime = (frames - end) / fps;
                                frames = end;
                                m_Time = frames / fps;
                                didLoop = true;
@@ -42,6 +53,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds)
                case Loop::loop:
                        if (frames >= end)
                        {
+                               m_SpilledTime = (frames - end) / fps;
                                frames = m_Time * fps;
                                frames = start + std::fmod(frames - start, range);
                                m_Time = frames / fps;
@@ -53,6 +65,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds)
                        {
                                if (m_Direction == 1 && frames >= end)
                                {
+                                       m_SpilledTime = (frames - end) / fps;
                                        m_Direction = -1;
                                        frames = end + (end - frames);
                                        m_Time = frames / fps;
@@ -60,6 +73,7 @@ bool LinearAnimationInstance::advance(float elapsedSeconds)
                                }
                                else if (m_Direction == -1 && frames < start)
                                {
+                                       m_SpilledTime = (start - frames) / fps;
                                        m_Direction = 1;
                                        frames = start + (start - frames);
                                        m_Time = frames / fps;
@@ -89,5 +103,14 @@ void LinearAnimationInstance::time(float 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;
+
        m_Direction = 1;
 }
diff --git a/submodule/src/animation/state_machine.cpp b/submodule/src/animation/state_machine.cpp
new file mode 100644 (file)
index 0000000..4d28625
--- /dev/null
@@ -0,0 +1,124 @@
+#include "animation/state_machine.hpp"
+#include "artboard.hpp"
+#include "importers/artboard_importer.hpp"
+#include "animation/state_machine_layer.hpp"
+#include "animation/state_machine_input.hpp"
+
+using namespace rive;
+
+StateMachine::~StateMachine()
+{
+       for (auto object : m_Inputs)
+       {
+               delete object;
+       }
+       for (auto object : m_Layers)
+       {
+               delete object;
+       }
+}
+
+StatusCode StateMachine::onAddedDirty(CoreContext* context)
+{
+       StatusCode code;
+       for (auto object : m_Inputs)
+       {
+               if ((code = object->onAddedDirty(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
+       for (auto object : m_Layers)
+       {
+               if ((code = object->onAddedDirty(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
+       return StatusCode::Ok;
+}
+
+StatusCode StateMachine::onAddedClean(CoreContext* context)
+{
+       StatusCode code;
+       for (auto object : m_Inputs)
+       {
+               if ((code = object->onAddedClean(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
+       for (auto object : m_Layers)
+       {
+               if ((code = object->onAddedClean(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
+       return StatusCode::Ok;
+}
+
+StatusCode StateMachine::import(ImportStack& importStack)
+{
+       auto artboardImporter =
+           importStack.latest<ArtboardImporter>(ArtboardBase::typeKey);
+       if (artboardImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+       artboardImporter->addStateMachine(this);
+       return Super::import(importStack);
+}
+
+void StateMachine::addLayer(StateMachineLayer* layer)
+{
+       m_Layers.push_back(layer);
+}
+
+void StateMachine::addInput(StateMachineInput* input)
+{
+       m_Inputs.push_back(input);
+}
+
+const StateMachineInput* StateMachine::input(std::string name) const
+{
+       for (auto input : m_Inputs)
+       {
+               if (input->name() == name)
+               {
+                       return input;
+               }
+       }
+       return nullptr;
+}
+
+const StateMachineInput* StateMachine::input(size_t index) const
+{
+       if (index >= 0 && index < m_Inputs.size())
+       {
+               return m_Inputs[index];
+       }
+       return nullptr;
+}
+
+const StateMachineLayer* StateMachine::layer(std::string name) const
+{
+       for (auto layer : m_Layers)
+       {
+               if (layer->name() == name)
+               {
+                       return layer;
+               }
+       }
+       return nullptr;
+}
+
+const StateMachineLayer* StateMachine::layer(size_t index) const
+{
+
+       if (index >= 0 && index < m_Layers.size())
+       {
+               return m_Layers[index];
+       }
+       return nullptr;
+}
\ No newline at end of file
index f7dd7782b6b36eca3f2afd8228ab46e068c27fed..db65528263b4c97becb1721051197705f7e09eee 100644 (file)
@@ -1,4 +1,7 @@
 #include "animation/state_machine_input.hpp"
+#include "importers/import_stack.hpp"
+#include "importers/state_machine_importer.hpp"
+#include "generated/animation/state_machine_base.hpp"
 
 using namespace rive;
 
@@ -10,4 +13,16 @@ StatusCode StateMachineInput::onAddedDirty(CoreContext* context)
 StatusCode StateMachineInput::onAddedClean(CoreContext* context)
 {
        return StatusCode::Ok;
+}
+
+StatusCode StateMachineInput::import(ImportStack& importStack)
+{
+       auto stateMachineImporter =
+           importStack.latest<StateMachineImporter>(StateMachineBase::typeKey);
+       if (stateMachineImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+       stateMachineImporter->addInput(this);
+       return Super::import(importStack);
 }
\ No newline at end of file
diff --git a/submodule/src/animation/state_machine_input_instance.cpp b/submodule/src/animation/state_machine_input_instance.cpp
new file mode 100644 (file)
index 0000000..692a8c0
--- /dev/null
@@ -0,0 +1,71 @@
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine_bool.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/state_machine_trigger.hpp"
+#include "animation/state_machine_instance.hpp"
+
+using namespace rive;
+
+SMIInput::SMIInput(const StateMachineInput* input,
+                   StateMachineInstance* machineInstance) :
+    m_MachineInstance(machineInstance), m_Input(input)
+{
+}
+
+uint16_t SMIInput::inputCoreType() const { return m_Input->coreType(); }
+
+const std::string& SMIInput::name() const { return m_Input->name(); } 
+
+void SMIInput::valueChanged() { m_MachineInstance->markNeedsAdvance(); }
+
+// bool
+
+SMIBool::SMIBool(const StateMachineBool* input,
+                 StateMachineInstance* machineInstance) :
+    SMIInput(input, machineInstance), m_Value(input->value())
+{
+}
+
+void SMIBool::value(bool newValue)
+{
+       if (m_Value == newValue)
+       {
+               return;
+       }
+       m_Value = newValue;
+       valueChanged();
+}
+
+// number
+SMINumber::SMINumber(const StateMachineNumber* input,
+                     StateMachineInstance* machineInstance) :
+    SMIInput(input, machineInstance), m_Value(input->value())
+{
+}
+
+void SMINumber::value(float newValue)
+{
+       if (m_Value == newValue)
+       {
+               return;
+       }
+       m_Value = newValue;
+       valueChanged();
+}
+
+// trigger
+SMITrigger::SMITrigger(const StateMachineTrigger* input,
+                       StateMachineInstance* machineInstance) :
+    SMIInput(input, machineInstance)
+{
+}
+
+void SMITrigger::fire()
+{
+       if (m_Fired)
+       {
+               return;
+       }
+       m_Fired = true;
+       valueChanged();
+}
diff --git a/submodule/src/animation/state_machine_instance.cpp b/submodule/src/animation/state_machine_instance.cpp
new file mode 100644 (file)
index 0000000..9a38637
--- /dev/null
@@ -0,0 +1,353 @@
+#include "animation/state_machine_instance.hpp"
+#include "animation/state_machine_input.hpp"
+#include "animation/state_machine_bool.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/state_machine_trigger.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine.hpp"
+#include "animation/state_machine_layer.hpp"
+#include "animation/any_state.hpp"
+#include "animation/entry_state.hpp"
+#include "animation/state_transition.hpp"
+#include "animation/transition_condition.hpp"
+#include "animation/linear_animation_instance.hpp"
+#include "animation/animation_state.hpp"
+
+using namespace rive;
+
+namespace rive
+{
+       class StateMachineLayerInstance
+       {
+       private:
+               static const int maxIterations = 100;
+               const StateMachineLayer* m_Layer = nullptr;
+               const LayerState* m_CurrentState = nullptr;
+               const LayerState* m_StateFrom = nullptr;
+               const StateTransition* m_Transition = nullptr;
+
+               bool m_HoldAnimationFrom = false;
+               LinearAnimationInstance* m_AnimationInstance = nullptr;
+               LinearAnimationInstance* m_AnimationInstanceFrom = nullptr;
+               float m_Mix = 1.0f;
+
+       public:
+               void init(const StateMachineLayer* layer)
+               {
+                       m_Layer = layer;
+                       m_CurrentState = m_Layer->entryState();
+               }
+
+               bool advance(float seconds, SMIInput** inputs)
+               {
+                       bool keepGoing = false;
+                       if (m_AnimationInstance != nullptr)
+                       {
+                               keepGoing = m_AnimationInstance->advance(seconds);
+                       }
+
+                       if (m_Transition != nullptr && m_StateFrom != nullptr &&
+                           m_Transition->duration() != 0)
+                       {
+                               m_Mix =
+                                   std::min(1.0f,
+                                            std::max(0.0f,
+                                                     (m_Mix + seconds / m_Transition->mixTime(
+                                                                            m_StateFrom))));
+                       }
+                       else
+                       {
+                               m_Mix = 1.0f;
+                       }
+
+                       if (m_AnimationInstanceFrom != nullptr && m_Mix < 1.0f &&
+                           !m_HoldAnimationFrom)
+                       {
+                               m_AnimationInstanceFrom->advance(seconds);
+                       }
+
+                       for (int i = 0; updateState(inputs); i++)
+                       {
+                               if (i == maxIterations)
+                               {
+                                       fprintf(stderr, "StateMachine exceeded max iterations.\n");
+                                       return false;
+                               }
+                       }
+                       return m_Mix != 1.0f || keepGoing;
+               }
+
+               bool updateState(SMIInput** inputs)
+               {
+                       if (tryChangeState(m_Layer->anyState(), inputs))
+                       {
+                               return true;
+                       }
+
+                       return tryChangeState(m_CurrentState, inputs);
+               }
+
+               bool changeState(const LayerState* stateTo)
+               {
+                       if (m_CurrentState == stateTo)
+                       {
+                               return false;
+                       }
+                       m_CurrentState = stateTo;
+                       return true;
+               }
+
+               bool tryChangeState(const LayerState* stateFrom, SMIInput** inputs)
+               {
+                       if (stateFrom == nullptr)
+                       {
+                               return false;
+                       }
+                       for (size_t i = 0, length = stateFrom->transitionCount();
+                            i < length;
+                            i++)
+                       {
+                               auto transition = stateFrom->transition(i);
+
+                               if (transition->isDisabled())
+                               {
+                                       continue;
+                               }
+
+                               bool valid = true;
+                               for (size_t j = 0, length = transition->conditionCount();
+                                    j < length;
+                                    j++)
+                               {
+                                       auto condition = transition->condition(j);
+                                       auto input = inputs[condition->inputId()];
+                                       if (!condition->evaluate(input))
+                                       {
+                                               valid = false;
+                                               break;
+                                       }
+                               }
+
+                               // If all the conditions evaluated to true, make sure the exit
+                               // time (when set) is also valid.
+                               if (valid && stateFrom->is<AnimationState>() &&
+                                   transition->enableExitTime())
+                               {
+                                       auto fromAnimation =
+                                           stateFrom->as<AnimationState>()->animation();
+                                       assert(fromAnimation != nullptr);
+                                       auto lastTime = m_AnimationInstance->lastTotalTime();
+                                       auto time = m_AnimationInstance->totalTime();
+                                       auto exitTime =
+                                           transition->exitTimeSeconds(stateFrom, true);
+                                       if (exitTime < fromAnimation->durationSeconds())
+                                       {
+                                               // Get exit time relative to the loop lastTime was in.
+                                               exitTime +=
+                                                   (int)(lastTime / fromAnimation->durationSeconds()) *
+                                                   fromAnimation->durationSeconds();
+                                       }
+                                       if (time < exitTime)
+                                       {
+                                               valid = false;
+                                       }
+                               }
+
+                               // Try to change the state...
+                               if (valid && changeState(transition->stateTo()))
+                               {
+                                       // state actually has changed
+                                       m_Transition = transition;
+                                       m_StateFrom = stateFrom;
+
+                                       // If we had an exit time and wanted to pause on exit, make
+                                       // sure to hold the exit time.
+                                       if (transition->pauseOnExit() &&
+                                           transition->enableExitTime() &&
+                                           m_AnimationInstance != nullptr)
+                                       {
+                                               m_AnimationInstance->time(
+                                                   transition->exitTimeSeconds(stateFrom, false));
+                                       }
+
+                                       if (m_Mix != 0.0f)
+                                       {
+                                               m_HoldAnimationFrom = transition->pauseOnExit();
+                                               delete m_AnimationInstanceFrom;
+                                               m_AnimationInstanceFrom = m_AnimationInstance;
+
+                                               // Don't let it get deleted as we've passed it on to the
+                                               // from.
+                                               m_AnimationInstance = nullptr;
+                                       }
+                                       else
+                                       {
+                                               delete m_AnimationInstance;
+                                               m_AnimationInstance = nullptr;
+                                       }
+                                       if (m_CurrentState->is<AnimationState>())
+                                       {
+                                               auto animationState =
+                                                   m_CurrentState->as<AnimationState>();
+                                               auto spilledTime =
+                                                   m_AnimationInstanceFrom == nullptr
+                                                       ? 0
+                                                       : m_AnimationInstanceFrom->spilledTime();
+                                               if (animationState->animation() != nullptr)
+                                               {
+                                                       m_AnimationInstance = new LinearAnimationInstance(
+                                                           animationState->animation());
+                                                       m_AnimationInstance->advance(spilledTime);
+                                               }
+                                               m_Mix = 0.0f;
+                                       }
+                                       return true;
+                               }
+                       }
+                       return false;
+               }
+
+               void apply(Artboard* artboard) const
+               {
+                       if (m_AnimationInstanceFrom != nullptr && m_Mix < 1.0f)
+                       {
+                               m_AnimationInstanceFrom->animation()->apply(
+                                   artboard, m_AnimationInstanceFrom->time(), 1.0 - m_Mix);
+                       }
+                       if (m_AnimationInstance != nullptr)
+                       {
+                               m_AnimationInstance->animation()->apply(
+                                   artboard, m_AnimationInstance->time(), m_Mix);
+                       }
+               }
+       };
+} // namespace rive
+
+StateMachineInstance::StateMachineInstance(StateMachine* machine) :
+    m_Machine(machine)
+{
+       m_InputCount = machine->inputCount();
+       m_InputInstances = new SMIInput*[m_InputCount];
+       for (int i = 0; i < m_InputCount; i++)
+       {
+               auto input = machine->input(i);
+               if (input == nullptr)
+               {
+                       m_InputInstances[i] = nullptr;
+                       continue;
+               }
+               switch (input->coreType())
+               {
+                       case StateMachineBool::typeKey:
+                               m_InputInstances[i] =
+                                   new SMIBool(input->as<StateMachineBool>(), this);
+                               break;
+                       case StateMachineNumber::typeKey:
+                               m_InputInstances[i] =
+                                   new SMINumber(input->as<StateMachineNumber>(), this);
+                               break;
+                       case StateMachineTrigger::typeKey:
+                               m_InputInstances[i] =
+                                   new SMITrigger(input->as<StateMachineTrigger>(), this);
+                               break;
+                       default:
+                               // Sanity check.
+                               m_InputInstances[i] = nullptr;
+                               break;
+               }
+       }
+
+       m_LayerCount = machine->layerCount();
+       m_Layers = new StateMachineLayerInstance[m_LayerCount];
+       for (int i = 0; i < m_LayerCount; i++)
+       {
+               m_Layers[i].init(machine->layer(i));
+       }
+}
+
+StateMachineInstance::~StateMachineInstance()
+{
+       for (int i = 0; i < m_InputCount; i++)
+       {
+               delete m_InputInstances[i];
+       }
+       delete[] m_InputInstances;
+       delete[] m_Layers;
+}
+
+bool StateMachineInstance::advance(float seconds)
+{
+       m_NeedsAdvance = false;
+       for (int i = 0; i < m_LayerCount; i++)
+       {
+               if (m_Layers[i].advance(seconds, m_InputInstances))
+               {
+                       m_NeedsAdvance = true;
+               }
+       }
+
+       for (int i = 0; i < m_InputCount; i++)
+       {
+               m_InputInstances[i]->advanced();
+       }
+
+       return m_NeedsAdvance;
+}
+
+void StateMachineInstance::apply(Artboard* artboard) const
+{
+       for (int i = 0; i < m_LayerCount; i++)
+       {
+               m_Layers[i].apply(artboard);
+       }
+}
+
+void StateMachineInstance::markNeedsAdvance() { m_NeedsAdvance = true; }
+bool StateMachineInstance::needsAdvance() const { return m_NeedsAdvance; }
+
+SMIInput* StateMachineInstance::input(size_t index) const
+{
+       if (index < m_InputCount)
+       {
+               return m_InputInstances[index];
+       }
+       return nullptr;
+}
+
+SMIBool* StateMachineInstance::getBool(std::string name) const
+{
+       for (int i = 0; i < m_InputCount; i++)
+       {
+               auto input = m_InputInstances[i]->input();
+               if (input->is<StateMachineBool>() && input->name() == name)
+               {
+                       return static_cast<SMIBool*>(m_InputInstances[i]);
+               }
+       }
+       return nullptr;
+}
+
+SMINumber* StateMachineInstance::getNumber(std::string name) const
+{
+       for (int i = 0; i < m_InputCount; i++)
+       {
+               auto input = m_InputInstances[i]->input();
+               if (input->is<StateMachineNumber>() && input->name() == name)
+               {
+                       return static_cast<SMINumber*>(m_InputInstances[i]);
+               }
+       }
+       return nullptr;
+}
+SMITrigger* StateMachineInstance::getTrigger(std::string name) const
+{
+       for (int i = 0; i < m_InputCount; i++)
+       {
+               auto input = m_InputInstances[i]->input();
+               if (input->is<StateMachineTrigger>() && input->name() == name)
+               {
+                       return static_cast<SMITrigger*>(m_InputInstances[i]);
+               }
+       }
+       return nullptr;
+}
\ No newline at end of file
index 90cdc93589232bb353c83bfe44ffcc5cc5dfe032..5f170073309d1d8c8e09ff3b235b6d1d7f6f3ade 100644 (file)
@@ -1,13 +1,79 @@
 #include "animation/state_machine_layer.hpp"
+#include "importers/import_stack.hpp"
+#include "importers/state_machine_importer.hpp"
+#include "generated/animation/state_machine_base.hpp"
+#include "animation/any_state.hpp"
+#include "animation/entry_state.hpp"
+#include "animation/exit_state.hpp"
 
 using namespace rive;
 
+StateMachineLayer::~StateMachineLayer()
+{
+       for (auto state : m_States)
+       {
+               delete state;
+       }
+}
+
 StatusCode StateMachineLayer::onAddedDirty(CoreContext* context)
 {
+       StatusCode code;
+       for (auto state : m_States)
+       {
+               if ((code = state->onAddedDirty(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+               switch (state->coreType())
+               {
+                       case AnyState::typeKey:
+                               m_Any = state->as<AnyState>();
+                               break;
+                       case EntryState::typeKey:
+                               m_Entry = state->as<EntryState>();
+                               break;
+                       case ExitState::typeKey:
+                               m_Exit = state->as<ExitState>();
+                               break;
+               }
+       }
+       if (m_Any == nullptr || m_Entry == nullptr || m_Exit == nullptr)
+       {
+               // The layer is corrupt, we must have all three of these states.
+               return StatusCode::InvalidObject;
+       }
+
        return StatusCode::Ok;
 }
 
 StatusCode StateMachineLayer::onAddedClean(CoreContext* context)
 {
+       StatusCode code;
+       for (auto state : m_States)
+       {
+               if ((code = state->onAddedClean(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
+
        return StatusCode::Ok;
-}
\ No newline at end of file
+}
+
+void StateMachineLayer::addState(LayerState* state)
+{
+       m_States.push_back(state);
+}
+
+StatusCode StateMachineLayer::import(ImportStack& importStack)
+{
+       auto stateMachineImporter =
+           importStack.latest<StateMachineImporter>(StateMachineBase::typeKey);
+       if (stateMachineImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+       stateMachineImporter->addLayer(this);
+       return Super::import(importStack);
+}
diff --git a/submodule/src/animation/state_machine_trigger.cpp b/submodule/src/animation/state_machine_trigger.cpp
deleted file mode 100644 (file)
index 122efc5..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#include "animation/state_machine_trigger.hpp"
-
-using namespace rive;
-
-void StateMachineTrigger::fire() { m_Fired = true; }
-
-void StateMachineTrigger::reset() { m_Fired = false; }
\ No newline at end of file
index 7f39b279d81f74416d26cac33a404abe5066c883..b1a882ddfc31dde1c33084583dd456400dd1745d 100644 (file)
 #include "animation/state_transition.hpp"
+#include "importers/import_stack.hpp"
+#include "importers/layer_state_importer.hpp"
+#include "animation/layer_state.hpp"
+#include "animation/transition_condition.hpp"
+#include "animation/animation_state.hpp"
+#include "animation/linear_animation.hpp"
 
 using namespace rive;
 
+StateTransition::~StateTransition()
+{
+       for (auto condition : m_Conditions)
+       {
+               delete condition;
+       }
+}
+
 StatusCode StateTransition::onAddedDirty(CoreContext* context)
 {
+       StatusCode code;
+       for (auto condition : m_Conditions)
+       {
+               if ((code = condition->onAddedDirty(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
        return StatusCode::Ok;
 }
 
 StatusCode StateTransition::onAddedClean(CoreContext* context)
 {
+       StatusCode code;
+       for (auto condition : m_Conditions)
+       {
+               if ((code = condition->onAddedClean(context)) != StatusCode::Ok)
+               {
+                       return code;
+               }
+       }
        return StatusCode::Ok;
-}
\ No newline at end of file
+}
+
+StatusCode StateTransition::import(ImportStack& importStack)
+{
+       auto stateImporter =
+           importStack.latest<LayerStateImporter>(LayerState::typeKey);
+       if (stateImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+       stateImporter->addTransition(this);
+       return Super::import(importStack);
+}
+
+void StateTransition::addCondition(TransitionCondition* condition)
+{
+       m_Conditions.push_back(condition);
+}
+
+float StateTransition::mixTime(const LayerState* stateFrom) const
+{
+       if (duration() == 0)
+       {
+               return 0;
+       }
+       if ((transitionFlags() & StateTransitionFlags::DurationIsPercentage) ==
+           StateTransitionFlags::DurationIsPercentage)
+       {
+               float animationDuration = 0.0f;
+               if (stateFrom->is<AnimationState>())
+               {
+                       auto animation = stateFrom->as<AnimationState>()->animation();
+                       if (animation != nullptr)
+                       {
+                               animationDuration = animation->durationSeconds();
+                       }
+               }
+               return duration() / 100.0f * animationDuration;
+       }
+       else
+       {
+               return duration() / 1000.0f;
+       }
+}
+
+float StateTransition::exitTimeSeconds(const LayerState* stateFrom,
+                                       bool relativeToWorkArea) const
+{
+       auto exitValue = exitTime();
+       if (exitValue == 0)
+       {
+               return 0;
+       }
+
+       float animationDuration = 0.0f;
+       float animationOrigin = 0.0f;
+       if (stateFrom->is<AnimationState>())
+       {
+               auto animation = stateFrom->as<AnimationState>()->animation();
+               animationDuration = animation->durationSeconds();
+               animationOrigin = relativeToWorkArea ? 0 : animation->startSeconds();
+       }
+
+       if ((transitionFlags() & StateTransitionFlags::ExitTimeIsPercentage) ==
+           StateTransitionFlags::ExitTimeIsPercentage)
+       {
+               return animationOrigin + exitValue / 100.0f * animationDuration;
+       }
+       else
+       {
+               return animationOrigin + exitValue / 1000.0f;
+       }
+}
diff --git a/submodule/src/animation/transition_bool_condition.cpp b/submodule/src/animation/transition_bool_condition.cpp
new file mode 100644 (file)
index 0000000..a4c795c
--- /dev/null
@@ -0,0 +1,27 @@
+#include "animation/transition_bool_condition.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine_bool.hpp"
+#include "animation/transition_condition_op.hpp"
+
+using namespace rive;
+
+bool TransitionBoolCondition::validateInputType(
+    const StateMachineInput* input) const
+{
+       // A null input is valid as the StateMachine can attempt to limp along if we
+       // introduce new input types that old conditions are expected to handle in
+       // newer runtimes. The older runtimes will just evaluate them to true.
+       return input == nullptr || input->is<StateMachineBool>();
+}
+
+bool TransitionBoolCondition::evaluate(const SMIInput* inputInstance) const
+{
+       if (inputInstance == nullptr)
+       {
+               return true;
+       }
+       auto boolInput = reinterpret_cast<const SMIBool*>(inputInstance);
+
+       return (boolInput->value() && op() == TransitionConditionOp::equal) ||
+              (!boolInput->value() && op() == TransitionConditionOp::notEqual);
+}
\ No newline at end of file
index 34737c68993605ab50ada5ca4b8461344146ff40..e12a50b36e4a9e44889703515d423ffea40629c5 100644 (file)
@@ -1,4 +1,8 @@
 #include "animation/transition_bool_condition.hpp"
+#include "animation/state_transition.hpp"
+#include "importers/state_transition_importer.hpp"
+#include "importers/state_machine_importer.hpp"
+#include "animation/state_machine.hpp"
 
 using namespace rive;
 
@@ -10,4 +14,35 @@ StatusCode TransitionCondition::onAddedDirty(CoreContext* context)
 StatusCode TransitionCondition::onAddedClean(CoreContext* context)
 {
        return StatusCode::Ok;
+}
+
+StatusCode TransitionCondition::import(ImportStack& importStack)
+{
+       auto stateMachineImporter =
+           importStack.latest<StateMachineImporter>(StateMachine::typeKey);
+       if (stateMachineImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+
+       // Make sure the inputId doesn't overflow the input buffer.
+       if (inputId() < 0 ||
+           inputId() >= stateMachineImporter->stateMachine()->inputCount())
+       {
+               return StatusCode::InvalidObject;
+       }
+       if (!validateInputType(
+               stateMachineImporter->stateMachine()->input((size_t)inputId())))
+       {
+               return StatusCode::InvalidObject;
+       }
+
+       auto transitionImporter =
+           importStack.latest<StateTransitionImporter>(StateTransition::typeKey);
+       if (transitionImporter == nullptr)
+       {
+               return StatusCode::MissingObject;
+       }
+       transitionImporter->addCondition(this);
+       return Super::import(importStack);
 }
\ No newline at end of file
diff --git a/submodule/src/animation/transition_number_condition.cpp b/submodule/src/animation/transition_number_condition.cpp
new file mode 100644 (file)
index 0000000..877dd04
--- /dev/null
@@ -0,0 +1,40 @@
+#include "animation/transition_number_condition.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/transition_condition_op.hpp"
+
+using namespace rive;
+
+bool TransitionNumberCondition::validateInputType(
+    const StateMachineInput* input) const
+{
+       // A null input is valid as the StateMachine can attempt to limp along if we
+       // introduce new input types that old conditions are expected to handle in
+       // newer runtimes. The older runtimes will just evaluate them to true.
+       return input == nullptr || input->is<StateMachineNumber>();
+}
+
+bool TransitionNumberCondition::evaluate(const SMIInput* inputInstance) const
+{
+       if (inputInstance == nullptr)
+       {
+               return true;
+       }
+       auto numberInput = reinterpret_cast<const SMINumber*>(inputInstance);
+
+       switch (op())
+       {
+               case TransitionConditionOp::equal:
+                       return numberInput->value() == value();
+               case TransitionConditionOp::notEqual:
+                       return numberInput->value() != value();
+               case TransitionConditionOp::lessThanOrEqual:
+                       return numberInput->value() <= value();
+               case TransitionConditionOp::lessThan:
+                       return numberInput->value() < value();
+               case TransitionConditionOp::greaterThanOrEqual:
+                       return numberInput->value() >= value();
+               case TransitionConditionOp::greaterThan:
+                       return numberInput->value() > value();
+       }
+}
\ No newline at end of file
diff --git a/submodule/src/animation/transition_trigger_condition.cpp b/submodule/src/animation/transition_trigger_condition.cpp
new file mode 100644 (file)
index 0000000..63306e8
--- /dev/null
@@ -0,0 +1,30 @@
+#include "animation/transition_trigger_condition.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine_trigger.hpp"
+#include "animation/transition_condition_op.hpp"
+
+using namespace rive;
+
+bool TransitionTriggerCondition::validateInputType(
+    const StateMachineInput* input) const
+{
+       // A null input is valid as the StateMachine can attempt to limp along if we
+       // introduce new input types that old conditions are expected to handle in
+       // newer runtimes. The older runtimes will just evaluate them to true.
+       return input == nullptr || input->is<StateMachineTrigger>();
+}
+
+bool TransitionTriggerCondition::evaluate(const SMIInput* inputInstance) const
+{
+       if (inputInstance == nullptr)
+       {
+               return true;
+       }
+       auto triggerInput = reinterpret_cast<const SMITrigger*>(inputInstance);
+
+       if (triggerInput->m_Fired)
+       {
+               return true;
+       }
+       return false;
+}
\ No newline at end of file
index 5fae92f3dbf69123f4acd9e204b80cd170d048af..0f367bda4b01ce2e99bd94815eb685d3bf84ef9a 100644 (file)
@@ -327,7 +327,7 @@ void Artboard::addStateMachine(StateMachine* object)
 
 Core* Artboard::resolve(int id) const
 {
-       if (id < 0 || id >= m_Objects.size())
+       if (id < 0 || id >= static_cast<int>(m_Objects.size()))
        {
                return nullptr;
        }
@@ -373,14 +373,14 @@ bool Artboard::updateComponents()
        {
                const int maxSteps = 100;
                int step = 0;
-               int count = (int)m_DependencyOrder.size();
+               auto count = m_DependencyOrder.size();
                while (hasDirt(ComponentDirt::Components) && step < maxSteps)
                {
                        // m_Dirt = m_Dirt & ~ComponentDirt::Components;
 
                        // Track dirt depth here so that if something else marks
                        // dirty, we restart.
-                       for (int i = 0; i < count; i++)
+                       for (unsigned int i = 0; i < count; i++)
                        {
                                auto component = m_DependencyOrder[i];
                                m_DirtDepth = i;
@@ -437,7 +437,7 @@ void Artboard::draw(Renderer* renderer)
 
 AABB Artboard::bounds() const { return AABB(0.0f, 0.0f, width(), height()); }
 
-LinearAnimation* Artboard::firstAnimation()
+LinearAnimation* Artboard::firstAnimation() const
 {
        if (m_Animations.empty())
        {
@@ -446,7 +446,7 @@ LinearAnimation* Artboard::firstAnimation()
        return m_Animations.front();
 }
 
-LinearAnimation* Artboard::animation(std::string name)
+LinearAnimation* Artboard::animation(std::string name) const
 {
        for (auto animation : m_Animations)
        {
@@ -458,16 +458,16 @@ LinearAnimation* Artboard::animation(std::string name)
        return nullptr;
 }
 
-LinearAnimation* Artboard::animation(size_t index)
+LinearAnimation* Artboard::animation(size_t index) const
 {
-       if (index < 0 || index >= m_Animations.size())
+       if (index >= m_Animations.size())
        {
                return nullptr;
        }
        return m_Animations[index];
 }
 
-StateMachine* Artboard::firstStateMachine()
+StateMachine* Artboard::firstStateMachine() const
 {
        if (m_StateMachines.empty())
        {
@@ -476,7 +476,7 @@ StateMachine* Artboard::firstStateMachine()
        return m_StateMachines.front();
 }
 
-StateMachine* Artboard::stateMachine(std::string name)
+StateMachine* Artboard::stateMachine(std::string name) const
 {
        for (auto machine : m_StateMachines)
        {
@@ -488,11 +488,11 @@ StateMachine* Artboard::stateMachine(std::string name)
        return nullptr;
 }
 
-StateMachine* Artboard::stateMachine(size_t index)
+StateMachine* Artboard::stateMachine(size_t index) const
 {
-       if (index < 0 || index >= m_StateMachines.size())
+       if (index >= m_StateMachines.size())
        {
                return nullptr;
        }
        return m_StateMachines[index];
-}
\ No newline at end of file
+}
index cdfe615721cccb70a238fcb2e8ad10f56b7aa59e..e0dbaa1c5b3d0d71cb38ea421840b143bb335460 100644 (file)
 #include "importers/keyed_object_importer.hpp"
 #include "importers/keyed_property_importer.hpp"
 #include "importers/linear_animation_importer.hpp"
+#include "importers/state_machine_importer.hpp"
+#include "importers/state_machine_layer_importer.hpp"
+#include "importers/layer_state_importer.hpp"
+#include "importers/state_transition_importer.hpp"
+#include "animation/any_state.hpp"
+#include "animation/entry_state.hpp"
+#include "animation/exit_state.hpp"
+#include "animation/animation_state.hpp"
 
 // Default namespace for Rive Cpp code
 using namespace rive;
@@ -76,10 +84,10 @@ static Core* readRuntimeObject(BinaryReader& reader,
        }
        if (object == nullptr)
        {
-               fprintf(stderr,
-                       "File contains an unknown object with coreType %llu, which "
-                       "this runtime doesn't understand.\n",
-                       coreObjectKey);
+               // fprintf(stderr,
+               //         "File contains an unknown object with coreType %llu, which "
+               //         "this runtime doesn't understand.\n",
+               //         coreObjectKey);
                return nullptr;
        }
        return object;
@@ -132,15 +140,7 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header)
                auto object = readRuntimeObject(reader, header);
                if (object == nullptr)
                {
-                       // See if there's an artboard on the stack, need to track the null
-                       // object as it'll still hold an id.
-                       auto importer =
-                           importStack.latest<ArtboardImporter>(Artboard::typeKey);
-                       if (importer == nullptr)
-                       {
-                               return ImportResult::malformed;
-                       }
-                       importer->addComponent(object);
+                       importStack.readNullObject();
                        continue;
                }
                ImportStackObject* stackObject = nullptr;
@@ -171,6 +171,35 @@ ImportResult File::read(BinaryReader& reader, const RuntimeHeader& header)
                                    importer->animation(), object->as<KeyedProperty>());
                                break;
                        }
+                       case StateMachine::typeKey:
+                               stackObject =
+                                   new StateMachineImporter(object->as<StateMachine>());
+                               break;
+                       case StateMachineLayer::typeKey:
+                       {
+                               auto artboardImporter =
+                                   importStack.latest<ArtboardImporter>(ArtboardBase::typeKey);
+                               if (artboardImporter == nullptr)
+                               {
+                                       return ImportResult::malformed;
+                               }
+
+                               stackObject = new StateMachineLayerImporter(
+                                   object->as<StateMachineLayer>(), artboardImporter);
+
+                               break;
+                       }
+                       case EntryState::typeKey:
+                       case ExitState::typeKey:
+                       case AnyState::typeKey:
+                       case AnimationState::typeKey:
+                               stackObject = new LayerStateImporter(object->as<LayerState>());
+                               stackType = LayerState::typeKey;
+                               break;
+                       case StateTransition::typeKey:
+                               stackObject =
+                                   new StateTransitionImporter(object->as<StateTransition>());
+                               break;
                }
                if (importStack.makeLatest(stackType, stackObject) != StatusCode::Ok)
                {
@@ -217,3 +246,12 @@ Artboard* File::artboard() const
        }
        return m_Artboards[0];
 }
+
+Artboard* File::artboard(size_t index) const
+{
+       if (index >= m_Artboards.size())
+       {
+               return nullptr;
+       }
+       return m_Artboards[index];
+}
index 41b07195c9ac8b415fd780dcacb5423bc44c1bbb..768912b6c8eea8e60c147bfb0c2caca2e1d074df 100644 (file)
@@ -23,4 +23,10 @@ void ArtboardImporter::addStateMachine(StateMachine* stateMachine)
        m_Artboard->addStateMachine(stateMachine);
 }
 
-StatusCode ArtboardImporter::resolve() { return m_Artboard->initialize(); }
\ No newline at end of file
+StatusCode ArtboardImporter::resolve() { return m_Artboard->initialize(); }
+
+bool ArtboardImporter::readNullObject()
+{
+       addComponent(nullptr);
+       return true;
+}
\ No newline at end of file
index ac917c7b0fba36284c6bf7617acd5e130dc59074..733bd0092a1fe2fb54800c272c6127b139bf5186 100644 (file)
@@ -15,4 +15,11 @@ void KeyedPropertyImporter::addKeyFrame(KeyFrame* keyFrame)
 {
        keyFrame->computeSeconds(m_Animation->fps());
        m_KeyedProperty->addKeyFrame(keyFrame);
+}
+
+bool KeyedPropertyImporter::readNullObject()
+{
+       // We don't need to add the null keyframe as nothing references them, but we
+       // do need to not allow the null to propagate up.
+       return true;
 }
\ No newline at end of file
diff --git a/submodule/src/importers/layer_state_importer.cpp b/submodule/src/importers/layer_state_importer.cpp
new file mode 100644 (file)
index 0000000..6f3a7ce
--- /dev/null
@@ -0,0 +1,13 @@
+#include "importers/layer_state_importer.hpp"
+#include "animation/state_transition.hpp"
+#include "animation/layer_state.hpp"
+
+using namespace rive;
+
+LayerStateImporter::LayerStateImporter(LayerState* state) : m_State(state) {}
+void LayerStateImporter::addTransition(StateTransition* transition)
+{
+       m_State->addTransition(transition);
+}
+
+StatusCode LayerStateImporter::resolve() { return StatusCode::Ok; }
\ No newline at end of file
diff --git a/submodule/src/importers/state_machine_importer.cpp b/submodule/src/importers/state_machine_importer.cpp
new file mode 100644 (file)
index 0000000..f734d73
--- /dev/null
@@ -0,0 +1,28 @@
+#include "importers/state_machine_importer.hpp"
+#include "animation/state_machine.hpp"
+
+using namespace rive;
+
+StateMachineImporter::StateMachineImporter(StateMachine* machine) :
+    m_StateMachine(machine)
+{
+}
+
+void StateMachineImporter::addLayer(StateMachineLayer* layer)
+{
+       m_StateMachine->addLayer(layer);
+}
+
+void StateMachineImporter::addInput(StateMachineInput* input)
+{
+       m_StateMachine->addInput(input);
+}
+
+bool StateMachineImporter::readNullObject()
+{
+       // Hard assumption that we won't add new layer types...
+       m_StateMachine->addInput(nullptr);
+       return true;
+}
+
+StatusCode StateMachineImporter::resolve() { return StatusCode::Ok; }
\ No newline at end of file
diff --git a/submodule/src/importers/state_machine_layer_importer.cpp b/submodule/src/importers/state_machine_layer_importer.cpp
new file mode 100644 (file)
index 0000000..80fa057
--- /dev/null
@@ -0,0 +1,59 @@
+#include "importers/state_machine_layer_importer.hpp"
+#include "importers/artboard_importer.hpp"
+#include "animation/state_machine_layer.hpp"
+#include "animation/animation_state.hpp"
+#include "animation/state_transition.hpp"
+#include "artboard.hpp"
+
+using namespace rive;
+StateMachineLayerImporter::StateMachineLayerImporter(
+    StateMachineLayer* layer, ArtboardImporter* artboardImporter) :
+    m_Layer(layer), m_ArtboardImporter(artboardImporter)
+{
+}
+void StateMachineLayerImporter::addState(LayerState* state)
+{
+       m_Layer->addState(state);
+}
+
+StatusCode StateMachineLayerImporter::resolve()
+{
+       auto artboard = m_ArtboardImporter->artboard();
+       for (auto state : m_Layer->m_States)
+       {
+               if (state->is<AnimationState>())
+               {
+                       auto animationState = state->as<AnimationState>();
+
+                       if (animationState->animationId() != -1)
+                       {
+                               animationState->m_Animation =
+                                   artboard->animation(animationState->animationId());
+                               if (animationState->m_Animation == nullptr)
+                               {
+                                       return StatusCode::MissingObject;
+                               }
+                       }
+               }
+               for (auto transition : state->m_Transitions)
+               {
+                       if (transition->stateToId() < 0 ||
+                           transition->stateToId() > m_Layer->m_States.size())
+                       {
+                               return StatusCode::InvalidObject;
+                       }
+                       transition->m_StateTo = m_Layer->m_States[transition->stateToId()];
+               }
+       }
+       return StatusCode::Ok;
+}
+
+bool StateMachineLayerImporter::readNullObject()
+{
+       // Add an 'empty' generic state that can be transitioned to/from but doesn't
+       // effectively do anything. This allows us to deal with unexpected new state
+       // types the runtime won't be able to understand. It'll still be able to
+       // make use of the state but it won't do anything visually.
+       addState(new LayerState());
+       return true;
+}
\ No newline at end of file
diff --git a/submodule/src/importers/state_transition_importer.cpp b/submodule/src/importers/state_transition_importer.cpp
new file mode 100644 (file)
index 0000000..387ae6e
--- /dev/null
@@ -0,0 +1,16 @@
+#include "importers/state_transition_importer.hpp"
+#include "animation/transition_condition.hpp"
+#include "animation/state_transition.hpp"
+
+using namespace rive;
+
+StateTransitionImporter::StateTransitionImporter(StateTransition* transition) :
+    m_Transition(transition)
+{
+}
+void StateTransitionImporter::addCondition(TransitionCondition* condition)
+{
+       m_Transition->addCondition(condition);
+}
+
+StatusCode StateTransitionImporter::resolve() { return StatusCode::Ok; }
\ No newline at end of file
index c910840c8ab171f26b4526e4eb3bd44c50ac52e7..c49ce80afe5bdc58e99eddbbd1b9b94259e6779c 100644 (file)
@@ -157,7 +157,7 @@ static void buildPath(CommandPath& commandPath,
                }
        }
 
-       for (int i = 1; i < length; i++)
+       for (size_t i = 1; i < length; i++)
        {
                auto vertex = vertices[i];
 
@@ -300,4 +300,4 @@ void Path::update(ComponentDirt value)
        //      // ascertaning it updates after both)
        //      m_Shape->pathChanged();
        // }
-}
\ No newline at end of file
+}
index 2af38175a921abe37c7be6210d72170c5f10f058..24dfdced85a8942c712645dec1609ad56c6351b2 100644 (file)
@@ -21,7 +21,7 @@ void Polygon::pointsChanged() { markPathDirty(); }
 
 void Polygon::resizeVertices(int newSize)
 {
-       auto currentSize = m_Vertices.size();
+       auto currentSize = static_cast<int>(m_Vertices.size());
 
        if (newSize == currentSize)
        {
@@ -73,11 +73,11 @@ void Polygon::update(ComponentDirt value)
 {
        if (hasDirt(value, ComponentDirt::Path))
        {
-               if (m_Vertices.size() != expectedSize())
+               if (static_cast<int>(m_Vertices.size()) != expectedSize())
                {
                        resizeVertices(expectedSize());
                }
                buildPolygon();
        }
        Super::update(value);
-}
\ No newline at end of file
+}
diff --git a/submodule/test/assets/rocket.riv b/submodule/test/assets/rocket.riv
new file mode 100644 (file)
index 0000000..6e9f38d
Binary files /dev/null and b/submodule/test/assets/rocket.riv differ
index 9215d7fba628f7c2b240a30fe03e85ac759e74fd..1a3f45bf1701662ae42dee167f0c3f61d9f8bce6 100644 (file)
@@ -78,6 +78,37 @@ TEST_CASE("file with animation can be read", "[file]")
        delete[] bytes;
 }
 
+TEST_CASE("artboards can be counted and accessed via index or name", "[file]")
+{
+       FILE* fp = fopen("../../test/assets/dependency_test.riv", "r");
+       REQUIRE(fp != nullptr);
+
+       fseek(fp, 0, SEEK_END);
+       auto length = ftell(fp);
+       fseek(fp, 0, SEEK_SET);
+       uint8_t* bytes = new uint8_t[length];
+       REQUIRE(fread(bytes, 1, length, fp) == length);
+       auto reader = rive::BinaryReader(bytes, length);
+       rive::File* file = nullptr;
+       auto result = rive::File::import(reader, &file);
+
+       REQUIRE(result == rive::ImportResult::success);
+       REQUIRE(file != nullptr);
+
+       // The default artboard can be accessed
+       REQUIRE(file->artboard() != nullptr);
+
+       // The artboards caqn be counted
+       REQUIRE(file->artboardCount() == 1);
+
+       // Artboards can be access by index
+       REQUIRE(file->artboard(0) != nullptr);
+
+       // Artboards can be accessed by name
+       REQUIRE(file->artboard("Blue") != nullptr);
+
+}
+
 TEST_CASE("dependencies are as expected", "[file]")
 {
        // ┌────┐
diff --git a/submodule/test/state_machine_test.cpp b/submodule/test/state_machine_test.cpp
new file mode 100644 (file)
index 0000000..853bc70
--- /dev/null
@@ -0,0 +1,98 @@
+#include "catch.hpp"
+#include "core/binary_reader.hpp"
+#include "file.hpp"
+#include "animation/state_machine_bool.hpp"
+#include "animation/state_machine_layer.hpp"
+#include "animation/animation_state.hpp"
+#include "animation/entry_state.hpp"
+#include "animation/state_transition.hpp"
+#include "animation/state_machine_instance.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include <cstdio>
+
+TEST_CASE("file with state machine be read", "[file]")
+{
+       FILE* fp = fopen("../../test/assets/rocket.riv", "r");
+       REQUIRE(fp != nullptr);
+
+       fseek(fp, 0, SEEK_END);
+       auto length = ftell(fp);
+       fseek(fp, 0, SEEK_SET);
+       uint8_t* bytes = new uint8_t[length];
+       REQUIRE(fread(bytes, 1, length, fp) == length);
+       auto reader = rive::BinaryReader(bytes, length);
+       rive::File* file = nullptr;
+       auto result = rive::File::import(reader, &file);
+
+       REQUIRE(result == rive::ImportResult::success);
+       REQUIRE(file != nullptr);
+       auto artboard = file->artboard();
+       REQUIRE(artboard != nullptr);
+       REQUIRE(artboard->animationCount() == 3);
+       REQUIRE(artboard->stateMachineCount() == 1);
+
+       auto stateMachine = artboard->stateMachine("Button");
+       REQUIRE(stateMachine != nullptr);
+
+       REQUIRE(stateMachine->layerCount() == 1);
+       REQUIRE(stateMachine->inputCount() == 2);
+
+       auto hover = stateMachine->input("Hover");
+       REQUIRE(hover != nullptr);
+       REQUIRE(hover->is<rive::StateMachineBool>());
+
+       auto press = stateMachine->input("Press");
+       REQUIRE(press != nullptr);
+       REQUIRE(press->is<rive::StateMachineBool>());
+
+       auto layer = stateMachine->layer(0);
+       REQUIRE(layer->stateCount() == 6);
+
+       REQUIRE(layer->anyState() != nullptr);
+       REQUIRE(layer->entryState() != nullptr);
+       REQUIRE(layer->exitState() != nullptr);
+
+       int foundAnimationStates = 0;
+       for (int i = 0; i < layer->stateCount(); i++)
+       {
+               auto state = layer->state(i);
+               if (state->is<rive::AnimationState>())
+               {
+                       foundAnimationStates++;
+                       REQUIRE(state->as<rive::AnimationState>()->animation() != nullptr);
+               }
+       }
+
+       REQUIRE(foundAnimationStates == 3);
+
+       REQUIRE(layer->entryState()->transitionCount() == 1);
+       auto stateTo = layer->entryState()->transition(0)->stateTo();
+       REQUIRE(stateTo != nullptr);
+       REQUIRE(stateTo->is<rive::AnimationState>());
+       REQUIRE(stateTo->as<rive::AnimationState>()->animation() != nullptr);
+       REQUIRE(stateTo->as<rive::AnimationState>()->animation()->name() == "idle");
+
+       auto idleState = stateTo->as<rive::AnimationState>();
+       REQUIRE(idleState->transitionCount() == 2);
+       for (int i = 0; i < idleState->transitionCount(); i++)
+       {
+               auto transition = idleState->transition(i);
+               if (transition->stateTo()
+                       ->as<rive::AnimationState>()
+                       ->animation()
+                       ->name() == "Roll_over")
+               {
+                       // Check the condition
+                       REQUIRE(transition->conditionCount() == 1);
+               }
+       }
+
+       rive::StateMachineInstance smi(artboard->stateMachine("Button"));
+       REQUIRE(smi.getBool("Hover")->name() == "Hover");
+       REQUIRE(smi.getBool("Press")->name() == "Press");
+       REQUIRE(smi.getBool("Hover") != nullptr);
+       REQUIRE(smi.getBool("Press") != nullptr);
+
+       delete file;
+       delete[] bytes;
+}
\ No newline at end of file