diff --git a/sources/enduro2d/high/assets/prefab_asset.cpp b/sources/enduro2d/high/assets/prefab_asset.cpp index 7028c4b3..45d964f9 100644 --- a/sources/enduro2d/high/assets/prefab_asset.cpp +++ b/sources/enduro2d/high/assets/prefab_asset.cpp @@ -27,9 +27,14 @@ namespace "uuid" : { "$ref": "#/common_definitions/uuid" }, "parent" : { "$ref": "#/common_definitions/address" }, "children" : { "$ref": "#/definitions/children" }, + "mod_children" : { "$ref": "#/definitions/mod_children" }, "components" : { "type" : "object" } }, "definitions" : { + "children" : { + "type" : "array", + "items" : { "$ref": "#/definitions/child" } + }, "child" : { "type" : "object", "required" : [], @@ -38,12 +43,24 @@ namespace "uuid" : { "$ref": "#/common_definitions/uuid" }, "parent" : { "$ref": "#/common_definitions/address" }, "children" : { "$ref": "#/definitions/children" }, + "mod_children" : { "$ref": "#/definitions/mod_children" }, "components" : { "type" : "object" } } }, - "children" : { + "mod_children" : { "type" : "array", - "items" : { "$ref": "#/definitions/child" } + "items" : { "$ref": "#/definitions/mod_child" } + }, + "mod_child" : { + "type" : "object", + "required" : [ "uuid" ], + "additionalProperties" : false, + "properties" : { + "uuid" : { "$ref": "#/common_definitions/uuid" }, + "children" : { "$ref": "#/definitions/children" }, + "mod_children" : { "$ref": "#/definitions/mod_children" }, + "components" : { "type" : "object" } + } } } })json"; @@ -83,6 +100,13 @@ namespace } } + if ( root.HasMember("mod_children") ) { + const rapidjson::Value& mod_children_root = root["mod_children"]; + for ( rapidjson::SizeType i = 0; i < mod_children_root.Size(); ++i ) { + collect_dependencies(parent_address, mod_children_root[i], dependencies); + } + } + if ( root.HasMember("components") ) { const rapidjson::Value& components_root = root["components"]; for ( rapidjson::Value::ConstMemberIterator component_root = components_root.MemberBegin(); @@ -123,13 +147,28 @@ namespace return dependencies.load_async(library); } - prefab parse_prefab( + prefab* find_prefab_child(prefab& parent, str_view child_uuid) noexcept { + for ( prefab& child : parent.children() ) { + if ( child.uuid() == child_uuid ) { + return &child; + } + } + + for ( prefab& child : parent.children() ) { + if ( prefab* sub_child = find_prefab_child(child, child_uuid) ) { + return sub_child; + } + } + + return nullptr; + } + + void parse_prefab_inplace( + prefab& content, str_view parent_address, const rapidjson::Value& root, const asset_group& dependencies) { - prefab content; - if ( root.HasMember("uuid") ) { str uuid = content.uuid(); if ( !json_utils::try_parse_value(root["uuid"], uuid) ) { @@ -161,13 +200,39 @@ namespace children.reserve(children_root.Size()); for ( rapidjson::SizeType i = 0; i < children_root.Size(); ++i ) { - children.emplace_back(parse_prefab( - parent_address, children_root[i], dependencies)); + prefab child; + parse_prefab_inplace( + child, + parent_address, + children_root[i], + dependencies); + children.push_back(std::move(child)); } content.set_children(std::move(children)); } + if ( root.HasMember("mod_children") ) { + const rapidjson::Value& mod_children_root = root["mod_children"]; + + for ( rapidjson::SizeType i = 0; i < mod_children_root.Size(); ++i ) { + const rapidjson::Value& mod_child_root = mod_children_root[i]; + E2D_ASSERT(mod_child_root.HasMember("uuid") && mod_child_root["uuid"].IsString()); + prefab* child_prefab = find_prefab_child(content, mod_child_root["uuid"].GetString()); + if ( !child_prefab ) { + the().error("PREFAB: Modifiable child is not found:\n" + "--> Child UUID: %0", + mod_child_root["uuid"].GetString()); + throw prefab_asset_loading_exception(); + } + parse_prefab_inplace( + *child_prefab, + parent_address, + mod_child_root, + dependencies); + } + } + if ( root.HasMember("components") ) { const rapidjson::Value& components_root = root["components"]; for ( rapidjson::Value::ConstMemberIterator component_root = components_root.MemberBegin(); @@ -187,7 +252,15 @@ namespace } } } + } + prefab parse_prefab( + str_view parent_address, + const rapidjson::Value& root, + const asset_group& dependencies) + { + prefab content; + parse_prefab_inplace(content, parent_address, root, dependencies); return content; } } diff --git a/untests/bin/library/parent.json b/untests/bin/library/parent.json index 37baa7e8..afc24701 100644 --- a/untests/bin/library/parent.json +++ b/untests/bin/library/parent.json @@ -8,5 +8,20 @@ "radius" : 5, "offset" : [4,2] } - } + }, + "children" : [{ + "uuid" : "4A93547E-4635-4C2F-9C59-3546E11B1722", + "components" : { + "named" : { + "name" : "child(1)" + } + } + }, { + "uuid" : "58063213-9FC1-457C-B773-B826BE1BE6D7", + "components" : { + "named" : { + "name" : "child(2)" + } + } + }] } diff --git a/untests/bin/library/prefab.json b/untests/bin/library/prefab.json index 12ad20a9..801cc8e8 100644 --- a/untests/bin/library/prefab.json +++ b/untests/bin/library/prefab.json @@ -1,5 +1,5 @@ { - "parent" : "prototype.json", + "parent" : "parent.json", "components" : { "named" : { "name" : "prefab" @@ -12,5 +12,20 @@ "points" : [[1,2],[2,3],[3,4]], "offset" : [8,4] } - } + }, + "mod_children" : [{ + "uuid" : "4A93547E-4635-4C2F-9C59-3546E11B1722", + "components" : { + "widget" : { + "size" : [10,10] + } + } + }, { + "uuid" : "58063213-9FC1-457C-B773-B826BE1BE6D7", + "components" : { + "widget" : { + "size" : [20,20] + } + } + }] } diff --git a/untests/sources/untests_high/node.cpp b/untests/sources/untests_high/node.cpp index 9306eddf..87d72882 100644 --- a/untests/sources/untests_high/node.cpp +++ b/untests/sources/untests_high/node.cpp @@ -9,19 +9,6 @@ using namespace e2d; namespace { - class safe_starter_initializer final : private noncopyable { - public: - safe_starter_initializer() { - modules::initialize(0, nullptr, - starter::parameters( - engine::parameters("world_untests", "enduro2d"))); - } - - ~safe_starter_initializer() noexcept { - modules::shutdown(); - } - }; - class fake_node final : public node { protected: fake_node() : node() { @@ -61,7 +48,6 @@ namespace } TEST_CASE("node") { - safe_starter_initializer initializer; SECTION("empty_node") { auto n = node::create(); REQUIRE(n); diff --git a/untests/sources/untests_high/prefab.cpp b/untests/sources/untests_high/prefab.cpp new file mode 100644 index 00000000..693c642c --- /dev/null +++ b/untests/sources/untests_high/prefab.cpp @@ -0,0 +1,136 @@ +/******************************************************************************* + * This file is part of the "Enduro2D" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "_high.hpp" +using namespace e2d; + +namespace +{ + class safe_starter_initializer final : private noncopyable { + public: + safe_starter_initializer() { + modules::initialize(0, nullptr, + starter::parameters( + engine::parameters("prefab_untests", "enduro2d"))); + } + + ~safe_starter_initializer() noexcept { + modules::shutdown(); + } + }; +} + +TEST_CASE("prefab"){ + safe_starter_initializer initializer; + library& l = the(); + { + auto parent_res = l.load_asset("parent.json"); + REQUIRE(parent_res); + REQUIRE(parent_res->content().uuid() == "73740BC4-CE9F-4A7F-A029-4AB65027A8AE"); + + REQUIRE(parent_res->content().children().size() == 2u); + REQUIRE(parent_res->content().children()[0].uuid() == "4A93547E-4635-4C2F-9C59-3546E11B1722"); + REQUIRE(parent_res->content().children()[1].uuid() == "58063213-9FC1-457C-B773-B826BE1BE6D7"); + + auto go = the().instantiate(parent_res->content()); + REQUIRE(go); + + REQUIRE(go.component()); + REQUIRE(go.component()->name() == "parent"); + + REQUIRE(go.component()); + REQUIRE(math::approximately(go.component()->radius(), 5.f)); + REQUIRE(go.component()->offset() == v2f(4.f,2.f)); + + { + node_iptr go_node = go.component() + ? go.component()->node() + : node_iptr(); + + REQUIRE(go_node); + REQUIRE(go_node->owner() == go); + REQUIRE(go_node->child_count() == 2u); + + node_iptr child1 = go_node->child_at(0u); + node_iptr child2 = go_node->child_at(1u); + + REQUIRE(child1); + REQUIRE(child2); + + gobject child1_go = child1->owner(); + gobject child2_go = child2->owner(); + + REQUIRE(child1_go); + REQUIRE(child2_go); + + REQUIRE(child1_go.component()); + REQUIRE(child1_go.component()->name() == "child(1)"); + + REQUIRE(child2_go.component()); + REQUIRE(child2_go.component()->name() == "child(2)"); + } + } + + { + auto prefab_res = l.load_asset("prefab.json"); + REQUIRE(prefab_res); + REQUIRE(prefab_res->content().uuid().empty()); + + REQUIRE(prefab_res->content().children().size() == 2u); + REQUIRE(prefab_res->content().children()[0].uuid() == "4A93547E-4635-4C2F-9C59-3546E11B1722"); + REQUIRE(prefab_res->content().children()[1].uuid() == "58063213-9FC1-457C-B773-B826BE1BE6D7"); + + auto go = the().instantiate(prefab_res->content()); + REQUIRE(go); + + REQUIRE(go.component()); + REQUIRE(go.component()->name() == "prefab"); + + REQUIRE(go.component()); + REQUIRE(go.component()->size() == v2f(1.f,2.f)); + REQUIRE(go.component()->offset() == v2f(2.f,4.f)); + + REQUIRE(go.component()); + REQUIRE(math::approximately(go.component()->radius(), 5.f)); + REQUIRE(go.component()->offset() == v2f(4.f,2.f)); + + REQUIRE(go.component()); + REQUIRE(go.component()->points() == vector{{1,2},{2,3},{3,4}}); + REQUIRE(go.component()->offset() == v2f(8.f,4.f)); + + { + node_iptr go_node = go.component() + ? go.component()->node() + : node_iptr(); + + REQUIRE(go_node); + REQUIRE(go_node->owner() == go); + REQUIRE(go_node->child_count() == 2u); + + node_iptr child1 = go_node->child_at(0u); + node_iptr child2 = go_node->child_at(1u); + + REQUIRE(child1); + REQUIRE(child2); + + gobject child1_go = child1->owner(); + gobject child2_go = child2->owner(); + + REQUIRE(child1_go); + REQUIRE(child2_go); + + REQUIRE(child1_go.component()); + REQUIRE(child1_go.component()->name() == "child(1)"); + + REQUIRE(child1_go.component()); + REQUIRE(child1_go.component()->size() == v2f(10.f, 10.f)); + + REQUIRE(child2_go.component()); + REQUIRE(child2_go.component()->name() == "child(2)"); + REQUIRE(child2_go.component()->size() == v2f(20.f, 20.f)); + } + } +}