mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-14 16:09:06 +07:00
rewrite spine model loading to async method
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<spAnimationStateData>;
|
||||
using skeleton_data_ptr = std::shared_ptr<spSkeletonData>;
|
||||
using atlas_ptr = std::shared_ptr<spAtlas>;
|
||||
using skeleton_data_ptr = std::shared_ptr<spSkeletonData>;
|
||||
using animation_data_ptr = std::shared_ptr<spAnimationStateData>;
|
||||
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;
|
||||
|
||||
Submodule modules/spine updated: 3432d16374...45b8125a8e
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"skeleton" : "coin/coin-pro.skel",
|
||||
"atlas" : "coin/coin-pma.atlas",
|
||||
"scale" : 1.0,
|
||||
"premultiplied_alpha" : true
|
||||
"skeleton" : "coin/coin-pro.skel",
|
||||
"skeleton_scale" : 1.0
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
]
|
||||
|
||||
@@ -5,18 +5,18 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/high/assets/spine_model_asset.hpp>
|
||||
|
||||
#include <enduro2d/high/assets/json_asset.hpp>
|
||||
#include <enduro2d/high/assets/binary_asset.hpp>
|
||||
#include <enduro2d/high/assets/texture_asset.hpp>
|
||||
|
||||
#include <spine/SkeletonJson.h>
|
||||
#include <spine/SkeletonBinary.h>
|
||||
#include <spine/spine.h>
|
||||
#include <spine/extension.h>
|
||||
|
||||
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<debug>().error("SPINE: Incorrect formatting of 'from_anim' property");
|
||||
return false;
|
||||
E2D_ASSERT(root.HasMember("from") && root["from"].IsString());
|
||||
if ( !json_utils::try_parse_value(root["from"], mix.from) ) {
|
||||
the<debug>().error("SPINE: Incorrect formating of 'from' property");
|
||||
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(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(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;
|
||||
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();
|
||||
}
|
||||
|
||||
model.mix_animations(from_anim, to_anim, duration);
|
||||
return mix;
|
||||
}
|
||||
return true;
|
||||
|
||||
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(
|
||||
@@ -112,148 +253,75 @@ namespace
|
||||
const str& parent_address,
|
||||
const rapidjson::Value& root)
|
||||
{
|
||||
using skeleton_json_ptr = std::unique_ptr<spSkeletonJson, void(*)(spSkeletonJson*)>;
|
||||
using skeleton_bin_ptr = std::unique_ptr<spSkeletonBinary, void(*)(spSkeletonBinary*)>;
|
||||
f32 skeleton_scale{1.0f};
|
||||
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());
|
||||
binary_asset::load_result atlas_data = library.load_asset<binary_asset>(
|
||||
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");
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
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<binary_asset>(
|
||||
path::combine(parent_address, root["skeleton"].GetString()));
|
||||
skeleton = spine_model::skeleton_data_ptr(
|
||||
spSkeletonBinary_readSkeletonData(
|
||||
skeleton_binary.get(),
|
||||
reinterpret_cast<const unsigned char*>(skeleton_data->content().data()),
|
||||
math::numeric_cast<int>(skeleton_data->content().size())),
|
||||
spSkeletonData_dispose);
|
||||
|
||||
if ( !skeleton ) {
|
||||
the<debug>().error("SPINE: Failed to read skeleton binary data:\n"
|
||||
"--> Error: $s",
|
||||
skeleton_binary->error);
|
||||
return stdex::make_rejected_promise<spine_model>(
|
||||
spine_model_asset_loading_exception());
|
||||
}
|
||||
} 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<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");
|
||||
}
|
||||
}
|
||||
const str skeleton_address = root["skeleton"].GetString();
|
||||
|
||||
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(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");
|
||||
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);
|
||||
}
|
||||
return content;
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include <enduro2d/high/spine_model.hpp>
|
||||
#include <spine/AnimationStateData.h>
|
||||
#include <spine/SkeletonData.h>
|
||||
#include <spine/Atlas.h>
|
||||
|
||||
#include <spine/spine.h>
|
||||
|
||||
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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user