diff --git a/.rive_head b/.rive_head index ee9abc18..30ea12c2 100644 --- a/.rive_head +++ b/.rive_head @@ -1 +1 @@ -01d20e02661309cd2f8a0702f9de102107c1a0f1 +dde676085908d492af545660cbbb19ee0d10d91d diff --git a/dependencies/jconfig.h b/dependencies/jconfig.h index b0daaa1a..84c05fc1 100644 --- a/dependencies/jconfig.h +++ b/dependencies/jconfig.h @@ -1,3 +1,5 @@ +#include // Required on Mac -- libjpg expects FILE to be already defined. + #define HAVE_PROTOTYPES #define HAVE_UNSIGNED_CHAR #define HAVE_UNSIGNED_SHORT @@ -8,4 +10,4 @@ #undef NEED_SYS_TYPES_H #undef NEED_FAR_POINTERS #undef NEED_SHORT_EXTERNAL_NAMES -#undef INCOMPLETE_TYPES_BROKEN \ No newline at end of file +#undef INCOMPLETE_TYPES_BROKEN diff --git a/include/rive/animation/nested_input.hpp b/include/rive/animation/nested_input.hpp index 0db46dfe..fe75467c 100644 --- a/include/rive/animation/nested_input.hpp +++ b/include/rive/animation/nested_input.hpp @@ -1,6 +1,7 @@ #ifndef _RIVE_NESTED_INPUT_HPP_ #define _RIVE_NESTED_INPUT_HPP_ #include "rive/animation/nested_state_machine.hpp" +#include "rive/animation/state_machine_input_instance.hpp" #include "rive/generated/animation/nested_input_base.hpp" #include namespace rive @@ -21,7 +22,6 @@ public: virtual void applyValue() {} -protected: SMIInput* input() const { auto parent = this->parent(); @@ -34,6 +34,16 @@ protected: } return nullptr; } + + const std::string name() const + { + auto smi = input(); + if (smi != nullptr) + { + return smi->name(); + } + return std::string(); + } }; } // namespace rive diff --git a/include/rive/animation/nested_state_machine.hpp b/include/rive/animation/nested_state_machine.hpp index 489d631c..c76729aa 100644 --- a/include/rive/animation/nested_state_machine.hpp +++ b/include/rive/animation/nested_state_machine.hpp @@ -30,6 +30,9 @@ public: HitResult pointerExit(Vec2D position); void addNestedInput(NestedInput* input); + size_t inputCount() { return m_nestedInputs.size(); } + NestedInput* input(size_t index); + NestedInput* input(std::string name); }; } // namespace rive diff --git a/include/rive/artboard.hpp b/include/rive/artboard.hpp index 69a339de..b8fdea35 100644 --- a/include/rive/artboard.hpp +++ b/include/rive/artboard.hpp @@ -32,6 +32,10 @@ class StateMachineInstance; class Joystick; class TextValueRun; class Event; +class SMIBool; +class SMIInput; +class SMINumber; +class SMITrigger; class Artboard : public ArtboardBase, public CoreContext, public ShapePaintContainer { @@ -120,6 +124,8 @@ public: const std::vector& objects() const { return m_Objects; } const std::vector nestedArtboards() const { return m_NestedArtboards; } + NestedArtboard* nestedArtboard(const std::string& name) const; + NestedArtboard* nestedArtboardAtPath(const std::string& path) const; AABB bounds() const; @@ -295,6 +301,13 @@ public: // 3. first animation instance // 4. nullptr std::unique_ptr defaultScene(); + + SMIInput* input(const std::string& name, const std::string& path); + template + InstType* getNamedInput(const std::string& name, const std::string& path); + SMIBool* getBool(const std::string& name, const std::string& path); + SMINumber* getNumber(const std::string& name, const std::string& path); + SMITrigger* getTrigger(const std::string& name, const std::string& path); }; } // namespace rive diff --git a/include/rive/nested_artboard.hpp b/include/rive/nested_artboard.hpp index e075c863..14bd7c13 100644 --- a/include/rive/nested_artboard.hpp +++ b/include/rive/nested_artboard.hpp @@ -10,6 +10,9 @@ namespace rive { class ArtboardInstance; class NestedAnimation; +class NestedInput; +class NestedStateMachine; +class StateMachineInstance; class NestedArtboard : public NestedArtboardBase { @@ -37,6 +40,10 @@ public: bool hasNestedStateMachines() const; Span nestedAnimations(); + NestedArtboard* nestedArtboard(std::string name) const; + NestedStateMachine* stateMachine(std::string name) const; + NestedInput* input(std::string name) const; + NestedInput* input(std::string name, std::string stateMachineName) const; /// Convert a world space (relative to the artboard that this /// NestedArtboard is a child of) to the local space of the Artboard diff --git a/src/animation/nested_state_machine.cpp b/src/animation/nested_state_machine.cpp index f04477f2..f9734299 100644 --- a/src/animation/nested_state_machine.cpp +++ b/src/animation/nested_state_machine.cpp @@ -32,7 +32,6 @@ void NestedStateMachine::initializeAnimation(ArtboardInstance* artboard) nestedInput->applyValue(); } } - m_nestedInputs.clear(); } StateMachineInstance* NestedStateMachine::stateMachineInstance() @@ -76,4 +75,25 @@ HitResult NestedStateMachine::pointerExit(Vec2D position) return HitResult::none; } +NestedInput* NestedStateMachine::input(size_t index) +{ + if (index < m_nestedInputs.size()) + { + return m_nestedInputs[index]; + } + return nullptr; +} + +NestedInput* NestedStateMachine::input(std::string name) +{ + for (auto input : m_nestedInputs) + { + if (input->name() == name) + { + return input; + } + } + return nullptr; +} + void NestedStateMachine::addNestedInput(NestedInput* input) { m_nestedInputs.push_back(input); } \ No newline at end of file diff --git a/src/artboard.cpp b/src/artboard.cpp index 0ddaf7b8..4458ecf5 100644 --- a/src/artboard.cpp +++ b/src/artboard.cpp @@ -15,6 +15,10 @@ #include "rive/importers/backboard_importer.hpp" #include "rive/nested_artboard.hpp" #include "rive/joystick.hpp" +#include "rive/animation/nested_bool.hpp" +#include "rive/animation/nested_number.hpp" +#include "rive/animation/nested_trigger.hpp" +#include "rive/animation/state_machine_input_instance.hpp" #include "rive/animation/state_machine_instance.hpp" #include "rive/shapes/shape.hpp" #include "rive/text/text_value_run.hpp" @@ -758,6 +762,47 @@ int Artboard::defaultStateMachineIndex() const return index; } +NestedArtboard* Artboard::nestedArtboard(const std::string& name) const +{ + for (auto nested : m_NestedArtboards) + { + if (nested->name() == name) + { + return nested; + } + } + return nullptr; +} + +NestedArtboard* Artboard::nestedArtboardAtPath(const std::string& path) const +{ + // name parameter can be a name or a path to recursively find a nested artboard + std::string delimiter = "/"; + size_t firstDelim = path.find(delimiter); + std::string artboardName = firstDelim == std::string::npos ? path : path.substr(0, firstDelim); + std::string restOfPath = + firstDelim == std::string::npos ? "" : path.substr(firstDelim + 1, path.size()); + + // Find the nested artboard at this level + if (!artboardName.empty()) + { + auto nested = nestedArtboard(artboardName); + if (nested != nullptr) + { + if (restOfPath.empty()) + { + return nested; + } + else + { + auto artboard = nested->artboard(); + return artboard->nestedArtboardAtPath(restOfPath); + } + } + } + return nullptr; +} + // std::unique_ptr Artboard::instance() const // { // std::unique_ptr artboardClone(new ArtboardInstance); @@ -896,6 +941,43 @@ std::unique_ptr ArtboardInstance::defaultScene() return scene; } +SMIInput* ArtboardInstance::input(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} + +template +InstType* ArtboardInstance::getNamedInput(const std::string& name, const std::string& path) +{ + if (!path.empty()) + { + auto nestedArtboard = nestedArtboardAtPath(path); + if (nestedArtboard != nullptr) + { + auto input = nestedArtboard->input(name); + if (input != nullptr && input->input() != nullptr) + { + return static_cast(input->input()); + } + } + } + return nullptr; +} + +SMIBool* ArtboardInstance::getBool(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} + +SMINumber* ArtboardInstance::getNumber(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} +SMITrigger* ArtboardInstance::getTrigger(const std::string& name, const std::string& path) +{ + return getNamedInput(name, path); +} + #ifdef EXTERNAL_RIVE_AUDIO_ENGINE rcp Artboard::audioEngine() const { return m_audioEngine; } void Artboard::audioEngine(rcp audioEngine) diff --git a/src/nested_artboard.cpp b/src/nested_artboard.cpp index 901661c0..1c580409 100644 --- a/src/nested_artboard.cpp +++ b/src/nested_artboard.cpp @@ -164,6 +164,56 @@ bool NestedArtboard::hasNestedStateMachines() const Span NestedArtboard::nestedAnimations() { return m_NestedAnimations; } +NestedArtboard* NestedArtboard::nestedArtboard(std::string name) const +{ + if (m_Instance != nullptr) + { + return m_Instance->nestedArtboard(name); + } + return nullptr; +} + +NestedStateMachine* NestedArtboard::stateMachine(std::string name) const +{ + for (auto animation : m_NestedAnimations) + { + if (animation->is() && animation->name() == name) + { + return animation->as(); + } + } + return nullptr; +} + +NestedInput* NestedArtboard::input(std::string name) const { return input(name, ""); } + +NestedInput* NestedArtboard::input(std::string name, std::string stateMachineName) const +{ + if (!stateMachineName.empty()) + { + auto nestedSM = stateMachine(stateMachineName); + if (nestedSM != nullptr) + { + return nestedSM->input(name); + } + } + else + { + for (auto animation : m_NestedAnimations) + { + if (animation->is()) + { + auto input = animation->as()->input(name); + if (input != nullptr) + { + return input; + } + } + } + } + return nullptr; +} + bool NestedArtboard::worldToLocal(Vec2D world, Vec2D* local) { assert(local != nullptr); diff --git a/test/assets/runtime_nested_inputs.riv b/test/assets/runtime_nested_inputs.riv new file mode 100644 index 00000000..04c2395c Binary files /dev/null and b/test/assets/runtime_nested_inputs.riv differ diff --git a/test/nested_input_test.cpp b/test/nested_input_test.cpp new file mode 100644 index 00000000..a40b58c6 --- /dev/null +++ b/test/nested_input_test.cpp @@ -0,0 +1,139 @@ +#include "rive/core/binary_reader.hpp" +#include "rive/file.hpp" +#include "rive/nested_artboard.hpp" +#include "rive/animation/nested_bool.hpp" +#include "rive/animation/nested_input.hpp" +#include "rive/animation/nested_number.hpp" +#include "rive/animation/nested_trigger.hpp" +#include "rive/animation/state_machine_instance.hpp" +#include "rive/animation/state_machine_input_instance.hpp" +#include "catch.hpp" +#include "rive_file_reader.hpp" +#include + +TEST_CASE("validate nested boolean get/set", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting boolean SMIInput via nested artboard path + auto boolInput = artboard->getBool("CircleOuterState", "CircleOuter"); + auto smiInput = artboard->input("CircleOuterState", "CircleOuter"); + auto smiBoolInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboard("CircleOuter"); + auto nestedInput = nestedArtboard->input("CircleOuterState")->as(); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + boolInput->value(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); + + smiBoolInput->value(false); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + nestedInput->nestedValue(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); +} + +TEST_CASE("validate nested number get/set", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting number SMIInput via nested artboard path + auto numInput = artboard->getNumber("CircleOuterNumber", "CircleOuter"); + auto smiInput = artboard->input("CircleOuterNumber", "CircleOuter"); + auto smiNumInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboardAtPath("CircleOuter"); + auto nestedInput = nestedArtboard->input("CircleOuterNumber")->as(); + REQUIRE(numInput->value() == 0); + REQUIRE(smiNumInput->value() == 0); + REQUIRE(nestedInput->nestedValue() == 0); + + numInput->value(10); + REQUIRE(numInput->value() == 10); + REQUIRE(smiNumInput->value() == 10); + REQUIRE(nestedInput->nestedValue() == 10); + + smiNumInput->value(5); + REQUIRE(numInput->value() == 5); + REQUIRE(smiNumInput->value() == 5); + REQUIRE(nestedInput->nestedValue() == 5); + + nestedInput->nestedValue(99); + REQUIRE(numInput->value() == 99); + REQUIRE(smiNumInput->value() == 99); + REQUIRE(nestedInput->nestedValue() == 99); +} + +TEST_CASE("validate nested trigger fire", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting number SMIInput via nested artboard path + auto tInput = artboard->getTrigger("CircleOuterTrigger", "CircleOuter"); + auto smiInput = artboard->input("CircleOuterTrigger", "CircleOuter"); + auto smiTInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboardAtPath("CircleOuter"); + auto nestedInput = nestedArtboard->input("CircleOuterTrigger")->as(); + auto nestedSMI = static_cast(nestedInput->input()); + REQUIRE(tInput->didFire() == false); + REQUIRE(smiTInput->didFire() == false); + REQUIRE(nestedSMI->didFire() == false); + + tInput->fire(); + REQUIRE(tInput->didFire() == true); + REQUIRE(smiTInput->didFire() == true); + REQUIRE(nestedSMI->didFire() == true); +} + +TEST_CASE("validate nested boolean get/set multiple nested artboards deep", "[nestedInput]") +{ + auto file = ReadRiveFile("../../test/assets/runtime_nested_inputs.riv"); + + auto artboard = file->artboard("MainArtboard")->instance(); + REQUIRE(artboard != nullptr); + REQUIRE(artboard->stateMachineCount() == 1); + + // Test getting/setting boolean SMIInput via nested artboard path + auto boolInput = artboard->getBool("CircleInnerState", "CircleOuter/CircleInner"); + auto smiInput = artboard->input("CircleInnerState", "CircleOuter/CircleInner"); + auto smiBoolInput = static_cast(smiInput); + auto nestedArtboard = artboard->nestedArtboardAtPath("CircleOuter/CircleInner"); + auto nestedInput = nestedArtboard->input("CircleInnerState")->as(); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + boolInput->value(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); + + smiBoolInput->value(false); + REQUIRE(boolInput->value() == false); + REQUIRE(smiBoolInput->value() == false); + REQUIRE(nestedInput->nestedValue() == false); + + nestedInput->nestedValue(true); + REQUIRE(boolInput->value() == true); + REQUIRE(smiBoolInput->value() == true); + REQUIRE(nestedInput->nestedValue() == true); +} \ No newline at end of file