diff --git a/sources/enduro2d/high/assets/model_asset.cpp b/sources/enduro2d/high/assets/model_asset.cpp index 5d880e9e..d53eba29 100644 --- a/sources/enduro2d/high/assets/model_asset.cpp +++ b/sources/enduro2d/high/assets/model_asset.cpp @@ -17,6 +17,75 @@ namespace return "model asset loading exception"; } }; + + const char* model_asset_schema_source = R"json( + { + "type" : "object", + "required" : [ "mesh" ], + "additionalProperties" : false, + "properties" : { + "mesh" : { "$ref": "#/common_definitions/address" }, + "materials" : { + "type" : "array", + "items" : { "$ref" : "#/common_definitions/address" } + } + } + })json"; + + const rapidjson::SchemaDocument& model_asset_schema() { + static std::mutex mutex; + static std::unique_ptr schema; + + std::lock_guard guard(mutex); + if ( !schema ) { + rapidjson::Document doc; + if ( doc.Parse(model_asset_schema_source).HasParseError() ) { + the().error("ASSETS: Failed to parse model asset schema"); + throw model_asset_loading_exception(); + } + json_utils::add_common_schema_definitions(doc); + schema = std::make_unique(doc); + } + + return *schema; + } + + stdex::promise parse_model( + library& library, + str_view parent_address, + const rapidjson::Value& root) + { + E2D_ASSERT(root.HasMember("mesh") && root["mesh"].IsString()); + auto mesh_p = library.load_asset_async( + path::combine(parent_address, root["mesh"].GetString())); + + vector> materials_p; + if ( root.HasMember("materials") ) { + E2D_ASSERT(root["materials"].IsArray()); + const auto& materials_json = root["materials"]; + + materials_p.reserve(materials_json.Size()); + for ( rapidjson::SizeType i = 0; i < materials_json.Size(); ++i ) { + E2D_ASSERT(materials_json[i].IsString()); + materials_p.emplace_back( + library.load_asset_async( + path::combine(parent_address, materials_json[i].GetString()))); + } + } + + return stdex::make_tuple_promise(std::make_tuple( + std::move(mesh_p), + stdex::make_all_promise(materials_p))) + .then([](const std::tuple< + mesh_asset::load_result, + vector + >& results){ + model content; + content.set_mesh(std::get<0>(results)); + content.set_materials(std::get<1>(results)); + return content; + }); + } } namespace e2d @@ -25,9 +94,27 @@ namespace e2d library& library, str_view address) { return library.load_asset_async(address) - .then([](const json_asset::load_result& model_data){ - model mdl; - return model_asset::create(std::move(mdl)); + .then([ + &library, + parent_address = path::parent_path(address) + ](const json_asset::load_result& model_data){ + if ( !modules::is_initialized() ) { + throw model_asset_loading_exception(); + } + return the().do_in_worker_thread([model_data](){ + const rapidjson::Document& doc = model_data->content(); + rapidjson::SchemaValidator validator(model_asset_schema()); + if ( !doc.Accept(validator) ) { + throw model_asset_loading_exception(); + } + }) + .then([&library, parent_address, model_data](){ + return parse_model( + library, parent_address, model_data->content()); + }) + .then([](const model& model){ + return model_asset::create(model); }); + }); } } diff --git a/sources/enduro2d/high/assets/sprite_asset.cpp b/sources/enduro2d/high/assets/sprite_asset.cpp index 27895627..bd72d998 100644 --- a/sources/enduro2d/high/assets/sprite_asset.cpp +++ b/sources/enduro2d/high/assets/sprite_asset.cpp @@ -17,6 +17,95 @@ namespace return "sprite asset loading exception"; } }; + + const char* sprite_asset_schema_source = R"json( + { + "type" : "object", + "required" : [ "size", "pivot", "texrect" ], + "additionalProperties" : false, + "properties" : { + "size" : { "$ref": "#/common_definitions/v2" }, + "pivot" : { "$ref": "#/common_definitions/v2" }, + "texrect" : { "$ref": "#/common_definitions/b2" }, + "texture" : { "$ref": "#/common_definitions/address" }, + "material" : { "$ref": "#/common_definitions/address" } + } + })json"; + + const rapidjson::SchemaDocument& sprite_asset_schema() { + static std::mutex mutex; + static std::unique_ptr schema; + + std::lock_guard guard(mutex); + if ( !schema ) { + rapidjson::Document doc; + if ( doc.Parse(sprite_asset_schema_source).HasParseError() ) { + the().error("ASSETS: Failed to parse sprite asset schema"); + throw sprite_asset_loading_exception(); + } + json_utils::add_common_schema_definitions(doc); + schema = std::make_unique(doc); + } + + return *schema; + } + + stdex::promise parse_sprite( + library& library, + str_view parent_address, + const rapidjson::Value& root) + { + v2f size; + E2D_ASSERT(root.HasMember("size")); + if ( !json_utils::try_parse_value(root["size"], size) ) { + return stdex::make_rejected_promise( + sprite_asset_loading_exception()); + } + + v2f pivot; + E2D_ASSERT(root.HasMember("pivot")); + if ( !json_utils::try_parse_value(root["pivot"], pivot) ) { + return stdex::make_rejected_promise( + sprite_asset_loading_exception()); + } + + b2f texrect; + E2D_ASSERT(root.HasMember("texrect")); + if ( !json_utils::try_parse_value(root["texrect"], texrect) ) { + return stdex::make_rejected_promise( + sprite_asset_loading_exception()); + } + + E2D_ASSERT(!root.HasMember("texture") || root["texture"].IsString()); + auto texture_p = root.HasMember("texture") + ? library.load_asset_async(path::combine( + parent_address, + root["texture"].GetString())) + : stdex::make_resolved_promise(texture_asset::ptr()); + + E2D_ASSERT(!root.HasMember("material") || root["material"].IsString()); + auto material_p = root.HasMember("material") + ? library.load_asset_async(path::combine( + parent_address, + root["material"].GetString())) + : stdex::make_resolved_promise(material_asset::ptr()); + + return stdex::make_tuple_promise(std::make_tuple( + std::move(texture_p), + std::move(material_p) + )).then([size, pivot, texrect](const std::tuple< + texture_asset::load_result, + material_asset::load_result + >& results){ + sprite content; + content.set_size(size); + content.set_pivot(pivot); + content.set_texrect(texrect); + content.set_texture(std::get<0>(results)); + content.set_material(std::get<1>(results)); + return content; + }); + } } namespace e2d @@ -25,9 +114,27 @@ namespace e2d library& library, str_view address) { return library.load_asset_async(address) - .then([](const json_asset::load_result& sprite_data){ - sprite spr; - return sprite_asset::create(std::move(spr)); + .then([ + &library, + parent_address = path::parent_path(address) + ](const json_asset::load_result& sprite_data){ + if ( !modules::is_initialized() ) { + throw sprite_asset_loading_exception(); + } + return the().do_in_worker_thread([sprite_data](){ + const rapidjson::Document& doc = sprite_data->content(); + rapidjson::SchemaValidator validator(sprite_asset_schema()); + if ( !doc.Accept(validator) ) { + throw sprite_asset_loading_exception(); + } + }) + .then([&library, parent_address, sprite_data](){ + return parse_sprite( + library, parent_address, sprite_data->content()); + }) + .then([](const sprite& sprite){ + return sprite_asset::create(sprite); }); + }); } } diff --git a/untests/bin/library/mesh.e2d_mesh b/untests/bin/library/mesh.e2d_mesh new file mode 100644 index 00000000..e7edaa33 --- /dev/null +++ b/untests/bin/library/mesh.e2d_mesh @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ab164b79bb190d2ffb325801de93a71b5d7059ad78da21b324532c680029951 +size 20856 diff --git a/untests/bin/library/model.json b/untests/bin/library/model.json new file mode 100644 index 00000000..a7a37cbc --- /dev/null +++ b/untests/bin/library/model.json @@ -0,0 +1,6 @@ +{ + "mesh" : "mesh.e2d_mesh", + "materials" : [ + "material.json" + ] +} diff --git a/untests/bin/library/sprite.json b/untests/bin/library/sprite.json new file mode 100644 index 00000000..dcd83cd3 --- /dev/null +++ b/untests/bin/library/sprite.json @@ -0,0 +1,7 @@ +{ + "size" : { "x" : 10, "y" : 20 }, + "pivot" : { "x" : 0.5, "y" : 0.7 }, + "texrect" : { "w" : 0.5, "h" : 1 }, + "texture" : "image.png", + "material" : "material.json" +} diff --git a/untests/sources/untests_high/library.cpp b/untests/sources/untests_high/library.cpp index e4b5cc54..deb7b43d 100644 --- a/untests/sources/untests_high/library.cpp +++ b/untests/sources/untests_high/library.cpp @@ -94,6 +94,23 @@ TEST_CASE("library"){ auto texture_res = l.load_asset("image.png"); REQUIRE(texture_res); REQUIRE(texture_res->content()); + + auto sprite_res = l.load_asset("sprite.json"); + REQUIRE(sprite_res); + REQUIRE(sprite_res->content().size() == v2f(10.f, 20.f)); + REQUIRE(sprite_res->content().pivot() == v2f(0.5f, 0.7f)); + REQUIRE(sprite_res->content().texrect() == b2f(0.5f, 1.f)); + REQUIRE(sprite_res->content().texture()); + REQUIRE(sprite_res->content().material()); + + auto model_res = l.load_asset("model.json"); + REQUIRE(model_res); + REQUIRE(model_res->content().mesh()); + REQUIRE(model_res->content().material_count() == 1); + REQUIRE(model_res->content().material(0)); + REQUIRE_FALSE(model_res->content().mesh()->content().vertices().empty()); + REQUIRE(model_res->content().mesh()->content().indices_submesh_count() == 1); + REQUIRE_FALSE(model_res->content().mesh()->content().indices(0).empty()); } } {