spine events as components

This commit is contained in:
andrey.zhirnov
2019-08-28 12:39:54 +03:00
parent c355291ade
commit 4289ea804f
13 changed files with 453 additions and 235 deletions

View File

@@ -36,6 +36,7 @@
#include "components/renderer.hpp"
#include "components/scene.hpp"
#include "components/spine_player.hpp"
#include "components/spine_animation_event.hpp"
#include "components/sprite_renderer.hpp"
#include "systems/flipbook_system.hpp"

View File

@@ -0,0 +1,70 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include "../_high.hpp"
#include "../factory.hpp"
namespace e2d
{
class spine_animation_event final {
public:
struct clear_track {
u32 track;
};
struct set_anim {
u32 track;
str name;
bool loop;
str on_complete;
};
struct add_anim {
u32 track;
str name;
bool loop;
secf delay;
str on_complete;
};
struct empty_anim {
u32 track;
secf duration;
secf delay;
str on_complete;
};
using commands_t = stdex::variant<
clear_track,
set_anim,
add_anim,
empty_anim>;
public:
spine_animation_event() = default;
std::vector<commands_t>& commands() noexcept { return commands_; }
const std::vector<commands_t>& commands() const noexcept { return commands_; }
private:
std::vector<commands_t> commands_;
};
template <>
class factory_loader<spine_animation_event> final : factory_loader<> {
public:
static const char* schema_source;
bool operator()(
spine_animation_event& component,
const fill_context& ctx) const;
bool operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const;
};
}

View File

@@ -15,11 +15,23 @@ struct spAnimationState;
struct spSkeleton;
struct spSkeletonClipping;
struct spVertexEffect;
struct spTrackEntry;
struct spEvent;
namespace e2d
{
class spine_player final {
public:
class on_complete_event final {
public:
on_complete_event() = default;
std::vector<str>& completed() noexcept { return completed_; }
const std::vector<str>& completed() const noexcept { return completed_; }
private:
std::vector<str> completed_;
};
using animation_ptr = std::shared_ptr<spAnimationState>;
using skeleton_ptr = std::shared_ptr<spSkeleton>;
using clipping_ptr = std::shared_ptr<spSkeletonClipping>;
@@ -37,27 +49,15 @@ namespace e2d
[[nodiscard]] bool has_animation(const str& name) const noexcept;
spine_player& set_animation(u32 track, const str& name, bool loop = false) noexcept;
spine_player& add_animation(u32 track, const str& name, bool loop, secf delay = secf(0.0f)) noexcept;
spine_player& add_animation(u32 track, const str& name, secf delay = secf(0.0f)) noexcept;
spine_player& add_empty_animation(u32 track, secf duration, secf delay = secf(0.0f)) noexcept;
spine_player& clear(u32 track) noexcept;
spine_player& clear() noexcept;
const animation_ptr& animation() const noexcept;
const skeleton_ptr& skeleton() const noexcept;
const clipping_ptr& clipper() const noexcept;
const effect_ptr& effect() const noexcept;
const spine_model_asset::ptr& model() const noexcept;
private:
animation_ptr animation_;
spine_model_asset::ptr model_;
skeleton_ptr skeleton_;
clipping_ptr clipping_;
effect_ptr effect_;
};
template <>

View File

@@ -15,5 +15,8 @@ namespace e2d
spine_system();
~spine_system() noexcept final;
void process(ecs::registry& owner) override;
private:
class internal_state;
std::unique_ptr<internal_state> state_;
};
}

View File

@@ -0,0 +1,24 @@
{
"components" : {
"scene" : {}
},
"children" : [{
"prototype" : "camera_prefab.json"
},{
"prototype" : "spine_coin_prefab.json",
"components" : {
"actor" : {
"translation" : [200,180,0],
"scale" : 0.25
}
}
}, {
"prototype" : "spine_raptor_prefab.json",
"components" : {
"actor" : {
"translation" : [-80,-100,0],
"scale" : 0.25
}
}
}]
}

View File

@@ -0,0 +1,17 @@
{
"components" : {
"renderer" : {
"materials" : ["spine_material.json"]
},
"spine_player" : {
"model" : "spine_coin.json"
},
"spine_animation" : {
"animations" : [{
"track" : 0,
"name" : "animation",
"loop" : true
}]
}
}
}

View File

