rewrite spine model loading to async method

This commit is contained in:
2019-08-26 09:18:22 +07:00
parent 1ab974455b
commit c7afd46c2b
9 changed files with 376 additions and 261 deletions

View File

@@ -21,6 +21,7 @@
#include "assets/shader_asset.hpp" #include "assets/shader_asset.hpp"
#include "assets/shape_asset.hpp" #include "assets/shape_asset.hpp"
#include "assets/sound_asset.hpp" #include "assets/sound_asset.hpp"
#include "assets/spine_model_asset.hpp"
#include "assets/sprite_asset.hpp" #include "assets/sprite_asset.hpp"
#include "assets/text_asset.hpp" #include "assets/text_asset.hpp"
#include "assets/texture_asset.hpp" #include "assets/texture_asset.hpp"
@@ -34,9 +35,9 @@
#include "components/model_renderer.hpp" #include "components/model_renderer.hpp"
#include "components/renderer.hpp" #include "components/renderer.hpp"
#include "components/scene.hpp" #include "components/scene.hpp"
#include "components/sprite_renderer.hpp"
#include "components/spine_renderer.hpp"
#include "components/spine_player.hpp" #include "components/spine_player.hpp"
#include "components/spine_renderer.hpp"
#include "components/sprite_renderer.hpp"
#include "systems/flipbook_system.hpp" #include "systems/flipbook_system.hpp"
#include "systems/label_system.hpp" #include "systems/label_system.hpp"

View File

@@ -33,6 +33,7 @@ namespace e2d
class shader_asset; class shader_asset;
class shape_asset; class shape_asset;
class sound_asset; class sound_asset;
class spine_model_asset;
class sprite_asset; class sprite_asset;
class text_asset; class text_asset;
class texture_asset; class texture_asset;
@@ -46,9 +47,9 @@ namespace e2d
class model_renderer; class model_renderer;
class renderer; class renderer;
class scene; class scene;
class sprite_renderer;
class spine_renderer;
class spine_player; class spine_player;
class spine_renderer;
class sprite_renderer;
class flipbook_system; class flipbook_system;
class label_system; class label_system;

View File

@@ -8,20 +8,26 @@
#include "_high.hpp" #include "_high.hpp"
struct spAnimationStateData;
struct spSkeletonData;
struct spAtlas; struct spAtlas;
struct spSkeletonData;
struct spAnimationStateData;
namespace e2d 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 { class spine_model final {
public: public:
using animation_data_ptr = std::shared_ptr<spAnimationStateData>;
using skeleton_data_ptr = std::shared_ptr<spSkeletonData>;
using atlas_ptr = std::shared_ptr<spAtlas>; using atlas_ptr = std::shared_ptr<spAtlas>;
using skeleton_data_ptr = std::shared_ptr<spSkeletonData>;
using animation_data_ptr = std::shared_ptr<spAnimationStateData>;
public: public:
spine_model() noexcept = default; spine_model() = default;
~spine_model() noexcept = default; ~spine_model() noexcept = default;
spine_model(spine_model&& other) noexcept; spine_model(spine_model&& other) noexcept;
@@ -36,21 +42,22 @@ namespace e2d
spine_model& assign(spine_model&& other) noexcept; spine_model& assign(spine_model&& other) noexcept;
spine_model& assign(const spine_model& other); spine_model& assign(const spine_model& other);
spine_model& set_skeleton(skeleton_data_ptr data); spine_model& set_atlas(atlas_ptr atlas);
spine_model& set_atlas(atlas_ptr atlas, bool premultiplied_alpha); 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_default_mix(secf duration);
spine_model& set_animation_mix(
const str& from,
const str& to,
secf duration);
const atlas_ptr& atlas() const noexcept; const atlas_ptr& atlas() const noexcept;
const animation_data_ptr& animation() const noexcept;
const skeleton_data_ptr& skeleton() const noexcept; const skeleton_data_ptr& skeleton() const noexcept;
bool premultiplied_alpha() const noexcept; const animation_data_ptr& animation() const noexcept;
private: private:
animation_data_ptr animation_;
skeleton_data_ptr skeleton_;
atlas_ptr atlas_; atlas_ptr atlas_;
bool premultiplied_alpha_ = false; skeleton_data_ptr skeleton_;
animation_data_ptr animation_;
}; };
void swap(spine_model& l, spine_model& r) noexcept; void swap(spine_model& l, spine_model& r) noexcept;

View File

