Timeline Events for runtime Follow on to https://github.com/rive-app/rive/pull/5877! Does similar work for the C++ runtime and reconsiders some naming, will likely need to fix some higher level runtimes @zplata. I renamed the fired to reportedEvent and added in the time delay too. Diffs= f96c86fcc Timeline Events for runtime (#5951) Co-authored-by: Luigi Rosso <luigi-rosso@users.noreply.github.com>
diff --git a/.rive_head b/.rive_head index 290281e..e1220dc 100644 --- a/.rive_head +++ b/.rive_head
@@ -1 +1 @@ -853ae7de1d020bc13e84359f816215764923215c +f96c86fcc800a1fa2bec3e97be00cab395ea838d
diff --git a/dev/core_generator/lib/src/definition.dart b/dev/core_generator/lib/src/definition.dart index 596b969..6515f77 100644 --- a/dev/core_generator/lib/src/definition.dart +++ b/dev/core_generator/lib/src/definition.dart
@@ -26,6 +26,9 @@ .where((property) => property.isRuntime) .toList(growable: false); + Iterable<Property> get storedProperties => + properties.where((property) => property.getExportType().storesData); + Definition? _extensionOf; Key? _key; bool _isAbstract = false; @@ -167,13 +170,13 @@ code.writeln('static const uint16_t ${property.name}PropertyKey = ' '${property.key!.intValue};'); } - if (properties.any((prop) => !prop.isEncoded)) { + if (storedProperties.any((prop) => !prop.isEncoded)) { code.writeln('private:'); } // Write fields. for (final property in properties) { - if (property.isEncoded) { + if (property.isEncoded || !property.getExportType().storesData) { // Encoded properties don't store data, it's up to the implementation // to decode and store what it needs. continue; @@ -195,7 +198,13 @@ // Write getter/setters. code.writeln('public:'); for (final property in properties) { - if (property.isEncoded) { + if (!property.getExportType().storesData) { + code.writeln((property.isSetOverride ? '' : 'virtual ') + + 'void ${property.name}' + + '(const ${property.type.cppName}& value) ' + + (property.isSetOverride ? 'override' : '') + + '= 0;'); + } else if (property.isEncoded) { // Encoded properties just have a pure virtual decoder that needs to // be implemented. Also requires an implemention of copyPropertyName // as that will no longer automatically be copied by the generated @@ -235,9 +244,9 @@ code.writeln('Core* clone() const override;'); } - if (properties.isNotEmpty || _extensionOf == null) { + if (storedProperties.isNotEmpty || _extensionOf == null) { code.writeln('void copy(const ${_name}Base& object) {'); - for (final property in properties) { + for (final property in storedProperties) { if (property.isEncoded) { code.writeln('copy${property.capitalizedName}(object);'); } else { @@ -255,7 +264,7 @@ code.writeln('bool deserialize(uint16_t propertyKey, ' 'BinaryReader& reader) override {'); - if (properties.isNotEmpty) { + if (storedProperties.isNotEmpty) { code.writeln('switch (propertyKey){'); for (final property in properties) { code.writeln('case ${property.name}PropertyKey:'); @@ -279,8 +288,8 @@ } code.writeln('protected:'); - if (properties.isNotEmpty) { - for (final property in properties) { + if (storedProperties.isNotEmpty) { + for (final property in storedProperties) { code.writeln('virtual void ${property.name}Changed() {}'); } } @@ -465,6 +474,9 @@ ctxCode.writeln('}}'); } for (final fieldType in getSetFieldTypes.keys) { + if (!fieldType.storesData) { + continue; + } ctxCode.writeln( 'static ${fieldType.cppName} get${fieldType.capitalizedName}(' 'Core* object, int propertyKey){'); @@ -488,6 +500,9 @@ ctxCode.writeln('switch(propertyKey) {'); for (final fieldType in usedFieldTypes.keys) { + if (!fieldType.storesData) { + continue; + } var properties = usedFieldTypes[fieldType]; if (properties != null) { for (final property in properties) { @@ -500,6 +515,28 @@ ctxCode.writeln('default: return -1;}}'); + ctxCode.writeln(''' + static bool isCallback(uint32_t propertyKey) { + switch(propertyKey) {'''); + for (final fieldType in usedFieldTypes.keys) { + var properties = usedFieldTypes[fieldType]; + if (properties != null) { + bool found = false; + for (final property in properties) { + if (property.getExportType().name == 'callback') { + found = true; + ctxCode.write('case ${property.definition._name}Base'); + ctxCode.write('::${property.name}PropertyKey:'); + } + } + if (found) { + ctxCode.writeln('return true;'); + } + } + } + ctxCode.writeln('default:return false;'); + ctxCode.writeln('}}'); + ctxCode.writeln('};}'); var output = generatedHppPath;
diff --git a/dev/core_generator/lib/src/field_type.dart b/dev/core_generator/lib/src/field_type.dart index 60f3025..49edcd0 100644 --- a/dev/core_generator/lib/src/field_type.dart +++ b/dev/core_generator/lib/src/field_type.dart
@@ -16,11 +16,14 @@ final String _runtimeCoreType; String get runtimeCoreType => _runtimeCoreType; + final bool storesData; + FieldType( this.name, this._runtimeCoreType, { String? cppName, this.include, + this.storesData = true, }) { _cppName = cppName ?? name; _types[name] = this;
diff --git a/dev/core_generator/lib/src/field_types/callback_field_type.dart b/dev/core_generator/lib/src/field_types/callback_field_type.dart new file mode 100644 index 0000000..5c596e2 --- /dev/null +++ b/dev/core_generator/lib/src/field_types/callback_field_type.dart
@@ -0,0 +1,14 @@ +import 'package:core_generator/src/field_type.dart'; + +class CallbackFieldType extends FieldType { + CallbackFieldType() + : super( + 'callback', + 'CoreCallbackType', + cppName: 'CallbackData', + storesData: false, + ); + + @override + String get defaultValue => '0'; +}
diff --git a/dev/core_generator/lib/src/field_types/initialize.dart b/dev/core_generator/lib/src/field_types/initialize.dart index 5d37260..4c94698 100644 --- a/dev/core_generator/lib/src/field_types/initialize.dart +++ b/dev/core_generator/lib/src/field_types/initialize.dart
@@ -2,6 +2,7 @@ import 'package:core_generator/src/field_type.dart'; import 'package:core_generator/src/field_types/bytes_field_type.dart'; +import 'package:core_generator/src/field_types/callback_field_type.dart'; late List<FieldType> fields; @@ -13,5 +14,6 @@ DoubleFieldType(), BoolFieldType(), ColorFieldType(), + CallbackFieldType(), ]; }
diff --git a/dev/defs/animation/interpolating_keyframe.json b/dev/defs/animation/interpolating_keyframe.json new file mode 100644 index 0000000..ddc5375 --- /dev/null +++ b/dev/defs/animation/interpolating_keyframe.json
@@ -0,0 +1,31 @@ +{ + "name": "InterpolatingKeyFrame", + "key": { + "int": 170, + "string": "interpolatingkeyframe" + }, + "abstract": true, + "extends": "animation/keyframe.json", + "properties": { + "interpolationType": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 68, + "string": "interpolation" + }, + "description": "The type of interpolation index in KeyframeInterpolation applied to this keyframe." + }, + "interpolatorId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 69, + "string": "interpolatorid" + }, + "description": "The id of the custom interpolator used when interpolation is Cubic." + } + } +} \ No newline at end of file
diff --git a/dev/defs/animation/keyframe.json b/dev/defs/animation/keyframe.json index 1d22274..1384e8d 100644 --- a/dev/defs/animation/keyframe.json +++ b/dev/defs/animation/keyframe.json
@@ -25,26 +25,6 @@ "string": "frame" }, "description": "Timecode as frame number can be converted to time by dividing by animation fps." - }, - "interpolationType": { - "type": "uint", - "initialValue": "0", - "key": { - "int": 68, - "string": "interpolation" - }, - "description": "The type of interpolation index in KeyframeInterpolation applied to this keyframe." - }, - "interpolatorId": { - "type": "Id", - "typeRuntime": "uint", - "initialValue": "Core.missingId", - "initialValueRuntime": "-1", - "key": { - "int": 69, - "string": "interpolatorid" - }, - "description": "The id of the custom interpolator used when interpolation is Cubic." } } } \ No newline at end of file
diff --git a/dev/defs/animation/keyframe_bool.json b/dev/defs/animation/keyframe_bool.json index 7deb70b..bdf1d68 100644 --- a/dev/defs/animation/keyframe_bool.json +++ b/dev/defs/animation/keyframe_bool.json
@@ -4,7 +4,7 @@ "int": 84, "string": "keyframebool" }, - "extends": "animation/keyframe.json", + "extends": "animation/interpolating_keyframe.json", "properties": { "value": { "type": "bool",
diff --git a/dev/defs/animation/keyframe_callback.json b/dev/defs/animation/keyframe_callback.json new file mode 100644 index 0000000..61e84b5 --- /dev/null +++ b/dev/defs/animation/keyframe_callback.json
@@ -0,0 +1,8 @@ +{ + "name": "KeyFrameCallback", + "key": { + "int": 171, + "string": "keyframe_callback" + }, + "extends": "animation/keyframe.json" +} \ No newline at end of file
diff --git a/dev/defs/animation/keyframe_color.json b/dev/defs/animation/keyframe_color.json index f13ab7b..c456254 100644 --- a/dev/defs/animation/keyframe_color.json +++ b/dev/defs/animation/keyframe_color.json
@@ -4,7 +4,7 @@ "int": 37, "string": "keyframecolor" }, - "extends": "animation/keyframe.json", + "extends": "animation/interpolating_keyframe.json", "properties": { "value": { "type": "Color",
diff --git a/dev/defs/animation/keyframe_double.json b/dev/defs/animation/keyframe_double.json index 532976a..4ea3405 100644 --- a/dev/defs/animation/keyframe_double.json +++ b/dev/defs/animation/keyframe_double.json
@@ -4,7 +4,7 @@ "int": 30, "string": "keyframedouble" }, - "extends": "animation/keyframe.json", + "extends": "animation/interpolating_keyframe.json", "properties": { "value": { "type": "double",
diff --git a/dev/defs/animation/keyframe_id.json b/dev/defs/animation/keyframe_id.json index 421a34b..299be61 100644 --- a/dev/defs/animation/keyframe_id.json +++ b/dev/defs/animation/keyframe_id.json
@@ -4,7 +4,7 @@ "int": 50, "string": "keyframeid" }, - "extends": "animation/keyframe.json", + "extends": "animation/interpolating_keyframe.json", "properties": { "value": { "type": "Id",
diff --git a/dev/defs/animation/keyframe_string.json b/dev/defs/animation/keyframe_string.json index 81fec8b..c4b301a 100644 --- a/dev/defs/animation/keyframe_string.json +++ b/dev/defs/animation/keyframe_string.json
@@ -4,7 +4,7 @@ "int": 142, "string": "keyframestring" }, - "extends": "animation/keyframe.json", + "extends": "animation/interpolating_keyframe.json", "properties": { "value": { "type": "String",
diff --git a/dev/defs/animation/state_machine_fire_event.json b/dev/defs/animation/state_machine_fire_event.json index 063ff2c..41db898 100644 --- a/dev/defs/animation/state_machine_fire_event.json +++ b/dev/defs/animation/state_machine_fire_event.json
@@ -1,49 +1,49 @@ { - "name": "StateMachineFireEvent", - "key": { - "int": 169, - "string": "statemachinefireevent" + "name": "StateMachineFireEvent", + "key": { + "int": 169, + "string": "statemachinefireevent" + }, + "properties": { + "layerComponentId": { + "type": "Id", + "initialValue": "Core.missingId", + "key": { + "int": 391, + "string": "layercomponentid" + }, + "description": "Id of the transition or layer this belongs to.", + "runtime": false }, - "properties": { - "layerComponentId": { - "type": "Id", - "initialValue": "Core.missingId", - "key": { - "int": 391, - "string": "layercomponentid" - }, - "description": "Id of the transition or layer this belongs to.", - "runtime": false - }, - "eventId": { - "type": "Id", - "typeRuntime": "uint", - "initialValue": "Core.missingId", - "initialValueRuntime": "-1", - "key": { - "int": 392, - "string": "eventid" - }, - "description": "Id of the Event referenced." - }, - "occursValue": { - "type": "uint", - "initialValue": "0", - "key": { - "int": 393, - "string": "occursvalue" - }, - "description": "When the event fires." - }, - "fireOrder": { - "type": "FractionalIndex", - "initialValue": "FractionalIndex.invalid", - "key": { - "int": 394, - "string": "fireorder" - }, - "description": "Order value for sorting transitions in states.", - "runtime": false - } + "eventId": { + "type": "Id", + "typeRuntime": "uint", + "initialValue": "Core.missingId", + "initialValueRuntime": "-1", + "key": { + "int": 392, + "string": "eventid" + }, + "description": "Id of the Event referenced." + }, + "occursValue": { + "type": "uint", + "initialValue": "0", + "key": { + "int": 393, + "string": "occursvalue" + }, + "description": "When the event fires." + }, + "fireOrder": { + "type": "FractionalIndex", + "initialValue": "FractionalIndex.invalid", + "key": { + "int": 394, + "string": "fireorder" + }, + "description": "Order value for sorting transitions in states.", + "runtime": false } + } } \ No newline at end of file
diff --git a/dev/defs/event.json b/dev/defs/event.json index 900d71e..3cc5000 100644 --- a/dev/defs/event.json +++ b/dev/defs/event.json
@@ -4,5 +4,15 @@ "int": 128, "string": "event" }, - "extends": "container_component.json" + "extends": "container_component.json", + "properties": { + "trigger": { + "type": "callback", + "animates": true, + "key": { + "int": 395, + "string": "trigger" + } + } + } } \ No newline at end of file
diff --git a/include/rive/animation/animation_state_instance.hpp b/include/rive/animation/animation_state_instance.hpp index 689b8ce..0eb15cc 100644 --- a/include/rive/animation/animation_state_instance.hpp +++ b/include/rive/animation/animation_state_instance.hpp
@@ -19,7 +19,7 @@ public: AnimationStateInstance(const AnimationState* animationState, ArtboardInstance* instance); - void advance(float seconds, Span<SMIInput*>) override; + void advance(float seconds, StateMachineInstance* stateMachineInstance) override; void apply(float mix) override; bool keepGoing() const override;
diff --git a/include/rive/animation/blend_state_1d_instance.hpp b/include/rive/animation/blend_state_1d_instance.hpp index 5f97cde..d0c81a2 100644 --- a/include/rive/animation/blend_state_1d_instance.hpp +++ b/include/rive/animation/blend_state_1d_instance.hpp
@@ -16,7 +16,7 @@ public: BlendState1DInstance(const BlendState1D* blendState, ArtboardInstance* instance); - void advance(float seconds, Span<SMIInput*> inputs) override; + void advance(float seconds, StateMachineInstance* stateMachineInstance) override; }; } // namespace rive #endif \ No newline at end of file
diff --git a/include/rive/animation/blend_state_direct_instance.hpp b/include/rive/animation/blend_state_direct_instance.hpp index 8fd8cf8..75fcfdf 100644 --- a/include/rive/animation/blend_state_direct_instance.hpp +++ b/include/rive/animation/blend_state_direct_instance.hpp
@@ -11,7 +11,7 @@ { public: BlendStateDirectInstance(const BlendStateDirect* blendState, ArtboardInstance* instance); - void advance(float seconds, Span<SMIInput*> inputs) override; + void advance(float seconds, StateMachineInstance* stateMachineInstance) override; }; } // namespace rive #endif \ No newline at end of file
diff --git a/include/rive/animation/blend_state_instance.hpp b/include/rive/animation/blend_state_instance.hpp index 9f035cb..c95111a 100644 --- a/include/rive/animation/blend_state_instance.hpp +++ b/include/rive/animation/blend_state_instance.hpp
@@ -6,6 +6,7 @@ #include "rive/animation/state_instance.hpp" #include "rive/animation/blend_state.hpp" #include "rive/animation/linear_animation_instance.hpp" +#include "rive/animation/state_machine_instance.hpp" namespace rive { @@ -52,7 +53,7 @@ bool keepGoing() const override { return m_KeepGoing; } - void advance(float seconds, Span<SMIInput*>) override + void advance(float seconds, StateMachineInstance* stateMachineInstance) override { // NOTE: we are intentionally ignoring the animationInstances' keepGoing // return value. @@ -62,7 +63,7 @@ { if (animation.m_AnimationInstance.keepGoing()) { - animation.m_AnimationInstance.advance(seconds); + animation.m_AnimationInstance.advance(seconds, stateMachineInstance); } } }
diff --git a/include/rive/animation/interpolating_keyframe.hpp b/include/rive/animation/interpolating_keyframe.hpp new file mode 100644 index 0000000..8255f77 --- /dev/null +++ b/include/rive/animation/interpolating_keyframe.hpp
@@ -0,0 +1,24 @@ +#ifndef _RIVE_INTERPOLATING_KEY_FRAME_HPP_ +#define _RIVE_INTERPOLATING_KEY_FRAME_HPP_ +#include "rive/generated/animation/interpolating_keyframe_base.hpp" +#include <stdio.h> +namespace rive +{ +class InterpolatingKeyFrame : public InterpolatingKeyFrameBase +{ +public: + inline CubicInterpolator* interpolator() const { return m_interpolator; } + virtual void apply(Core* object, int propertyKey, float mix) = 0; + virtual void applyInterpolation(Core* object, + int propertyKey, + float seconds, + const KeyFrame* nextFrame, + float mix) = 0; + StatusCode onAddedDirty(CoreContext* context) override; + +private: + CubicInterpolator* m_interpolator = nullptr; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/animation/keyed_callback_reporter.hpp b/include/rive/animation/keyed_callback_reporter.hpp new file mode 100644 index 0000000..7f691a3 --- /dev/null +++ b/include/rive/animation/keyed_callback_reporter.hpp
@@ -0,0 +1,16 @@ +#ifndef _RIVE_KEYED_CALLBACK_REPORTER_HPP_ +#define _RIVE_KEYED_CALLBACK_REPORTER_HPP_ + +namespace rive +{ +class KeyedCallbackReporter +{ +public: + virtual ~KeyedCallbackReporter() {} + virtual void reportKeyedCallback(uint32_t objectId, + uint32_t propertyKey, + float elapsedSeconds) = 0; +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/animation/keyed_object.hpp b/include/rive/animation/keyed_object.hpp index 70c97ab..04acdd6 100644 --- a/include/rive/animation/keyed_object.hpp +++ b/include/rive/animation/keyed_object.hpp
@@ -6,11 +6,9 @@ { class Artboard; class KeyedProperty; +class KeyedCallbackReporter; class KeyedObject : public KeyedObjectBase { -private: - std::vector<std::unique_ptr<KeyedProperty>> m_KeyedProperties; - public: KeyedObject(); ~KeyedObject() override; @@ -18,9 +16,15 @@ StatusCode onAddedDirty(CoreContext* context) override; StatusCode onAddedClean(CoreContext* context) override; + void reportKeyedCallbacks(KeyedCallbackReporter* reporter, + float secondsFrom, + float secondsTo) const; void apply(Artboard* coreContext, float time, float mix); StatusCode import(ImportStack& importStack) override; + +private: + std::vector<std::unique_ptr<KeyedProperty>> m_keyedProperties; }; } // namespace rive
diff --git a/include/rive/animation/keyed_property.hpp b/include/rive/animation/keyed_property.hpp index 29b8ffe..e092cfe 100644 --- a/include/rive/animation/keyed_property.hpp +++ b/include/rive/animation/keyed_property.hpp
@@ -5,11 +5,9 @@ namespace rive { class KeyFrame; +class KeyedCallbackReporter; class KeyedProperty : public KeyedPropertyBase { -private: - std::vector<std::unique_ptr<KeyFrame>> m_KeyFrames; - public: KeyedProperty(); ~KeyedProperty() override; @@ -17,9 +15,20 @@ StatusCode onAddedClean(CoreContext* context) override; StatusCode onAddedDirty(CoreContext* context) override; + /// Report any keyframes that occured between secondsFrom and secondsTo. + void reportKeyedCallbacks(KeyedCallbackReporter* reporter, + uint32_t objectId, + float secondsFrom, + float secondsTo) const; + + /// Apply interpolating key frames. void apply(Core* object, float time, float mix); StatusCode import(ImportStack& importStack) override; + +private: + int closestFrameIndex(float seconds, int exactOffset = 0) const; + std::vector<std::unique_ptr<KeyFrame>> m_keyFrames; }; } // namespace rive
diff --git a/include/rive/animation/keyframe.hpp b/include/rive/animation/keyframe.hpp index 961f2aa..a8b9397 100644 --- a/include/rive/animation/keyframe.hpp +++ b/include/rive/animation/keyframe.hpp
@@ -7,25 +7,15 @@ class KeyFrame : public KeyFrameBase { -private: - CubicInterpolator* m_Interpolator = nullptr; - float m_Seconds; - public: - inline float seconds() const { return m_Seconds; } - inline CubicInterpolator* interpolator() const { return m_Interpolator; } + inline float seconds() const { return m_seconds; } void computeSeconds(int fps); - StatusCode onAddedDirty(CoreContext* context) override; - virtual void apply(Core* object, int propertyKey, float mix) = 0; - virtual void applyInterpolation(Core* object, - int propertyKey, - float seconds, - const KeyFrame* nextFrame, - float mix) = 0; - StatusCode import(ImportStack& importStack) override; + +private: + float m_seconds; }; } // namespace rive
diff --git a/include/rive/animation/keyframe_callback.hpp b/include/rive/animation/keyframe_callback.hpp new file mode 100644 index 0000000..9c8c344 --- /dev/null +++ b/include/rive/animation/keyframe_callback.hpp
@@ -0,0 +1,13 @@ +#ifndef _RIVE_KEY_FRAME_CALLBACK_HPP_ +#define _RIVE_KEY_FRAME_CALLBACK_HPP_ +#include "rive/generated/animation/keyframe_callback_base.hpp" + +namespace rive +{ +class KeyFrameCallback : public KeyFrameCallbackBase +{ +public: +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/animation/linear_animation.hpp b/include/rive/animation/linear_animation.hpp index 6d8aeb5..2efcfeb 100644 --- a/include/rive/animation/linear_animation.hpp +++ b/include/rive/animation/linear_animation.hpp
@@ -7,6 +7,7 @@ { class Artboard; class KeyedObject; +class KeyedCallbackReporter; class LinearAnimation : public LinearAnimationBase { @@ -45,6 +46,10 @@ // Used in testing to check how many animations gets deleted. static int deleteCount; #endif + + void reportKeyedCallbacks(KeyedCallbackReporter* reporter, + float secondsFrom, + float secondsTo) const; }; } // namespace rive
diff --git a/include/rive/animation/linear_animation_instance.hpp b/include/rive/animation/linear_animation_instance.hpp index d7f6b25..ff5ea09 100644 --- a/include/rive/animation/linear_animation_instance.hpp +++ b/include/rive/animation/linear_animation_instance.hpp
@@ -7,6 +7,7 @@ namespace rive { class LinearAnimation; +class KeyedCallbackReporter; class LinearAnimationInstance : public Scene { @@ -17,7 +18,8 @@ // Advance the animation by the specified time. Returns true if the // animation will continue to animate after this advance. - bool advance(float seconds); + bool advance(float seconds, KeyedCallbackReporter* reporter); + bool advance(float seconds) { return advance(seconds, nullptr); } void clearSpilledTime() { m_spilledTime = 0; }
diff --git a/include/rive/animation/state_instance.hpp b/include/rive/animation/state_instance.hpp index 549555d..401b9f0 100644 --- a/include/rive/animation/state_instance.hpp +++ b/include/rive/animation/state_instance.hpp
@@ -9,7 +9,7 @@ namespace rive { class LayerState; -class SMIInput; +class StateMachineInstance; class ArtboardInstance; /// Represents an instance of a state tracked by the State Machine. @@ -21,7 +21,7 @@ public: StateInstance(const LayerState* layerState); virtual ~StateInstance(); - virtual void advance(float seconds, Span<SMIInput*> inputs) = 0; + virtual void advance(float seconds, StateMachineInstance* stateMachineInstance) = 0; virtual void apply(float mix) = 0; /// Returns true when the State Machine needs to keep advancing this
diff --git a/include/rive/animation/state_machine_instance.hpp b/include/rive/animation/state_machine_instance.hpp index e08e82d..cb1d7d8 100644 --- a/include/rive/animation/state_machine_instance.hpp +++ b/include/rive/animation/state_machine_instance.hpp
@@ -5,6 +5,7 @@ #include <stddef.h> #include <vector> #include "rive/animation/linear_animation_instance.hpp" +#include "rive/animation/keyed_callback_reporter.hpp" #include "rive/listener_type.hpp" #include "rive/scene.hpp" @@ -22,10 +23,24 @@ class HitShape; class NestedArtboard; class Event; +class KeyedProperty; -class StateMachineInstance : public Scene +class EventReport +{ +public: + EventReport(Event* event, float secondsDelay) : m_event(event), m_secondsDelay(secondsDelay) {} + Event* event() const { return m_event; } + float secondsDelay() const { return m_secondsDelay; } + +private: + Event* m_event; + float m_secondsDelay; +}; + +class StateMachineInstance : public Scene, public KeyedCallbackReporter { friend class SMIInput; + friend class KeyedProperty; private: void markNeedsAdvance(); @@ -84,17 +99,23 @@ /// the backing artboard (explicitly not allowed on Scenes). Artboard* artboard() { return m_artboardInstance; } - /// Tracks an event that fired, will be cleared at the end of the next advance. - void fireEvent(Event* event); + /// Tracks an event that reported, will be cleared at the end of the next advance. + void reportEvent(Event* event, float secondsDelay = 0.0f); - /// Gets the number of events that fired since the last advance. - std::size_t firedEventCount() const; + /// Gets the number of events that reported since the last advance. + std::size_t reportedEventCount() const; - /// Gets a fired event at an index < firedEventCount(). - const Event* firedEventAt(std::size_t index) const; + /// Gets a reported event at an index < reportedEventCount(). + const EventReport reportedEventAt(std::size_t index) const; + + /// Report which time based events have elapsed on a timeline within this + /// state machine. + void reportKeyedCallback(uint32_t objectId, + uint32_t propertyKey, + float elapsedSeconds) override; private: - std::vector<Event*> m_firedEvents; + std::vector<EventReport> m_reportedEvents; const StateMachine* m_machine; bool m_needsAdvance = false; std::vector<SMIInput*> m_inputInstances; // we own each pointer
diff --git a/include/rive/animation/state_transition.hpp b/include/rive/animation/state_transition.hpp index 5fefb4c..ff37527 100644 --- a/include/rive/animation/state_transition.hpp +++ b/include/rive/animation/state_transition.hpp
@@ -13,7 +13,7 @@ class StateTransitionImporter; class TransitionCondition; class StateInstance; -class SMIInput; +class StateMachineInstance; class LinearAnimation; class LinearAnimationInstance; @@ -59,7 +59,7 @@ /// Returns AllowTransition::yes when this transition can be taken from /// stateFrom with the given inputs. AllowTransition allowed(StateInstance* stateFrom, - Span<SMIInput*> inputs, + StateMachineInstance* stateMachineInstance, bool ignoreTriggers) const; /// Whether the animation is held at exit or if it keeps advancing
diff --git a/include/rive/animation/system_state_instance.hpp b/include/rive/animation/system_state_instance.hpp index 0adc062..b439da8 100644 --- a/include/rive/animation/system_state_instance.hpp +++ b/include/rive/animation/system_state_instance.hpp
@@ -6,6 +6,8 @@ namespace rive { +class StateMachineInstance; + /// Represents an instance of a system state machine. Basically a /// placeholder that may have meaning to the state machine itself, or is /// just a no-op state (perhaps an unknown to this runtime state-type). @@ -14,7 +16,7 @@ public: SystemStateInstance(const LayerState* layerState, ArtboardInstance* instance); - void advance(float seconds, Span<SMIInput*> inputs) override; + void advance(float seconds, StateMachineInstance* stateMachineInstance) override; void apply(float mix) override; bool keepGoing() const override;
diff --git a/include/rive/core/field_types/core_callback_type.hpp b/include/rive/core/field_types/core_callback_type.hpp new file mode 100644 index 0000000..047aaa0 --- /dev/null +++ b/include/rive/core/field_types/core_callback_type.hpp
@@ -0,0 +1,21 @@ +#ifndef _RIVE_CORE_CALLBACK_TYPE_HPP_ +#define _RIVE_CORE_CALLBACK_TYPE_HPP_ + +namespace rive +{ +class StateMachineInstance; +class CallbackData +{ +public: + StateMachineInstance* context() const { return m_context; } + float delaySeconds() const { return m_delaySeconds; } + CallbackData(StateMachineInstance* context, float delaySeconds) : + m_context(context), m_delaySeconds(delaySeconds) + {} + +private: + StateMachineInstance* m_context; + float m_delaySeconds; +}; +} // namespace rive +#endif \ No newline at end of file
diff --git a/include/rive/event.hpp b/include/rive/event.hpp index faa5518..743c6c5 100644 --- a/include/rive/event.hpp +++ b/include/rive/event.hpp
@@ -1,12 +1,13 @@ #ifndef _RIVE_EVENT_HPP_ #define _RIVE_EVENT_HPP_ #include "rive/generated/event_base.hpp" -#include <stdio.h> + namespace rive { class Event : public EventBase { public: + void trigger(const CallbackData& value) override; }; } // namespace rive
diff --git a/include/rive/generated/animation/interpolating_keyframe_base.hpp b/include/rive/generated/animation/interpolating_keyframe_base.hpp new file mode 100644 index 0000000..7e84216 --- /dev/null +++ b/include/rive/generated/animation/interpolating_keyframe_base.hpp
@@ -0,0 +1,88 @@ +#ifndef _RIVE_INTERPOLATING_KEY_FRAME_BASE_HPP_ +#define _RIVE_INTERPOLATING_KEY_FRAME_BASE_HPP_ +#include "rive/animation/keyframe.hpp" +#include "rive/core/field_types/core_uint_type.hpp" +namespace rive +{ +class InterpolatingKeyFrameBase : public KeyFrame +{ +protected: + typedef KeyFrame Super; + +public: + static const uint16_t typeKey = 170; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case InterpolatingKeyFrameBase::typeKey: + case KeyFrameBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + static const uint16_t interpolationTypePropertyKey = 68; + static const uint16_t interpolatorIdPropertyKey = 69; + +private: + uint32_t m_InterpolationType = 0; + uint32_t m_InterpolatorId = -1; + +public: + inline uint32_t interpolationType() const { return m_InterpolationType; } + void interpolationType(uint32_t value) + { + if (m_InterpolationType == value) + { + return; + } + m_InterpolationType = value; + interpolationTypeChanged(); + } + + inline uint32_t interpolatorId() const { return m_InterpolatorId; } + void interpolatorId(uint32_t value) + { + if (m_InterpolatorId == value) + { + return; + } + m_InterpolatorId = value; + interpolatorIdChanged(); + } + + void copy(const InterpolatingKeyFrameBase& object) + { + m_InterpolationType = object.m_InterpolationType; + m_InterpolatorId = object.m_InterpolatorId; + KeyFrame::copy(object); + } + + bool deserialize(uint16_t propertyKey, BinaryReader& reader) override + { + switch (propertyKey) + { + case interpolationTypePropertyKey: + m_InterpolationType = CoreUintType::deserialize(reader); + return true; + case interpolatorIdPropertyKey: + m_InterpolatorId = CoreUintType::deserialize(reader); + return true; + } + return KeyFrame::deserialize(propertyKey, reader); + } + +protected: + virtual void interpolationTypeChanged() {} + virtual void interpolatorIdChanged() {} +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/animation/keyframe_base.hpp b/include/rive/generated/animation/keyframe_base.hpp index 7570d87..9e8fa47 100644 --- a/include/rive/generated/animation/keyframe_base.hpp +++ b/include/rive/generated/animation/keyframe_base.hpp
@@ -28,13 +28,9 @@ uint16_t coreType() const override { return typeKey; } static const uint16_t framePropertyKey = 67; - static const uint16_t interpolationTypePropertyKey = 68; - static const uint16_t interpolatorIdPropertyKey = 69; private: uint32_t m_Frame = 0; - uint32_t m_InterpolationType = 0; - uint32_t m_InterpolatorId = -1; public: inline uint32_t frame() const { return m_Frame; } @@ -48,34 +44,7 @@ frameChanged(); } - inline uint32_t interpolationType() const { return m_InterpolationType; } - void interpolationType(uint32_t value) - { - if (m_InterpolationType == value) - { - return; - } - m_InterpolationType = value; - interpolationTypeChanged(); - } - - inline uint32_t interpolatorId() const { return m_InterpolatorId; } - void interpolatorId(uint32_t value) - { - if (m_InterpolatorId == value) - { - return; - } - m_InterpolatorId = value; - interpolatorIdChanged(); - } - - void copy(const KeyFrameBase& object) - { - m_Frame = object.m_Frame; - m_InterpolationType = object.m_InterpolationType; - m_InterpolatorId = object.m_InterpolatorId; - } + void copy(const KeyFrameBase& object) { m_Frame = object.m_Frame; } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override { @@ -84,20 +53,12 @@ case framePropertyKey: m_Frame = CoreUintType::deserialize(reader); return true; - case interpolationTypePropertyKey: - m_InterpolationType = CoreUintType::deserialize(reader); - return true; - case interpolatorIdPropertyKey: - m_InterpolatorId = CoreUintType::deserialize(reader); - return true; } return false; } protected: virtual void frameChanged() {} - virtual void interpolationTypeChanged() {} - virtual void interpolatorIdChanged() {} }; } // namespace rive
diff --git a/include/rive/generated/animation/keyframe_bool_base.hpp b/include/rive/generated/animation/keyframe_bool_base.hpp index c20a703..ffe12be 100644 --- a/include/rive/generated/animation/keyframe_bool_base.hpp +++ b/include/rive/generated/animation/keyframe_bool_base.hpp
@@ -1,13 +1,13 @@ #ifndef _RIVE_KEY_FRAME_BOOL_BASE_HPP_ #define _RIVE_KEY_FRAME_BOOL_BASE_HPP_ -#include "rive/animation/keyframe.hpp" +#include "rive/animation/interpolating_keyframe.hpp" #include "rive/core/field_types/core_bool_type.hpp" namespace rive { -class KeyFrameBoolBase : public KeyFrame +class KeyFrameBoolBase : public InterpolatingKeyFrame { protected: - typedef KeyFrame Super; + typedef InterpolatingKeyFrame Super; public: static const uint16_t typeKey = 84; @@ -19,6 +19,7 @@ switch (typeKey) { case KeyFrameBoolBase::typeKey: + case InterpolatingKeyFrameBase::typeKey: case KeyFrameBase::typeKey: return true; default: @@ -49,7 +50,7 @@ void copy(const KeyFrameBoolBase& object) { m_Value = object.m_Value; - KeyFrame::copy(object); + InterpolatingKeyFrame::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -60,7 +61,7 @@ m_Value = CoreBoolType::deserialize(reader); return true; } - return KeyFrame::deserialize(propertyKey, reader); + return InterpolatingKeyFrame::deserialize(propertyKey, reader); } protected:
diff --git a/include/rive/generated/animation/keyframe_callback_base.hpp b/include/rive/generated/animation/keyframe_callback_base.hpp new file mode 100644 index 0000000..066256e --- /dev/null +++ b/include/rive/generated/animation/keyframe_callback_base.hpp
@@ -0,0 +1,36 @@ +#ifndef _RIVE_KEY_FRAME_CALLBACK_BASE_HPP_ +#define _RIVE_KEY_FRAME_CALLBACK_BASE_HPP_ +#include "rive/animation/keyframe.hpp" +namespace rive +{ +class KeyFrameCallbackBase : public KeyFrame +{ +protected: + typedef KeyFrame Super; + +public: + static const uint16_t typeKey = 171; + + /// Helper to quickly determine if a core object extends another without RTTI + /// at runtime. + bool isTypeOf(uint16_t typeKey) const override + { + switch (typeKey) + { + case KeyFrameCallbackBase::typeKey: + case KeyFrameBase::typeKey: + return true; + default: + return false; + } + } + + uint16_t coreType() const override { return typeKey; } + + Core* clone() const override; + +protected: +}; +} // namespace rive + +#endif \ No newline at end of file
diff --git a/include/rive/generated/animation/keyframe_color_base.hpp b/include/rive/generated/animation/keyframe_color_base.hpp index 16d80fe..4814a47 100644 --- a/include/rive/generated/animation/keyframe_color_base.hpp +++ b/include/rive/generated/animation/keyframe_color_base.hpp
@@ -1,13 +1,13 @@ #ifndef _RIVE_KEY_FRAME_COLOR_BASE_HPP_ #define _RIVE_KEY_FRAME_COLOR_BASE_HPP_ -#include "rive/animation/keyframe.hpp" +#include "rive/animation/interpolating_keyframe.hpp" #include "rive/core/field_types/core_color_type.hpp" namespace rive { -class KeyFrameColorBase : public KeyFrame +class KeyFrameColorBase : public InterpolatingKeyFrame { protected: - typedef KeyFrame Super; + typedef InterpolatingKeyFrame Super; public: static const uint16_t typeKey = 37; @@ -19,6 +19,7 @@ switch (typeKey) { case KeyFrameColorBase::typeKey: + case InterpolatingKeyFrameBase::typeKey: case KeyFrameBase::typeKey: return true; default: @@ -49,7 +50,7 @@ void copy(const KeyFrameColorBase& object) { m_Value = object.m_Value; - KeyFrame::copy(object); + InterpolatingKeyFrame::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -60,7 +61,7 @@ m_Value = CoreColorType::deserialize(reader); return true; } - return KeyFrame::deserialize(propertyKey, reader); + return InterpolatingKeyFrame::deserialize(propertyKey, reader); } protected:
diff --git a/include/rive/generated/animation/keyframe_double_base.hpp b/include/rive/generated/animation/keyframe_double_base.hpp index df52746..0ee26f0 100644 --- a/include/rive/generated/animation/keyframe_double_base.hpp +++ b/include/rive/generated/animation/keyframe_double_base.hpp
@@ -1,13 +1,13 @@ #ifndef _RIVE_KEY_FRAME_DOUBLE_BASE_HPP_ #define _RIVE_KEY_FRAME_DOUBLE_BASE_HPP_ -#include "rive/animation/keyframe.hpp" +#include "rive/animation/interpolating_keyframe.hpp" #include "rive/core/field_types/core_double_type.hpp" namespace rive { -class KeyFrameDoubleBase : public KeyFrame +class KeyFrameDoubleBase : public InterpolatingKeyFrame { protected: - typedef KeyFrame Super; + typedef InterpolatingKeyFrame Super; public: static const uint16_t typeKey = 30; @@ -19,6 +19,7 @@ switch (typeKey) { case KeyFrameDoubleBase::typeKey: + case InterpolatingKeyFrameBase::typeKey: case KeyFrameBase::typeKey: return true; default: @@ -49,7 +50,7 @@ void copy(const KeyFrameDoubleBase& object) { m_Value = object.m_Value; - KeyFrame::copy(object); + InterpolatingKeyFrame::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -60,7 +61,7 @@ m_Value = CoreDoubleType::deserialize(reader); return true; } - return KeyFrame::deserialize(propertyKey, reader); + return InterpolatingKeyFrame::deserialize(propertyKey, reader); } protected:
diff --git a/include/rive/generated/animation/keyframe_id_base.hpp b/include/rive/generated/animation/keyframe_id_base.hpp index 086f5c9..229b1a3 100644 --- a/include/rive/generated/animation/keyframe_id_base.hpp +++ b/include/rive/generated/animation/keyframe_id_base.hpp
@@ -1,13 +1,13 @@ #ifndef _RIVE_KEY_FRAME_ID_BASE_HPP_ #define _RIVE_KEY_FRAME_ID_BASE_HPP_ -#include "rive/animation/keyframe.hpp" +#include "rive/animation/interpolating_keyframe.hpp" #include "rive/core/field_types/core_uint_type.hpp" namespace rive { -class KeyFrameIdBase : public KeyFrame +class KeyFrameIdBase : public InterpolatingKeyFrame { protected: - typedef KeyFrame Super; + typedef InterpolatingKeyFrame Super; public: static const uint16_t typeKey = 50; @@ -19,6 +19,7 @@ switch (typeKey) { case KeyFrameIdBase::typeKey: + case InterpolatingKeyFrameBase::typeKey: case KeyFrameBase::typeKey: return true; default: @@ -49,7 +50,7 @@ void copy(const KeyFrameIdBase& object) { m_Value = object.m_Value; - KeyFrame::copy(object); + InterpolatingKeyFrame::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -60,7 +61,7 @@ m_Value = CoreUintType::deserialize(reader); return true; } - return KeyFrame::deserialize(propertyKey, reader); + return InterpolatingKeyFrame::deserialize(propertyKey, reader); } protected:
diff --git a/include/rive/generated/animation/keyframe_string_base.hpp b/include/rive/generated/animation/keyframe_string_base.hpp index cadab51..e1a54c7 100644 --- a/include/rive/generated/animation/keyframe_string_base.hpp +++ b/include/rive/generated/animation/keyframe_string_base.hpp
@@ -1,14 +1,14 @@ #ifndef _RIVE_KEY_FRAME_STRING_BASE_HPP_ #define _RIVE_KEY_FRAME_STRING_BASE_HPP_ #include <string> -#include "rive/animation/keyframe.hpp" +#include "rive/animation/interpolating_keyframe.hpp" #include "rive/core/field_types/core_string_type.hpp" namespace rive { -class KeyFrameStringBase : public KeyFrame +class KeyFrameStringBase : public InterpolatingKeyFrame { protected: - typedef KeyFrame Super; + typedef InterpolatingKeyFrame Super; public: static const uint16_t typeKey = 142; @@ -20,6 +20,7 @@ switch (typeKey) { case KeyFrameStringBase::typeKey: + case InterpolatingKeyFrameBase::typeKey: case KeyFrameBase::typeKey: return true; default: @@ -50,7 +51,7 @@ void copy(const KeyFrameStringBase& object) { m_Value = object.m_Value; - KeyFrame::copy(object); + InterpolatingKeyFrame::copy(object); } bool deserialize(uint16_t propertyKey, BinaryReader& reader) override @@ -61,7 +62,7 @@ m_Value = CoreStringType::deserialize(reader); return true; } - return KeyFrame::deserialize(propertyKey, reader); + return InterpolatingKeyFrame::deserialize(propertyKey, reader); } protected:
diff --git a/include/rive/generated/core_registry.hpp b/include/rive/generated/core_registry.hpp index 7984af9..f062ab5 100644 --- a/include/rive/generated/core_registry.hpp +++ b/include/rive/generated/core_registry.hpp
@@ -17,10 +17,12 @@ #include "rive/animation/cubic_value_interpolator.hpp" #include "rive/animation/entry_state.hpp" #include "rive/animation/exit_state.hpp" +#include "rive/animation/interpolating_keyframe.hpp" #include "rive/animation/keyed_object.hpp" #include "rive/animation/keyed_property.hpp" #include "rive/animation/keyframe.hpp" #include "rive/animation/keyframe_bool.hpp" +#include "rive/animation/keyframe_callback.hpp" #include "rive/animation/keyframe_color.hpp" #include "rive/animation/keyframe_double.hpp" #include "rive/animation/keyframe_id.hpp" @@ -252,6 +254,8 @@ return new NestedNumber(); case BlendState1DBase::typeKey: return new BlendState1D(); + case KeyFrameCallbackBase::typeKey: + return new KeyFrameCallback(); case NestedRemapAnimationBase::typeKey: return new NestedRemapAnimation(); case TransitionBoolConditionBase::typeKey: @@ -478,11 +482,11 @@ case KeyFrameBase::framePropertyKey: object->as<KeyFrameBase>()->frame(value); break; - case KeyFrameBase::interpolationTypePropertyKey: - object->as<KeyFrameBase>()->interpolationType(value); + case InterpolatingKeyFrameBase::interpolationTypePropertyKey: + object->as<InterpolatingKeyFrameBase>()->interpolationType(value); break; - case KeyFrameBase::interpolatorIdPropertyKey: - object->as<KeyFrameBase>()->interpolatorId(value); + case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: + object->as<InterpolatingKeyFrameBase>()->interpolatorId(value); break; case KeyFrameIdBase::valuePropertyKey: object->as<KeyFrameIdBase>()->value(value); @@ -1146,6 +1150,15 @@ break; } } + static void setCallback(Core* object, int propertyKey, CallbackData value) + { + switch (propertyKey) + { + case EventBase::triggerPropertyKey: + object->as<EventBase>()->trigger(value); + break; + } + } static std::string getString(Core* object, int propertyKey) { switch (propertyKey) @@ -1227,10 +1240,10 @@ return object->as<StateMachineListenerBase>()->listenerTypeValue(); case KeyFrameBase::framePropertyKey: return object->as<KeyFrameBase>()->frame(); - case KeyFrameBase::interpolationTypePropertyKey: - return object->as<KeyFrameBase>()->interpolationType(); - case KeyFrameBase::interpolatorIdPropertyKey: - return object->as<KeyFrameBase>()->interpolatorId(); + case InterpolatingKeyFrameBase::interpolationTypePropertyKey: + return object->as<InterpolatingKeyFrameBase>()->interpolationType(); + case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: + return object->as<InterpolatingKeyFrameBase>()->interpolatorId(); case KeyFrameIdBase::valuePropertyKey: return object->as<KeyFrameIdBase>()->value(); case ListenerBoolChangeBase::valuePropertyKey: @@ -1723,8 +1736,8 @@ case StateMachineListenerBase::targetIdPropertyKey: case StateMachineListenerBase::listenerTypeValuePropertyKey: case KeyFrameBase::framePropertyKey: - case KeyFrameBase::interpolationTypePropertyKey: - case KeyFrameBase::interpolatorIdPropertyKey: + case InterpolatingKeyFrameBase::interpolationTypePropertyKey: + case InterpolatingKeyFrameBase::interpolatorIdPropertyKey: case KeyFrameIdBase::valuePropertyKey: case ListenerBoolChangeBase::valuePropertyKey: case ListenerAlignTargetBase::targetIdPropertyKey: @@ -1950,6 +1963,16 @@ return -1; } } + static bool isCallback(uint32_t propertyKey) + { + switch (propertyKey) + { + case EventBase::triggerPropertyKey: + return true; + default: + return false; + } + } }; } // namespace rive
diff --git a/include/rive/generated/event_base.hpp b/include/rive/generated/event_base.hpp index b2d4017..dd04e08 100644 --- a/include/rive/generated/event_base.hpp +++ b/include/rive/generated/event_base.hpp
@@ -1,6 +1,7 @@ #ifndef _RIVE_EVENT_BASE_HPP_ #define _RIVE_EVENT_BASE_HPP_ #include "rive/container_component.hpp" +#include "rive/core/field_types/core_callback_type.hpp" namespace rive { class EventBase : public ContainerComponent @@ -28,6 +29,11 @@ uint16_t coreType() const override { return typeKey; } + static const uint16_t triggerPropertyKey = 395; + +public: + virtual void trigger(const CallbackData& value) = 0; + Core* clone() const override; protected:
diff --git a/src/animation/animation_state_instance.cpp b/src/animation/animation_state_instance.cpp index a2ed1ed..0739550 100644 --- a/src/animation/animation_state_instance.cpp +++ b/src/animation/animation_state_instance.cpp
@@ -1,5 +1,6 @@ #include "rive/animation/animation_state_instance.hpp" #include "rive/animation/animation_state.hpp" +#include "rive/animation/state_machine_instance.hpp" using namespace rive; @@ -23,9 +24,10 @@ // NOTE:: should we return bool here? we are not currently using the output of this, we are instead // using m_keepGoing directly. -void AnimationStateInstance::advance(float seconds, Span<SMIInput*>) +void AnimationStateInstance::advance(float seconds, StateMachineInstance* stateMachineInstance) { - m_KeepGoing = m_AnimationInstance.advance(seconds * state()->as<AnimationState>()->speed()); + m_KeepGoing = m_AnimationInstance.advance(seconds * state()->as<AnimationState>()->speed(), + stateMachineInstance); } void AnimationStateInstance::apply(float mix) { m_AnimationInstance.apply(mix); }
diff --git a/src/animation/blend_state_1d_instance.cpp b/src/animation/blend_state_1d_instance.cpp index 29e76da..6902273 100644 --- a/src/animation/blend_state_1d_instance.cpp +++ b/src/animation/blend_state_1d_instance.cpp
@@ -39,16 +39,16 @@ return idx; } -void BlendState1DInstance::advance(float seconds, Span<SMIInput*> inputs) +void BlendState1DInstance::advance(float seconds, StateMachineInstance* stateMachineInstance) { - BlendStateInstance<BlendState1D, BlendAnimation1D>::advance(seconds, inputs); + BlendStateInstance<BlendState1D, BlendAnimation1D>::advance(seconds, stateMachineInstance); auto blendState = state()->as<BlendState1D>(); float value = 0.0f; if (blendState->hasValidInputId()) { // TODO: https://github.com/rive-app/rive-cpp/issues/229 - auto inputInstance = inputs[blendState->inputId()]; + auto inputInstance = stateMachineInstance->input(blendState->inputId()); auto numberInput = static_cast<const SMINumber*>(inputInstance); value = numberInput->value(); }
diff --git a/src/animation/blend_state_direct_instance.cpp b/src/animation/blend_state_direct_instance.cpp index be42eaf..5c52763 100644 --- a/src/animation/blend_state_direct_instance.cpp +++ b/src/animation/blend_state_direct_instance.cpp
@@ -11,9 +11,11 @@ BlendStateInstance<BlendStateDirect, BlendAnimationDirect>(blendState, instance) {} -void BlendStateDirectInstance::advance(float seconds, Span<SMIInput*> inputs) +void BlendStateDirectInstance::advance(float seconds, StateMachineInstance* stateMachineInstance) { - BlendStateInstance<BlendStateDirect, BlendAnimationDirect>::advance(seconds, inputs); + BlendStateInstance<BlendStateDirect, BlendAnimationDirect>::advance(seconds, + stateMachineInstance); + for (auto& animation : m_AnimationInstances) { if (animation.blendAnimation()->blendSource() == @@ -24,7 +26,7 @@ } else { - auto inputInstance = inputs[animation.blendAnimation()->inputId()]; + auto inputInstance = stateMachineInstance->input(animation.blendAnimation()->inputId()); auto numberInput = static_cast<const SMINumber*>(inputInstance); auto value = numberInput->value(); animation.mix(std::min(1.0f, std::max(0.0f, value / 100.0f)));
diff --git a/src/animation/interpolating_keyframe.cpp b/src/animation/interpolating_keyframe.cpp new file mode 100644 index 0000000..5330538 --- /dev/null +++ b/src/animation/interpolating_keyframe.cpp
@@ -0,0 +1,20 @@ +#include "rive/animation/interpolating_keyframe.hpp" +#include "rive/animation/cubic_interpolator.hpp" +#include "rive/core_context.hpp" + +using namespace rive; + +StatusCode InterpolatingKeyFrame::onAddedDirty(CoreContext* context) +{ + if (interpolatorId() != -1) + { + auto coreObject = context->resolve(interpolatorId()); + if (coreObject == nullptr || !coreObject->is<CubicInterpolator>()) + { + return StatusCode::MissingObject; + } + m_interpolator = coreObject->as<CubicInterpolator>(); + } + + return StatusCode::Ok; +} \ No newline at end of file
diff --git a/src/animation/keyed_object.cpp b/src/animation/keyed_object.cpp index 6866579..c43e9de 100644 --- a/src/animation/keyed_object.cpp +++ b/src/animation/keyed_object.cpp
@@ -3,6 +3,7 @@ #include "rive/animation/linear_animation.hpp" #include "rive/artboard.hpp" #include "rive/importers/linear_animation_importer.hpp" +#include "rive/generated/core_registry.hpp" using namespace rive; @@ -11,7 +12,7 @@ void KeyedObject::addKeyedProperty(std::unique_ptr<KeyedProperty> property) { - m_KeyedProperties.push_back(std::move(property)); + m_keyedProperties.push_back(std::move(property)); } StatusCode KeyedObject::onAddedDirty(CoreContext* context) @@ -22,7 +23,7 @@ return StatusCode::MissingObject; } - for (auto& property : m_KeyedProperties) + for (auto& property : m_keyedProperties) { StatusCode code; if ((code = property->onAddedDirty(context)) != StatusCode::Ok) @@ -35,13 +36,27 @@ StatusCode KeyedObject::onAddedClean(CoreContext* context) { - for (auto& property : m_KeyedProperties) + for (auto& property : m_keyedProperties) { property->onAddedClean(context); } return StatusCode::Ok; } +void KeyedObject::reportKeyedCallbacks(KeyedCallbackReporter* reporter, + float secondsFrom, + float secondsTo) const +{ + for (const std::unique_ptr<KeyedProperty>& property : m_keyedProperties) + { + if (!CoreRegistry::isCallback(property->propertyKey())) + { + continue; + } + property->reportKeyedCallbacks(reporter, objectId(), secondsFrom, secondsTo); + } +} + void KeyedObject::apply(Artboard* artboard, float time, float mix) { Core* object = artboard->resolve(objectId()); @@ -49,8 +64,12 @@ { return; } - for (auto& property : m_KeyedProperties) + for (std::unique_ptr<KeyedProperty>& property : m_keyedProperties) { + if (CoreRegistry::isCallback(property->propertyKey())) + { + continue; + } property->apply(object, time, mix); } }
diff --git a/src/animation/keyed_property.cpp b/src/animation/keyed_property.cpp index 8aa58d5..b200df7 100644 --- a/src/animation/keyed_property.cpp +++ b/src/animation/keyed_property.cpp
@@ -1,6 +1,8 @@ #include "rive/animation/keyed_property.hpp" #include "rive/animation/keyed_object.hpp" #include "rive/animation/keyframe.hpp" +#include "rive/animation/interpolating_keyframe.hpp" +#include "rive/animation/keyed_callback_reporter.hpp" #include "rive/importers/import_stack.hpp" #include "rive/importers/keyed_object_importer.hpp" @@ -11,23 +13,22 @@ void KeyedProperty::addKeyFrame(std::unique_ptr<KeyFrame> keyframe) { - m_KeyFrames.push_back(std::move(keyframe)); + m_keyFrames.push_back(std::move(keyframe)); } -void KeyedProperty::apply(Core* object, float seconds, float mix) +int KeyedProperty::closestFrameIndex(float seconds, int exactOffset) const { - assert(!m_KeyFrames.empty()); - int idx = 0; int mid = 0; - float closestSeconds = 0.0f; + float closestSeconds = 0; int start = 0; - auto numKeyFrames = static_cast<int>(m_KeyFrames.size()); + auto numKeyFrames = static_cast<int>(m_keyFrames.size()); int end = numKeyFrames - 1; + while (start <= end) { mid = (start + end) >> 1; - closestSeconds = m_KeyFrames[mid]->seconds(); + closestSeconds = m_keyFrames[mid]->seconds(); if (closestSeconds < seconds) { start = mid + 1; @@ -38,23 +39,54 @@ } else { - idx = start = mid; - break; + return mid + exactOffset; } idx = start; } + return idx; +} + +void KeyedProperty::reportKeyedCallbacks(KeyedCallbackReporter* reporter, + uint32_t objectId, + float secondsFrom, + float secondsTo) const +{ + int idx = closestFrameIndex(secondsFrom, 1); + int idxTo = closestFrameIndex(secondsTo, 1); + + if (idxTo < idx) + { + auto swap = idx; + idx = idxTo; + idxTo = swap; + } + while (idxTo > idx) + { + const std::unique_ptr<KeyFrame>& frame = m_keyFrames[idx]; + reporter->reportKeyedCallback(objectId, propertyKey(), secondsTo - frame->seconds()); + idx++; + } +} + +void KeyedProperty::apply(Core* object, float seconds, float mix) +{ + assert(!m_keyFrames.empty()); + + int idx = closestFrameIndex(seconds); int pk = propertyKey(); if (idx == 0) { - m_KeyFrames[0]->apply(object, pk, mix); + static_cast<InterpolatingKeyFrame*>(m_keyFrames[0].get())->apply(object, pk, mix); } else { - if (idx < numKeyFrames) + if (idx < static_cast<int>(m_keyFrames.size())) { - KeyFrame* fromFrame = m_KeyFrames[idx - 1].get(); - KeyFrame* toFrame = m_KeyFrames[idx].get(); + InterpolatingKeyFrame* fromFrame = + static_cast<InterpolatingKeyFrame*>(m_keyFrames[idx - 1].get()); + InterpolatingKeyFrame* toFrame = + static_cast<InterpolatingKeyFrame*>(m_keyFrames[idx].get()); if (seconds == toFrame->seconds()) { toFrame->apply(object, pk, mix); @@ -73,7 +105,7 @@ } else { - m_KeyFrames[idx - 1]->apply(object, pk, mix); + static_cast<InterpolatingKeyFrame*>(m_keyFrames[idx - 1].get())->apply(object, pk, mix); } } } @@ -81,7 +113,7 @@ StatusCode KeyedProperty::onAddedDirty(CoreContext* context) { StatusCode code; - for (auto& keyframe : m_KeyFrames) + for (auto& keyframe : m_keyFrames) { if ((code = keyframe->onAddedDirty(context)) != StatusCode::Ok) { @@ -94,7 +126,7 @@ StatusCode KeyedProperty::onAddedClean(CoreContext* context) { StatusCode code; - for (auto& keyframe : m_KeyFrames) + for (auto& keyframe : m_keyFrames) { if ((code = keyframe->onAddedClean(context)) != StatusCode::Ok) {
diff --git a/src/animation/keyframe.cpp b/src/animation/keyframe.cpp index 671beb8..9989fd7 100644 --- a/src/animation/keyframe.cpp +++ b/src/animation/keyframe.cpp
@@ -7,22 +7,7 @@ using namespace rive; -StatusCode KeyFrame::onAddedDirty(CoreContext* context) -{ - if (interpolatorId() != -1) - { - auto coreObject = context->resolve(interpolatorId()); - if (coreObject == nullptr || !coreObject->is<CubicInterpolator>()) - { - return StatusCode::MissingObject; - } - m_Interpolator = coreObject->as<CubicInterpolator>(); - } - - return StatusCode::Ok; -} - -void KeyFrame::computeSeconds(int fps) { m_Seconds = frame() / (float)fps; } +void KeyFrame::computeSeconds(int fps) { m_seconds = frame() / (float)fps; } StatusCode KeyFrame::import(ImportStack& importStack) {
diff --git a/src/animation/keyframe_callback.cpp b/src/animation/keyframe_callback.cpp new file mode 100644 index 0000000..4989039 --- /dev/null +++ b/src/animation/keyframe_callback.cpp
@@ -0,0 +1,2 @@ +#include "rive/animation/keyframe_callback.hpp" +#include "rive/core_context.hpp"
diff --git a/src/animation/linear_animation.cpp b/src/animation/linear_animation.cpp index 1db8f58..bb38502 100644 --- a/src/animation/linear_animation.cpp +++ b/src/animation/linear_animation.cpp
@@ -1,5 +1,6 @@ #include "rive/animation/linear_animation.hpp" #include "rive/animation/keyed_object.hpp" +#include "rive/animation/keyed_callback_reporter.hpp" #include "rive/artboard.hpp" #include "rive/importers/artboard_importer.hpp" #include "rive/importers/import_stack.hpp" @@ -114,4 +115,14 @@ return direction == 0 ? localTime + startTime() : endTime() - localTime; } RIVE_UNREACHABLE(); +} + +void LinearAnimation::reportKeyedCallbacks(KeyedCallbackReporter* reporter, + float secondsFrom, + float secondsTo) const +{ + for (const auto& object : m_KeyedObjects) + { + object->reportKeyedCallbacks(reporter, secondsFrom, secondsTo); + } } \ No newline at end of file
diff --git a/src/animation/linear_animation_instance.cpp b/src/animation/linear_animation_instance.cpp index 7be5d6a..76049f5 100644 --- a/src/animation/linear_animation_instance.cpp +++ b/src/animation/linear_animation_instance.cpp
@@ -1,6 +1,7 @@ #include "rive/animation/linear_animation_instance.hpp" #include "rive/animation/linear_animation.hpp" #include "rive/animation/loop.hpp" +#include "rive/animation/keyed_callback_reporter.hpp" #include "rive/rive_counter.hpp" #include <cmath> #include <cassert> @@ -48,7 +49,7 @@ return more; } -bool LinearAnimationInstance::advance(float elapsedSeconds) +bool LinearAnimationInstance::advance(float elapsedSeconds, KeyedCallbackReporter* reporter) { const LinearAnimation& animation = *m_animation; float deltaSeconds = elapsedSeconds * animation.speed() * m_direction; @@ -67,7 +68,12 @@ // stop gap before we move spilled tracking into state machine logic. bool killSpilledTime = !this->keepGoing(); + float lastTime = m_time; m_time += deltaSeconds; + if (reporter != nullptr) + { + animation.reportKeyedCallbacks(reporter, lastTime, m_time); + } int fps = animation.fps(); float frames = m_time * fps;
diff --git a/src/animation/listener_fire_event.cpp b/src/animation/listener_fire_event.cpp index 39d2fd1..41e8dc6 100644 --- a/src/animation/listener_fire_event.cpp +++ b/src/animation/listener_fire_event.cpp
@@ -11,5 +11,5 @@ { return; } - stateMachineInstance->fireEvent(coreEvent->as<Event>()); + stateMachineInstance->reportEvent(coreEvent->as<Event>()); } \ No newline at end of file
diff --git a/src/animation/state_machine_instance.cpp b/src/animation/state_machine_instance.cpp index cc6c649..a01685d 100644 --- a/src/animation/state_machine_instance.cpp +++ b/src/animation/state_machine_instance.cpp
@@ -23,6 +23,8 @@ #include "rive/nested_artboard.hpp" #include "rive/rive_counter.hpp" #include "rive/shapes/shape.hpp" +#include "rive/core/field_types/core_callback_type.hpp" +#include "rive/generated/core_registry.hpp" #include <unordered_map> using namespace rive; @@ -69,20 +71,20 @@ } } - bool advance(float seconds, Span<SMIInput*> inputs) + bool advance(float seconds) { m_stateMachineChangedOnAdvance = false; - m_currentState->advance(seconds, inputs); + m_currentState->advance(seconds, m_stateMachineInstance); updateMix(seconds); if (m_stateFrom != nullptr && m_mix < 1.0f && !m_holdAnimationFrom) { // This didn't advance during our updateState, but it should now // that we realize we need to mix it in. - m_stateFrom->advance(seconds, inputs); + m_stateFrom->advance(seconds, m_stateMachineInstance); } - for (int i = 0; updateState(inputs, i != 0); i++) + for (int i = 0; updateState(i != 0); i++) { apply(); @@ -107,7 +109,7 @@ m_mix < 1.0f; } - bool updateState(Span<SMIInput*> inputs, bool ignoreTriggers) + bool updateState(bool ignoreTriggers) { // Don't allow changing state while a transition is taking place // (we're mixing one state onto another). @@ -118,12 +120,12 @@ m_waitingForExit = false; - if (tryChangeState(m_anyStateInstance, inputs, ignoreTriggers)) + if (tryChangeState(m_anyStateInstance, ignoreTriggers)) { return true; } - return tryChangeState(m_currentState, inputs, ignoreTriggers); + return tryChangeState(m_currentState, ignoreTriggers); } void fireEvents(StateMachineFireOccurance occurs, @@ -162,9 +164,7 @@ return true; } - bool tryChangeState(StateInstance* stateFromInstance, - Span<SMIInput*> inputs, - bool ignoreTriggers) + bool tryChangeState(StateInstance* stateFromInstance, bool ignoreTriggers) { if (stateFromInstance == nullptr) { @@ -175,7 +175,8 @@ for (size_t i = 0, length = stateFrom->transitionCount(); i < length; i++) { auto transition = stateFrom->transition(i); - auto allowed = transition->allowed(stateFromInstance, inputs, ignoreTriggers); + auto allowed = + transition->allowed(stateFromInstance, m_stateMachineInstance, ignoreTriggers); if (allowed == AllowTransition::yes && changeState(transition->stateTo())) { m_stateMachineChangedOnAdvance = true; @@ -226,7 +227,7 @@ static_cast<AnimationStateInstance*>(m_stateFrom)->animationInstance(); auto spilledTime = instance->spilledTime(); - m_currentState->advance(spilledTime, inputs); + m_currentState->advance(spilledTime, m_stateMachineInstance); } m_mix = 0.0f; updateMix(0.0f); @@ -524,11 +525,11 @@ bool StateMachineInstance::advance(float seconds) { - m_firedEvents.clear(); + m_reportedEvents.clear(); m_needsAdvance = false; for (size_t i = 0; i < m_layerCount; i++) { - if (m_layers[i].advance(seconds, m_inputInstances)) + if (m_layers[i].advance(seconds)) { m_needsAdvance = true; } @@ -650,15 +651,27 @@ return nullptr; } -void StateMachineInstance::fireEvent(Event* event) { m_firedEvents.push_back(event); } - -std::size_t StateMachineInstance::firedEventCount() const { return m_firedEvents.size(); } - -const Event* StateMachineInstance::firedEventAt(std::size_t index) const +void StateMachineInstance::reportEvent(Event* event, float delaySeconds) { - if (index >= m_firedEvents.size()) - { - return nullptr; - } - return m_firedEvents[index]; + m_reportedEvents.push_back(EventReport(event, delaySeconds)); } + +std::size_t StateMachineInstance::reportedEventCount() const { return m_reportedEvents.size(); } + +const EventReport StateMachineInstance::reportedEventAt(std::size_t index) const +{ + if (index >= m_reportedEvents.size()) + { + return EventReport(nullptr, 0.0f); + } + return m_reportedEvents[index]; +} + +void StateMachineInstance::reportKeyedCallback(uint32_t objectId, + uint32_t propertyKey, + float elapsedSeconds) +{ + auto coreObject = m_artboardInstance->resolve(objectId); + CallbackData data(this, elapsedSeconds); + CoreRegistry::setCallback(coreObject, propertyKey, data); +} \ No newline at end of file
diff --git a/src/animation/state_transition.cpp b/src/animation/state_transition.cpp index 208790b..1d9bf08 100644 --- a/src/animation/state_transition.cpp +++ b/src/animation/state_transition.cpp
@@ -8,6 +8,7 @@ #include "rive/animation/state_transition.hpp" #include "rive/animation/transition_condition.hpp" #include "rive/animation/transition_trigger_condition.hpp" +#include "rive/animation/state_machine_instance.hpp" #include "rive/importers/import_stack.hpp" #include "rive/importers/layer_state_importer.hpp" @@ -136,7 +137,7 @@ } AllowTransition StateTransition::allowed(StateInstance* stateFrom, - Span<SMIInput*> inputs, + StateMachineInstance* stateMachineInstance, bool ignoreTriggers) const { if (isDisabled()) @@ -147,7 +148,7 @@ for (auto condition : m_Conditions) { // N.B. state machine instance sanitizes these for us... - auto input = inputs[condition->inputId()]; + auto input = stateMachineInstance->input(condition->inputId()); if ((ignoreTriggers && condition->is<TransitionTriggerCondition>()) || !condition->evaluate(input))
diff --git a/src/animation/system_state_instance.cpp b/src/animation/system_state_instance.cpp index 88e6194..92e28de 100644 --- a/src/animation/system_state_instance.cpp +++ b/src/animation/system_state_instance.cpp
@@ -5,7 +5,7 @@ StateInstance(layerState) {} -void SystemStateInstance::advance(float seconds, Span<SMIInput*>) {} +void SystemStateInstance::advance(float seconds, StateMachineInstance* stateMachineInstance) {} void SystemStateInstance::apply(float mix) {} bool SystemStateInstance::keepGoing() const { return false; } \ No newline at end of file
diff --git a/src/event.cpp b/src/event.cpp new file mode 100644 index 0000000..84be0ee --- /dev/null +++ b/src/event.cpp
@@ -0,0 +1,9 @@ +#include "rive/event.hpp" +#include "rive/animation/state_machine_instance.hpp" + +using namespace rive; + +void Event::trigger(const CallbackData& value) +{ + value.context()->reportEvent(this, value.delaySeconds()); +} \ No newline at end of file
diff --git a/src/generated/animation/keyframe_callback_base.cpp b/src/generated/animation/keyframe_callback_base.cpp new file mode 100644 index 0000000..d68eefb --- /dev/null +++ b/src/generated/animation/keyframe_callback_base.cpp
@@ -0,0 +1,11 @@ +#include "rive/generated/animation/keyframe_callback_base.hpp" +#include "rive/animation/keyframe_callback.hpp" + +using namespace rive; + +Core* KeyFrameCallbackBase::clone() const +{ + auto cloned = new KeyFrameCallback(); + cloned->copy(*this); + return cloned; +}
diff --git a/src/generated/animation/state_machine_fire_event.cpp b/src/generated/animation/state_machine_fire_event.cpp index f5f4d7b..9513eb7 100644 --- a/src/generated/animation/state_machine_fire_event.cpp +++ b/src/generated/animation/state_machine_fire_event.cpp
@@ -26,5 +26,5 @@ { return; } - stateMachineInstance->fireEvent(coreEvent->as<Event>()); + stateMachineInstance->reportEvent(coreEvent->as<Event>()); } \ No newline at end of file
diff --git a/test/animation_state_instance_test.cpp b/test/animation_state_instance_test.cpp index af648d4..29b8522 100644 --- a/test/animation_state_instance_test.cpp +++ b/test/animation_state_instance_test.cpp
@@ -1,9 +1,10 @@ -#include <rive/animation/loop.hpp> -#include <rive/animation/linear_animation.hpp> -#include <rive/animation/linear_animation_instance.hpp> +#include "rive/animation/loop.hpp" +#include "rive/animation/linear_animation.hpp" +#include "rive/animation/linear_animation_instance.hpp" -#include <rive/animation/animation_state.hpp> -#include <rive/animation/animation_state_instance.hpp> +#include "rive/animation/animation_state.hpp" +#include "rive/animation/animation_state_instance.hpp" +#include "rive/animation/state_machine_instance.hpp" #include "utils/no_op_factory.hpp" #include "rive/scene.hpp" #include <catch.hpp> @@ -30,8 +31,9 @@ new rive::AnimationStateInstance(animationState, abi.get()); // play from beginning. - std::vector<rive::SMIInput*> m_InputInstances; - animationStateInstance->advance(2.0, m_InputInstances); + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + animationStateInstance->advance(2.0, &stateMachineInstance); REQUIRE(animationStateInstance->animationInstance()->time() == 2.0); REQUIRE(animationStateInstance->animationInstance()->totalTime() == 2.0); @@ -49,6 +51,9 @@ rive::Artboard ab(&emptyFactory); auto abi = ab.instance(); + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + rive::LinearAnimation* linearAnimation = new rive::LinearAnimation(); // duration in seconds is 5 linearAnimation->duration(10); @@ -63,8 +68,7 @@ new rive::AnimationStateInstance(animationState, abi.get()); // play from beginning. - std::vector<rive::SMIInput*> m_InputInstances; - animationStateInstance->advance(2.0, m_InputInstances); + animationStateInstance->advance(2.0, &stateMachineInstance); REQUIRE(animationStateInstance->animationInstance()->time() == 4.0); REQUIRE(animationStateInstance->animationInstance()->totalTime() == 4.0); @@ -96,8 +100,9 @@ new rive::AnimationStateInstance(animationState, abi.get()); // play from beginning. - std::vector<rive::SMIInput*> m_InputInstances; - animationStateInstance->advance(2.0, m_InputInstances); + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + animationStateInstance->advance(2.0, &stateMachineInstance); REQUIRE(animationStateInstance->animationInstance()->time() == 1.0); REQUIRE(animationStateInstance->animationInstance()->totalTime() == 1.0); @@ -129,8 +134,9 @@ new rive::AnimationStateInstance(animationState, abi.get()); // play from beginning. - std::vector<rive::SMIInput*> m_InputInstances; - animationStateInstance->advance(2.0, m_InputInstances); + rive::StateMachine machine; + rive::StateMachineInstance stateMachineInstance(&machine, abi.get()); + animationStateInstance->advance(2.0, &stateMachineInstance); // backwards 2 seconds from 5. REQUIRE(animationStateInstance->animationInstance()->time() == 3.0);
diff --git a/test/assets/timeline_event_test.riv b/test/assets/timeline_event_test.riv new file mode 100644 index 0000000..cec3239 --- /dev/null +++ b/test/assets/timeline_event_test.riv Binary files differ
diff --git a/test/state_machine_event_test.cpp b/test/state_machine_event_test.cpp index a72e47d..b801a03 100644 --- a/test/state_machine_event_test.cpp +++ b/test/state_machine_event_test.cpp
@@ -150,20 +150,20 @@ REQUIRE(event->is<rive::Event>()); REQUIRE(event->as<rive::Event>()->name() == "Footstep"); - REQUIRE(stateMachineInstance->firedEventCount() == 0); + REQUIRE(stateMachineInstance->reportedEventCount() == 0); stateMachineInstance->pointerDown(rive::Vec2D(343.0f, 116.0f)); stateMachineInstance->pointerUp(rive::Vec2D(343.0f, 116.0f)); // There are two events on the listener. - REQUIRE(stateMachineInstance->firedEventCount() == 2); - auto firedEvent1 = stateMachineInstance->firedEventAt(0); - REQUIRE(firedEvent1->name() == "Footstep"); - auto firedEvent2 = stateMachineInstance->firedEventAt(1); - REQUIRE(firedEvent2->name() == "Event 3"); + REQUIRE(stateMachineInstance->reportedEventCount() == 2); + auto reportedEvent1 = stateMachineInstance->reportedEventAt(0); + REQUIRE(reportedEvent1.event()->name() == "Footstep"); + auto reportedEvent2 = stateMachineInstance->reportedEventAt(1); + REQUIRE(reportedEvent2.event()->name() == "Event 3"); - // After advancing again the firedEventCount should return to 0. + // After advancing again the reportedEventCount should return to 0. stateMachineInstance->advance(0.0f); - REQUIRE(stateMachineInstance->firedEventCount() == 0); + REQUIRE(stateMachineInstance->reportedEventCount() == 0); } TEST_CASE("events load correctly on a state and transition", "[events]") @@ -197,21 +197,47 @@ REQUIRE(transition->events().size() == 2); // First should've fired as we immediately went to Timeline 1. - REQUIRE(stateMachineInstance->firedEventCount() == 1); - REQUIRE(stateMachineInstance->firedEventAt(0)->name() == "First"); + REQUIRE(stateMachineInstance->reportedEventCount() == 1); + REQUIRE(stateMachineInstance->reportedEventAt(0).event()->name() == "First"); stateMachineInstance->advance(1.0f); // Exits after 2 seconds so 1 second in no events should've fired yet - REQUIRE(stateMachineInstance->firedEventCount() == 0); + REQUIRE(stateMachineInstance->reportedEventCount() == 0); stateMachineInstance->advance(1.0f); // At 2 seconds 2 events should fire, one for exiting the state and for taking the transition. - REQUIRE(stateMachineInstance->firedEventCount() == 2); - REQUIRE(stateMachineInstance->firedEventAt(0)->name() == "Second"); - REQUIRE(stateMachineInstance->firedEventAt(1)->name() == "Third"); + REQUIRE(stateMachineInstance->reportedEventCount() == 2); + REQUIRE(stateMachineInstance->reportedEventAt(0).event()->name() == "Second"); + REQUIRE(stateMachineInstance->reportedEventAt(1).event()->name() == "Third"); stateMachineInstance->advance(1.0f); // Another second in the transition should complete - REQUIRE(stateMachineInstance->firedEventCount() == 1); - REQUIRE(stateMachineInstance->firedEventAt(0)->name() == "Fourth"); + REQUIRE(stateMachineInstance->reportedEventCount() == 1); + REQUIRE(stateMachineInstance->reportedEventAt(0).event()->name() == "Fourth"); +} + +TEST_CASE("timeline events load correctly and report", "[events]") +{ + auto file = ReadRiveFile("../../test/assets/timeline_event_test.riv"); + + auto artboard = file->artboard()->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + auto stateMachineInstance = artboard->stateMachineAt(0); + REQUIRE(stateMachineInstance != nullptr); + + artboard->advance(0.0f); + stateMachineInstance->advance(0.0f); + REQUIRE(stateMachineInstance->reportedEventCount() == 0); + + stateMachineInstance->advance(0.4f); + REQUIRE(stateMachineInstance->reportedEventCount() == 0); + + stateMachineInstance->advance(0.2f); + REQUIRE(stateMachineInstance->reportedEventCount() == 1); + REQUIRE(stateMachineInstance->reportedEventAt(0).event()->name() == "Half"); + + // Event should've occurred right at 0.5 seconds. + REQUIRE(stateMachineInstance->reportedEventAt(0).secondsDelay() == Approx(0.1f)); }