@@ -0,0 +1,17 @@
{
"components" : {
"renderer" : {
"materials" : ["spine_material.json"]
},
"spine_player" : {
"model" : "spine_raptor.json"
},
"spine_animation" : {
"animations" : [{
"track" : 0,
"name" : "walk",
"loop" : true
}]
}
}
}

View File

@@ -11,11 +11,6 @@ namespace
{
class game_system final : public ecs::system {
public:
game_system(gobject_iptr raptor)
: raptor_gobj_(raptor) {}
~game_system() noexcept override {}
void process(ecs::registry& owner) override {
E2D_UNUSED(owner);
const keyboard& k = the<input>().keyboard();
@@ -33,33 +28,39 @@ namespace
}
// use keys R, J, G to start animations
if ( raptor_gobj_ ) {
if ( k.is_key_just_pressed(keyboard_key::r) ) {
auto player = raptor_gobj_->get_component<spine_player>();
if ( player ) {
(*player).set_animation(0, "roar")
.add_animation(0, "walk", true);
}
}
if ( k.is_key_just_pressed(keyboard_key::j) ) {
auto player = raptor_gobj_->get_component<spine_player>();
if ( player ) {
(*player).set_animation(0, "jump")
.add_animation(0, "walk", true);
}
}
if ( k.is_key_just_pressed(keyboard_key::g) ) {
auto player = raptor_gobj_->get_component<spine_player>();
if ( player ) {
(*player).set_animation(1, "gun-grab")
.add_animation(1, "gun-holster", secf(3.0f));
}
}
}
}
const bool roar = k.is_key_just_pressed(keyboard_key::r);
const bool jump = k.is_key_just_pressed(keyboard_key::j);
const bool gun_grab = k.is_key_just_pressed(keyboard_key::g);
private:
gobject_iptr raptor_gobj_;
if ( roar || jump || gun_grab ) {
owner.for_each_component<spine_player>(
[&owner, roar, jump, gun_grab] (ecs::entity_id e, const spine_player& player) {
if ( !player.has_animation("walk") ) {
return;
}
auto& events = ecs::entity(owner, e).assign_component<spine_animation_event>();
if ( roar ) {
events.commands().push_back(spine_animation_event::set_anim{0, "roar", false, "1"});
} else if ( jump ) {
events.commands().push_back(spine_animation_event::set_anim{0, "jump", false, "1"});
} else if ( gun_grab ) {
events.commands().push_back(spine_animation_event::set_anim{1, "gun-grab", false, ""});
events.commands().push_back(spine_animation_event::add_anim{1, "gun-holster", false, secf(3.0f), ""});
}
});
}
owner.for_joined_components<spine_player::on_complete_event, spine_player>(
[&owner](ecs::entity_id e, const spine_player::on_complete_event& events, spine_player& player) {
auto& new_events = ecs::entity(owner, e).assign_component<spine_animation_event>();
for ( auto& ev : events.completed() ) {
if ( ev == "1" ) {
new_events.commands().push_back(spine_animation_event::add_anim{0, "walk", true, secf(), ""});
}
}
});
}
};
class camera_system final : public ecs::system {
@@ -86,47 +87,11 @@ namespace
}
private:
bool create_scene() {
auto spine_raptor = the<library>().load_asset<spine_model_asset>("spine_raptor.json");
auto spine_coin = the<library>().load_asset<spine_model_asset>("spine_coin.json");
auto spine_mat = the<library>().load_asset<material_asset>("spine_material.json");
if ( !spine_raptor || !spine_coin || !spine_mat ) {
return false;
}
auto scene_i = the<world>().instantiate();
scene_i->entity_filler()
.component<scene>()
.component<actor>(node::create(scene_i));
node_iptr scene_r = scene_i->get_component<actor>().get().node();
auto coin_i = the<world>().instantiate();
coin_i->entity_filler()
.component<actor>(node::create(coin_i, scene_r))
.component<renderer>(renderer()
.materials({spine_mat}))
.component<spine_player>(spine_player(spine_coin)
.set_animation(0, "animation", true));
node_iptr coin_n = coin_i->get_component<actor>().get().node();
coin_n->scale(v3f(0.25f));
coin_n->translation(v3f{200.0f, 180.0f, 0.0f});
raptor_gobj_ = the<world>().instantiate();
raptor_gobj_->entity_filler()
.component<actor>(node::create(raptor_gobj_, scene_r))
.component<renderer>(renderer()
.materials({spine_mat}))
.component<spine_player>(spine_player(spine_raptor)
.set_animation(0, "walk", true));
node_iptr raptor_n = raptor_gobj_->get_component<actor>().get().node();
raptor_n->scale(v3f(0.25f));
raptor_n->translation(v3f{-80.f, -100.f, 0.0f});
return true;
auto scene_prefab_res = the<library>().load_asset<prefab_asset>("scene_spine_prefab.json");
auto scene_go = scene_prefab_res
? the<world>().instantiate(scene_prefab_res->content())
: nullptr;
return !!scene_go;
}
bool create_camera() {
@@ -140,13 +105,10 @@ namespace
bool create_systems() {
ecs::registry_filler(the<world>().registry())
.system<game_system>(world::priority_update, raptor_gobj_)
.system<game_system>(world::priority_update)
.system<camera_system>(world::priority_pre_render);
return true;
}
private:
gobject_iptr raptor_gobj_;
};
}