@@ -1,6 +1,5 @@
{ {
"skeleton" : "coin/coin-pro.skel",
"atlas" : "coin/coin-pma.atlas", "atlas" : "coin/coin-pma.atlas",
"scale" : 1.0, "skeleton" : "coin/coin-pro.skel",
"premultiplied_alpha" : true "skeleton_scale" : 1.0
} }

View File

@@ -1,37 +1,37 @@
{ {
"skeleton": "raptor/raptor-pro.json-large",
"atlas": "raptor/raptor.atlas", "atlas": "raptor/raptor.atlas",
"scale": 1.0, "skeleton": "raptor/raptor-pro.json-large",
"premultiplied_alpha": false, "skeleton_scale": 1.0,
"mix_animations": [ "default_animation_mix" : 0.5,
"animation_mixes": [
{ {
"from_anim": "walk", "from": "walk",
"to_anim": "roar", "to": "roar",
"duration": 0.5 "duration": 0.5
}, },
{ {
"from_anim": "roar", "from": "roar",
"to_anim": "walk", "to": "walk",
"duration": 0.5 "duration": 0.5
}, },
{ {
"from_anim": "walk", "from": "walk",
"to_anim": "jump", "to": "jump",
"duration": 0.5 "duration": 0.5
}, },
{ {
"from_anim": "jump", "from": "jump",
"to_anim": "walk", "to": "walk",
"duration": 0.5 "duration": 0.5
}, },
{ {
"from_anim": "roar", "from": "roar",
"to_anim": "jump", "to": "jump",
"duration": 0.5 "duration": 0.5
}, },
{ {
"from_anim": "jump", "from": "jump",
"to_anim": "roar", "to": "roar",
"duration": 0.5 "duration": 0.5
} }
] ]

View File

@@ -5,18 +5,18 @@
******************************************************************************/ ******************************************************************************/
#include <enduro2d/high/assets/spine_model_asset.hpp> #include <enduro2d/high/assets/spine_model_asset.hpp>
#include <enduro2d/high/assets/json_asset.hpp> #include <enduro2d/high/assets/json_asset.hpp>
#include <enduro2d/high/assets/binary_asset.hpp> #include <enduro2d/high/assets/binary_asset.hpp>
#include <enduro2d/high/assets/texture_asset.hpp> #include <enduro2d/high/assets/texture_asset.hpp>
#include <spine/SkeletonJson.h> #include <spine/spine.h>
#include <spine/SkeletonBinary.h>
#include <spine/extension.h> #include <spine/extension.h>
using namespace e2d;
namespace namespace
{ {
using namespace e2d;
class spine_model_asset_loading_exception final : public asset_loading_exception { class spine_model_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final { const char* what() const noexcept final {
return "spine model asset loading exception"; return "spine model asset loading exception";
@@ -25,28 +25,27 @@ namespace
const char* spine_model_asset_schema_source = R"json({ const char* spine_model_asset_schema_source = R"json({
"type" : "object", "type" : "object",
"required" : [ "skeleton", "atlas" ], "required" : [ "atlas", "skeleton" ],
"additionalProperties" : false, "additionalProperties" : false,
"properties" : { "properties" : {
"skeleton" : { "$ref" : "#/common_definitions/address" },
"scale" : { "type" : "number" },
"atlas" : { "$ref" : "#/common_definitions/address" }, "atlas" : { "$ref" : "#/common_definitions/address" },
"premultiplied_alpha" : { "type" : "boolean" }, "skeleton" : { "$ref" : "#/common_definitions/address" },
"default_mix" : { "type" : "number" }, "skeleton_scale" : { "type" : "number" },
"mix_animations" : { "$ref": "#/definitions/spine_animation_mix_array" } "default_animation_mix" : { "type" : "number" },
"animation_mixes" : { "$ref": "#/definitions/animation_mixes" }
}, },
"definitions" : { "definitions" : {
"spine_animation_mix_array" : { "animation_mixes" : {
"type" : "array", "type" : "array",
"items" : { "$ref": "#/definitions/spine_animation_mix" } "items" : { "$ref": "#/definitions/animation_mix" }
}, },
"spine_animation_mix" : { "animation_mix" : {
"type" : "object", "type" : "object",
"required" : [ "from_anim", "to_anim", "duration" ], "required" : [ "from", "to", "duration" ],
"additionalProperties" : false, "additionalProperties" : false,
"properties" : { "properties" : {
"from_anim" : { "$ref": "#/common_definitions/name" }, "from" : { "$ref": "#/common_definitions/name" },
"to_anim" : { "$ref": "#/common_definitions/name" }, "to" : { "$ref": "#/common_definitions/name" },
"duration" : { "type" : "number" } "duration" : { "type" : "number" }
} }
} }
@@ -71,40 +70,182 @@ namespace
return *schema; return *schema;
} }
bool parse_mix_animations( struct animation_mix {
const rapidjson::Value& root, str from;
spine_model& model) str to;
{
E2D_ASSERT(root.IsArray());
str from_anim;
str to_anim;
secf duration; secf duration;
};
for ( rapidjson::SizeType i = 0; i < root.Size(); ++i ) { animation_mix parse_animation_mix(const rapidjson::Value& root) {
E2D_ASSERT(root[i].IsObject()); animation_mix mix;
const auto& item_json = root[i];
E2D_ASSERT(item_json.HasMember("from_anim")); E2D_ASSERT(root.HasMember("from") && root["from"].IsString());
if ( !json_utils::try_parse_value(item_json["from_anim"], from_anim) ) { if ( !json_utils::try_parse_value(root["from"], mix.from) ) {
the<debug>().error("SPINE: Incorrect formatting of 'from_anim' property"); the<debug>().error("SPINE: Incorrect formating of 'from' property");
return false; throw spine_model_asset_loading_exception();
}
E2D_ASSERT(item_json.HasMember("to_anim"));
if ( !json_utils::try_parse_value(item_json["to_anim"], to_anim) ) {
the<debug>().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<debug>().error("SPINE: Incorrect formatting of 'duration' property");
return false;
}
model.mix_animations(from_anim, to_anim, duration);
} }
return true;
E2D_ASSERT(root.HasMember("to") && root["to"].IsString());
if ( !json_utils::try_parse_value(root["to"], mix.to) ) {
the<debug>().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<debug>().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<spine_model::atlas_ptr> load_atlas(
const library& library,
const str& parent_address,
const str& atlas_address)
{
return library.load_asset_async<binary_asset>(
path::combine(parent_address, atlas_address))
.then([
&library,
parent_address
](const binary_asset::load_result& atlas_data){
auto atlas_internal = std::make_unique<atlas_internal_state>();
spine_model::atlas_ptr atlas(
spAtlas_create(
reinterpret_cast<const char*>(atlas_data->content().data()),
math::numeric_cast<int>(atlas_data->content().size()),
parent_address.c_str(),
atlas_internal.get()),
spAtlas_dispose);
if ( !atlas ) {
the<debug>().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_state>();
atlas_internal->loaded = std::get<0>(results);
spine_model::atlas_ptr atlas(
spAtlas_create(
reinterpret_cast<const char*>(std::get<1>(results)->content().data()),
math::numeric_cast<int>(std::get<1>(results)->content().size()),
parent_address.c_str(),
atlas_internal.get()),
spAtlas_dispose);
if ( !atlas ) {
the<debug>().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<debug>().error("SPINE: Failed to create preloaded atlas");
throw spine_model_asset_loading_exception();
}
}
atlas->rendererObject = nullptr;
return atlas;
});
}
stdex::promise<spine_model::skeleton_data_ptr> 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<binary_asset>(
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<debug>().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<const unsigned char*>(skeleton_data->content().data()),
math::numeric_cast<int>(skeleton_data->content().size())),
spSkeletonData_dispose);
if ( !data_skeleton ) {
the<debug>().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<debug>().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<const char*>(skeleton_data->content().data())),
spSkeletonData_dispose);
if ( !data_skeleton ) {
the<debug>().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<spine_model> parse_spine_model( stdex::promise<spine_model> parse_spine_model(
@@ -112,150 +253,77 @@ namespace
const str& parent_address, const str& parent_address,
const rapidjson::Value& root) const rapidjson::Value& root)
{ {
using skeleton_json_ptr = std::unique_ptr<spSkeletonJson, void(*)(spSkeletonJson*)>; f32 skeleton_scale{1.0f};
using skeleton_bin_ptr = std::unique_ptr<spSkeletonBinary, void(*)(spSkeletonBinary*)>; if ( root.HasMember("skeleton_scale") ) {
if ( !json_utils::try_parse_value(root["skeleton_scale"], skeleton_scale) ) {
the<debug>().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<debug>().error("SPINE: Incorrect formating of 'default_animation_mix' property");
}
}
vector<animation_mix> 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()); E2D_ASSERT(root.HasMember("atlas") && root["atlas"].IsString());
binary_asset::load_result atlas_data = library.load_asset<binary_asset>( const str atlas_address = root["atlas"].GetString();
path::combine(parent_address, root["atlas"].GetString()));
spine_model::atlas_ptr atlas(
spAtlas_create(
reinterpret_cast<const char*>(atlas_data->content().data()),
math::numeric_cast<int>(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<debug>().error("SPINE: Incorrect formating of 'scale' property");
}
}
E2D_ASSERT(root.HasMember("skeleton") && root["skeleton"].IsString()); E2D_ASSERT(root.HasMember("skeleton") && root["skeleton"].IsString());
const str ext = path::extension(root["skeleton"].GetString()); const str skeleton_address = root["skeleton"].GetString();
spine_model::skeleton_data_ptr skeleton;
if ( ext == ".skel" ) { return load_atlas(
skeleton_bin_ptr skeleton_binary( library,
spSkeletonBinary_create(atlas.get()), parent_address,
spSkeletonBinary_dispose); atlas_address)
skeleton_binary->scale = skeleton_scale; .then([
&library,
binary_asset::load_result skeleton_data = library.load_asset<binary_asset>( parent_address,
path::combine(parent_address, root["skeleton"].GetString())); atlas_address,
skeleton = spine_model::skeleton_data_ptr( skeleton_scale,
spSkeletonBinary_readSkeletonData( skeleton_address
skeleton_binary.get(), ](const spine_model::atlas_ptr& atlas){
reinterpret_cast<const unsigned char*>(skeleton_data->content().data()), return stdex::make_tuple_promise(std::make_tuple(
math::numeric_cast<int>(skeleton_data->content().size())), stdex::make_resolved_promise(atlas),
spSkeletonData_dispose); load_skeleton_data(
library,
if ( !skeleton ) { parent_address,
the<debug>().error("SPINE: Failed to read skeleton binary data:\n" skeleton_address,
"--> Error: $s", skeleton_scale,
skeleton_binary->error); atlas)));
return stdex::make_rejected_promise<spine_model>( })
spine_model_asset_loading_exception()); .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 { return content;
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<binary_asset>(
path::combine(parent_address, root["skeleton"].GetString()));
skeleton = spine_model::skeleton_data_ptr(
spSkeletonJson_readSkeletonData(
skeleton_json.get(),
reinterpret_cast<const char*>(skeleton_data->content().data())),
spSkeletonData_dispose);
if ( !skeleton ) {
the<debug>().error("SPINE: Failed to read skeleton json data:\n"
"--> Error: $s",
skeleton_json->error);
return stdex::make_rejected_promise<spine_model>(
spine_model_asset_loading_exception());
}
}
bool pma = false;
if ( root.HasMember("premultiplied_alpha") ) {
if ( !json_utils::try_parse_value(root["premultiplied_alpha"], pma) ) {
the<debug>().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<debug>().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>(
spine_model_asset_loading_exception());
}
}
return stdex::make_resolved_promise(std::move(content));
} }
} }
extern "C" void _spAtlasPage_createTexture (spAtlasPage* self, const char* path) {
try {
texture_asset::load_result texture = the<library>().load_asset<texture_asset>(path);
if ( !texture ) {
throw;
}
self->width = texture->content()->size().x;
self->height = texture->content()->size().y;
self->rendererObject = texture.release();
} catch(...) {
the<debug>().error("SPINE: Failed to load atlas texture");
}
}
extern "C" void _spAtlasPage_disposeTexture (spAtlasPage* self) {
// decrease ref counter
E2D_UNUSED(texture_asset::ptr(static_cast<texture_asset*>(self->rendererObject), false));
}
extern "C" char* _spUtil_readFile (const char* path, int* length) {
try {
binary_asset::load_result file = the<library>().load_asset<binary_asset>(path);
if ( !file ) {
throw;
}
if ( file->content().size() > std::numeric_limits<int>::max() ) {
throw;
}
*length = math::numeric_cast<int>(file->content().size());
char* data = MALLOC(char, *length);
if ( !data ) {
throw;
}
memcpy(data, file->content().data(), *length);
return data;
} catch(...) {
the<debug>().error("SPINE: Failed to read file");
}
return nullptr;
}
namespace e2d namespace e2d
{ {
spine_model_asset::load_async_result spine_model_asset::load_async( 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<atlas_internal_state*>(self->atlas->rendererObject);
auto texture_res = atlas_internal.loaded.find_asset<texture_asset>(path);
if ( texture_res ) {
self->width = math::numeric_cast<int>(texture_res->content()->size().x);
self->height = math::numeric_cast<int>(texture_res->content()->size().y);
self->rendererObject = texture_res.release();
} else {
atlas_internal.loading.add_dependency<texture_asset>(path);
}
} catch(...) {
// nothing
}
}
extern "C" void _spAtlasPage_disposeTexture(spAtlasPage* self) {
E2D_UNUSED(texture_asset::load_result(
static_cast<texture_asset*>(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;
}

View File

@@ -5,9 +5,8 @@
******************************************************************************/ ******************************************************************************/
#include <enduro2d/high/spine_model.hpp> #include <enduro2d/high/spine_model.hpp>
#include <spine/AnimationStateData.h>
#include <spine/SkeletonData.h> #include <spine/spine.h>
#include <spine/Atlas.h>
namespace e2d namespace e2d
{ {
@@ -28,17 +27,16 @@ namespace e2d
} }
void spine_model::clear() noexcept { void spine_model::clear() noexcept {
animation_.reset();
skeleton_.reset();
atlas_.reset(); atlas_.reset();
skeleton_.reset();
animation_.reset();
} }
void spine_model::swap(spine_model& other) noexcept { void spine_model::swap(spine_model& other) noexcept {
using std::swap; using std::swap;
swap(animation_, other.animation_);
swap(skeleton_, other.skeleton_);
swap(atlas_, other.atlas_); 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 { spine_model& spine_model::assign(spine_model&& other) noexcept {
@@ -52,61 +50,73 @@ namespace e2d
spine_model& spine_model::assign(const spine_model& other) { spine_model& spine_model::assign(const spine_model& other) {
if ( this != &other ) { if ( this != &other ) {
spine_model m; spine_model m;
m.animation_ = other.animation_;
m.skeleton_ = other.skeleton_;
m.atlas_ = other.atlas_; m.atlas_ = other.atlas_;
m.premultiplied_alpha_ = other.premultiplied_alpha_; m.skeleton_ = other.skeleton_;
m.animation_ = other.animation_;
swap(m); swap(m);
} }
return *this; return *this;
} }
spine_model& spine_model::set_skeleton(skeleton_data_ptr data) { spine_model& spine_model::set_atlas(atlas_ptr atlas) {
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) {
atlas_ = std::move(atlas); atlas_ = std::move(atlas);
premultiplied_alpha_ = premultiplied_alpha;
return *this; return *this;
} }
spine_model& spine_model::mix_animations(const str& from, const str& to, secf duration) { spine_model& spine_model::set_skeleton(skeleton_data_ptr skeleton) {
E2D_ASSERT(animation_); animation_data_ptr animation;
E2D_ASSERT(spSkeletonData_findAnimation(animation_->skeletonData, from.c_str())); if ( skeleton ) {
E2D_ASSERT(spSkeletonData_findAnimation(animation_->skeletonData, to.c_str())); animation.reset(
spAnimationStateData_create(skeleton.get()),
spAnimationStateData_setMixByName(animation_.get(), from.c_str(), to.c_str(), duration.value); spAnimationStateData_dispose);
if ( !animation ) {
throw std::bad_alloc();
}
}
skeleton_ = std::move(skeleton);
animation_ = std::move(animation);
return *this; return *this;
} }
spine_model& spine_model::set_default_mix(secf duration) { spine_model& spine_model::set_default_mix(secf duration) {
E2D_ASSERT(animation_); if ( !animation_ ) {
throw bad_spine_model_access();
}
animation_->defaultMix = duration.value; animation_->defaultMix = duration.value;
return *this; 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 { const spine_model::atlas_ptr& spine_model::atlas() const noexcept {
return atlas_; 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 { const spine_model::skeleton_data_ptr& spine_model::skeleton() const noexcept {
return skeleton_; return skeleton_;
} }
bool spine_model::premultiplied_alpha() const noexcept { const spine_model::animation_data_ptr& spine_model::animation() const noexcept {
return premultiplied_alpha_; return animation_;
} }
} }
@@ -118,9 +128,8 @@ namespace e2d
bool operator==(const spine_model& l, const spine_model& r) noexcept { bool operator==(const spine_model& l, const spine_model& r) noexcept {
return l.atlas() == r.atlas() return l.atlas() == r.atlas()
&& l.animation() == r.animation()
&& l.skeleton() == r.skeleton() && 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 { bool operator!=(const spine_model& l, const spine_model& r) noexcept {

View File

@@ -269,7 +269,8 @@ namespace e2d::render_system_impl
spSkeletonClipping* clipper = spine_r.clipper().operator->(); spSkeletonClipping* clipper = spine_r.clipper().operator->();
spVertexEffect* effect = spine_r.effect().operator->(); spVertexEffect* effect = spine_r.effect().operator->();
const material_asset::ptr& src_mat = node_r.materials().front(); 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 ) { if ( !skeleton || !clipper || !src_mat ) {
return; return;