two separated spine system for pre and post update

This commit is contained in:
2019-09-02 07:00:07 +07:00
parent f4a4d612cf
commit 8ec358afb0
7 changed files with 321 additions and 209 deletions

View File

@@ -43,7 +43,7 @@
#include "systems/flipbook_system.hpp"
#include "systems/label_system.hpp"
#include "systems/render_system.hpp"
#include "systems/spine_system.hpp"
#include "systems/spine_systems.hpp"
#include "address.hpp"
#include "asset.hpp"

View File

@@ -55,7 +55,8 @@ namespace e2d
class flipbook_system;
class label_system;
class render_system;
class spine_system;
class spine_pre_system;
class spine_post_system;
template < typename Asset, typename Content >
class content_asset;

View File

@@ -10,10 +10,20 @@
namespace e2d
{
class spine_system final : public ecs::system {
class spine_pre_system final : public ecs::system {
public:
spine_system();
~spine_system() noexcept final;
spine_pre_system();
~spine_pre_system() noexcept final;
void process(ecs::registry& owner) override;
private:
class internal_state;
std::unique_ptr<internal_state> state_;
};
class spine_post_system final : public ecs::system {
public:
spine_post_system();
~spine_post_system() noexcept final;
void process(ecs::registry& owner) override;
private:
class internal_state;

View File

@@ -26,7 +26,7 @@
#include <enduro2d/high/systems/flipbook_system.hpp>
#include <enduro2d/high/systems/label_system.hpp>
#include <enduro2d/high/systems/render_system.hpp>
#include <enduro2d/high/systems/spine_system.hpp>
#include <enduro2d/high/systems/spine_systems.hpp>
namespace
{
@@ -46,10 +46,11 @@ namespace
bool initialize() final {
ecs::registry_filler(the<world>().registry())
.system<flipbook_system>(world::priority_update)
.system<label_system>(world::priority_update)
.system<render_system>(world::priority_render)
.system<spine_system>(world::priority_update);
.system<flipbook_system>(world::priority_pre_update)
.system<label_system>(world::priority_pre_update)
.system<spine_pre_system>(world::priority_pre_update)
.system<spine_post_system>(world::priority_post_update)
.system<render_system>(world::priority_render);
return !application_ || application_->initialize();
}

View File

@@ -0,0 +1,237 @@
/*******************************************************************************
* 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/systems/spine_systems.hpp>
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/spine_player_cmd.hpp>
#include <enduro2d/high/components/spine_player_evt.hpp>
#include <spine/spine.h>
namespace
{
using namespace e2d;
struct entry_internal_state {
ecs::entity target;
str end_message;
str complete_message;
entry_internal_state(ecs::entity target, str end_msg, str complete_msg)
: target(std::move(target))
, end_message(std::move(end_msg))
, complete_message(std::move(complete_msg)) {}
};
void entry_listener(
spAnimationState*,
spEventType type,
spTrackEntry* entry,
spEvent* event)
{
if ( !entry->userData ) {
return;
}
entry_internal_state& entry_state =
*static_cast<entry_internal_state*>(entry->userData);
if ( type == SP_ANIMATION_DISPOSE || !entry_state.target.valid() ) {
std::unique_ptr<entry_internal_state> entry_internal(&entry_state);
entry->userData = nullptr;
return;
}
if ( type == SP_ANIMATION_EVENT ) {
if ( !event || !event->data ) {
return;
}
entry_state.target.ensure_component<spine_player_evt>()
.add_event(spine_player_evt::custom_evt(event->data->name ? event->data->name : "")
.int_value(event->intValue)
.float_value(event->floatValue)
.string_value(event->stringValue ? event->stringValue : ""));
} else if ( type == SP_ANIMATION_END ) {
if ( entry_state.end_message.empty() ) {
return;
}
entry_state.target.ensure_component<spine_player_evt>()
.add_event(spine_player_evt::end_evt(entry_state.end_message));
} else if ( type == SP_ANIMATION_COMPLETE ) {
if ( entry_state.complete_message.empty() ) {
return;
}
entry_state.target.ensure_component<spine_player_evt>()
.add_event(spine_player_evt::complete_evt(entry_state.complete_message));
}
}
class command_visitor final : private noncopyable {
public:
command_visitor(
ecs::entity target,
spSkeletonData& skeleton_data,
spAnimationState& animation_state)
: target_(std::move(target))
, skeleton_data_(skeleton_data)
, animation_state_(animation_state) {}
void operator()(const spine_player_cmd::clear_track_cmd& cmd) const noexcept {
spAnimationState_clearTrack(
&animation_state_,
math::numeric_cast<int>(cmd.track()));
}
void operator()(const spine_player_cmd::set_anim_cmd& cmd) const noexcept {
spAnimation* animation = spSkeletonData_findAnimation(&skeleton_data_, cmd.name().c_str());
if ( !animation ) {
the<debug>().error("SPINE_POST_SYSTEM: animation '%0' is not found", cmd.name());
return;
}
auto entry_state = std::make_unique<entry_internal_state>(
target_,
cmd.end_message(),
cmd.complete_message());
spTrackEntry* entry = spAnimationState_setAnimation(
&animation_state_,
math::numeric_cast<int>(cmd.track()),
animation,
cmd.loop() ? 1 : 0);
entry->listener = &entry_listener;
entry->userData = entry_state.release();
}
void operator()(const spine_player_cmd::add_anim_cmd& cmd) const noexcept {
spAnimation* animation = spSkeletonData_findAnimation(&skeleton_data_, cmd.name().c_str());
if ( !animation ) {
the<debug>().error("SPINE_POST_SYSTEM: animation '%0' is not found", cmd.name());
return;
}
auto entry_state = std::make_unique<entry_internal_state>(
target_,
cmd.end_message(),
cmd.complete_message());
spTrackEntry* entry = spAnimationState_addAnimation(
&animation_state_,
math::numeric_cast<int>(cmd.track()),
animation,
cmd.loop() ? 1 : 0,
cmd.delay().value);
entry->listener = &entry_listener;
entry->userData = entry_state.release();
}
void operator()(const spine_player_cmd::set_empty_anim_cmd& cmd) const noexcept {
auto entry_state = std::make_unique<entry_internal_state>(
target_,
cmd.end_message(),
cmd.complete_message());
spTrackEntry* entry = spAnimationState_setEmptyAnimation(
&animation_state_,
math::numeric_cast<int>(cmd.track()),
cmd.mix_duration().value);
entry->listener = &entry_listener;
entry->userData = entry_state.release();
}
void operator()(const spine_player_cmd::add_empty_anim_cmd& cmd) const noexcept {
auto entry_state = std::make_unique<entry_internal_state>(
target_,
cmd.end_message(),
cmd.complete_message());
spTrackEntry* entry = spAnimationState_addEmptyAnimation(
&animation_state_,
math::numeric_cast<int>(cmd.track()),
cmd.mix_duration().value,
cmd.delay().value);
entry->listener = &entry_listener;
entry->userData = entry_state.release();
}
private:
ecs::entity target_;
spSkeletonData& skeleton_data_;
spAnimationState& animation_state_;
};
}
namespace e2d
{
//
// internal_state
//
class spine_post_system::internal_state final {
public:
internal_state() = default;
~internal_state() noexcept = default;
void process(ecs::registry& owner) {
process_commands(owner);
clear_commands(owner);
clear_events(owner);
}
private:
void process_commands(ecs::registry& owner) {
owner.for_joined_components<spine_player_cmd, spine_player>([](
ecs::entity e,
const spine_player_cmd& pc,
spine_player& p)
{
spSkeleton* skeleton = p.skeleton().get();
spAnimationState* animation_state = p.animation().get();
if ( !skeleton || !skeleton->data || !animation_state ) {
return;
}
command_visitor v(e, *skeleton->data, *animation_state);
for ( const auto& cmd : pc.commands() ) {
stdex::visit(v, cmd);
}
});
}
void clear_commands(ecs::registry& owner) {
owner.for_each_component<spine_player_cmd>([
](const ecs::const_entity&, spine_player_cmd& pc) {
pc.clear_commands();
});
}
void clear_events(ecs::registry& owner) {
owner.for_each_component<spine_player_evt>([
](const ecs::const_entity&, spine_player_evt& pe) {
pe.clear_events();
});
}
};
//
// spine_post_system
//
spine_post_system::spine_post_system()
: state_(new internal_state()) {}
spine_post_system::~spine_post_system() noexcept {
spAnimationState_disposeStatics();
}
void spine_post_system::process(ecs::registry& owner) {
state_->process(owner);
}
}

View File

@@ -0,0 +1,62 @@
/*******************************************************************************
* 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/systems/spine_systems.hpp>
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/spine_player_cmd.hpp>
#include <enduro2d/high/components/spine_player_evt.hpp>
#include <spine/spine.h>
namespace e2d
{
//
// internal_state
//
class spine_pre_system::internal_state final {
public:
internal_state() = default;
~internal_state() noexcept = default;
void process(ecs::registry& owner) {
update_animations(owner);
}
private:
void update_animations(ecs::registry& owner) {
const float dt = the<engine>().delta_time();
owner.for_each_component<spine_player>([dt](
const ecs::const_entity&,
spine_player& p)
{
spSkeleton* skeleton = p.skeleton().get();
spAnimationState* anim_state = p.animation().get();
if ( !skeleton || !anim_state ) {
return;
}
spSkeleton_update(skeleton, dt);
spAnimationState_update(anim_state, dt);
spAnimationState_apply(anim_state, skeleton);
spSkeleton_updateWorldTransform(skeleton);
});
}
};
//
// spine_pre_system
//
spine_pre_system::spine_pre_system()
: state_(new internal_state()) {}
spine_pre_system::~spine_pre_system() noexcept = default;
void spine_pre_system::process(ecs::registry& owner) {
state_->process(owner);
}
}

View File

@@ -1,199 +0,0 @@
/*******************************************************************************
* 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/systems/spine_system.hpp>
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/spine_animation_event.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
{
//
// internal_state
//
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::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().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;
}
spSkeleton_update(skeleton, dt);
spAnimationState_update(anim_state, dt);
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);
}
}