View File

@@ -0,0 +1,94 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2019, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#include <enduro2d/high/components/spine_animation_event.hpp>
namespace e2d
{
const char* factory_loader<spine_animation_event>::schema_source = R"json({
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"animations" : {
"type" : "array",
"items" : { "$ref": "#/definitions/spine_animation" }
}
},
"definitions" : {
"spine_animation" : {
"type" : "object",
"required" : [ "track", "name" ],
"additionalProperties" : false,
"properties" : {
"track" : { "type" : "integer", "minimum" : 0, "maximum": 8 },
"name" : { "$ref": "#/common_definitions/name" },
"loop" : { "type" : "boolean" },
"delay" : { "type" : "number" }
}
}
}
})json";
bool factory_loader<spine_animation_event>::operator()(
spine_animation_event& component,
const fill_context& ctx) const
{
if ( ctx.root.HasMember("animations") ) {
const auto& animations_json = ctx.root["animations"];
E2D_ASSERT(animations_json.IsArray());
for ( rapidjson::SizeType i = 0; i < animations_json.Size(); ++i ) {
E2D_ASSERT(animations_json[i].IsObject());
const auto& item_json = animations_json[i];
E2D_ASSERT(item_json.HasMember("track") && item_json.HasMember("name"));
u32 track = 0;
secf delay;
bool loop = false;
const char* name = "";
if ( item_json.HasMember("name") ) {
name = item_json["name"].GetString();
}
if ( item_json.HasMember("track") ) {
if ( !json_utils::try_parse_value(item_json["track"], track) ) {
the<debug>().error("SPINE_ANIMATION: Incorrect formatting of 'animations.track' property");
return false;
}
}
if ( item_json.HasMember("delay") ) {
if ( !json_utils::try_parse_value(item_json["delay"], delay.value) ) {
the<debug>().error("SPINE_ANIMATION: Incorrect formatting of 'animations.delay' property");
return false;
}
}
if ( item_json.HasMember("loop") ) {
if ( !json_utils::try_parse_value(item_json["loop"], loop) ) {
the<debug>().error("SPINE_ANIMATION: Incorrect formatting of 'animations.loop' property");
return false;
}
}
component.commands().emplace_back(spine_animation_event::add_anim{
track, name, loop, delay, ""});
}
}
return true;
}
bool factory_loader<spine_animation_event>::operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const
{
E2D_UNUSED(dependencies, ctx);
return true;
}
}

View File

