'submodule/src/math/aabb.cpp',
'submodule/src/math/vec2d.cpp',
'submodule/src/math/mat2d.cpp',
+ 'submodule/src/animation/animation_state.cpp',
+ 'submodule/src/animation/animation_state_instance.cpp',
+ 'submodule/src/animation/blend_animation.cpp',
+ 'submodule/src/animation/blend_animation_1d.cpp',
+ 'submodule/src/animation/blend_animation_direct.cpp',
+ 'submodule/src/animation/blend_state.cpp',
+ 'submodule/src/animation/blend_state_1d.cpp',
+ 'submodule/src/animation/blend_state_1d_instance.cpp',
+ 'submodule/src/animation/blend_state_direct.cpp',
+ 'submodule/src/animation/blend_state_direct_instance.cpp',
+ 'submodule/src/animation/blend_state_transition.cpp',
'submodule/src/animation/cubic_interpolator.cpp',
'submodule/src/animation/keyed_object.cpp',
'submodule/src/animation/keyed_property.cpp',
'submodule/src/animation/linear_animation.cpp',
'submodule/src/animation/linear_animation_instance.cpp',
'submodule/src/animation/layer_state.cpp',
+ 'submodule/src/animation/state_instance.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_transition.cpp',
+ 'submodule/src/animation/system_state_instance.cpp',
'submodule/src/animation/transition_bool_condition.cpp',
'submodule/src/animation/transition_condition.cpp',
'submodule/src/animation/transition_number_condition.cpp',
'submodule/src/layout.cpp',
'submodule/src/node.cpp',
'submodule/src/transform_component.cpp',
+ 'submodule/src/generated/artboard_base.cpp',
+ 'submodule/src/generated/backboard_base.cpp',
+ 'submodule/src/generated/draw_rules_base.cpp',
+ 'submodule/src/generated/draw_target_base.cpp',
+ 'submodule/src/generated/node_base.cpp',
+ 'submodule/src/generated/animation/animation_base.cpp',
+ 'submodule/src/generated/animation/linear_animation_base.cpp',
+ 'submodule/src/generated/animation/animation_state_base.cpp',
+ 'submodule/src/generated/animation/state_machine_base.cpp',
+ 'submodule/src/generated/animation/any_state_base.cpp',
+ 'submodule/src/generated/animation/state_machine_bool_base.cpp',
+ 'submodule/src/generated/animation/cubic_interpolator_base.cpp',
+ 'submodule/src/generated/animation/state_machine_layer_base.cpp',
+ 'submodule/src/generated/animation/entry_state_base.cpp',
+ 'submodule/src/generated/animation/state_machine_number_base.cpp',
+ 'submodule/src/generated/animation/exit_state_base.cpp',
+ 'submodule/src/generated/animation/state_machine_trigger_base.cpp',
+ 'submodule/src/generated/animation/keyed_object_base.cpp',
+ 'submodule/src/generated/animation/state_transition_base.cpp',
+ 'submodule/src/generated/animation/keyed_property_base.cpp',
+ 'submodule/src/generated/animation/transition_bool_condition_base.cpp',
+ 'submodule/src/generated/animation/keyframe_color_base.cpp',
+ 'submodule/src/generated/animation/transition_number_condition_base.cpp',
+ 'submodule/src/generated/animation/keyframe_double_base.cpp',
+ 'submodule/src/generated/animation/transition_trigger_condition_base.cpp',
+ 'submodule/src/generated/animation/keyframe_id_base.cpp',
+ 'submodule/src/generated/bones/bone_base.cpp',
+ 'submodule/src/generated/bones/cubic_weight_base.cpp',
+ 'submodule/src/generated/bones/root_bone_base.cpp',
+ 'submodule/src/generated/bones/skin_base.cpp',
+ 'submodule/src/generated/bones/tendon_base.cpp',
+ 'submodule/src/generated/bones/weight_base.cpp',
+ 'submodule/src/generated/shapes/clipping_shape_base.cpp',
+ 'submodule/src/generated/shapes/cubic_asymmetric_vertex_base.cpp',
+ 'submodule/src/generated/shapes/cubic_detached_vertex_base.cpp',
+ 'submodule/src/generated/shapes/cubic_mirrored_vertex_base.cpp',
+ 'submodule/src/generated/shapes/ellipse_base.cpp',
+ 'submodule/src/generated/shapes/points_path_base.cpp',
+ 'submodule/src/generated/shapes/polygon_base.cpp',
+ 'submodule/src/generated/shapes/rectangle_base.cpp',
+ 'submodule/src/generated/shapes/shape_base.cpp',
+ 'submodule/src/generated/shapes/star_base.cpp',
+ 'submodule/src/generated/shapes/straight_vertex_base.cpp',
+ 'submodule/src/generated/shapes/triangle_base.cpp',
+ 'submodule/src/generated/shapes/paint/fill_base.cpp',
+ 'submodule/src/generated/shapes/paint/gradient_stop_base.cpp',
+ 'submodule/src/generated/shapes/paint/linear_gradient_base.cpp',
+ 'submodule/src/generated/shapes/paint/radial_gradient_base.cpp',
+ 'submodule/src/generated/shapes/paint/solid_color_base.cpp',
+ 'submodule/src/generated/shapes/paint/stroke_base.cpp',
+ 'submodule/src/generated/shapes/paint/trim_path_base.cpp',
]
rive_cpp_dep = declare_dependency(
--- /dev/null
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [{
+ "label": "run tests",
+ "type": "shell",
+ "command": "cd dev && ./test.sh",
+ "group": "test",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ },
+ {
+ "label": "gen assembly",
+ "type": "shell",
+ "command": "mkdir -p ${workspaceFolder}/build/bin/assembly/${fileDirname}/ && clang++ -O3 -g -std=c++17 -o ${workspaceFolder}/build/bin/assembly/${fileDirname}/${fileBasenameNoExtension}.S -Wall -fno-exceptions -fno-rtti -I${workspaceFolder}/include -S ${file}",
+ "group": "test",
+ "presentation": {
+ "reveal": "silent",
+ "panel": "new"
+ }
+ },
+ {
+ "label": "disassemble",
+ "command": "${command:disasexpl.show}",
+ "dependsOn": ["gen assembly"],
+ "problemMatcher": [],
+ "presentation": {
+ "reveal": "silent",
+ "panel": "new"
+ }
+ }
+ ]
+}
\ No newline at end of file
String defsPath = "./defs/";
String generatedHppPath = "../include/generated/";
String concreteHppPath = "../include/";
+String generatedCppPath = "../src/generated/";
String get localCodeFilename => '${stripExtension(_filename)}_base.hpp';
String get concreteCodeFilename => '${stripExtension(_filename)}.hpp';
- String get codeFilename => 'lib/src/generated/$localCodeFilename';
+ String get localCppCodeFilename => '${stripExtension(_filename)}_base.cpp';
/// Generates cpp header code based on the Definition
Future<void> generateCode() async {
}
}
+ if (!_isAbstract) {
+ code.writeln('Core* clone() const override;');
+ }
+
if (properties.isNotEmpty || _extensionOf == null) {
+ code.writeln('void copy(const ${_name}Base& object) {');
+ for (final property in properties) {
+ code.writeln('m_${property.capitalizedName} = '
+ 'object.m_${property.capitalizedName};');
+ }
+ if (_extensionOf != null) {
+ code.writeln('${_extensionOf.name}::'
+ 'copy(object); ');
+ }
+ code.writeln('}');
+ code.writeln();
+
code.writeln('bool deserialize(uint16_t propertyKey, '
'BinaryReader& reader) override {');
await _formatter.formatAndGuard(_name, concreteCode.toString());
concreteFile.writeAsStringSync(formattedCode, flush: true);
}
+ if (!_isAbstract) {
+ StringBuffer cppCode = StringBuffer();
+ cppCode.writeln('#include "generated/$localCodeFilename"');
+ cppCode.writeln('#include "$concreteCodeFilename"');
+ cppCode.writeln();
+ cppCode.writeln('using namespace rive;');
+ cppCode.writeln();
+ cppCode.writeln('Core* ${_name}Base::clone() const { '
+ 'auto cloned = new $_name(); '
+ 'cloned->copy(*this); '
+ 'return cloned; '
+ '}');
+ var cppFile = File('$generatedCppPath$localCppCodeFilename');
+ cppFile.createSync(recursive: true);
+ var formattedCode = await _formatter.format(cppCode.toString());
+ cppFile.writeAsStringSync(formattedCode, flush: true);
+ }
}
@override
--- /dev/null
+{
+ "name": "BlendAnimation",
+ "key": {
+ "int": 74,
+ "string": "blendanimation"
+ },
+ "abstract": true,
+ "properties": {
+ "blendStateId": {
+ "type": "Id",
+ "typeRuntime": "uint",
+ "initialValue": "Core.missingId",
+ "initialValueRuntime": "-1",
+ "key": {
+ "int": 170,
+ "string": "blendstateid"
+ },
+ "description": "Id of the BlendState that this BlendAnimation belongs to.",
+ "runtime": false
+ },
+ "animationId": {
+ "type": "Id",
+ "typeRuntime": "uint",
+ "initialValue": "Core.missingId",
+ "initialValueRuntime": "-1",
+ "key": {
+ "int": 165,
+ "string": "animationid"
+ },
+ "description": "Id of the animation this BlendAnimation references."
+ },
+ "animationOrder": {
+ "type": "FractionalIndex",
+ "initialValue": "FractionalIndex.invalid",
+ "key": {
+ "int": 169,
+ "string": "animationorder"
+ },
+ "description": "Order value for sorting animations in blend states.",
+ "runtime": false
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "BlendAnimation1D",
+ "key": {
+ "int": 75,
+ "string": "blendanimation1d"
+ },
+ "extends": "animation/blend_animation.json",
+ "properties": {
+ "value": {
+ "type": "double",
+ "initialValue": "0",
+ "key": {
+ "int": 166,
+ "string": "value"
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "BlendAnimationDirect",
+ "key": {
+ "int": 77,
+ "string": "blendanimationdirect"
+ },
+ "extends": "animation/blend_animation.json",
+ "properties": {
+ "inputId": {
+ "type": "Id",
+ "typeRuntime": "uint",
+ "initialValue": "Core.missingId",
+ "initialValueRuntime": "-1",
+ "key": {
+ "int": 168,
+ "string": "inputid"
+ },
+ "description": "Id of the input that drives the direct mix value for this animation."
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "BlendState",
+ "key": {
+ "int": 72,
+ "string": "blendstate"
+ },
+ "abstract": true,
+ "extends": "animation/layer_state.json"
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "BlendState1D",
+ "key": {
+ "int": 76,
+ "string": "blendstate1d"
+ },
+ "extends": "animation/blend_state.json",
+ "generic": "animation/blend_animation_1d.json",
+ "properties": {
+ "inputId": {
+ "type": "Id",
+ "typeRuntime": "uint",
+ "initialValue": "Core.missingId",
+ "initialValueRuntime": "-1",
+ "key": {
+ "int": 167,
+ "string": "inputid"
+ },
+ "description": "Id of the input that drives the mix value for this blend state."
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "BlendStateDirect",
+ "key": {
+ "int": 73,
+ "string": "blendstatedirect"
+ },
+ "extends": "animation/blend_state.json",
+ "generic": "animation/blend_animation_direct.json"
+}
\ No newline at end of file
--- /dev/null
+{
+ "name": "BlendStateTransition",
+ "key": {
+ "int": 78,
+ "string": "blendstatetransition"
+ },
+ "extends": "animation/state_transition.json",
+ "properties": {
+ "exitBlendAnimationId": {
+ "type": "Id",
+ "typeRuntime": "uint",
+ "initialValue": "Core.missingId",
+ "initialValueRuntime": "-1",
+ "key": {
+ "int": 171,
+ "string": "exitblendanimationid"
+ },
+ "description": "Id of the state the blend state animation used for exit time calculation."
+ }
+ }
+}
\ No newline at end of file
echo Will perform memory checks...
UTILITY='valgrind --leak-check=full'
shift
+elif [ "$OPTION" = "debug" ]
+then
+ echo Starting debugger...
+ UTILITY='lldb'
+ shift
fi
premake5 gmake2 || exit 1
-- require "lfs"
-
-- Clean Function --
newaction {
- trigger = "clean",
+ trigger = "clean",
description = "clean the build",
- execute = function ()
- print("clean the build...")
- os.rmdir("build")
- os.remove("Makefile")
- -- no wildcards in os.remove, so use shell
- os.execute("rm *.make")
- print("build cleaned")
+ execute = function()
+ print("clean the build...")
+ os.rmdir("build")
+ os.remove("Makefile")
+ -- no wildcards in os.remove, so use shell
+ os.execute("rm *.make")
+ print("build cleaned")
end
- }
+}
workspace "rive_tests"
-configurations {
- "debug"
-}
+configurations {"debug"}
project("tests")
kind "ConsoleApp"
targetdir "build/bin/%{cfg.buildcfg}"
objdir "build/obj/%{cfg.buildcfg}"
-buildoptions {
- "-Wall",
- "-fno-exceptions",
- "-fno-rtti"
-}
+buildoptions {"-Wall", "-fno-exceptions", "-fno-rtti"}
-includedirs {
- "./include",
- "../../include"
-}
+includedirs {"./include", "../../include"}
-files {
- "../../src/**.cpp", -- the Rive runtime source
- "../../test/**.cpp" -- the tests
-
+files {"../../src/**.cpp", -- the Rive runtime source
+"../../test/**.cpp" -- the tests
}
-defines { "TESTING" }
+defines {"TESTING", "ENABLE_QUERY_FLAT_VERTICES"}
filter "configurations:debug"
- defines { "DEBUG" }
- symbols "On"
-
+defines {"DEBUG"}
+symbols "On"
--[[
test_precompiled(cppFile)
end
---]]
\ No newline at end of file
+--]]
{
class Animation : public AnimationBase
{
- public:
- StatusCode onAddedDirty(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
};
} // namespace rive
friend class StateMachineLayerImporter;
private:
- LinearAnimation* m_Animation;
+ LinearAnimation* m_Animation = nullptr;
public:
const LinearAnimation* animation() const { return m_Animation; }
+ StateInstance* makeInstance() const override;
};
} // namespace rive
--- /dev/null
+#ifndef _RIVE_ANIMATION_STATE_INSTANCE_HPP_
+#define _RIVE_ANIMATION_STATE_INSTANCE_HPP_
+
+#include <string>
+#include "animation/state_instance.hpp"
+#include "animation/linear_animation_instance.hpp"
+
+namespace rive
+{
+ class AnimationState;
+
+ /// Represents an instance of an animation state.
+ class AnimationStateInstance : public StateInstance
+ {
+ private:
+ LinearAnimationInstance m_AnimationInstance;
+ bool m_KeepGoing;
+
+ public:
+ AnimationStateInstance(const AnimationState* animationState);
+
+ void advance(float seconds, SMIInput** inputs) override;
+ void apply(Artboard* artboard, float mix) override;
+
+ bool keepGoing() const override;
+
+ const LinearAnimationInstance* animationInstance() const
+ {
+ return &m_AnimationInstance;
+ }
+
+ LinearAnimationInstance* animationInstance()
+ {
+ return &m_AnimationInstance;
+ }
+ };
+} // namespace rive
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_ANIMATION_HPP_
+#define _RIVE_BLEND_ANIMATION_HPP_
+#include "generated/animation/blend_animation_base.hpp"
+namespace rive
+{
+ class LinearAnimation;
+ class BlendAnimation : public BlendAnimationBase
+ {
+ private:
+ LinearAnimation* m_Animation = nullptr;
+
+ public:
+ const LinearAnimation* animation() const { return m_Animation; }
+ StatusCode import(ImportStack& importStack) override;
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_ANIMATION1_D_HPP_
+#define _RIVE_BLEND_ANIMATION1_D_HPP_
+#include "generated/animation/blend_animation_1d_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+ class BlendAnimation1D : public BlendAnimation1DBase
+ {
+ public:
+ StatusCode onAddedDirty(CoreContext* context) override;
+ StatusCode onAddedClean(CoreContext* context) override;
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_ANIMATION_DIRECT_HPP_
+#define _RIVE_BLEND_ANIMATION_DIRECT_HPP_
+#include "generated/animation/blend_animation_direct_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+ class BlendAnimationDirect : public BlendAnimationDirectBase
+ {
+ public:
+ StatusCode onAddedDirty(CoreContext* context) override;
+ StatusCode onAddedClean(CoreContext* context) override;
+ StatusCode import(ImportStack& importStack) override;
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_HPP_
+#define _RIVE_BLEND_STATE_HPP_
+#include "generated/animation/blend_state_base.hpp"
+#include <stdio.h>
+#include <vector>
+#include <algorithm>
+
+namespace rive
+{
+ class BlendAnimation;
+ class LayerStateImporter;
+
+ class BlendState : public BlendStateBase
+ {
+ friend class LayerStateImporter;
+
+ private:
+ std::vector<BlendAnimation*> m_Animations;
+ void addAnimation(BlendAnimation* animation);
+
+ public:
+ inline const std::vector<BlendAnimation*>& animations() const
+ {
+ return m_Animations;
+ }
+
+#ifdef TESTING
+ size_t animationCount() { return m_Animations.size(); }
+ BlendAnimation* animation(size_t index) { return m_Animations[index]; }
+#endif
+ };
+} // namespace rive
+
+#endif
--- /dev/null
+#ifndef _RIVE_BLEND_STATE1_D_HPP_
+#define _RIVE_BLEND_STATE1_D_HPP_
+#include "generated/animation/blend_state_1d_base.hpp"
+
+namespace rive
+{
+ class BlendState1D : public BlendState1DBase
+ {
+ public:
+ StatusCode import(ImportStack& importStack) override;
+
+ StateInstance* makeInstance() const override;
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_1D_INSTANCE_HPP_
+#define _RIVE_BLEND_STATE_1D_INSTANCE_HPP_
+
+#include "animation/blend_state_instance.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_animation_1d.hpp"
+
+namespace rive
+{
+ class BlendState1DInstance
+ : public BlendStateInstance<BlendState1D, BlendAnimation1D>
+ {
+ private:
+ BlendStateAnimationInstance<BlendAnimation1D>* m_From = nullptr;
+ BlendStateAnimationInstance<BlendAnimation1D>* m_To = nullptr;
+ int animationIndex(float value);
+
+ public:
+ BlendState1DInstance(const BlendState1D* blendState);
+ void advance(float seconds, SMIInput** inputs) override;
+ };
+} // namespace rive
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_DIRECT_HPP_
+#define _RIVE_BLEND_STATE_DIRECT_HPP_
+#include "generated/animation/blend_state_direct_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+ class BlendStateDirect : public BlendStateDirectBase
+ {
+ public:
+ StateInstance* makeInstance() const override;
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_DIRECT_INSTANCE_HPP_
+#define _RIVE_BLEND_STATE_DIRECT_INSTANCE_HPP_
+
+#include "animation/blend_state_instance.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_animation_direct.hpp"
+
+namespace rive
+{
+ class BlendStateDirectInstance
+ : public BlendStateInstance<BlendStateDirect, BlendAnimationDirect>
+ {
+ public:
+ BlendStateDirectInstance(const BlendStateDirect* blendState);
+ void advance(float seconds, SMIInput** inputs) override;
+ };
+} // namespace rive
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_INSTANCE_HPP_
+#define _RIVE_BLEND_STATE_INSTANCE_HPP_
+
+#include <string>
+#include <vector>
+#include "animation/state_instance.hpp"
+#include "animation/blend_state.hpp"
+#include "animation/linear_animation_instance.hpp"
+
+namespace rive
+{
+ class AnimationState;
+
+ template <class K, class T> class BlendStateInstance;
+ template <class T> class BlendStateAnimationInstance
+ {
+ template <class A, class B> friend class BlendStateInstance;
+
+ private:
+ const T* m_BlendAnimation;
+ LinearAnimationInstance m_AnimationInstance;
+ float m_Mix = 0.0f;
+
+ public:
+ const T* blendAnimation() const { return m_BlendAnimation; }
+ const LinearAnimationInstance* animationInstance() const
+ {
+ return &m_AnimationInstance;
+ }
+
+ BlendStateAnimationInstance(const T* blendAnimation) :
+ m_BlendAnimation(blendAnimation),
+ m_AnimationInstance(blendAnimation->animation())
+ {
+ }
+
+ void mix(float value) { m_Mix = value; }
+ };
+
+ template <class K, class T> class BlendStateInstance : public StateInstance
+ {
+ protected:
+ std::vector<BlendStateAnimationInstance<T>> m_AnimationInstances;
+ bool m_KeepGoing = true;
+
+ public:
+ BlendStateInstance(const K* blendState) : StateInstance(blendState)
+ {
+ for (auto blendAnimation : blendState->animations())
+ {
+ m_AnimationInstances.emplace_back(
+ BlendStateAnimationInstance<T>(
+ static_cast<T*>(blendAnimation)));
+ }
+ }
+
+ bool keepGoing() const override { return m_KeepGoing; }
+
+ void advance(float seconds, SMIInput** inputs) override
+ {
+ m_KeepGoing = false;
+ for (auto& animation : m_AnimationInstances)
+ {
+ if (animation.m_AnimationInstance.advance(seconds))
+ {
+ m_KeepGoing = true;
+ }
+ }
+ }
+
+ void apply(Artboard* artboard, float mix) override
+ {
+ for (auto& animation : m_AnimationInstances)
+ {
+ float m = mix * animation.m_Mix;
+ animation.m_AnimationInstance.apply(artboard, m);
+ }
+ }
+
+ // Find the animationInstance that corresponds to the blendAnimation.
+ const LinearAnimationInstance*
+ animationInstance(const BlendAnimation* blendAnimation) const
+ {
+ for (auto& animation : m_AnimationInstances)
+ {
+ if (animation.m_BlendAnimation == blendAnimation)
+ {
+ return animation.animationInstance();
+ }
+ }
+ return nullptr;
+ }
+ };
+} // namespace rive
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_TRANSITION_HPP_
+#define _RIVE_BLEND_STATE_TRANSITION_HPP_
+#include "generated/animation/blend_state_transition_base.hpp"
+#include <stdio.h>
+namespace rive
+{
+ class BlendAnimation;
+ class LayerStateImporter;
+ class BlendStateTransition : public BlendStateTransitionBase
+ {
+ friend class LayerStateImporter;
+
+ private:
+ BlendAnimation* m_ExitBlendAnimation = nullptr;
+
+ public:
+ BlendAnimation* exitBlendAnimation() const
+ {
+ return m_ExitBlendAnimation;
+ }
+
+ const LinearAnimationInstance*
+ exitTimeAnimationInstance(const StateInstance* from) const override;
+
+ const LinearAnimation*
+ exitTimeAnimation(const LayerState* from) const override;
+ };
+
+} // namespace rive
+
+#endif
\ No newline at end of file
float getT(float x) const;
public:
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
StatusCode onAddedDirty(CoreContext* context) override;
/// Convert a linear interpolation factor to an eased one.
void computeSeconds(int fps);
StatusCode onAddedDirty(CoreContext* context) override;
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
-
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;
};
} // namespace rive
class StateTransition;
class LayerStateImporter;
class StateMachineLayerImporter;
+ class StateInstance;
class LayerState : public LayerStateBase
{
}
return nullptr;
}
+
+ /// Make an instance of this state that can be advanced and applied by
+ /// the state machine when it is active or being transitioned from.
+ virtual StateInstance* makeInstance() const;
};
} // namespace rive
--- /dev/null
+#ifndef _RIVE_STATE_INSTANCE_HPP_
+#define _RIVE_STATE_INSTANCE_HPP_
+
+#include <string>
+#include <stddef.h>
+
+namespace rive
+{
+ class LayerState;
+ class SMIInput;
+ class Artboard;
+
+ /// Represents an instance of a state tracked by the State Machine.
+ class StateInstance
+ {
+ private:
+ const LayerState* m_LayerState;
+
+ public:
+ StateInstance(const LayerState* layerState);
+ virtual ~StateInstance();
+ virtual void advance(float seconds, SMIInput** inputs) = 0;
+ virtual void apply(Artboard* artboard, float mix) = 0;
+
+ /// Returns true when the State Machine needs to keep advancing this
+ /// state.
+ virtual bool keepGoing() const = 0;
+
+ const LayerState* state() const;
+ };
+} // namespace rive
+#endif
\ No newline at end of file
// 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;
+ bool advance(Artboard* artboard, float seconds);
// Returns true when the StateMachineInstance has more data to process.
bool needsAdvance() const;
+ // Returns a pointer to the instance's stateMachine
+ const StateMachine* stateMachine() const { return m_Machine; }
+
size_t inputCount() const { return m_InputCount; }
SMIInput* input(size_t index) const;
SMITrigger* getTrigger(std::string name) const;
const size_t currentAnimationCount() const;
- const LinearAnimationInstance* currentAnimationByIndex(size_t index) const;
+ const LinearAnimationInstance*
+ currentAnimationByIndex(size_t index) const;
// The number of state changes that occurred across all layers on the
// previous advance.
class StateMachineLayerImporter;
class StateTransitionImporter;
class TransitionCondition;
+ class StateInstance;
+ class SMIInput;
+ class LinearAnimation;
+ class LinearAnimationInstance;
+
+ enum class AllowTransition : unsigned char
+ {
+ no,
+ waitingForExit,
+ yes
+ };
+
class StateTransition : public StateTransitionBase
{
friend class StateMachineLayerImporter;
StateTransitionFlags::Disabled;
}
+ /// Returns AllowTransition::yes when this transition can be taken from
+ /// stateFrom with the given inputs.
+ AllowTransition allowed(StateInstance* stateFrom,
+ SMIInput** inputs,
+ bool ignoreTriggers) const;
+
/// Whether the animation is held at exit or if it keeps advancing
/// during mixing.
bool pauseOnExit() const
/// 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.
+ /// Computes the exit time in seconds of the stateFrom. Set absolute to
+ /// true if you want the returned time to be relative to the entire
+ /// animation. Set absolute to false if you want it relative to the work
+ /// area.
float exitTimeSeconds(const LayerState* stateFrom,
- bool relativeToWorkArea) const;
+ bool absolute = false) const;
+
+ /// Provide the animation instance to use for computing percentage
+ /// durations for exit time.
+ virtual const LinearAnimationInstance*
+ exitTimeAnimationInstance(const StateInstance* from) const;
+
+ /// Provide the animation to use for computing percentage durations for
+ /// exit time.
+ virtual const LinearAnimation*
+ exitTimeAnimation(const LayerState* from) const;
+
+ /// Retruns true when we need to hold the exit time, also applies the
+ /// correct time to the animation instance in the stateFrom, when
+ /// applicable (when it's an AnimationState).
+ bool applyExitCondition(StateInstance* stateFrom) const;
};
} // namespace rive
--- /dev/null
+#ifndef _RIVE_SYSTEM_STATE_INSTANCE_HPP_
+#define _RIVE_SYSTEM_STATE_INSTANCE_HPP_
+
+#include <string>
+#include "animation/state_instance.hpp"
+
+namespace rive
+{
+ /// 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).
+ class SystemStateInstance : public StateInstance
+ {
+ public:
+ SystemStateInstance(const LayerState* layerState);
+
+ void advance(float seconds, SMIInput** inputs) override;
+ void apply(Artboard* artboard, float mix) override;
+
+ bool keepGoing() const override;
+ };
+} // namespace rive
+#endif
\ No newline at end of file
Core* resolve(int id) const override;
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
void onComponentDirty(Component* component);
/// Update components that depend on each other in DAG order.
{
class Backboard : public BackboardBase
{
- public:
- StatusCode onAddedDirty(CoreContext* context) { return StatusCode::Ok; }
- StatusCode onAddedClean(CoreContext* context) { return StatusCode::Ok; }
};
} // namespace rive
public:
Vec2D& translation() { return m_Translation; }
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
StatusCode onAddedDirty(CoreContext* context) override;
static void deform(float x,
virtual bool deserialize(uint16_t propertyKey,
BinaryReader& reader) = 0;
- template <typename T> bool is() const { return isTypeOf(T::typeKey); }
- template <typename T> T* as()
+ template <typename T> inline bool is() const
+ {
+ return isTypeOf(T::typeKey);
+ }
+ template <typename T> inline T* as()
{
assert(is<T>());
return reinterpret_cast<T*>(this);
}
- template <typename T> const T* as() const
+ virtual Core* clone() const { return nullptr; }
+
+ template <typename T> inline const T* as() const
{
assert(is<T>());
return reinterpret_cast<const T*>(this);
/// to look up objects referenced by id, but not assume that they in
/// turn have resolved their references yet. Called during
/// load/instance.
- virtual StatusCode onAddedDirty(CoreContext* context) = 0;
+ virtual StatusCode onAddedDirty(CoreContext* context)
+ {
+ return StatusCode::Ok;
+ }
/// Called when all the objects in the context have had onAddedDirty
/// called. This is an opportunity to reference things referenced by
/// dependencies. (A path should be able to find a Shape somewhere in
/// its hierarchy, which may be multiple levels up).
- virtual StatusCode onAddedClean(CoreContext* context) = 0;
+ virtual StatusCode onAddedClean(CoreContext* context)
+ {
+ return StatusCode::Ok;
+ }
virtual StatusCode import(ImportStack& importStack)
{
nameChanged();
}
+ Core* clone() const override;
+ void copy(const AnimationBase& object) { m_Name = object.m_Name; }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
animationIdChanged();
}
+ Core* clone() const override;
+ void copy(const AnimationStateBase& object)
+ {
+ m_AnimationId = object.m_AnimationId;
+ LayerState::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
--- /dev/null
+#ifndef _RIVE_BLEND_ANIMATION1_DBASE_HPP_
+#define _RIVE_BLEND_ANIMATION1_DBASE_HPP_
+#include "animation/blend_animation.hpp"
+#include "core/field_types/core_double_type.hpp"
+namespace rive
+{
+ class BlendAnimation1DBase : public BlendAnimation
+ {
+ protected:
+ typedef BlendAnimation Super;
+
+ public:
+ static const uint16_t typeKey = 75;
+
+ /// Helper to quickly determine if a core object extends another without
+ /// RTTI at runtime.
+ bool isTypeOf(uint16_t typeKey) const override
+ {
+ switch (typeKey)
+ {
+ case BlendAnimation1DBase::typeKey:
+ case BlendAnimationBase::typeKey:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ uint16_t coreType() const override { return typeKey; }
+
+ static const uint16_t valuePropertyKey = 166;
+
+ 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 BlendAnimation::deserialize(propertyKey, reader);
+ }
+
+ protected:
+ virtual void valueChanged() {}
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_ANIMATION_BASE_HPP_
+#define _RIVE_BLEND_ANIMATION_BASE_HPP_
+#include "core.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+ class BlendAnimationBase : public Core
+ {
+ protected:
+ typedef Core Super;
+
+ public:
+ static const uint16_t typeKey = 74;
+
+ /// Helper to quickly determine if a core object extends another without
+ /// RTTI at runtime.
+ bool isTypeOf(uint16_t typeKey) const override
+ {
+ switch (typeKey)
+ {
+ case BlendAnimationBase::typeKey:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ uint16_t coreType() const override { return typeKey; }
+
+ static const uint16_t animationIdPropertyKey = 165;
+
+ private:
+ int m_AnimationId = -1;
+ public:
+ inline int animationId() const { return m_AnimationId; }
+ void animationId(int value)
+ {
+ if (m_AnimationId == value)
+ {
+ return;
+ }
+ m_AnimationId = value;
+ animationIdChanged();
+ }
+
+ bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+ {
+ switch (propertyKey)
+ {
+ case animationIdPropertyKey:
+ m_AnimationId = CoreUintType::deserialize(reader);
+ return true;
+ }
+ return false;
+ }
+
+ protected:
+ virtual void animationIdChanged() {}
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_ANIMATION_DIRECT_BASE_HPP_
+#define _RIVE_BLEND_ANIMATION_DIRECT_BASE_HPP_
+#include "animation/blend_animation.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+ class BlendAnimationDirectBase : public BlendAnimation
+ {
+ protected:
+ typedef BlendAnimation Super;
+
+ public:
+ static const uint16_t typeKey = 77;
+
+ /// Helper to quickly determine if a core object extends another without
+ /// RTTI at runtime.
+ bool isTypeOf(uint16_t typeKey) const override
+ {
+ switch (typeKey)
+ {
+ case BlendAnimationDirectBase::typeKey:
+ case BlendAnimationBase::typeKey:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ uint16_t coreType() const override { return typeKey; }
+
+ static const uint16_t inputIdPropertyKey = 168;
+
+ private:
+ int m_InputId = -1;
+ public:
+ inline int inputId() const { return m_InputId; }
+ void inputId(int value)
+ {
+ if (m_InputId == value)
+ {
+ return;
+ }
+ m_InputId = value;
+ inputIdChanged();
+ }
+
+ bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+ {
+ switch (propertyKey)
+ {
+ case inputIdPropertyKey:
+ m_InputId = CoreUintType::deserialize(reader);
+ return true;
+ }
+ return BlendAnimation::deserialize(propertyKey, reader);
+ }
+
+ protected:
+ virtual void inputIdChanged() {}
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE1_DBASE_HPP_
+#define _RIVE_BLEND_STATE1_DBASE_HPP_
+#include "animation/blend_state.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+ class BlendState1DBase : public BlendState
+ {
+ protected:
+ typedef BlendState Super;
+
+ public:
+ static const uint16_t typeKey = 76;
+
+ /// Helper to quickly determine if a core object extends another without
+ /// RTTI at runtime.
+ bool isTypeOf(uint16_t typeKey) const override
+ {
+ switch (typeKey)
+ {
+ case BlendState1DBase::typeKey:
+ case BlendStateBase::typeKey:
+ case LayerStateBase::typeKey:
+ case StateMachineLayerComponentBase::typeKey:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ uint16_t coreType() const override { return typeKey; }
+
+ static const uint16_t inputIdPropertyKey = 167;
+
+ private:
+ int m_InputId = -1;
+ public:
+ inline int inputId() const { return m_InputId; }
+ void inputId(int value)
+ {
+ if (m_InputId == value)
+ {
+ return;
+ }
+ m_InputId = value;
+ inputIdChanged();
+ }
+
+ bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+ {
+ switch (propertyKey)
+ {
+ case inputIdPropertyKey:
+ m_InputId = CoreUintType::deserialize(reader);
+ return true;
+ }
+ return BlendState::deserialize(propertyKey, reader);
+ }
+
+ protected:
+ virtual void inputIdChanged() {}
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_BASE_HPP_
+#define _RIVE_BLEND_STATE_BASE_HPP_
+#include "animation/layer_state.hpp"
+namespace rive
+{
+ class BlendStateBase : public LayerState
+ {
+ protected:
+ typedef LayerState Super;
+
+ public:
+ static const uint16_t typeKey = 72;
+
+ /// Helper to quickly determine if a core object extends another without
+ /// RTTI at runtime.
+ bool isTypeOf(uint16_t typeKey) const override
+ {
+ switch (typeKey)
+ {
+ case BlendStateBase::typeKey:
+ case LayerStateBase::typeKey:
+ case StateMachineLayerComponentBase::typeKey:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ uint16_t coreType() const override { return typeKey; }
+
+ protected:
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_DIRECT_BASE_HPP_
+#define _RIVE_BLEND_STATE_DIRECT_BASE_HPP_
+#include "animation/blend_state.hpp"
+namespace rive
+{
+ class BlendStateDirectBase : public BlendState
+ {
+ protected:
+ typedef BlendState Super;
+
+ public:
+ static const uint16_t typeKey = 73;
+
+ /// Helper to quickly determine if a core object extends another without
+ /// RTTI at runtime.
+ bool isTypeOf(uint16_t typeKey) const override
+ {
+ switch (typeKey)
+ {
+ case BlendStateDirectBase::typeKey:
+ case BlendStateBase::typeKey:
+ case LayerStateBase::typeKey:
+ case StateMachineLayerComponentBase::typeKey:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ uint16_t coreType() const override { return typeKey; }
+
+ protected:
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef _RIVE_BLEND_STATE_TRANSITION_BASE_HPP_
+#define _RIVE_BLEND_STATE_TRANSITION_BASE_HPP_
+#include "animation/state_transition.hpp"
+#include "core/field_types/core_uint_type.hpp"
+namespace rive
+{
+ class BlendStateTransitionBase : public StateTransition
+ {
+ protected:
+ typedef StateTransition Super;
+
+ public:
+ static const uint16_t typeKey = 78;
+
+ /// Helper to quickly determine if a core object extends another without
+ /// RTTI at runtime.
+ bool isTypeOf(uint16_t typeKey) const override
+ {
+ switch (typeKey)
+ {
+ case BlendStateTransitionBase::typeKey:
+ case StateTransitionBase::typeKey:
+ case StateMachineLayerComponentBase::typeKey:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ uint16_t coreType() const override { return typeKey; }
+
+ static const uint16_t exitBlendAnimationIdPropertyKey = 171;
+
+ private:
+ int m_ExitBlendAnimationId = -1;
+ public:
+ inline int exitBlendAnimationId() const
+ {
+ return m_ExitBlendAnimationId;
+ }
+ void exitBlendAnimationId(int value)
+ {
+ if (m_ExitBlendAnimationId == value)
+ {
+ return;
+ }
+ m_ExitBlendAnimationId = value;
+ exitBlendAnimationIdChanged();
+ }
+
+ bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
+ {
+ switch (propertyKey)
+ {
+ case exitBlendAnimationIdPropertyKey:
+ m_ExitBlendAnimationId = CoreUintType::deserialize(reader);
+ return true;
+ }
+ return StateTransition::deserialize(propertyKey, reader);
+ }
+
+ protected:
+ virtual void exitBlendAnimationIdChanged() {}
+ };
+} // namespace rive
+
+#endif
\ No newline at end of file
y2Changed();
}
+ Core* clone() const override;
+ void copy(const CubicInterpolatorBase& object)
+ {
+ m_X1 = object.m_X1;
+ m_Y1 = object.m_Y1;
+ m_X2 = object.m_X2;
+ m_Y2 = object.m_Y2;
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
objectIdChanged();
}
+ Core* clone() const override;
+ void copy(const KeyedObjectBase& object)
+ {
+ m_ObjectId = object.m_ObjectId;
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
propertyKeyChanged();
}
+ Core* clone() const override;
+ void copy(const KeyedPropertyBase& object)
+ {
+ m_PropertyKey = object.m_PropertyKey;
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
interpolatorIdChanged();
}
+ void copy(const KeyFrameBase& object)
+ {
+ m_Frame = object.m_Frame;
+ m_InterpolationType = object.m_InterpolationType;
+ m_InterpolatorId = object.m_InterpolatorId;
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
valueChanged();
}
+ Core* clone() const override;
+ void copy(const KeyFrameColorBase& object)
+ {
+ m_Value = object.m_Value;
+ KeyFrame::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
valueChanged();
}
+ Core* clone() const override;
+ void copy(const KeyFrameDoubleBase& object)
+ {
+ m_Value = object.m_Value;
+ KeyFrame::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
valueChanged();
}
+ Core* clone() const override;
+ void copy(const KeyFrameIdBase& object)
+ {
+ m_Value = object.m_Value;
+ KeyFrame::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
enableWorkAreaChanged();
}
+ Core* clone() const override;
+ void copy(const LinearAnimationBase& object)
+ {
+ m_Fps = object.m_Fps;
+ m_Duration = object.m_Duration;
+ m_Speed = object.m_Speed;
+ m_LoopValue = object.m_LoopValue;
+ m_WorkStart = object.m_WorkStart;
+ m_WorkEnd = object.m_WorkEnd;
+ m_EnableWorkArea = object.m_EnableWorkArea;
+ Animation::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
valueChanged();
}
+ Core* clone() const override;
+ void copy(const StateMachineBoolBase& object)
+ {
+ m_Value = object.m_Value;
+ StateMachineInput::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
nameChanged();
}
+ void copy(const StateMachineComponentBase& object)
+ {
+ m_Name = object.m_Name;
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
uint16_t coreType() const override { return typeKey; }
+ void copy(const StateMachineLayerComponentBase& object) {}
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
valueChanged();
}
+ Core* clone() const override;
+ void copy(const StateMachineNumberBase& object)
+ {
+ m_Value = object.m_Value;
+ StateMachineInput::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
exitTimeChanged();
}
+ Core* clone() const override;
+ void copy(const StateTransitionBase& object)
+ {
+ m_StateToId = object.m_StateToId;
+ m_Flags = object.m_Flags;
+ m_Duration = object.m_Duration;
+ m_ExitTime = object.m_ExitTime;
+ StateMachineLayerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
inputIdChanged();
}
+ void copy(const TransitionConditionBase& object)
+ {
+ m_InputId = object.m_InputId;
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
valueChanged();
}
+ Core* clone() const override;
+ void copy(const TransitionNumberConditionBase& object)
+ {
+ m_Value = object.m_Value;
+ TransitionValueCondition::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
opValueChanged();
}
+ void copy(const TransitionValueConditionBase& object)
+ {
+ m_OpValue = object.m_OpValue;
+ TransitionCondition::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
originYChanged();
}
+ Core* clone() const override;
+ void copy(const ArtboardBase& object)
+ {
+ m_Width = object.m_Width;
+ m_Height = object.m_Height;
+ m_X = object.m_X;
+ m_Y = object.m_Y;
+ m_OriginX = object.m_OriginX;
+ m_OriginY = object.m_OriginY;
+ ContainerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+ void copy(const BackboardBase& object) {}
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
lengthChanged();
}
+ Core* clone() const override;
+ void copy(const BoneBase& object)
+ {
+ m_Length = object.m_Length;
+ SkeletalComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
outIndicesChanged();
}
+ Core* clone() const override;
+ void copy(const CubicWeightBase& object)
+ {
+ m_InValues = object.m_InValues;
+ m_InIndices = object.m_InIndices;
+ m_OutValues = object.m_OutValues;
+ m_OutIndices = object.m_OutIndices;
+ Weight::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
yChanged();
}
+ Core* clone() const override;
+ void copy(const RootBoneBase& object)
+ {
+ m_X = object.m_X;
+ m_Y = object.m_Y;
+ Bone::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
tyChanged();
}
+ Core* clone() const override;
+ void copy(const SkinBase& object)
+ {
+ m_Xx = object.m_Xx;
+ m_Yx = object.m_Yx;
+ m_Xy = object.m_Xy;
+ m_Yy = object.m_Yy;
+ m_Tx = object.m_Tx;
+ m_Ty = object.m_Ty;
+ ContainerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
tyChanged();
}
+ Core* clone() const override;
+ void copy(const TendonBase& object)
+ {
+ m_BoneId = object.m_BoneId;
+ m_Xx = object.m_Xx;
+ m_Yx = object.m_Yx;
+ m_Xy = object.m_Xy;
+ m_Yy = object.m_Yy;
+ m_Tx = object.m_Tx;
+ m_Ty = object.m_Ty;
+ Component::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
indicesChanged();
}
+ Core* clone() const override;
+ void copy(const WeightBase& object)
+ {
+ m_Values = object.m_Values;
+ m_Indices = object.m_Indices;
+ Component::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
parentIdChanged();
}
+ void copy(const ComponentBase& object)
+ {
+ m_Name = object.m_Name;
+ m_ParentId = object.m_ParentId;
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
#include "animation/animation.hpp"
#include "animation/animation_state.hpp"
#include "animation/any_state.hpp"
+#include "animation/blend_animation.hpp"
+#include "animation/blend_animation_1d.hpp"
+#include "animation/blend_animation_direct.hpp"
+#include "animation/blend_state.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_state_transition.hpp"
#include "animation/cubic_interpolator.hpp"
#include "animation/entry_state.hpp"
#include "animation/exit_state.hpp"
return new AnimationState();
case KeyedObjectBase::typeKey:
return new KeyedObject();
+ case BlendAnimationDirectBase::typeKey:
+ return new BlendAnimationDirect();
case StateMachineNumberBase::typeKey:
return new StateMachineNumber();
case TransitionTriggerConditionBase::typeKey:
return new LinearAnimation();
case StateMachineTriggerBase::typeKey:
return new StateMachineTrigger();
+ case BlendStateDirectBase::typeKey:
+ return new BlendStateDirect();
case ExitStateBase::typeKey:
return new ExitState();
+ case BlendState1DBase::typeKey:
+ return new BlendState1D();
case TransitionBoolConditionBase::typeKey:
return new TransitionBoolCondition();
+ case BlendStateTransitionBase::typeKey:
+ return new BlendStateTransition();
case StateMachineBoolBase::typeKey:
return new StateMachineBool();
+ case BlendAnimation1DBase::typeKey:
+ return new BlendAnimation1D();
case LinearGradientBase::typeKey:
return new LinearGradient();
case RadialGradientBase::typeKey:
case KeyedObjectBase::objectIdPropertyKey:
object->as<KeyedObjectBase>()->objectId(value);
break;
+ case BlendAnimationBase::animationIdPropertyKey:
+ object->as<BlendAnimationBase>()->animationId(value);
+ break;
+ case BlendAnimationDirectBase::inputIdPropertyKey:
+ object->as<BlendAnimationDirectBase>()->inputId(value);
+ break;
case TransitionConditionBase::inputIdPropertyKey:
object->as<TransitionConditionBase>()->inputId(value);
break;
case LinearAnimationBase::workEndPropertyKey:
object->as<LinearAnimationBase>()->workEnd(value);
break;
+ case BlendState1DBase::inputIdPropertyKey:
+ object->as<BlendState1DBase>()->inputId(value);
+ break;
+ case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey:
+ object->as<BlendStateTransitionBase>()
+ ->exitBlendAnimationId(value);
+ break;
case StrokeBase::capPropertyKey:
object->as<StrokeBase>()->cap(value);
break;
case LinearAnimationBase::speedPropertyKey:
object->as<LinearAnimationBase>()->speed(value);
break;
+ case BlendAnimation1DBase::valuePropertyKey:
+ object->as<BlendAnimation1DBase>()->value(value);
+ break;
case LinearGradientBase::startXPropertyKey:
object->as<LinearGradientBase>()->startX(value);
break;
return object->as<AnimationStateBase>()->animationId();
case KeyedObjectBase::objectIdPropertyKey:
return object->as<KeyedObjectBase>()->objectId();
+ case BlendAnimationBase::animationIdPropertyKey:
+ return object->as<BlendAnimationBase>()->animationId();
+ case BlendAnimationDirectBase::inputIdPropertyKey:
+ return object->as<BlendAnimationDirectBase>()->inputId();
case TransitionConditionBase::inputIdPropertyKey:
return object->as<TransitionConditionBase>()->inputId();
case KeyedPropertyBase::propertyKeyPropertyKey:
return object->as<LinearAnimationBase>()->workStart();
case LinearAnimationBase::workEndPropertyKey:
return object->as<LinearAnimationBase>()->workEnd();
+ case BlendState1DBase::inputIdPropertyKey:
+ return object->as<BlendState1DBase>()->inputId();
+ case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey:
+ return object->as<BlendStateTransitionBase>()
+ ->exitBlendAnimationId();
case StrokeBase::capPropertyKey:
return object->as<StrokeBase>()->cap();
case StrokeBase::joinPropertyKey:
return object->as<KeyFrameDoubleBase>()->value();
case LinearAnimationBase::speedPropertyKey:
return object->as<LinearAnimationBase>()->speed();
+ case BlendAnimation1DBase::valuePropertyKey:
+ return object->as<BlendAnimation1DBase>()->value();
case LinearGradientBase::startXPropertyKey:
return object->as<LinearGradientBase>()->startX();
case LinearGradientBase::startYPropertyKey:
case DrawTargetBase::placementValuePropertyKey:
case AnimationStateBase::animationIdPropertyKey:
case KeyedObjectBase::objectIdPropertyKey:
+ case BlendAnimationBase::animationIdPropertyKey:
+ case BlendAnimationDirectBase::inputIdPropertyKey:
case TransitionConditionBase::inputIdPropertyKey:
case KeyedPropertyBase::propertyKeyPropertyKey:
case KeyFrameBase::framePropertyKey:
case LinearAnimationBase::loopValuePropertyKey:
case LinearAnimationBase::workStartPropertyKey:
case LinearAnimationBase::workEndPropertyKey:
+ case BlendState1DBase::inputIdPropertyKey:
+ case BlendStateTransitionBase::exitBlendAnimationIdPropertyKey:
case StrokeBase::capPropertyKey:
case StrokeBase::joinPropertyKey:
case TrimPathBase::modeValuePropertyKey:
case CubicInterpolatorBase::y2PropertyKey:
case KeyFrameDoubleBase::valuePropertyKey:
case LinearAnimationBase::speedPropertyKey:
+ case BlendAnimation1DBase::valuePropertyKey:
case LinearGradientBase::startXPropertyKey:
case LinearGradientBase::startYPropertyKey:
case LinearGradientBase::endXPropertyKey:
drawTargetIdChanged();
}
+ Core* clone() const override;
+ void copy(const DrawRulesBase& object)
+ {
+ m_DrawTargetId = object.m_DrawTargetId;
+ ContainerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
placementValueChanged();
}
+ Core* clone() const override;
+ void copy(const DrawTargetBase& object)
+ {
+ m_DrawableId = object.m_DrawableId;
+ m_PlacementValue = object.m_PlacementValue;
+ Component::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
drawableFlagsChanged();
}
+ void copy(const DrawableBase& object)
+ {
+ m_BlendModeValue = object.m_BlendModeValue;
+ m_DrawableFlags = object.m_DrawableFlags;
+ Node::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
yChanged();
}
+ Core* clone() const override;
+ void copy(const NodeBase& object)
+ {
+ m_X = object.m_X;
+ m_Y = object.m_Y;
+ TransformComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
isVisibleChanged();
}
+ Core* clone() const override;
+ void copy(const ClippingShapeBase& object)
+ {
+ m_SourceId = object.m_SourceId;
+ m_FillRule = object.m_FillRule;
+ m_IsVisible = object.m_IsVisible;
+ Component::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
outDistanceChanged();
}
+ Core* clone() const override;
+ void copy(const CubicAsymmetricVertexBase& object)
+ {
+ m_Rotation = object.m_Rotation;
+ m_InDistance = object.m_InDistance;
+ m_OutDistance = object.m_OutDistance;
+ CubicVertex::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
outDistanceChanged();
}
+ Core* clone() const override;
+ void copy(const CubicDetachedVertexBase& object)
+ {
+ m_InRotation = object.m_InRotation;
+ m_InDistance = object.m_InDistance;
+ m_OutRotation = object.m_OutRotation;
+ m_OutDistance = object.m_OutDistance;
+ CubicVertex::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
distanceChanged();
}
+ Core* clone() const override;
+ void copy(const CubicMirroredVertexBase& object)
+ {
+ m_Rotation = object.m_Rotation;
+ m_Distance = object.m_Distance;
+ CubicVertex::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
fillRuleChanged();
}
+ Core* clone() const override;
+ void copy(const FillBase& object)
+ {
+ m_FillRule = object.m_FillRule;
+ ShapePaint::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
positionChanged();
}
+ Core* clone() const override;
+ void copy(const GradientStopBase& object)
+ {
+ m_ColorValue = object.m_ColorValue;
+ m_Position = object.m_Position;
+ Component::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
opacityChanged();
}
+ Core* clone() const override;
+ void copy(const LinearGradientBase& object)
+ {
+ m_StartX = object.m_StartX;
+ m_StartY = object.m_StartY;
+ m_EndX = object.m_EndX;
+ m_EndY = object.m_EndY;
+ m_Opacity = object.m_Opacity;
+ ContainerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
isVisibleChanged();
}
+ void copy(const ShapePaintBase& object)
+ {
+ m_IsVisible = object.m_IsVisible;
+ ContainerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
colorValueChanged();
}
+ Core* clone() const override;
+ void copy(const SolidColorBase& object)
+ {
+ m_ColorValue = object.m_ColorValue;
+ Component::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
transformAffectsStrokeChanged();
}
+ Core* clone() const override;
+ void copy(const StrokeBase& object)
+ {
+ m_Thickness = object.m_Thickness;
+ m_Cap = object.m_Cap;
+ m_Join = object.m_Join;
+ m_TransformAffectsStroke = object.m_TransformAffectsStroke;
+ ShapePaint::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
modeValueChanged();
}
+ Core* clone() const override;
+ void copy(const TrimPathBase& object)
+ {
+ m_Start = object.m_Start;
+ m_End = object.m_End;
+ m_Offset = object.m_Offset;
+ m_ModeValue = object.m_ModeValue;
+ Component::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
originYChanged();
}
+ void copy(const ParametricPathBase& object)
+ {
+ m_Width = object.m_Width;
+ m_Height = object.m_Height;
+ m_OriginX = object.m_OriginX;
+ m_OriginY = object.m_OriginY;
+ Path::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
pathFlagsChanged();
}
+ void copy(const PathBase& object)
+ {
+ m_PathFlags = object.m_PathFlags;
+ Node::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
yChanged();
}
+ void copy(const PathVertexBase& object)
+ {
+ m_X = object.m_X;
+ m_Y = object.m_Y;
+ ContainerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
isClosedChanged();
}
+ Core* clone() const override;
+ void copy(const PointsPathBase& object)
+ {
+ m_IsClosed = object.m_IsClosed;
+ Path::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
cornerRadiusChanged();
}
+ Core* clone() const override;
+ void copy(const PolygonBase& object)
+ {
+ m_Points = object.m_Points;
+ m_CornerRadius = object.m_CornerRadius;
+ ParametricPath::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
cornerRadiusBRChanged();
}
+ Core* clone() const override;
+ void copy(const RectangleBase& object)
+ {
+ m_LinkCornerRadius = object.m_LinkCornerRadius;
+ m_CornerRadiusTL = object.m_CornerRadiusTL;
+ m_CornerRadiusTR = object.m_CornerRadiusTR;
+ m_CornerRadiusBL = object.m_CornerRadiusBL;
+ m_CornerRadiusBR = object.m_CornerRadiusBR;
+ ParametricPath::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
innerRadiusChanged();
}
+ Core* clone() const override;
+ void copy(const StarBase& object)
+ {
+ m_InnerRadius = object.m_InnerRadius;
+ Polygon::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
radiusChanged();
}
+ Core* clone() const override;
+ void copy(const StraightVertexBase& object)
+ {
+ m_Radius = object.m_Radius;
+ PathVertex::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
uint16_t coreType() const override { return typeKey; }
+ Core* clone() const override;
+
protected:
};
} // namespace rive
opacityChanged();
}
+ void copy(const TransformComponentBase& object)
+ {
+ m_Rotation = object.m_Rotation;
+ m_ScaleX = object.m_ScaleX;
+ m_ScaleY = object.m_ScaleY;
+ m_Opacity = object.m_Opacity;
+ ContainerComponent::copy(object);
+ }
+
bool deserialize(uint16_t propertyKey, BinaryReader& reader) override
{
switch (propertyKey)
{
class LayerState;
class StateTransition;
+ class BlendAnimation;
class LayerStateImporter : public ImportStackObject
{
public:
LayerStateImporter(LayerState* state);
void addTransition(StateTransition* transition);
+ bool addBlendAnimation(BlendAnimation* animation);
StatusCode resolve() override;
};
} // namespace rive
{
public:
StatusCode onAddedDirty(CoreContext* context) override;
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
protected:
void colorValueChanged() override;
public:
StatusCode onAddedDirty(CoreContext* context) override;
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
void addStop(GradientStop* stop);
void update(ComponentDirt value) override;
void markGradientDirty();
class RadialGradient : public RadialGradientBase
{
public:
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
void makeGradient(const Vec2D& start, const Vec2D& end) override;
};
} // namespace rive
{
public:
StatusCode onAddedDirty(CoreContext* context) override;
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
protected:
void renderOpacityChanged() override;
class CommandPath;
class PathVertex;
+#ifdef ENABLE_QUERY_FLAT_VERTICES
+ /// Optionally compiled in for tools that need to compute per frame world
+ /// transformed path vertices. These should not be used at runtime as it's
+ /// not optimized for performance (it does a lot of memory allocation).
+
+ /// A flattened path is composed of only linear
+ /// and cubic vertices. No corner vertices and it's entirely in world space.
+ /// This is helpful for getting a close to identical representation of the
+ /// vertices used to issue the high level path draw commands.
+ class FlattenedPath
+ {
+ private:
+ std::vector<PathVertex*> m_Vertices;
+
+ public:
+ ~FlattenedPath();
+
+ const std::vector<PathVertex*>& vertices() const { return m_Vertices; }
+ void addVertex(PathVertex* vertex, const Mat2D& transform);
+ };
+#endif
+
class Path : public PathBase
{
protected:
virtual void markPathDirty();
virtual bool isPathClosed() const { return true; }
void onDirty(ComponentDirt dirt) override;
+#ifdef ENABLE_QUERY_FLAT_VERTICES
+ FlattenedPath* makeFlat(bool transformToParent);
+#endif
+
#ifdef TESTING
std::vector<PathVertex*>& vertices() { return m_Vertices; }
#endif
PathComposer(Shape* shape);
~PathComposer();
Shape* shape() const { return m_Shape; }
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
void buildDependencies() override;
void update(ComponentDirt value) override;
public:
StatusCode onAddedDirty(CoreContext* context) override;
- StatusCode onAddedClean(CoreContext* context) override
- {
- return StatusCode::Ok;
- }
template <typename T> T* weight() { return m_Weight->as<T>(); }
virtual void deform(Mat2D& worldTransform, float* boneTransforms);
bool hasWeight() { return m_Weight != nullptr; }
return value <= 0 ? default_value : value;
}
-void scale(int* value, int targetValue, int* otherValue)
+inline void scale(int* value, int targetValue, int* otherValue)
{
if (*value != targetValue)
{
_width, _height, kRGBA_8888_SkColorType, kPremul_SkAlphaType));
rasterCanvas = rasterSurface->getCanvas();
_fps = valueOrDefault(fps, animation->fps());
- ifps = 1.0 / fps;
+ ifps = 1.0 / _fps;
};
RiveFrameExtractor::~RiveFrameExtractor()
}
else if (stateMachineInstance != nullptr)
{
- stateMachineInstance->advance(elapsed);
- stateMachineInstance->apply(artboard);
+ stateMachineInstance->advance(artboard, elapsed);
}
artboard->advance(elapsed);
#include "animation/animation_state.hpp"
#include "animation/linear_animation.hpp"
+#include "animation/animation_state_instance.hpp"
+#include "animation/system_state_instance.hpp"
#include "core_context.hpp"
#include "artboard.hpp"
using namespace rive;
+
+StateInstance* AnimationState::makeInstance() const
+{
+ if (animation() == nullptr)
+ {
+ // Failed to load at runtime/some new type we don't understand.
+ return new SystemStateInstance(this);
+ }
+ return new AnimationStateInstance(this);
+}
\ No newline at end of file
--- /dev/null
+#include "animation/animation_state_instance.hpp"
+#include "animation/animation_state.hpp"
+
+using namespace rive;
+
+AnimationStateInstance::AnimationStateInstance(const AnimationState* state) :
+ StateInstance(state),
+ m_AnimationInstance(state->animation()),
+ m_KeepGoing(true)
+{
+}
+
+void AnimationStateInstance::advance(float seconds, SMIInput** inputs)
+{
+ m_KeepGoing = m_AnimationInstance.advance(seconds);
+}
+
+void AnimationStateInstance::apply(Artboard* artboard, float mix)
+{
+ m_AnimationInstance.apply(artboard, mix);
+}
+
+bool AnimationStateInstance::keepGoing() const { return m_KeepGoing; }
\ No newline at end of file
--- /dev/null
+#include "artboard.hpp"
+#include "animation/blend_animation.hpp"
+#include "animation/layer_state.hpp"
+#include "importers/layer_state_importer.hpp"
+#include "importers/artboard_importer.hpp"
+
+using namespace rive;
+
+StatusCode BlendAnimation::import(ImportStack& importStack)
+{
+ auto importer =
+ importStack.latest<LayerStateImporter>(LayerStateBase::typeKey);
+ if (importer == nullptr)
+ {
+ return StatusCode::MissingObject;
+ }
+ else if (!importer->addBlendAnimation(this))
+ {
+ return StatusCode::InvalidObject;
+ }
+
+ auto artboardImporter =
+ importStack.latest<ArtboardImporter>(ArtboardBase::typeKey);
+ if (artboardImporter == nullptr)
+ {
+ return StatusCode::MissingObject;
+ }
+
+ auto artboard = artboardImporter->artboard();
+ auto animationCount = artboard->animationCount();
+ if (animationId() >= 0 && animationId() < animationCount)
+ {
+ m_Animation = artboardImporter->artboard()->animation(animationId());
+ }
+
+ return Super::import(importStack);
+}
\ No newline at end of file
--- /dev/null
+#include "animation/blend_animation_1d.hpp"
+
+using namespace rive;
+
+StatusCode BlendAnimation1D::onAddedDirty(CoreContext* context)
+{
+ return StatusCode::Ok;
+}
+
+StatusCode BlendAnimation1D::onAddedClean(CoreContext* context)
+{
+ return StatusCode::Ok;
+}
\ No newline at end of file
--- /dev/null
+#include "animation/blend_animation_direct.hpp"
+#include "animation/state_machine.hpp"
+#include "animation/state_machine_number.hpp"
+#include "importers/state_machine_importer.hpp"
+
+using namespace rive;
+
+StatusCode BlendAnimationDirect::onAddedDirty(CoreContext* context)
+{
+ return StatusCode::Ok;
+}
+
+StatusCode BlendAnimationDirect::onAddedClean(CoreContext* context)
+{
+ return StatusCode::Ok;
+}
+
+StatusCode BlendAnimationDirect::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;
+ }
+ auto input = stateMachineImporter->stateMachine()->input((size_t)inputId());
+ if (input == nullptr || !input->is<StateMachineNumber>())
+ {
+ return StatusCode::InvalidObject;
+ }
+ return Super::import(importStack);
+}
\ No newline at end of file
--- /dev/null
+#include "animation/blend_state.hpp"
+
+using namespace rive;
+
+void BlendState::addAnimation(BlendAnimation* animation)
+{
+ // Assert it's not already contained.
+ assert(std::find(m_Animations.begin(), m_Animations.end(), animation) ==
+ m_Animations.end());
+ m_Animations.push_back(animation);
+}
\ No newline at end of file
--- /dev/null
+#include "animation/blend_state_1d.hpp"
+#include "animation/state_machine.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/blend_state_1d_instance.hpp"
+#include "importers/state_machine_importer.hpp"
+
+using namespace rive;
+
+StateInstance* BlendState1D::makeInstance() const
+{
+ return new BlendState1DInstance(this);
+}
+
+StatusCode BlendState1D::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;
+ }
+ auto input = stateMachineImporter->stateMachine()->input((size_t)inputId());
+ if (input == nullptr || !input->is<StateMachineNumber>())
+ {
+ return StatusCode::InvalidObject;
+ }
+ return Super::import(importStack);
+}
\ No newline at end of file
--- /dev/null
+#include "animation/blend_state_1d_instance.hpp"
+#include "animation/state_machine_input_instance.hpp"
+
+using namespace rive;
+
+BlendState1DInstance::BlendState1DInstance(const BlendState1D* blendState) :
+ BlendStateInstance<BlendState1D, BlendAnimation1D>(blendState)
+{
+}
+
+int BlendState1DInstance::animationIndex(float value)
+{
+ int idx = 0;
+ int mid = 0;
+ float closestValue = 0;
+ int start = 0;
+ int end = static_cast<int>(m_AnimationInstances.size()) - 1;
+
+ while (start <= end)
+ {
+ mid = (start + end) >> 1;
+ closestValue = m_AnimationInstances[mid].blendAnimation()->value();
+ if (closestValue < value)
+ {
+ start = mid + 1;
+ }
+ else if (closestValue > value)
+ {
+ end = mid - 1;
+ }
+ else
+ {
+ idx = start = mid;
+ break;
+ }
+
+ idx = start;
+ }
+ return idx;
+}
+
+void BlendState1DInstance::advance(float seconds, SMIInput** inputs)
+{
+ BlendStateInstance<BlendState1D, BlendAnimation1D>::advance(seconds,
+ inputs);
+
+ auto inputInstance = inputs[state()->as<BlendState1D>()->inputId()];
+
+ auto numberInput = reinterpret_cast<const SMINumber*>(inputInstance);
+ auto value = numberInput->value();
+ int index = animationIndex(value);
+ auto animationsCount = static_cast<int>(m_AnimationInstances.size());
+ m_To = index >= 0 && index < animationsCount ? &m_AnimationInstances[index]
+ : nullptr;
+ m_From = index - 1 >= 0 && index - 1 < animationsCount
+ ? &m_AnimationInstances[index - 1]
+ : nullptr;
+
+ float mix, mixFrom;
+ auto toValue = m_To == nullptr ? 0.0f : m_To->blendAnimation()->value();
+ auto fromValue =
+ m_From == nullptr ? 0.0f : m_From->blendAnimation()->value();
+
+ if (m_To == nullptr || m_From == nullptr || toValue == fromValue)
+ {
+ mix = mixFrom = 1.0f;
+ }
+ else
+ {
+ mix = (value - fromValue) / (toValue - fromValue);
+ mixFrom = 1.0f - mix;
+ }
+
+ for (auto& animation : m_AnimationInstances)
+ {
+ auto animationValue = animation.blendAnimation()->value();
+ if (m_To != nullptr && animationValue == toValue)
+ {
+ animation.mix(mix);
+ }
+ else if (m_From != nullptr && animationValue == fromValue)
+ {
+ animation.mix(mixFrom);
+ }
+ else
+ {
+ animation.mix(0.0f);
+ }
+ }
+}
--- /dev/null
+#include "animation/blend_state_direct.hpp"
+#include "animation/state_machine.hpp"
+#include "animation/state_machine_number.hpp"
+#include "animation/blend_state_direct_instance.hpp"
+#include "importers/state_machine_importer.hpp"
+
+using namespace rive;
+
+StateInstance* BlendStateDirect::makeInstance() const
+{
+ return new BlendStateDirectInstance(this);
+}
\ No newline at end of file
--- /dev/null
+#include "animation/blend_state_direct_instance.hpp"
+#include "animation/state_machine_input_instance.hpp"
+
+using namespace rive;
+
+BlendStateDirectInstance::BlendStateDirectInstance(
+ const BlendStateDirect* blendState) :
+ BlendStateInstance<BlendStateDirect, BlendAnimationDirect>(blendState)
+{
+}
+
+void BlendStateDirectInstance::advance(float seconds, SMIInput** inputs)
+{
+ BlendStateInstance<BlendStateDirect, BlendAnimationDirect>::advance(seconds,
+ inputs);
+ for (auto& animation : m_AnimationInstances)
+ {
+ auto inputInstance = inputs[animation.blendAnimation()->inputId()];
+
+ auto numberInput = reinterpret_cast<const SMINumber*>(inputInstance);
+ auto value = numberInput->value();
+ animation.mix(std::min(1.0f, std::max(0.0f, value / 100.0f)));
+ }
+}
--- /dev/null
+#include "artboard.hpp"
+#include "animation/blend_state_transition.hpp"
+#include "animation/blend_state_instance.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_state_1d_instance.hpp"
+#include "animation/blend_state_direct_instance.hpp"
+
+using namespace rive;
+
+const LinearAnimationInstance*
+BlendStateTransition::exitTimeAnimationInstance(const StateInstance* from) const
+{
+ if (from != nullptr)
+ {
+ switch (from->state()->coreType())
+ {
+ case BlendState1D::typeKey:
+
+ return static_cast<const BlendState1DInstance*>(from)
+ ->animationInstance(m_ExitBlendAnimation);
+
+ case BlendStateDirect::typeKey:
+
+ return static_cast<const BlendStateDirectInstance*>(from)
+ ->animationInstance(m_ExitBlendAnimation);
+ }
+ }
+
+ return nullptr;
+}
+
+const LinearAnimation*
+BlendStateTransition::exitTimeAnimation(const LayerState* from) const
+{
+ if (m_ExitBlendAnimation != nullptr)
+ {
+ return m_ExitBlendAnimation->animation();
+ }
+ return nullptr;
+}
\ No newline at end of file
#include "importers/state_machine_layer_importer.hpp"
#include "generated/animation/state_machine_layer_base.hpp"
#include "animation/state_transition.hpp"
-
-using namespace rive;
+#include "animation/system_state_instance.hpp"
using namespace rive;
void LayerState::addTransition(StateTransition* transition)
{
m_Transitions.push_back(transition);
+}
+
+StateInstance* LayerState::makeInstance() const
+{
+ return new SystemStateInstance(this);
}
\ No newline at end of file
--- /dev/null
+#include "animation/state_instance.hpp"
+using namespace rive;
+
+StateInstance::StateInstance(const LayerState* layerState) :
+ m_LayerState(layerState)
+{
+}
+
+StateInstance::~StateInstance() {}
+
+const LayerState* StateInstance::state() const { return m_LayerState; }
\ No newline at end of file
const StateMachineLayer* StateMachine::layer(size_t index) const
{
-
if (index >= 0 && index < m_Layers.size())
{
return m_Layers[index];
#include "animation/state_transition.hpp"
#include "animation/transition_condition.hpp"
#include "animation/animation_state.hpp"
+#include "animation/state_instance.hpp"
+#include "animation/animation_state_instance.hpp"
using namespace rive;
private:
static const int maxIterations = 100;
const StateMachineLayer* m_Layer = nullptr;
- const LayerState* m_CurrentState = nullptr;
- const LayerState* m_StateFrom = nullptr;
+
+ StateInstance* m_AnyStateInstance = nullptr;
+ StateInstance* m_CurrentState = nullptr;
+ StateInstance* m_StateFrom = 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;
+ // LinearAnimationInstance* m_AnimationInstance = nullptr;
+ // LinearAnimationInstance* m_AnimationInstanceFrom = nullptr;
float m_Mix = 1.0f;
- bool m_stateChangedOnAdvance = false;
+ float m_MixFrom = 1.0f;
+ bool m_StateChangedOnAdvance = false;
+
+ bool m_WaitingForExit = false;
+ /// Used to ensure a specific animation is applied on the next apply.
+ const LinearAnimation* m_HoldAnimation = nullptr;
+ float m_HoldTime = 0.0f;
public:
+ ~StateMachineLayerInstance()
+ {
+ delete m_AnyStateInstance;
+ delete m_CurrentState;
+ delete m_StateFrom;
+ }
+
void init(const StateMachineLayer* layer)
{
+ assert(m_Layer == nullptr);
+ m_AnyStateInstance = layer->anyState()->makeInstance();
m_Layer = layer;
- m_CurrentState = m_Layer->entryState();
+ changeState(m_Layer->entryState());
}
- bool advance(float seconds, SMIInput** inputs, size_t inputCount)
+ void updateMix(float seconds)
{
- bool keepGoing = false;
- m_stateChangedOnAdvance = 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))));
+ m_Mix = std::min(
+ 1.0f,
+ std::max(0.0f,
+ (m_Mix + seconds / m_Transition->mixTime(
+ m_StateFrom->state()))));
}
else
{
m_Mix = 1.0f;
}
+ }
- if (m_AnimationInstanceFrom != nullptr && m_Mix < 1.0f &&
- !m_HoldAnimationFrom)
+ bool advance(Artboard* artboard,
+ float seconds,
+ SMIInput** inputs,
+ size_t inputCount)
+ {
+ m_StateChangedOnAdvance = false;
+
+ if (m_CurrentState != nullptr)
{
- m_AnimationInstanceFrom->advance(seconds);
+ m_CurrentState->advance(seconds, inputs);
}
- for (int i = 0; updateState(inputs); i++)
+ updateMix(seconds);
+
+ if (m_StateFrom != nullptr && m_Mix < 1.0f && !m_HoldAnimationFrom)
{
- // Reset inputs between updates.
- for (size_t i = 0; i < inputCount; i++)
- {
- inputs[i]->advanced();
- }
+ // 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);
+ }
+
+ for (int i = 0; updateState(inputs, i != 0); i++)
+ {
+ apply(artboard);
if (i == maxIterations)
{
return false;
}
}
- return m_Mix != 1.0f || keepGoing;
+
+ apply(artboard);
+
+ return m_Mix != 1.0f || m_WaitingForExit ||
+ (m_CurrentState != nullptr && m_CurrentState->keepGoing());
}
- bool updateState(SMIInput** inputs)
+ bool isTransitioning()
{
- if (tryChangeState(m_Layer->anyState(), inputs))
+ return m_Transition != nullptr && m_StateFrom != nullptr &&
+ m_Transition->duration() != 0 && m_Mix < 1.0f;
+ }
+
+ bool updateState(SMIInput** inputs, bool ignoreTriggers)
+ {
+ // Don't allow changing state while a transition is taking place
+ // (we're mixing one state onto another).
+ if (isTransitioning())
+ {
+ return false;
+ }
+
+ m_WaitingForExit = false;
+
+ if (tryChangeState(m_AnyStateInstance, inputs, ignoreTriggers))
{
return true;
}
- return tryChangeState(m_CurrentState, inputs);
+ return tryChangeState(m_CurrentState, inputs, ignoreTriggers);
}
bool changeState(const LayerState* stateTo)
{
- if (m_CurrentState == stateTo)
+ if ((m_CurrentState == nullptr
+ ? nullptr
+ : m_CurrentState->state()) == stateTo)
{
return false;
}
- m_CurrentState = stateTo;
- m_stateChangedOnAdvance = true;
+ m_CurrentState =
+ stateTo == nullptr ? nullptr : stateTo->makeInstance();
return true;
}
- bool tryChangeState(const LayerState* stateFrom, SMIInput** inputs)
+ bool tryChangeState(StateInstance* stateFromInstance,
+ SMIInput** inputs,
+ bool ignoreTriggers)
{
- if (stateFrom == nullptr)
+ if (stateFromInstance == nullptr)
{
return false;
}
+ auto stateFrom = stateFromInstance->state();
+ auto outState = m_CurrentState;
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()))
+ auto allowed = transition->allowed(
+ stateFromInstance, inputs, ignoreTriggers);
+ if (allowed == AllowTransition::yes &&
+ changeState(transition->stateTo()))
{
+ m_StateChangedOnAdvance = true;
// state actually has changed
m_Transition = transition;
- m_StateFrom = stateFrom;
+ if (m_StateFrom != m_AnyStateInstance)
+ {
+ // Old state from is done.
+ delete m_StateFrom;
+ }
+ m_StateFrom = outState;
// 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)
+ // sure to hold the exit time. Delegate this to the
+ // transition by telling it that it was completed.
+ if (outState != nullptr &&
+ transition->applyExitCondition(outState))
{
- m_AnimationInstance->time(
- transition->exitTimeSeconds(stateFrom, false));
+ // Make sure we apply this state. This only returns true
+ // when it's an animation state instance.
+ auto instance = static_cast<AnimationStateInstance*>(
+ stateFromInstance)
+ ->animationInstance();
+
+ m_HoldAnimation = instance->animation();
+ m_HoldTime = instance->time();
}
+ m_MixFrom = m_Mix;
+ // Keep mixing last animation that was mixed in.
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
+ if (m_StateFrom != nullptr &&
+ m_StateFrom->state()->is<AnimationState>() &&
+ m_CurrentState != nullptr)
{
- 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;
+ auto instance = static_cast<AnimationStateInstance*>(
+ stateFromInstance)
+ ->animationInstance();
+
+ auto spilledTime = instance->spilledTime();
+ m_CurrentState->advance(spilledTime, inputs);
}
+ m_Mix = 0.0f;
+ updateMix(0.0f);
+ m_WaitingForExit = false;
return true;
}
+ else if (allowed == AllowTransition::waitingForExit)
+ {
+ m_WaitingForExit = true;
+ }
}
return false;
}
- void apply(Artboard* artboard) const
+ void apply(Artboard* artboard)
{
- if (m_AnimationInstanceFrom != nullptr && m_Mix < 1.0f)
+ if (m_HoldAnimation != nullptr)
{
- m_AnimationInstanceFrom->animation()->apply(
- artboard, m_AnimationInstanceFrom->time(), 1.0 - m_Mix);
+ m_HoldAnimation->apply(artboard, m_HoldTime, m_MixFrom);
+ m_HoldAnimation = nullptr;
}
- if (m_AnimationInstance != nullptr)
+
+ if (m_StateFrom != nullptr && m_Mix < 1.0f)
+ {
+ m_StateFrom->apply(artboard, m_MixFrom);
+ }
+ if (m_CurrentState != nullptr)
{
- m_AnimationInstance->animation()->apply(
- artboard, m_AnimationInstance->time(), m_Mix);
+ m_CurrentState->apply(artboard, m_Mix);
}
}
- bool stateChangedOnAdvance() const
- {
- return m_stateChangedOnAdvance;
- }
+ bool stateChangedOnAdvance() const { return m_StateChangedOnAdvance; }
- const LayerState* currentState()
+ const LayerState* currentState()
{
- return m_CurrentState;
+ return m_CurrentState == nullptr ? nullptr
+ : m_CurrentState->state();
}
const LinearAnimationInstance* currentAnimation() const
{
- return m_AnimationInstance;
+ if (m_CurrentState == nullptr ||
+ !m_CurrentState->state()->is<AnimationState>())
+ {
+ return nullptr;
+ }
+ return static_cast<AnimationStateInstance*>(m_CurrentState)
+ ->animationInstance();
}
};
} // namespace rive
delete[] m_Layers;
}
-bool StateMachineInstance::advance(float seconds)
+bool StateMachineInstance::advance(Artboard* artboard, float seconds)
{
m_NeedsAdvance = false;
for (int i = 0; i < m_LayerCount; i++)
{
- if (m_Layers[i].advance(seconds, m_InputInstances, m_InputCount))
+ if (m_Layers[i].advance(
+ artboard, seconds, m_InputInstances, m_InputCount))
{
m_NeedsAdvance = true;
}
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; }
if (m_Layers[i].stateChangedOnAdvance())
{
count++;
- }
+ }
}
return count;
}
return m_Layers[i].currentState();
}
count++;
- }
+ }
}
return nullptr;
}
size_t count = 0;
for (int i = 0; i < m_LayerCount; i++)
{
- if(m_Layers[i].currentAnimation() != nullptr)
+ if (m_Layers[i].currentAnimation() != nullptr)
{
count++;
}
return count;
}
-const LinearAnimationInstance* StateMachineInstance::currentAnimationByIndex(size_t index) const
+const LinearAnimationInstance*
+StateMachineInstance::currentAnimationByIndex(size_t index) const
{
size_t count = 0;
for (int i = 0; i < m_LayerCount; i++)
return m_Layers[i].currentAnimation();
}
count++;
- }
+ }
}
return nullptr;
}
\ No newline at end of file
#include "animation/transition_condition.hpp"
#include "animation/animation_state.hpp"
#include "animation/linear_animation.hpp"
+#include "animation/state_machine_input_instance.hpp"
+#include "animation/state_machine_trigger.hpp"
+#include "animation/animation_state_instance.hpp"
+#include "animation/transition_trigger_condition.hpp"
using namespace rive;
}
float StateTransition::exitTimeSeconds(const LayerState* stateFrom,
- bool relativeToWorkArea) const
+ bool absolute) const
{
- auto exitValue = exitTime();
- if (exitValue == 0)
+ if ((transitionFlags() & StateTransitionFlags::ExitTimeIsPercentage) ==
+ StateTransitionFlags::ExitTimeIsPercentage)
{
- return 0;
+ float animationDuration = 0.0f;
+ float start = 0.0f;
+
+ auto exitAnimation = exitTimeAnimation(stateFrom);
+ if (exitAnimation != nullptr)
+ {
+ start = absolute ? exitAnimation->startSeconds() : 0.0f;
+ animationDuration = exitAnimation->durationSeconds();
+ }
+
+ return start + exitTime() / 100.0f * animationDuration;
}
+ return exitTime() / 1000.0f;
+}
- float animationDuration = 0.0f;
- float animationOrigin = 0.0f;
- if (stateFrom->is<AnimationState>())
+const LinearAnimationInstance*
+StateTransition::exitTimeAnimationInstance(const StateInstance* from) const
+{
+ return from != nullptr && from->state()->is<AnimationState>()
+ ? static_cast<const AnimationStateInstance*>(from)
+ ->animationInstance()
+ : nullptr;
+}
+
+const LinearAnimation*
+StateTransition::exitTimeAnimation(const LayerState* from) const
+{
+ return from != nullptr && from->is<AnimationState>()
+ ? from->as<AnimationState>()->animation()
+ : nullptr;
+}
+
+AllowTransition StateTransition::allowed(StateInstance* stateFrom,
+ SMIInput** inputs,
+ bool ignoreTriggers) const
+{
+ if (isDisabled())
{
- auto animation = stateFrom->as<AnimationState>()->animation();
- animationDuration = animation->durationSeconds();
- animationOrigin = relativeToWorkArea ? 0 : animation->startSeconds();
+ return AllowTransition::no;
}
- if ((transitionFlags() & StateTransitionFlags::ExitTimeIsPercentage) ==
- StateTransitionFlags::ExitTimeIsPercentage)
+ for (auto condition : m_Conditions)
{
- return animationOrigin + exitValue / 100.0f * animationDuration;
+ // N.B. state machine instance sanitizes these for us...
+ auto input = inputs[condition->inputId()];
+
+ if ((ignoreTriggers && condition->is<TransitionTriggerCondition>()) ||
+ !condition->evaluate(input))
+ {
+ return AllowTransition::no;
+ }
}
- else
+
+ if (enableExitTime())
{
- return animationOrigin + exitValue / 1000.0f;
+ auto exitAnimation = exitTimeAnimationInstance(stateFrom);
+ if (exitAnimation != nullptr)
+ {
+ // Exit time is specified in a value less than a single loop, so we
+ // want to allow exiting regardless of which loop we're on. To do
+ // that we bring the exit time up to the loop our lastTime is at.
+ auto lastTime = exitAnimation->lastTotalTime();
+ auto time = exitAnimation->totalTime();
+ auto exitTime = exitTimeSeconds(stateFrom->state());
+ auto animationFrom = exitAnimation->animation();
+ auto duration = animationFrom->durationSeconds();
+ if (exitTime < duration)
+ {
+ // Get exit time relative to the loop lastTime was in.
+ exitTime += std::floor(lastTime / duration) * duration;
+ }
+
+ if (time < exitTime)
+ {
+ return AllowTransition::waitingForExit;
+ }
+ }
}
+ return AllowTransition::yes;
}
+
+bool StateTransition::applyExitCondition(StateInstance* from) const
+{
+ // Hold exit time when the user has set to pauseOnExit on this condition
+ // (only valid when exiting from an Animation).
+ bool useExitTime = enableExitTime() &&
+ (from != nullptr && from->state()->is<AnimationState>());
+ if (pauseOnExit() && useExitTime)
+ {
+ static_cast<AnimationStateInstance*>(from)->animationInstance()->time(
+ exitTimeSeconds(from->state(), true));
+ return true;
+ }
+ return useExitTime;
+}
\ No newline at end of file
--- /dev/null
+#include "animation/system_state_instance.hpp"
+using namespace rive;
+
+SystemStateInstance::SystemStateInstance(const LayerState* layerState) :
+ StateInstance(layerState)
+{
+}
+
+void SystemStateInstance::advance(float seconds, SMIInput** inputs) {}
+void SystemStateInstance::apply(Artboard* artboard, float mix) {}
+
+bool SystemStateInstance::keepGoing() const { return false; }
\ No newline at end of file
#include "importers/state_machine_layer_importer.hpp"
#include "importers/layer_state_importer.hpp"
#include "importers/state_transition_importer.hpp"
+#include "animation/blend_state_transition.hpp"
#include "animation/any_state.hpp"
#include "animation/entry_state.hpp"
#include "animation/exit_state.hpp"
#include "animation/animation_state.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_state_direct.hpp"
// Default namespace for Rive Cpp code
using namespace rive;
case ExitState::typeKey:
case AnyState::typeKey:
case AnimationState::typeKey:
+ case BlendState1D::typeKey:
+ case BlendStateDirect::typeKey:
stackObject = new LayerStateImporter(object->as<LayerState>());
stackType = LayerState::typeKey;
break;
case StateTransition::typeKey:
+ case BlendStateTransition::typeKey:
stackObject =
new StateTransitionImporter(object->as<StateTransition>());
+ stackType = StateTransition::typeKey;
break;
}
if (importStack.makeLatest(stackType, stackObject) != StatusCode::Ok)
--- /dev/null
+#include "generated/animation/animation_base.hpp"
+#include "animation/animation.hpp"
+
+using namespace rive;
+
+Core* AnimationBase::clone() const
+{
+ auto cloned = new Animation();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/animation_state_base.hpp"
+#include "animation/animation_state.hpp"
+
+using namespace rive;
+
+Core* AnimationStateBase::clone() const
+{
+ auto cloned = new AnimationState();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/any_state_base.hpp"
+#include "animation/any_state.hpp"
+
+using namespace rive;
+
+Core* AnyStateBase::clone() const
+{
+ auto cloned = new AnyState();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/cubic_interpolator_base.hpp"
+#include "animation/cubic_interpolator.hpp"
+
+using namespace rive;
+
+Core* CubicInterpolatorBase::clone() const
+{
+ auto cloned = new CubicInterpolator();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/entry_state_base.hpp"
+#include "animation/entry_state.hpp"
+
+using namespace rive;
+
+Core* EntryStateBase::clone() const
+{
+ auto cloned = new EntryState();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/exit_state_base.hpp"
+#include "animation/exit_state.hpp"
+
+using namespace rive;
+
+Core* ExitStateBase::clone() const
+{
+ auto cloned = new ExitState();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/keyed_object_base.hpp"
+#include "animation/keyed_object.hpp"
+
+using namespace rive;
+
+Core* KeyedObjectBase::clone() const
+{
+ auto cloned = new KeyedObject();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/keyed_property_base.hpp"
+#include "animation/keyed_property.hpp"
+
+using namespace rive;
+
+Core* KeyedPropertyBase::clone() const
+{
+ auto cloned = new KeyedProperty();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/keyframe_color_base.hpp"
+#include "animation/keyframe_color.hpp"
+
+using namespace rive;
+
+Core* KeyFrameColorBase::clone() const
+{
+ auto cloned = new KeyFrameColor();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/keyframe_double_base.hpp"
+#include "animation/keyframe_double.hpp"
+
+using namespace rive;
+
+Core* KeyFrameDoubleBase::clone() const
+{
+ auto cloned = new KeyFrameDouble();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/keyframe_id_base.hpp"
+#include "animation/keyframe_id.hpp"
+
+using namespace rive;
+
+Core* KeyFrameIdBase::clone() const
+{
+ auto cloned = new KeyFrameId();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/linear_animation_base.hpp"
+#include "animation/linear_animation.hpp"
+
+using namespace rive;
+
+Core* LinearAnimationBase::clone() const
+{
+ auto cloned = new LinearAnimation();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/state_machine_base.hpp"
+#include "animation/state_machine.hpp"
+
+using namespace rive;
+
+Core* StateMachineBase::clone() const
+{
+ auto cloned = new StateMachine();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/state_machine_bool_base.hpp"
+#include "animation/state_machine_bool.hpp"
+
+using namespace rive;
+
+Core* StateMachineBoolBase::clone() const
+{
+ auto cloned = new StateMachineBool();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/state_machine_layer_base.hpp"
+#include "animation/state_machine_layer.hpp"
+
+using namespace rive;
+
+Core* StateMachineLayerBase::clone() const
+{
+ auto cloned = new StateMachineLayer();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/state_machine_number_base.hpp"
+#include "animation/state_machine_number.hpp"
+
+using namespace rive;
+
+Core* StateMachineNumberBase::clone() const
+{
+ auto cloned = new StateMachineNumber();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/state_machine_trigger_base.hpp"
+#include "animation/state_machine_trigger.hpp"
+
+using namespace rive;
+
+Core* StateMachineTriggerBase::clone() const
+{
+ auto cloned = new StateMachineTrigger();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/state_transition_base.hpp"
+#include "animation/state_transition.hpp"
+
+using namespace rive;
+
+Core* StateTransitionBase::clone() const
+{
+ auto cloned = new StateTransition();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/transition_bool_condition_base.hpp"
+#include "animation/transition_bool_condition.hpp"
+
+using namespace rive;
+
+Core* TransitionBoolConditionBase::clone() const
+{
+ auto cloned = new TransitionBoolCondition();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/transition_number_condition_base.hpp"
+#include "animation/transition_number_condition.hpp"
+
+using namespace rive;
+
+Core* TransitionNumberConditionBase::clone() const
+{
+ auto cloned = new TransitionNumberCondition();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/animation/transition_trigger_condition_base.hpp"
+#include "animation/transition_trigger_condition.hpp"
+
+using namespace rive;
+
+Core* TransitionTriggerConditionBase::clone() const
+{
+ auto cloned = new TransitionTriggerCondition();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/artboard_base.hpp"
+#include "artboard.hpp"
+
+using namespace rive;
+
+Core* ArtboardBase::clone() const
+{
+ auto cloned = new Artboard();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/backboard_base.hpp"
+#include "backboard.hpp"
+
+using namespace rive;
+
+Core* BackboardBase::clone() const
+{
+ auto cloned = new Backboard();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/bones/bone_base.hpp"
+#include "bones/bone.hpp"
+
+using namespace rive;
+
+Core* BoneBase::clone() const
+{
+ auto cloned = new Bone();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/bones/cubic_weight_base.hpp"
+#include "bones/cubic_weight.hpp"
+
+using namespace rive;
+
+Core* CubicWeightBase::clone() const
+{
+ auto cloned = new CubicWeight();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/bones/root_bone_base.hpp"
+#include "bones/root_bone.hpp"
+
+using namespace rive;
+
+Core* RootBoneBase::clone() const
+{
+ auto cloned = new RootBone();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/bones/skin_base.hpp"
+#include "bones/skin.hpp"
+
+using namespace rive;
+
+Core* SkinBase::clone() const
+{
+ auto cloned = new Skin();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/bones/tendon_base.hpp"
+#include "bones/tendon.hpp"
+
+using namespace rive;
+
+Core* TendonBase::clone() const
+{
+ auto cloned = new Tendon();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/bones/weight_base.hpp"
+#include "bones/weight.hpp"
+
+using namespace rive;
+
+Core* WeightBase::clone() const
+{
+ auto cloned = new Weight();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/draw_rules_base.hpp"
+#include "draw_rules.hpp"
+
+using namespace rive;
+
+Core* DrawRulesBase::clone() const
+{
+ auto cloned = new DrawRules();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/draw_target_base.hpp"
+#include "draw_target.hpp"
+
+using namespace rive;
+
+Core* DrawTargetBase::clone() const
+{
+ auto cloned = new DrawTarget();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/node_base.hpp"
+#include "node.hpp"
+
+using namespace rive;
+
+Core* NodeBase::clone() const
+{
+ auto cloned = new Node();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/clipping_shape_base.hpp"
+#include "shapes/clipping_shape.hpp"
+
+using namespace rive;
+
+Core* ClippingShapeBase::clone() const
+{
+ auto cloned = new ClippingShape();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/cubic_asymmetric_vertex_base.hpp"
+#include "shapes/cubic_asymmetric_vertex.hpp"
+
+using namespace rive;
+
+Core* CubicAsymmetricVertexBase::clone() const
+{
+ auto cloned = new CubicAsymmetricVertex();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/cubic_detached_vertex_base.hpp"
+#include "shapes/cubic_detached_vertex.hpp"
+
+using namespace rive;
+
+Core* CubicDetachedVertexBase::clone() const
+{
+ auto cloned = new CubicDetachedVertex();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/cubic_mirrored_vertex_base.hpp"
+#include "shapes/cubic_mirrored_vertex.hpp"
+
+using namespace rive;
+
+Core* CubicMirroredVertexBase::clone() const
+{
+ auto cloned = new CubicMirroredVertex();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/ellipse_base.hpp"
+#include "shapes/ellipse.hpp"
+
+using namespace rive;
+
+Core* EllipseBase::clone() const
+{
+ auto cloned = new Ellipse();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/paint/fill_base.hpp"
+#include "shapes/paint/fill.hpp"
+
+using namespace rive;
+
+Core* FillBase::clone() const
+{
+ auto cloned = new Fill();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/paint/gradient_stop_base.hpp"
+#include "shapes/paint/gradient_stop.hpp"
+
+using namespace rive;
+
+Core* GradientStopBase::clone() const
+{
+ auto cloned = new GradientStop();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/paint/linear_gradient_base.hpp"
+#include "shapes/paint/linear_gradient.hpp"
+
+using namespace rive;
+
+Core* LinearGradientBase::clone() const
+{
+ auto cloned = new LinearGradient();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/paint/radial_gradient_base.hpp"
+#include "shapes/paint/radial_gradient.hpp"
+
+using namespace rive;
+
+Core* RadialGradientBase::clone() const
+{
+ auto cloned = new RadialGradient();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/paint/solid_color_base.hpp"
+#include "shapes/paint/solid_color.hpp"
+
+using namespace rive;
+
+Core* SolidColorBase::clone() const
+{
+ auto cloned = new SolidColor();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/paint/stroke_base.hpp"
+#include "shapes/paint/stroke.hpp"
+
+using namespace rive;
+
+Core* StrokeBase::clone() const
+{
+ auto cloned = new Stroke();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/paint/trim_path_base.hpp"
+#include "shapes/paint/trim_path.hpp"
+
+using namespace rive;
+
+Core* TrimPathBase::clone() const
+{
+ auto cloned = new TrimPath();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/points_path_base.hpp"
+#include "shapes/points_path.hpp"
+
+using namespace rive;
+
+Core* PointsPathBase::clone() const
+{
+ auto cloned = new PointsPath();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/polygon_base.hpp"
+#include "shapes/polygon.hpp"
+
+using namespace rive;
+
+Core* PolygonBase::clone() const
+{
+ auto cloned = new Polygon();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/rectangle_base.hpp"
+#include "shapes/rectangle.hpp"
+
+using namespace rive;
+
+Core* RectangleBase::clone() const
+{
+ auto cloned = new Rectangle();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/shape_base.hpp"
+#include "shapes/shape.hpp"
+
+using namespace rive;
+
+Core* ShapeBase::clone() const
+{
+ auto cloned = new Shape();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/star_base.hpp"
+#include "shapes/star.hpp"
+
+using namespace rive;
+
+Core* StarBase::clone() const
+{
+ auto cloned = new Star();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/straight_vertex_base.hpp"
+#include "shapes/straight_vertex.hpp"
+
+using namespace rive;
+
+Core* StraightVertexBase::clone() const
+{
+ auto cloned = new StraightVertex();
+ cloned->copy(*this);
+ return cloned;
+}
--- /dev/null
+#include "generated/shapes/triangle_base.hpp"
+#include "shapes/triangle.hpp"
+
+using namespace rive;
+
+Core* TriangleBase::clone() const
+{
+ auto cloned = new Triangle();
+ cloned->copy(*this);
+ return cloned;
+}
#include "importers/layer_state_importer.hpp"
#include "animation/state_transition.hpp"
#include "animation/layer_state.hpp"
+#include "animation/blend_state.hpp"
+#include "animation/blend_state_transition.hpp"
using namespace rive;
m_State->addTransition(transition);
}
-StatusCode LayerStateImporter::resolve() { return StatusCode::Ok; }
\ No newline at end of file
+bool LayerStateImporter::addBlendAnimation(BlendAnimation* animation)
+{
+ if (!m_State->is<BlendState>())
+ {
+ return false;
+ }
+ auto blendState = m_State->as<BlendState>();
+
+ blendState->addAnimation(animation);
+ return true;
+}
+
+StatusCode LayerStateImporter::resolve()
+{
+ if (m_State->is<BlendState>())
+ {
+ auto blendState = m_State->as<BlendState>();
+ for (auto transition : blendState->m_Transitions)
+ {
+ if (!transition->is<BlendStateTransition>())
+ {
+ continue;
+ }
+
+ auto blendStateTransition = transition->as<BlendStateTransition>();
+ auto exitId = blendStateTransition->exitBlendAnimationId();
+ if (exitId >= 0 && exitId < blendState->m_Animations.size())
+ {
+ blendStateTransition->m_ExitBlendAnimation =
+ blendState->m_Animations[exitId];
+ }
+ }
+ }
+ return StatusCode::Ok;
+}
\ No newline at end of file
return m_RenderPath;
}
-
-
// Source is always a containing (shape) path.
const std::vector<MetricsPath*>& subPaths = source->paths();
endLength -= totalLength;
}
- int i = 0, subPathCount = (int) subPaths.size();
+ int i = 0, subPathCount = (int)subPaths.size();
while (endLength > 0)
{
auto path = subPaths[i % subPathCount];
#include "math/circle_constant.hpp"
#include "renderer.hpp"
#include "shapes/cubic_vertex.hpp"
+#include "shapes/cubic_detached_vertex.hpp"
#include "shapes/path_vertex.hpp"
#include "shapes/shape.hpp"
#include "shapes/straight_vertex.hpp"
// m_Shape->pathChanged();
// }
}
+
+#ifdef ENABLE_QUERY_FLAT_VERTICES
+
+class DisplayCubicVertex : public CubicVertex
+{
+public:
+ DisplayCubicVertex(const Vec2D& in,
+ const Vec2D& out,
+ const Vec2D& translation)
+
+ {
+ m_InPoint = in;
+ m_OutPoint = out;
+ m_InValid = true;
+ m_OutValid = true;
+ x(translation[0]);
+ y(translation[1]);
+ }
+
+ void computeIn() override {}
+ void computeOut() override {}
+};
+
+FlattenedPath* Path::makeFlat(bool transformToParent)
+{
+ if (m_Vertices.empty())
+ {
+ return nullptr;
+ }
+
+ // Path transform always puts the path into world space.
+ auto transform = pathTransform();
+
+ if (transformToParent && parent()->is<TransformComponent>())
+ {
+ // Put the transform in parent space.
+ auto world = parent()->as<TransformComponent>()->worldTransform();
+ Mat2D inverseWorld;
+ if (!Mat2D::invert(inverseWorld, world))
+ {
+ Mat2D::identity(inverseWorld);
+ }
+ Mat2D::multiply(transform, inverseWorld, transform);
+ }
+
+ FlattenedPath* flat = new FlattenedPath();
+ auto length = m_Vertices.size();
+ PathVertex* previous = isPathClosed() ? m_Vertices[length - 1] : nullptr;
+ for (size_t i = 0; i < length; i++)
+ {
+ auto vertex = m_Vertices[i];
+
+ switch (vertex->coreType())
+ {
+ case StraightVertex::typeKey:
+ {
+ auto point = *vertex->as<StraightVertex>();
+ if (point.radius() > 0.0f &&
+ (isPathClosed() || (i != 0 && i != length - 1)))
+ {
+ auto next = m_Vertices[(i + 1) % length];
+
+ Vec2D prevPoint =
+ previous->is<CubicVertex>()
+ ? previous->as<CubicVertex>()->renderOut()
+ : previous->renderTranslation();
+ Vec2D nextPoint = next->is<CubicVertex>()
+ ? next->as<CubicVertex>()->renderIn()
+ : next->renderTranslation();
+
+ Vec2D pos = point.renderTranslation();
+
+ Vec2D toPrev;
+ Vec2D::subtract(toPrev, prevPoint, pos);
+ auto toPrevLength = Vec2D::length(toPrev);
+ toPrev[0] /= toPrevLength;
+ toPrev[1] /= toPrevLength;
+
+ Vec2D toNext;
+ Vec2D::subtract(toNext, nextPoint, pos);
+ auto toNextLength = Vec2D::length(toNext);
+ toNext[0] /= toNextLength;
+ toNext[1] /= toNextLength;
+
+ auto renderRadius = std::min(
+ toPrevLength, std::min(toNextLength, point.radius()));
+ Vec2D translation;
+ Vec2D::scaleAndAdd(translation, pos, toPrev, renderRadius);
+
+ Vec2D out;
+ Vec2D::scaleAndAdd(
+ out, pos, toPrev, icircleConstant * renderRadius);
+ auto v1 =
+ new DisplayCubicVertex(translation, out, translation);
+ flat->addVertex(v1, transform);
+ delete v1;
+
+ Vec2D::scaleAndAdd(translation, pos, toNext, renderRadius);
+
+ Vec2D in;
+ Vec2D::scaleAndAdd(
+ in, pos, toNext, icircleConstant * renderRadius);
+ auto v2 =
+ new DisplayCubicVertex(in, translation, translation);
+
+ flat->addVertex(v2, transform);
+ delete v2;
+
+ previous = v2;
+ break;
+ }
+ }
+ default:
+ previous = vertex;
+ flat->addVertex(previous, transform);
+ break;
+ }
+ }
+ return flat;
+}
+
+void FlattenedPath::addVertex(PathVertex* vertex, const Mat2D& transform)
+{
+ // To make this easy and relatively clean we just transform the vertices.
+ // Requires the vertex to be passed in as a clone.
+ if (vertex->is<CubicVertex>())
+ {
+ auto cubic = vertex->as<CubicVertex>();
+
+ // Cubics need to be transformed so we create a Display version which
+ // has set in/out values.
+ Vec2D in, out, translation;
+ Vec2D::transform(in, cubic->renderIn(), transform);
+ Vec2D::transform(out, cubic->renderOut(), transform);
+ Vec2D::transform(translation, cubic->renderTranslation(), transform);
+
+ auto displayCubic = new DisplayCubicVertex(in, out, translation);
+ m_Vertices.push_back(displayCubic);
+ }
+ else
+ {
+ auto point = new PathVertex();
+ Vec2D translation;
+ Vec2D::transform(translation, vertex->renderTranslation(), transform);
+ point->x(translation[0]);
+ point->y(translation[1]);
+ m_Vertices.push_back(point);
+ }
+}
+
+FlattenedPath::~FlattenedPath()
+{
+ for (auto vertex : m_Vertices)
+ {
+ delete vertex;
+ }
+}
+
+#endif
#include "animation/state_transition.hpp"
#include "animation/state_machine_instance.hpp"
#include "animation/state_machine_input_instance.hpp"
+#include "animation/blend_state_1d.hpp"
+#include "animation/blend_animation_1d.hpp"
+#include "animation/blend_state_direct.hpp"
+#include "animation/blend_state_transition.hpp"
#include <cstdio>
TEST_CASE("file with state machine be read", "[file]")
delete file;
delete[] bytes;
+}
+
+TEST_CASE("file with blend states loads correctly", "[file]")
+{
+ FILE* fp = fopen("../../test/assets/blend_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);
+ auto artboard = file->artboard();
+ REQUIRE(artboard != nullptr);
+ REQUIRE(artboard->animationCount() == 4);
+ REQUIRE(artboard->stateMachineCount() == 2);
+
+ auto stateMachine = artboard->stateMachine("blend");
+ REQUIRE(stateMachine != nullptr);
+
+ REQUIRE(stateMachine->layerCount() == 1);
+ auto layer = stateMachine->layer(0);
+ REQUIRE(layer->stateCount() == 5);
+
+ REQUIRE(layer->anyState() != nullptr);
+ REQUIRE(layer->entryState() != nullptr);
+ REQUIRE(layer->exitState() != nullptr);
+
+ REQUIRE(layer->state(1)->is<rive::BlendState1D>());
+ REQUIRE(layer->state(2)->is<rive::BlendState1D>());
+
+ auto blendStateA = layer->state(1)->as<rive::BlendState1D>();
+ auto blendStateB = layer->state(2)->as<rive::BlendState1D>();
+
+ REQUIRE(blendStateA->animationCount() == 3);
+ REQUIRE(blendStateB->animationCount() == 3);
+
+ auto animation = blendStateA->animation(0);
+ REQUIRE(animation->is<rive::BlendAnimation1D>());
+ auto animation1D = animation->as<rive::BlendAnimation1D>();
+ REQUIRE(animation1D->animation() != nullptr);
+ REQUIRE(animation1D->animation()->name() == "horizontal");
+ REQUIRE(animation1D->value() == 0.0f);
+
+ animation = blendStateA->animation(1);
+ REQUIRE(animation->is<rive::BlendAnimation1D>());
+ animation1D = animation->as<rive::BlendAnimation1D>();
+ REQUIRE(animation1D->animation() != nullptr);
+ REQUIRE(animation1D->animation()->name() == "vertical");
+ REQUIRE(animation1D->value() == 100.0f);
+
+ animation = blendStateA->animation(2);
+ REQUIRE(animation->is<rive::BlendAnimation1D>());
+ animation1D = animation->as<rive::BlendAnimation1D>();
+ REQUIRE(animation1D->animation() != nullptr);
+ REQUIRE(animation1D->animation()->name() == "rotate");
+ REQUIRE(animation1D->value() == 0.0f);
+
+ REQUIRE(blendStateA->transitionCount() == 1);
+ REQUIRE(blendStateA->transition(0)->is<rive::BlendStateTransition>());
+ REQUIRE(blendStateA->transition(0)
+ ->as<rive::BlendStateTransition>()
+ ->exitBlendAnimation() != nullptr);
+
+ delete file;
+ delete[] bytes;
+}
+
+TEST_CASE("animation state with no animation doesn't crash", "[file]")
+{
+ FILE* fp = fopen("../../test/assets/multiple_state_machines.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() == 1);
+ REQUIRE(artboard->stateMachineCount() == 4);
+
+ auto stateMachine = artboard->stateMachine("two");
+ REQUIRE(stateMachine != nullptr);
+
+ REQUIRE(stateMachine->layerCount() == 1);
+ auto layer = stateMachine->layer(0);
+ REQUIRE(layer->stateCount() == 4);
+
+ REQUIRE(layer->anyState() != nullptr);
+ REQUIRE(layer->entryState() != nullptr);
+ REQUIRE(layer->exitState() != nullptr);
+
+ REQUIRE(layer->state(3)->is<rive::AnimationState>());
+
+ auto animationState = layer->state(3)->as<rive::AnimationState>();
+ REQUIRE(animationState->animation() == nullptr);
+
+ rive::StateMachineInstance smi(stateMachine);
+ smi.advance(artboard, 0.0f);
+
+ delete file;
+ delete[] bytes;
}
\ No newline at end of file