From c7afd46c2b0d395a8ee35b48a24a8000f567cd14 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Mon, 26 Aug 2019 09:18:22 +0700 Subject: [PATCH] rewrite spine model loading to async method --- headers/enduro2d/high/_all.hpp | 5 +- headers/enduro2d/high/_high.hpp | 5 +- headers/enduro2d/high/spine_model.hpp | 33 +- modules/spine | 2 +- samples/bin/library/spine_coin.json | 7 +- samples/bin/library/spine_raptor.json | 32 +- .../high/assets/spine_model_asset.cpp | 459 +++++++++++------- sources/enduro2d/high/spine_model.cpp | 91 ++-- .../render_system_drawer.cpp | 3 +- 9 files changed, 376 insertions(+), 261 deletions(-) diff --git a/headers/enduro2d/high/_all.hpp b/headers/enduro2d/high/_all.hpp index adc1d28f..5fc9cc6a 100644 --- a/headers/enduro2d/high/_all.hpp +++ b/headers/enduro2d/high/_all.hpp @@ -21,6 +21,7 @@ #include "assets/shader_asset.hpp" #include "assets/shape_asset.hpp" #include "assets/sound_asset.hpp" +#include "assets/spine_model_asset.hpp" #include "assets/sprite_asset.hpp" #include "assets/text_asset.hpp" #include "assets/texture_asset.hpp" @@ -34,9 +35,9 @@ #include "components/model_renderer.hpp" #include "components/renderer.hpp" #include "components/scene.hpp" -#include "components/sprite_renderer.hpp" -#include "components/spine_renderer.hpp" #include "components/spine_player.hpp" +#include "components/spine_renderer.hpp" +#include "components/sprite_renderer.hpp" #include "systems/flipbook_system.hpp" #include "systems/label_system.hpp" diff --git a/headers/enduro2d/high/_high.hpp b/headers/enduro2d/high/_high.hpp index c861e830..b5ffc9ad 100644 --- a/headers/enduro2d/high/_high.hpp +++ b/headers/enduro2d/high/_high.hpp @@ -33,6 +33,7 @@ namespace e2d class shader_asset; class shape_asset; class sound_asset; + class spine_model_asset; class sprite_asset; class text_asset; class texture_asset; @@ -46,9 +47,9 @@ namespace e2d class model_renderer; class renderer; class scene; - class sprite_renderer; - class spine_renderer; class spine_player; + class spine_renderer; + class sprite_renderer; class flipbook_system; class label_system; diff --git a/headers/enduro2d/high/spine_model.hpp b/headers/enduro2d/high/spine_model.hpp index b82d6d62..41e28f1b 100644 --- a/headers/enduro2d/high/spine_model.hpp +++ b/headers/enduro2d/high/spine_model.hpp @@ -8,20 +8,26 @@ #include "_high.hpp" -struct spAnimationStateData; -struct spSkeletonData; struct spAtlas; +struct spSkeletonData; +struct spAnimationStateData; namespace e2d { + class bad_spine_model_access final : public exception { + public: + const char* what() const noexcept final { + return "bad spine model access"; + } + }; class spine_model final { public: - using animation_data_ptr = std::shared_ptr; - using skeleton_data_ptr = std::shared_ptr; using atlas_ptr = std::shared_ptr; + using skeleton_data_ptr = std::shared_ptr; + using animation_data_ptr = std::shared_ptr; public: - spine_model() noexcept = default; + spine_model() = default; ~spine_model() noexcept = default; spine_model(spine_model&& other) noexcept; @@ -36,21 +42,22 @@ namespace e2d spine_model& assign(spine_model&& other) noexcept; spine_model& assign(const spine_model& other); - spine_model& set_skeleton(skeleton_data_ptr data); - spine_model& set_atlas(atlas_ptr atlas, bool premultiplied_alpha); + spine_model& set_atlas(atlas_ptr atlas); + spine_model& set_skeleton(skeleton_data_ptr skeleton); - spine_model& mix_animations(const str& from, const str& to, secf duration); spine_model& set_default_mix(secf duration); + spine_model& set_animation_mix( + const str& from, + const str& to, + secf duration); const atlas_ptr& atlas() const noexcept; - const animation_data_ptr& animation() const noexcept; const skeleton_data_ptr& skeleton() const noexcept; - bool premultiplied_alpha() const noexcept; + const animation_data_ptr& animation() const noexcept; private: - animation_data_ptr animation_; - skeleton_data_ptr skeleton_; atlas_ptr atlas_; - bool premultiplied_alpha_ = false; + skeleton_data_ptr skeleton_; + animation_data_ptr animation_; }; void swap(spine_model& l, spine_model& r) noexcept; diff --git a/modules/spine b/modules/spine index 3432d163..45b8125a 160000 --- a/modules/spine +++ b/modules/spine @@ -1 +1 @@ -Subproject commit 3432d16374e3c857b33462f2793a4bc40d04d60c +Subproject commit 45b8125a8eb2f69e692d835b0147e4e4775607a9 diff --git a/samples/bin/library/spine_coin.json b/samples/bin/library/spine_coin.json index 9e56fa47..7df2f2c7 100644 --- a/samples/bin/library/spine_coin.json +++ b/samples/bin/library/spine_coin.json @@ -1,6 +1,5 @@ { - "skeleton" : "coin/coin-pro.skel", "atlas" : "coin/coin-pma.atlas", - "scale" : 1.0, - "premultiplied_alpha" : true -} \ No newline at end of file + "skeleton" : "coin/coin-pro.skel", + "skeleton_scale" : 1.0 +} diff --git a/samples/bin/library/spine_raptor.json b/samples/bin/library/spine_raptor.json index 93f11198..3e86aa10 100644 --- a/samples/bin/library/spine_raptor.json +++ b/samples/bin/library/spine_raptor.json @@ -1,37 +1,37 @@ { - "skeleton": "raptor/raptor-pro.json-large", "atlas": "raptor/raptor.atlas", - "scale": 1.0, - "premultiplied_alpha": false, - "mix_animations": [ + "skeleton": "raptor/raptor-pro.json-large", + "skeleton_scale": 1.0, + "default_animation_mix" : 0.5, + "animation_mixes": [ { - "from_anim": "walk", - "to_anim": "roar", + "from": "walk", + "to": "roar", "duration": 0.5 }, { - "from_anim": "roar", - "to_anim": "walk", + "from": "roar", + "to": "walk", "duration": 0.5 }, { - "from_anim": "walk", - "to_anim": "jump", + "from": "walk", + "to": "jump", "duration": 0.5 }, { - "from_anim": "jump", - "to_anim": "walk", + "from": "jump", + "to": "walk", "duration": 0.5 }, { - "from_anim": "roar", - "to_anim": "jump", + "from": "roar", + "to": "jump", "duration": 0.5 }, { - "from_anim": "jump", - "to_anim": "roar", + "from": "jump", + "to": "roar", "duration": 0.5 } ] diff --git a/sources/enduro2d/high/assets/spine_model_asset.cpp b/sources/enduro2d/high/assets/spine_model_asset.cpp index 13a05043..9324c375 100644 --- a/sources/enduro2d/high/assets/spine_model_asset.cpp +++ b/sources/enduro2d/high/assets/spine_model_asset.cpp @@ -5,18 +5,18 @@ ******************************************************************************/ #include + #include #include #include -#include -#include +#include #include -using namespace e2d; - namespace { + using namespace e2d; + class spine_model_asset_loading_exception final : public asset_loading_exception { const char* what() const noexcept final { return "spine model asset loading exception"; @@ -25,28 +25,27 @@ namespace const char* spine_model_asset_schema_source = R"json({ "type" : "object", - "required" : [ "skeleton", "atlas" ], + "required" : [ "atlas", "skeleton" ], "additionalProperties" : false, "properties" : { - "skeleton" : { "$ref" : "#/common_definitions/address" }, - "scale" : { "type" : "number" }, "atlas" : { "$ref" : "#/common_definitions/address" }, - "premultiplied_alpha" : { "type" : "boolean" }, - "default_mix" : { "type" : "number" }, - "mix_animations" : { "$ref": "#/definitions/spine_animation_mix_array" } + "skeleton" : { "$ref" : "#/common_definitions/address" }, + "skeleton_scale" : { "type" : "number" }, + "default_animation_mix" : { "type" : "number" }, + "animation_mixes" : { "$ref": "#/definitions/animation_mixes" } }, "definitions" : { - "spine_animation_mix_array" : { + "animation_mixes" : { "type" : "array", - "items" : { "$ref": "#/definitions/spine_animation_mix" } + "items" : { "$ref": "#/definitions/animation_mix" } }, - "spine_animation_mix" : { + "animation_mix" : { "type" : "object", - "required" : [ "from_anim", "to_anim", "duration" ], + "required" : [ "from", "to", "duration" ], "additionalProperties" : false, "properties" : { - "from_anim" : { "$ref": "#/common_definitions/name" }, - "to_anim" : { "$ref": "#/common_definitions/name" }, + "from" : { "$ref": "#/common_definitions/name" }, + "to" : { "$ref": "#/common_definitions/name" }, "duration" : { "type" : "number" } } } @@ -71,40 +70,182 @@ namespace return *schema; } - bool parse_mix_animations( - const rapidjson::Value& root, - spine_model& model) - { - E2D_ASSERT(root.IsArray()); - str from_anim; - str to_anim; + struct animation_mix { + str from; + str to; secf duration; + }; - for ( rapidjson::SizeType i = 0; i < root.Size(); ++i ) { - E2D_ASSERT(root[i].IsObject()); - const auto& item_json = root[i]; + animation_mix parse_animation_mix(const rapidjson::Value& root) { + animation_mix mix; - E2D_ASSERT(item_json.HasMember("from_anim")); - if ( !json_utils::try_parse_value(item_json["from_anim"], from_anim) ) { - the().error("SPINE: Incorrect formatting of 'from_anim' property"); - return false; - } - - E2D_ASSERT(item_json.HasMember("to_anim")); - if ( !json_utils::try_parse_value(item_json["to_anim"], to_anim) ) { - the().error("SPINE: Incorrect formatting of 'to_anim' property"); - return false; - } - - E2D_ASSERT(item_json.HasMember("duration")); - if ( !json_utils::try_parse_value(item_json["duration"], duration.value) ) { - the().error("SPINE: Incorrect formatting of 'duration' property"); - return false; - } - - model.mix_animations(from_anim, to_anim, duration); + E2D_ASSERT(root.HasMember("from") && root["from"].IsString()); + if ( !json_utils::try_parse_value(root["from"], mix.from) ) { + the().error("SPINE: Incorrect formating of 'from' property"); + throw spine_model_asset_loading_exception(); } - return true; + + E2D_ASSERT(root.HasMember("to") && root["to"].IsString()); + if ( !json_utils::try_parse_value(root["to"], mix.to) ) { + the().error("SPINE: Incorrect formating of 'to' property"); + throw spine_model_asset_loading_exception(); + } + + E2D_ASSERT(root.HasMember("duration") && root["duration"].IsNumber()); + if ( !json_utils::try_parse_value(root["duration"], mix.duration) ) { + the().error("SPINE: Incorrect formating of 'duration' property"); + throw spine_model_asset_loading_exception(); + } + + return mix; + } + + struct atlas_internal_state { + asset_group loaded; + asset_dependencies loading; + }; + + stdex::promise load_atlas( + const library& library, + const str& parent_address, + const str& atlas_address) + { + return library.load_asset_async( + path::combine(parent_address, atlas_address)) + .then([ + &library, + parent_address + ](const binary_asset::load_result& atlas_data){ + auto atlas_internal = std::make_unique(); + + spine_model::atlas_ptr atlas( + spAtlas_create( + reinterpret_cast(atlas_data->content().data()), + math::numeric_cast(atlas_data->content().size()), + parent_address.c_str(), + atlas_internal.get()), + spAtlas_dispose); + + if ( !atlas ) { + the().error("SPINE: Failed to create preload atlas"); + throw spine_model_asset_loading_exception(); + } + + return stdex::make_tuple_promise(std::make_tuple( + atlas_internal->loading.load_async(library), + stdex::make_resolved_promise(atlas_data))); + }) + .then([ + parent_address + ](const std::tuple< + asset_group, + binary_asset::load_result + >& results){ + auto atlas_internal = std::make_unique(); + atlas_internal->loaded = std::get<0>(results); + + spine_model::atlas_ptr atlas( + spAtlas_create( + reinterpret_cast(std::get<1>(results)->content().data()), + math::numeric_cast(std::get<1>(results)->content().size()), + parent_address.c_str(), + atlas_internal.get()), + spAtlas_dispose); + + if ( !atlas ) { + the().error("SPINE: Failed to create preloaded atlas"); + throw spine_model_asset_loading_exception(); + } + + for ( const spAtlasPage* page = atlas->pages; page; page = page->next ) { + if ( !page->rendererObject ) { + the().error("SPINE: Failed to create preloaded atlas"); + throw spine_model_asset_loading_exception(); + } + } + + atlas->rendererObject = nullptr; + return atlas; + }); + } + + stdex::promise load_skeleton_data( + const library& library, + const str& parent_address, + const str& skeleton_address, + f32 skeleton_scale, + const spine_model::atlas_ptr& atlas) + { + return library.load_asset_async( + path::combine(parent_address, skeleton_address)) + .then([ + atlas, + skeleton_scale, + skeleton_address + ](const binary_asset::load_result& skeleton_data){ + if ( strings::ends_with(skeleton_address, ".skel") ) { + using skeleton_bin_ptr = std::unique_ptr< + spSkeletonBinary, + decltype(&::spSkeletonBinary_dispose)>; + + skeleton_bin_ptr binary_skeleton( + spSkeletonBinary_create(atlas.get()), + spSkeletonBinary_dispose); + + if ( !binary_skeleton ) { + the().error("SPINE: Failed to create binary skeleton"); + throw spine_model_asset_loading_exception(); + } + + binary_skeleton->scale = skeleton_scale; + + spine_model::skeleton_data_ptr data_skeleton( + spSkeletonBinary_readSkeletonData( + binary_skeleton.get(), + reinterpret_cast(skeleton_data->content().data()), + math::numeric_cast(skeleton_data->content().size())), + spSkeletonData_dispose); + + if ( !data_skeleton ) { + the().error("SPINE: Failed to read binary skeleton data:\n" + "--> Error: %0", + binary_skeleton->error); + throw spine_model_asset_loading_exception(); + } + + return data_skeleton; + } else { + using skeleton_json_ptr = std::unique_ptr< + spSkeletonJson, + decltype(&::spSkeletonJson_dispose)>; + + skeleton_json_ptr json_skeleton( + spSkeletonJson_create(atlas.get()), + spSkeletonJson_dispose); + + if ( !json_skeleton ) { + the().error("SPINE: Failed to create json skeleton"); + throw spine_model_asset_loading_exception(); + } + + json_skeleton->scale = skeleton_scale; + + spine_model::skeleton_data_ptr data_skeleton( + spSkeletonJson_readSkeletonData( + json_skeleton.get(), + reinterpret_cast(skeleton_data->content().data())), + spSkeletonData_dispose); + + if ( !data_skeleton ) { + the().error("SPINE: Failed to read json skeleton data:\n" + "--> Error: %0", + json_skeleton->error); + throw spine_model_asset_loading_exception(); + } + + return data_skeleton; + } + }); } stdex::promise parse_spine_model( @@ -112,150 +253,77 @@ namespace const str& parent_address, const rapidjson::Value& root) { - using skeleton_json_ptr = std::unique_ptr; - using skeleton_bin_ptr = std::unique_ptr; + f32 skeleton_scale{1.0f}; + if ( root.HasMember("skeleton_scale") ) { + if ( !json_utils::try_parse_value(root["skeleton_scale"], skeleton_scale) ) { + the().error("SPINE: Incorrect formating of 'skeleton_scale' property"); + } + } + + secf default_animation_mix{0.5f}; + if ( root.HasMember("default_animation_mix") ) { + if ( !json_utils::try_parse_value(root["default_animation_mix"], default_animation_mix) ) { + the().error("SPINE: Incorrect formating of 'default_animation_mix' property"); + } + } + + vector animation_mixes; + if ( root.HasMember("animation_mixes") ) { + E2D_ASSERT(root["animation_mixes"].IsArray()); + const auto& mixes_json = root["animation_mixes"]; + animation_mixes.reserve(mixes_json.Size()); + for ( rapidjson::SizeType i = 0; i < mixes_json.Size(); ++i ) { + E2D_ASSERT(mixes_json[i].IsObject()); + animation_mixes.push_back( + parse_animation_mix(mixes_json[i])); + } + } E2D_ASSERT(root.HasMember("atlas") && root["atlas"].IsString()); - binary_asset::load_result atlas_data = library.load_asset( - path::combine(parent_address, root["atlas"].GetString())); - spine_model::atlas_ptr atlas( - spAtlas_create( - reinterpret_cast(atlas_data->content().data()), - math::numeric_cast(atlas_data->content().size()), - parent_address.data(), - nullptr), - spAtlas_dispose); - - float skeleton_scale = 1.0f; - if ( root.HasMember("scale") ) { - if ( !json_utils::try_parse_value(root["scale"], skeleton_scale) ) { - the().error("SPINE: Incorrect formating of 'scale' property"); - } - } + const str atlas_address = root["atlas"].GetString(); E2D_ASSERT(root.HasMember("skeleton") && root["skeleton"].IsString()); - const str ext = path::extension(root["skeleton"].GetString()); - spine_model::skeleton_data_ptr skeleton; + const str skeleton_address = root["skeleton"].GetString(); - if ( ext == ".skel" ) { - skeleton_bin_ptr skeleton_binary( - spSkeletonBinary_create(atlas.get()), - spSkeletonBinary_dispose); - skeleton_binary->scale = skeleton_scale; - - binary_asset::load_result skeleton_data = library.load_asset( - path::combine(parent_address, root["skeleton"].GetString())); - skeleton = spine_model::skeleton_data_ptr( - spSkeletonBinary_readSkeletonData( - skeleton_binary.get(), - reinterpret_cast(skeleton_data->content().data()), - math::numeric_cast(skeleton_data->content().size())), - spSkeletonData_dispose); - - if ( !skeleton ) { - the().error("SPINE: Failed to read skeleton binary data:\n" - "--> Error: $s", - skeleton_binary->error); - return stdex::make_rejected_promise( - spine_model_asset_loading_exception()); + return load_atlas( + library, + parent_address, + atlas_address) + .then([ + &library, + parent_address, + atlas_address, + skeleton_scale, + skeleton_address + ](const spine_model::atlas_ptr& atlas){ + return stdex::make_tuple_promise(std::make_tuple( + stdex::make_resolved_promise(atlas), + load_skeleton_data( + library, + parent_address, + skeleton_address, + skeleton_scale, + atlas))); + }) + .then([ + default_animation_mix, + animation_mixes = std::move(animation_mixes) + ](const std::tuple< + spine_model::atlas_ptr, + spine_model::skeleton_data_ptr + >& results){ + spine_model content; + content.set_atlas(std::get<0>(results)); + content.set_skeleton(std::get<1>(results)); + content.set_default_mix(default_animation_mix); + for ( const animation_mix& mix : animation_mixes ) { + content.set_animation_mix(mix.from, mix.to, mix.duration); } - } else { - skeleton_json_ptr skeleton_json( - spSkeletonJson_create(atlas.get()), - spSkeletonJson_dispose); - skeleton_json->scale = skeleton_scale; - - binary_asset::load_result skeleton_data = library.load_asset( - path::combine(parent_address, root["skeleton"].GetString())); - skeleton = spine_model::skeleton_data_ptr( - spSkeletonJson_readSkeletonData( - skeleton_json.get(), - reinterpret_cast(skeleton_data->content().data())), - spSkeletonData_dispose); - - if ( !skeleton ) { - the().error("SPINE: Failed to read skeleton json data:\n" - "--> Error: $s", - skeleton_json->error); - return stdex::make_rejected_promise( - spine_model_asset_loading_exception()); - } - } - - - bool pma = false; - if ( root.HasMember("premultiplied_alpha") ) { - if ( !json_utils::try_parse_value(root["premultiplied_alpha"], pma) ) { - the().error("SPINE: Incorrect formating of 'premultiplied_alpha' property"); - } - } - - spine_model content; - content.set_atlas(atlas, pma); - content.set_skeleton(skeleton); - - secf default_mix(0.0f); - if ( root.HasMember("default_mix") ) { - if ( json_utils::try_parse_value(root["default_mix"], default_mix.value) ) { - content.set_default_mix(default_mix); - } else { - the().error("SPINE: Incorrect formating of 'default_mix' property"); - } - } - - if ( root.HasMember("mix_animations") ) { - const auto& mix_animations_json = root["mix_animations"]; - if ( !parse_mix_animations(mix_animations_json, content) ) { - return stdex::make_rejected_promise( - spine_model_asset_loading_exception()); - } - } - - return stdex::make_resolved_promise(std::move(content)); + return content; + }); } } -extern "C" void _spAtlasPage_createTexture (spAtlasPage* self, const char* path) { - try { - texture_asset::load_result texture = the().load_asset(path); - if ( !texture ) { - throw; - } - self->width = texture->content()->size().x; - self->height = texture->content()->size().y; - self->rendererObject = texture.release(); - } catch(...) { - the().error("SPINE: Failed to load atlas texture"); - } -} - -extern "C" void _spAtlasPage_disposeTexture (spAtlasPage* self) { - // decrease ref counter - E2D_UNUSED(texture_asset::ptr(static_cast(self->rendererObject), false)); -} - -extern "C" char* _spUtil_readFile (const char* path, int* length) { - try { - binary_asset::load_result file = the().load_asset(path); - if ( !file ) { - throw; - } - if ( file->content().size() > std::numeric_limits::max() ) { - throw; - } - *length = math::numeric_cast(file->content().size()); - char* data = MALLOC(char, *length); - if ( !data ) { - throw; - } - memcpy(data, file->content().data(), *length); - return data; - } catch(...) { - the().error("SPINE: Failed to read file"); - } - return nullptr; -} - namespace e2d { spine_model_asset::load_async_result spine_model_asset::load_async( @@ -301,3 +369,32 @@ namespace e2d }); } } + +extern "C" void _spAtlasPage_createTexture(spAtlasPage* self, const char* path) { + try { + E2D_ASSERT(self->atlas->rendererObject); + atlas_internal_state& atlas_internal = + *static_cast(self->atlas->rendererObject); + auto texture_res = atlas_internal.loaded.find_asset(path); + if ( texture_res ) { + self->width = math::numeric_cast(texture_res->content()->size().x); + self->height = math::numeric_cast(texture_res->content()->size().y); + self->rendererObject = texture_res.release(); + } else { + atlas_internal.loading.add_dependency(path); + } + } catch(...) { + // nothing + } +} + +extern "C" void _spAtlasPage_disposeTexture(spAtlasPage* self) { + E2D_UNUSED(texture_asset::load_result( + static_cast(self->rendererObject), false)); +} + +extern "C" char* _spUtil_readFile(const char* path, int* length) { + E2D_ASSERT_MSG(false, "unimplemented by design"); + E2D_UNUSED(path, length); + return nullptr; +} diff --git a/sources/enduro2d/high/spine_model.cpp b/sources/enduro2d/high/spine_model.cpp index 0575e647..08af0451 100644 --- a/sources/enduro2d/high/spine_model.cpp +++ b/sources/enduro2d/high/spine_model.cpp @@ -5,9 +5,8 @@ ******************************************************************************/ #include -#include -#include -#include + +#include namespace e2d { @@ -28,17 +27,16 @@ namespace e2d } void spine_model::clear() noexcept { - animation_.reset(); - skeleton_.reset(); atlas_.reset(); + skeleton_.reset(); + animation_.reset(); } void spine_model::swap(spine_model& other) noexcept { using std::swap; - swap(animation_, other.animation_); - swap(skeleton_, other.skeleton_); swap(atlas_, other.atlas_); - swap(premultiplied_alpha_, other.premultiplied_alpha_); + swap(skeleton_, other.skeleton_); + swap(animation_, other.animation_); } spine_model& spine_model::assign(spine_model&& other) noexcept { @@ -52,61 +50,73 @@ namespace e2d spine_model& spine_model::assign(const spine_model& other) { if ( this != &other ) { spine_model m; - m.animation_ = other.animation_; - m.skeleton_ = other.skeleton_; m.atlas_ = other.atlas_; - m.premultiplied_alpha_ = other.premultiplied_alpha_; + m.skeleton_ = other.skeleton_; + m.animation_ = other.animation_; swap(m); } return *this; } - spine_model& spine_model::set_skeleton(skeleton_data_ptr data) { - animation_.reset(); - skeleton_ = std::move(data); - if ( skeleton_ ) { - animation_ = animation_data_ptr( - spAnimationStateData_create(skeleton_.get()), - spAnimationStateData_dispose); - } - return *this; - } - - spine_model& spine_model::set_atlas(atlas_ptr atlas, bool premultiplied_alpha) { + spine_model& spine_model::set_atlas(atlas_ptr atlas) { atlas_ = std::move(atlas); - premultiplied_alpha_ = premultiplied_alpha; return *this; } - spine_model& spine_model::mix_animations(const str& from, const str& to, secf duration) { - E2D_ASSERT(animation_); - E2D_ASSERT(spSkeletonData_findAnimation(animation_->skeletonData, from.c_str())); - E2D_ASSERT(spSkeletonData_findAnimation(animation_->skeletonData, to.c_str())); - - spAnimationStateData_setMixByName(animation_.get(), from.c_str(), to.c_str(), duration.value); + spine_model& spine_model::set_skeleton(skeleton_data_ptr skeleton) { + animation_data_ptr animation; + if ( skeleton ) { + animation.reset( + spAnimationStateData_create(skeleton.get()), + spAnimationStateData_dispose); + if ( !animation ) { + throw std::bad_alloc(); + } + } + skeleton_ = std::move(skeleton); + animation_ = std::move(animation); return *this; } - + spine_model& spine_model::set_default_mix(secf duration) { - E2D_ASSERT(animation_); + if ( !animation_ ) { + throw bad_spine_model_access(); + } animation_->defaultMix = duration.value; return *this; } + spine_model& spine_model::set_animation_mix( + const str& from, + const str& to, + secf duration) + { + spAnimation* from_anim = animation_ + ? spSkeletonData_findAnimation(animation_->skeletonData, from.c_str()) + : nullptr; + + spAnimation* to_anim = animation_ + ? spSkeletonData_findAnimation(animation_->skeletonData, to.c_str()) + : nullptr; + + if ( !from_anim || !to_anim ) { + throw bad_spine_model_access(); + } + + spAnimationStateData_setMix(animation_.get(), from_anim, to_anim, duration.value); + return *this; + } + const spine_model::atlas_ptr& spine_model::atlas() const noexcept { return atlas_; } - const spine_model::animation_data_ptr& spine_model::animation() const noexcept { - return animation_; - } - const spine_model::skeleton_data_ptr& spine_model::skeleton() const noexcept { return skeleton_; } - - bool spine_model::premultiplied_alpha() const noexcept { - return premultiplied_alpha_; + + const spine_model::animation_data_ptr& spine_model::animation() const noexcept { + return animation_; } } @@ -118,9 +128,8 @@ namespace e2d bool operator==(const spine_model& l, const spine_model& r) noexcept { return l.atlas() == r.atlas() - && l.animation() == r.animation() && l.skeleton() == r.skeleton() - && l.premultiplied_alpha() == r.premultiplied_alpha(); + && l.animation() == r.animation(); } bool operator!=(const spine_model& l, const spine_model& r) noexcept { diff --git a/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp b/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp index cf964c02..735dfb6d 100644 --- a/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp +++ b/sources/enduro2d/high/systems/render_system_impl/render_system_drawer.cpp @@ -269,7 +269,8 @@ namespace e2d::render_system_impl spSkeletonClipping* clipper = spine_r.clipper().operator->(); spVertexEffect* effect = spine_r.effect().operator->(); const material_asset::ptr& src_mat = node_r.materials().front(); - const bool use_premultiplied_alpha = spine_r.model()->content().premultiplied_alpha(); + //const bool use_premultiplied_alpha = spine_r.model()->content().premultiplied_alpha(); + const bool use_premultiplied_alpha = false; // TODO: pma is not supported if ( !skeleton || !clipper || !src_mat ) { return;