@@ -7,12 +7,6 @@
#include <enduro2d/high/components/spine_player.hpp>
#include <spine/spine.h>
namespace
{
// spine will allocate storage for each track, so keep number of track as small as possible
constexpr e2d::u32 max_track_count = 8;
}
namespace e2d
{
spine_player::spine_player(const spine_model_asset::ptr& model) {
@@ -32,6 +26,7 @@ namespace e2d
animation_ = animation_ptr(
spAnimationState_create(model_->content().animation().get()),
spAnimationState_dispose);
E2D_ASSERT(animation_); // TODO exception?
} else {
animation_.reset();
}
@@ -42,7 +37,7 @@ namespace e2d
spSkeleton_setSkinByName(skeleton_.get(), value.empty() ? nullptr : value.c_str());
return *this;
}
spine_player& spine_player::attachment(const str& slot, const str& name) noexcept {
E2D_ASSERT(!slot.empty());
E2D_ASSERT(!name.empty());
@@ -51,7 +46,7 @@ namespace e2d
}
return *this;
}
const spine_player::skeleton_ptr& spine_player::skeleton() const noexcept {
return skeleton_;
}
@@ -60,10 +55,6 @@ namespace e2d
return clipping_;
}
const spine_player::effect_ptr& spine_player::effect() const noexcept {
return effect_;
}
spine_player& spine_player::time_scale(float value) noexcept {
E2D_ASSERT(animation_);
animation_->timeScale = value;
@@ -80,65 +71,11 @@ namespace e2d
return false;
}
spAnimation* anim = spSkeletonData_findAnimation(
model_->content().skeleton().operator->(),
model_->content().skeleton().get(),
name.c_str());
return anim != nullptr;
}
spine_player& spine_player::set_animation(u32 track, const str& name, bool loop) noexcept {
E2D_ASSERT(model_ && animation_);
E2D_ASSERT(track < max_track_count);
spAnimation* anim = spSkeletonData_findAnimation(
model_->content().skeleton().operator->(),
name.c_str());
if ( !anim ) {
the<debug>().error("SPINE_PLAYER: animation '%0' is not found", name);
return *this;
}
spAnimationState_setAnimation(animation_.get(), track, anim, loop);
return *this;
}
spine_player& spine_player::add_animation(u32 track, const str& name, secf delay) noexcept {
return add_animation(track, name, false, delay);
}
spine_player& spine_player::add_animation(u32 track, const str& name, bool loop, secf delay) noexcept {
E2D_ASSERT(model_ && animation_);
E2D_ASSERT(track < max_track_count);
spAnimation* anim = spSkeletonData_findAnimation(
model_->content().skeleton().operator->(),
name.c_str());
if ( !anim ) {
the<debug>().error("SPINE_PLAYER: animation '%0' is not found", name);
return *this;
}
spAnimationState_addAnimation(animation_.get(), track, anim, loop, delay.value);
return *this;
}
spine_player& spine_player::add_empty_animation(u32 track, secf duration, secf delay) noexcept {
E2D_ASSERT(animation_);
E2D_ASSERT(track < max_track_count);
spAnimationState_addEmptyAnimation(animation_.get(), track, duration.value, delay.value);
return *this;
}
spine_player& spine_player::clear(u32 track) noexcept {
E2D_ASSERT(animation_);
E2D_ASSERT(track < max_track_count);
spAnimationState_clearTrack(animation_.get(), track);
return *this;
}
spine_player& spine_player::clear() noexcept {
E2D_ASSERT(animation_);
spAnimationState_clearTracks(animation_.get());
return *this;
}
const spine_player::animation_ptr& spine_player::animation() const noexcept {
return animation_;
}
@@ -160,24 +97,9 @@ namespace e2d
"attachments" : {
"type" : "array",
"items" : { "$ref": "#/definitions/spine_attachment" }
},
"animations" : {
"type" : "array",
"items" : { "$ref": "#/definitions/spine_animation" }
}
},
"definitions" : {
"spine_animation" : {
"type" : "object",
"required" : [ "track", "name" ],
"additionalProperties" : false,
"properties" : {
"track" : { "type" : "integer", "minimum" : 0, "maximum": 8 },
"name" : { "$ref": "#/common_definitions/name" },
"loop" : { "type" : "boolean" },
"delay" : { "type" : "number" }
}
},
"spine_attachment" : {
"type" : "object",
"required" : [ "slot", "name" ],
@@ -241,49 +163,6 @@ namespace e2d
}
}
if ( ctx.root.HasMember("animations") ) {
const auto& animations_json = ctx.root["animations"];
E2D_ASSERT(animations_json.IsArray());
for ( rapidjson::SizeType i = 0; i < animations_json.Size(); ++i ) {
E2D_ASSERT(animations_json[i].IsObject());
const auto& item_json = animations_json[i];
E2D_ASSERT(item_json.HasMember("track") && item_json.HasMember("name"));
u32 track = 0;
secf delay;
bool loop = false;
const char* name = "";
if ( item_json.HasMember("name") ) {
name = item_json["name"].GetString();
}
if ( item_json.HasMember("track") ) {
if ( !json_utils::try_parse_value(item_json["track"], track) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'animations.track' property");
return false;
}
}
if ( item_json.HasMember("delay") ) {
if ( !json_utils::try_parse_value(item_json["delay"], delay.value) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'animations.delay' property");
return false;
}
}
if ( item_json.HasMember("loop") ) {
if ( !json_utils::try_parse_value(item_json["loop"], loop) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'animations.loop' property");
return false;
}
}
component.add_animation(track, name, loop, delay);
}
}
return true;
}

