mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-13 15:48:11 +07:00
two separated spine system for pre and post update
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
237
sources/enduro2d/high/systems/spine_post_system.cpp
Normal file
237
sources/enduro2d/high/systems/spine_post_system.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
62
sources/enduro2d/high/systems/spine_pre_system.cpp
Normal file
62
sources/enduro2d/high/systems/spine_pre_system.cpp
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user