View File

@@ -19,6 +19,7 @@
#include <enduro2d/high/components/renderer.hpp>
#include <enduro2d/high/components/scene.hpp>
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/spine_animation_event.hpp>
#include <enduro2d/high/components/sprite_renderer.hpp>
#include <enduro2d/high/systems/flipbook_system.hpp>
@@ -142,6 +143,7 @@ namespace e2d
.register_component<renderer>("renderer")
.register_component<scene>("scene")
.register_component<spine_player>("spine_player")
.register_component<spine_animation_event>("spine_animation")
.register_component<sprite_renderer>("sprite_renderer");
safe_module_initialize<library>(params.library_root(), the<deferrer>());
safe_module_initialize<world>();

View File

@@ -264,9 +264,8 @@ namespace e2d::render_system_impl
return;
}
spSkeleton* skeleton = spine_r.skeleton().operator->();
spSkeletonClipping* clipper = spine_r.clipper().operator->();
spVertexEffect* effect = spine_r.effect().operator->();
spSkeleton* skeleton = spine_r.skeleton().get();
spSkeletonClipping* clipper = spine_r.clipper().get();
const material_asset::ptr& src_mat = node_r.materials().front();
const bool use_premultiplied_alpha = spine_r.model()->content().premultiplied_alpha();
@@ -279,10 +278,6 @@ namespace e2d::render_system_impl
if ( skeleton->color.a == 0 ) {
return;
}
if ( effect ) {
effect->begin(effect, skeleton);
}
const m4f& sm = node->world_matrix();
@@ -431,10 +426,6 @@ namespace e2d::render_system_impl
}
spSkeletonClipping_clipEnd2(clipper);
if ( effect ) {
effect->end(effect);
}
property_cache_.clear();
}

View File

@@ -7,26 +7,170 @@
#include <enduro2d/high/systems/spine_system.hpp>
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/spine_animation_event.hpp>
#include <spine/AnimationState.h>
#include <spine/Skeleton.h>
#include <spine/spine.h>
namespace
{
// spine will allocate storage for each track, so keep number of track as small as possible
constexpr e2d::u32 max_track_count = 8;
}
namespace e2d
{
spine_system::spine_system() {}
//
// internal_state
//
spine_system::~spine_system() noexcept {
spAnimationState_disposeStatics();
class spine_system::internal_state final {
public:
void clear_events(ecs::registry& owner) const;
void process_new_animations(ecs::registry& owner);
void update_animations(ecs::registry& owner);
void add_event(spTrackEntry* entry, const str& ev_name);
private:
struct animation_visitor_;
static void event_listener_(spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event);
private:
flat_map<const spTrackEntry*, str> anim_events_;
ecs::registry* owner_ = nullptr;
};
//
// animation_visitor_
//
struct spine_system::internal_state::animation_visitor_ {
animation_visitor_(
spSkeletonData* skeleton,
spAnimationState* anim_state,
internal_state& state)
: skeleton_(skeleton)
, anim_state_(anim_state)
, state_(state) {}
void operator()(const spine_animation_event::clear_track& cmd) const noexcept {
E2D_ASSERT(cmd.track < max_track_count);
spAnimationState_clearTrack(anim_state_, cmd.track);
}
void operator()(const spine_animation_event::set_anim& cmd) const noexcept {
E2D_ASSERT(cmd.track < max_track_count);
spAnimation* anim = spSkeletonData_findAnimation(skeleton_, cmd.name.c_str());
if ( !anim ) {
the<debug>().error("SPINE_PLAYER: animation '%0' is not found", cmd.name);
return;
}
spTrackEntry* entry = spAnimationState_setAnimation(anim_state_, cmd.track, anim, cmd.loop);
if ( entry && !cmd.on_complete.empty() ) {
state_.add_event(entry, cmd.on_complete);
}
}
void operator()(const spine_animation_event::add_anim& cmd) const noexcept {
E2D_ASSERT(cmd.track < max_track_count);
spAnimation* anim = spSkeletonData_findAnimation(skeleton_, cmd.name.c_str());
if ( !anim ) {
the<debug>().error("SPINE_PLAYER: animation '%0' is not found", cmd.name);
return;
}
spTrackEntry* entry = spAnimationState_addAnimation(anim_state_, cmd.track, anim, cmd.loop, cmd.delay.value);
if ( entry && !cmd.on_complete.empty() ) {
state_.add_event(entry, cmd.on_complete);
}
}
void operator()(const spine_animation_event::empty_anim& cmd) const noexcept {
E2D_ASSERT(cmd.track < max_track_count);
spTrackEntry* entry = spAnimationState_addEmptyAnimation(anim_state_, cmd.track, cmd.duration.value, cmd.delay.value);
if ( entry && !cmd.on_complete.empty() ) {
state_.add_event(entry, cmd.on_complete);
}
}
private:
spSkeletonData* skeleton_;
spAnimationState* anim_state_;
internal_state& state_;
};
void spine_system::internal_state::add_event(spTrackEntry* entry, const str& ev_name) {
anim_events_.insert_or_assign(entry, ev_name);
entry->listener = &event_listener_;
entry->userData = this;
}
void spine_system::internal_state::event_listener_(spAnimationState* state, spEventType type, spTrackEntry* entry, spEvent* event) {
if ( !entry || !entry->userData || type == SP_ANIMATION_DISPOSE ) {
return;
}
internal_state* self = reinterpret_cast<internal_state*>(entry->userData);
auto entry_to_event = self->anim_events_.find(entry);
if ( entry_to_event == self->anim_events_.end() ) {
return;
}
ecs::entity_id id(size_t(state->userData));
ecs::entity ent(*self->owner_, id);
switch ( type ) {
case SP_ANIMATION_COMPLETE: {
auto* comp = ent.find_component<spine_player::on_complete_event>();
if ( !comp ) {
comp = &ent.assign_component<spine_player::on_complete_event>();
}
comp->completed().push_back(std::move(entry_to_event->second));
self->anim_events_.erase(entry_to_event);
break;
}
}
}
void spine_system::internal_state::clear_events(ecs::registry& owner) const {
owner.remove_all_components<spine_player::on_complete_event>();
}
void spine_system::process(ecs::registry& owner) {
float dt = the<engine>().delta_time();
owner.for_each_component<spine_player>([dt](
const ecs::const_entity&,
void spine_system::internal_state::process_new_animations(ecs::registry& owner) {
owner_ = &owner;
owner.for_joined_components<spine_animation_event, spine_player>(
[this, &owner](
ecs::entity_id id,
const spine_animation_event& events,
spine_player& player)
{
spSkeleton* skeleton = player.skeleton().operator->();
spAnimationState* anim_state = player.animation().operator->();
spSkeleton* skeleton = player.skeleton().get();
spAnimationState* anim_state = player.animation().get();
if ( !skeleton || !anim_state ) {
return;
}
anim_state->userData = reinterpret_cast<void*>(size_t(id));
animation_visitor_ vis(skeleton->data, anim_state, *this);
for ( auto& ev : events.commands() ) {
stdex::visit(vis, ev);
}
});
owner.remove_all_components<spine_animation_event>();
owner_ = nullptr;
}
void spine_system::internal_state::update_animations(ecs::registry& owner) {
const float dt = the<engine>().delta_time();
owner_ = &owner;
owner.for_each_component<spine_player>(
[dt](
ecs::entity_id id,
spine_player& player)
{
spSkeleton* skeleton = player.skeleton().get();
spAnimationState* anim_state = player.animation().get();
if ( !skeleton || !anim_state ) {
return;
@@ -37,5 +181,19 @@ namespace e2d
spAnimationState_apply(anim_state, skeleton);
spSkeleton_updateWorldTransform(skeleton);
});
owner_ = nullptr;
}
spine_system::spine_system()
: state_(new internal_state()) {}
spine_system::~spine_system() noexcept {
spAnimationState_disposeStatics();
}
void spine_system::process(ecs::registry& owner) {
state_->clear_events(owner);
state_->process_new_animations(owner);
state_->update_animations(owner);
}
}