remove spine integration

This commit is contained in:
BlackMATov
2020-10-30 02:34:05 +07:00
parent 5dfea8b512
commit 7cfc8fdac6
44 changed files with 0 additions and 2901 deletions

View File

@@ -14,7 +14,6 @@
-Imodules/glew/include
-Imodules/glfw/include
-Imodules/spine/spine-c/spine-c/include
-std=c++17
-stdlib=libc++

3
.gitmodules vendored
View File

@@ -34,9 +34,6 @@
[submodule "modules/flat.hpp"]
path = modules/flat.hpp
url = https://github.com/BlackMATov/flat.hpp
[submodule "modules/spine"]
path = modules/spine
url = https://github.com/EsotericSoftware/spine-runtimes.git
[submodule "modules/enum.hpp"]
path = modules/enum.hpp
url = https://github.com/BlackMATov/enum.hpp

View File

@@ -136,9 +136,6 @@ set(glew-cmake_BUILD_MULTI_CONTEXT OFF CACHE INTERNAL "" FORCE)
add_subdirectory(modules/glew)
set_target_properties(libglew_static PROPERTIES FOLDER modules)
add_subdirectory(modules/spine/spine-c)
set_target_properties(spine-c PROPERTIES FOLDER modules)
#
# e2d library target
#
@@ -163,7 +160,6 @@ target_link_libraries(${PROJECT_NAME}
target_link_libraries(${PROJECT_NAME}
PRIVATE glfw
PRIVATE libglew_static
PRIVATE spine-c
PRIVATE $<$<CXX_COMPILER_ID:MSVC>:winmm.lib>)
target_include_directories(${PROJECT_NAME}

View File

@@ -21,7 +21,6 @@
#include "assets/shader_asset.hpp"
#include "assets/shape_asset.hpp"
#include "assets/sound_asset.hpp"
#include "assets/spine_asset.hpp"
#include "assets/sprite_asset.hpp"
#include "assets/text_asset.hpp"
#include "assets/texture_asset.hpp"
@@ -40,7 +39,6 @@
#include "components/named.hpp"
#include "components/renderer.hpp"
#include "components/scene.hpp"
#include "components/spine_player.hpp"
#include "components/sprite_renderer.hpp"
#include "components/touchable.hpp"
#include "components/widget.hpp"
@@ -49,7 +47,6 @@
#include "resources/flipbook.hpp"
#include "resources/model.hpp"
#include "resources/prefab.hpp"
#include "resources/spine.hpp"
#include "resources/sprite.hpp"
#include "systems/camera_system.hpp"
@@ -59,7 +56,6 @@
#include "systems/label_system.hpp"
#include "systems/layout_system.hpp"
#include "systems/render_system.hpp"
#include "systems/spine_system.hpp"
#include "systems/touch_system.hpp"
#include "systems/world_system.hpp"

View File

@@ -33,7 +33,6 @@ namespace e2d
class shader_asset;
class shape_asset;
class sound_asset;
class spine_asset;
class sprite_asset;
class text_asset;
class texture_asset;
@@ -57,7 +56,6 @@ namespace e2d
class named;
class renderer;
class scene;
class spine_player;
class sprite_renderer;
class touchable;
class widget;
@@ -66,7 +64,6 @@ namespace e2d
class flipbook;
class model;
class prefab;
class spine;
class sprite;
class camera_system;
@@ -76,7 +73,6 @@ namespace e2d
class label_system;
class layout_system;
class render_system;
class spine_system;
class touch_system;
class world_system;

View File

@@ -1,21 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include "../_high.hpp"
#include "../library.hpp"
#include "../resources/spine.hpp"
namespace e2d
{
class spine_asset final : public content_asset<spine_asset, spine> {
public:
static const char* type_name() noexcept { return "spine_asset"; }
static load_async_result load_async(const library& library, str_view address);
};
}

View File

@@ -1,376 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include "_components.hpp"
#include "../assets/spine_asset.hpp"
#include "../assets/material_asset.hpp"
struct spSkeleton;
struct spSkeletonClipping;
struct spAnimationState;
namespace e2d::spine_player_events
{
class custom_evt;
class end_evt;
class complete_evt;
using event = std::variant<
custom_evt,
end_evt,
complete_evt>;
}
namespace e2d::spine_player_commands
{
class clear_track_cmd;
class set_anim_cmd;
class add_anim_cmd;
class set_empty_anim_cmd;
class add_empty_anim_cmd;
using command = std::variant<
clear_track_cmd,
set_anim_cmd,
add_anim_cmd,
set_empty_anim_cmd,
add_empty_anim_cmd>;
}
namespace e2d
{
class bad_spine_player_access final : public exception {
public:
const char* what() const noexcept final {
return "bad spine player access";
}
};
class spine_player final {
public:
using clipping_ptr = std::shared_ptr<spSkeletonClipping>;
using skeleton_ptr = std::shared_ptr<spSkeleton>;
using animation_ptr = std::shared_ptr<spAnimationState>;
public:
spine_player() = default;
spine_player(const spine_asset::ptr& spine);
spine_player& spine(
const spine_asset::ptr& value);
bool skin(str_view name);
bool attachment(str_view slot, str_view name);
bool has_skin(str_view name) const noexcept;
bool has_animation(str_view name) const noexcept;
const spine_asset::ptr& spine() const noexcept;
const clipping_ptr& clipper() const noexcept;
const skeleton_ptr& skeleton() const noexcept;
const animation_ptr& animation() const noexcept;
spine_player& materials(flat_map<str_hash, material_asset::ptr> value) noexcept;
const flat_map<str_hash, material_asset::ptr>& materials() const noexcept;
material_asset::ptr find_material(str_hash name) const noexcept;
private:
spine_asset::ptr spine_;
clipping_ptr clipping_;
skeleton_ptr skeleton_;
animation_ptr animation_;
flat_map<str_hash, material_asset::ptr> materials_;
};
}
namespace e2d
{
template <>
class factory_loader<spine_player> final : factory_loader<> {
public:
static const char* schema_source;
bool operator()(
spine_player& component,
const fill_context& ctx) const;
bool operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const;
};
template <>
class factory_loader<events<spine_player_events::event>> final : factory_loader<> {
public:
static const char* schema_source;
bool operator()(
events<spine_player_events::event>& component,
const fill_context& ctx) const;
bool operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const;
};
template <>
class factory_loader<commands<spine_player_commands::command>> final : factory_loader<> {
public:
static const char* schema_source;
bool operator()(
commands<spine_player_commands::command>& component,
const fill_context& ctx) const;
bool operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const;
};
}
namespace e2d
{
template <>
class component_inspector<spine_player> final : component_inspector<> {
public:
static const char* title;
void operator()(gcomponent<spine_player>& c) const;
};
}
namespace e2d::spine_player_events
{
class custom_evt final {
public:
custom_evt() = default;
custom_evt(str name)
: name_(std::move(name)) {}
custom_evt& name(str value) noexcept {
name_ = std::move(value);
return *this;
}
custom_evt& int_value(i32 value) noexcept {
int_value_ = value;
return *this;
}
custom_evt& float_value(f32 value) noexcept {
float_value_ = value;
return *this;
}
custom_evt& string_value(str value) noexcept {
string_value_ = std::move(value);
return *this;
}
[[nodiscard]] const str& name() const noexcept { return name_; }
[[nodiscard]] i32 int_value() const noexcept { return int_value_; }
[[nodiscard]] f32 float_value() const noexcept { return float_value_; }
[[nodiscard]] const str& string_value() const noexcept { return string_value_; }
private:
str name_;
i32 int_value_{0};
f32 float_value_{0.f};
str string_value_;
};
class end_evt final {
public:
end_evt() = default;
end_evt(str message)
: message_(std::move(message)) {}
end_evt& message(str value) noexcept {
message_ = std::move(value);
return *this;
}
[[nodiscard]] const str& message() const noexcept { return message_; }
private:
str message_;
};
class complete_evt final {
public:
complete_evt() = default;
complete_evt(str message)
: message_(std::move(message)) {}
complete_evt& message(str value) noexcept {
message_ = std::move(value);
return *this;
}
[[nodiscard]] const str& message() const noexcept { return message_; }
private:
str message_;
};
}
namespace e2d::spine_player_commands
{
class clear_track_cmd final {
public:
clear_track_cmd(u32 track)
: track_(track) {}
[[nodiscard]] u32 track() const noexcept { return track_; }
private:
u32 track_{0u};
};
class set_anim_cmd final {
public:
set_anim_cmd(u32 track, str name)
: track_(track)
, name_(std::move(name)) {}
set_anim_cmd& loop(bool value) noexcept {
loop_ = value;
return *this;
}
set_anim_cmd& end_message(str value) noexcept {
end_message_ = std::move(value);
return *this;
}
set_anim_cmd& complete_message(str value) noexcept {
complete_message_ = std::move(value);
return *this;
}
[[nodiscard]] u32 track() const noexcept { return track_; }
[[nodiscard]] const str& name() const noexcept { return name_; }
[[nodiscard]] bool loop() const noexcept { return loop_; }
[[nodiscard]] const str& end_message() const noexcept { return complete_message_; }
[[nodiscard]] const str& complete_message() const noexcept { return complete_message_; }
private:
u32 track_{0u};
str name_;
bool loop_{false};
str end_message_;
str complete_message_;
};
class add_anim_cmd final {
public:
add_anim_cmd(u32 track, str name)
: track_(track)
, name_(std::move(name)) {}
add_anim_cmd& loop(bool value) noexcept {
loop_ = value;
return *this;
}
add_anim_cmd& delay(f32 value) noexcept {
delay_ = value;
return *this;
}
add_anim_cmd& end_message(str value) noexcept {
end_message_ = std::move(value);
return *this;
}
add_anim_cmd& complete_message(str value) noexcept {
complete_message_ = std::move(value);
return *this;
}
[[nodiscard]] u32 track() const noexcept { return track_; }
[[nodiscard]] const str& name() const noexcept { return name_; }
[[nodiscard]] bool loop() const noexcept { return loop_; }
[[nodiscard]] f32 delay() const noexcept { return delay_; }
[[nodiscard]] const str& end_message() const noexcept { return end_message_; }
[[nodiscard]] const str& complete_message() const noexcept { return complete_message_; }
private:
u32 track_{0u};
str name_;
bool loop_{false};
f32 delay_{0.f};
str end_message_;
str complete_message_;
};
class set_empty_anim_cmd final {
public:
set_empty_anim_cmd(u32 track)
: track_(track) {}
set_empty_anim_cmd& mix_duration(f32 value) noexcept {
mix_duration_ = value;
return *this;
}
set_empty_anim_cmd& end_message(str value) noexcept {
end_message_ = std::move(value);
return *this;
}
set_empty_anim_cmd& complete_message(str value) noexcept {
complete_message_ = std::move(value);
return *this;
}
[[nodiscard]] u32 track() const noexcept { return track_; }
[[nodiscard]] f32 mix_duration() const noexcept { return mix_duration_; }
[[nodiscard]] const str& end_message() const noexcept { return end_message_; }
[[nodiscard]] const str& complete_message() const noexcept { return complete_message_; }
private:
u32 track_{0u};
f32 mix_duration_{0.f};
str end_message_;
str complete_message_;
};
class add_empty_anim_cmd final {
public:
add_empty_anim_cmd(u32 track)
: track_(track) {}
add_empty_anim_cmd& delay(f32 value) noexcept {
delay_ = value;
return *this;
}
add_empty_anim_cmd& mix_duration(f32 value) noexcept {
mix_duration_ = value;
return *this;
}
add_empty_anim_cmd& end_message(str value) noexcept {
end_message_ = std::move(value);
return *this;
}
add_empty_anim_cmd& complete_message(str value) noexcept {
complete_message_ = std::move(value);
return *this;
}
[[nodiscard]] u32 track() const noexcept { return track_; }
[[nodiscard]] f32 delay() const noexcept { return delay_; }
[[nodiscard]] f32 mix_duration() const noexcept { return mix_duration_; }
[[nodiscard]] const str& end_message() const noexcept { return end_message_; }
[[nodiscard]] const str& complete_message() const noexcept { return complete_message_; }
private:
u32 track_{0u};
f32 delay_{0.f};
f32 mix_duration_{0.f};
str end_message_;
str complete_message_;
};
}

View File

@@ -1,66 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include "../_high.hpp"
struct spAtlas;
struct spSkeletonData;
struct spAnimationStateData;
namespace e2d
{
class bad_spine_access final : public exception {
public:
const char* what() const noexcept final {
return "bad spine access";
}
};
class spine final {
public:
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() = default;
~spine() noexcept = default;
spine(spine&& other) noexcept;
spine& operator=(spine&& other) noexcept;
spine(const spine& other);
spine& operator=(const spine& other);
void clear() noexcept;
void swap(spine& other) noexcept;
spine& assign(spine&& other) noexcept;
spine& assign(const spine& other);
spine& set_atlas(atlas_ptr atlas);
spine& set_skeleton(skeleton_data_ptr skeleton);
spine& set_default_mix(f32 duration);
spine& set_animation_mix(
const str& from,
const str& to,
f32 duration);
const atlas_ptr& atlas() const noexcept;
const skeleton_data_ptr& skeleton() const noexcept;
const animation_data_ptr& animation() const noexcept;
private:
atlas_ptr atlas_;
skeleton_data_ptr skeleton_;
animation_data_ptr animation_;
};
void swap(spine& l, spine& r) noexcept;
bool operator==(const spine& l, const spine& r) noexcept;
bool operator!=(const spine& l, const spine& r) noexcept;
}

View File

@@ -1,26 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#pragma once
#include "_systems.hpp"
namespace e2d
{
class spine_system final
: public ecs::system<ecs::after<systems::update_event>> {
public:
spine_system();
~spine_system() noexcept;
void process(
ecs::registry& owner,
const ecs::after<systems::update_event>& trigger) override;
private:
class internal_state;
std::unique_ptr<internal_state> state_;
};
}

Submodule modules/spine deleted from 9fabc60323

View File

@@ -1,14 +0,0 @@
{
"passes" : [{
"shader" : "../shaders/spine_shader.json",
"state_block" : {
"blending_state" : {
"src_factor" : "one",
"dst_factor" : "one"
},
"capabilities_state" : {
"blending" : true
}
}
}]
}

View File

@@ -1,14 +0,0 @@
{
"passes" : [{
"shader" : "../shaders/spine_shader.json",
"state_block" : {
"blending_state" : {
"src_factor" : "dst_color",
"dst_factor" : "one_minus_src_alpha"
},
"capabilities_state" : {
"blending" : true
}
}
}]
}

View File

@@ -1,14 +0,0 @@
{
"passes" : [{
"shader" : "../shaders/spine_shader.json",
"state_block" : {
"blending_state" : {
"src_factor" : "one",
"dst_factor" : "one_minus_src_alpha"
},
"capabilities_state" : {
"blending" : true
}
}
}]
}

View File

@@ -1,14 +0,0 @@
{
"passes" : [{
"shader" : "../shaders/spine_shader.json",
"state_block" : {
"blending_state" : {
"src_factor" : "one",
"dst_factor" : "one_minus_src_color"
},
"capabilities_state" : {
"blending" : true
}
}
}]
}

View File

@@ -1,21 +0,0 @@
{
"prefab" : "spine_prefab.json",
"components" : {
"named" : {
"name" : "coin"
},
"spine_player" : {
"spine" : "../spines/coin_spine.json"
},
"spine_player.commands" : {
"commands" : [{
"type" : "set_anim_cmd",
"desc" : {
"track" : 0,
"name" : "animation",
"loop" : true
}
}]
}
}
}

View File

@@ -1,21 +0,0 @@
{
"prefab" : "spine_prefab.json",
"components" : {
"named" : {
"name" : "dragon"
},
"spine_player" : {
"spine" : "../spines/dragon_spine.json"
},
"spine_player.commands" : {
"commands" : [{
"type" : "set_anim_cmd",
"desc" : {
"track" : 0,
"name" : "flying",
"loop" : true
}
}]
}
}
}

View File

@@ -1,47 +0,0 @@
{
"prefab" : "spine_prefab.json",
"components" : {
"named" : {
"name" : "raptor"
},
"spine_player" : {
"spine" : "../spines/raptor_spine.json"
},
"spine_player.commands" : {
"commands" : [{
"type" : "add_anim_cmd",
"desc" : {
"track" : 0,
"name" : "walk"
}
}, {
"type" : "add_anim_cmd",
"desc" : {
"track" : 0,
"name" : "roar"
}
}, {
"type" : "add_anim_cmd",
"desc" : {
"track" : 0,
"name" : "walk",
"loop" : true
}
}, {
"type" : "add_anim_cmd",
"desc" : {
"track" : 1,
"name" : "gun-grab",
"delay" : 2
}
}, {
"type" : "add_anim_cmd",
"desc" : {
"track" : 1,
"name" : "gun-holster",
"delay" : 3
}
}]
}
}
}

View File

@@ -1,18 +0,0 @@
{
"components" : {
"named" : {
"name" : "spine"
},
"renderer" : {},
"spine_player" : {
"materials" : {
"additive" : "../materials/spine_material_additive.json",
"multiply" : "../materials/spine_material_multiply.json",
"normal" : "../materials/spine_material_normal.json",
"screen" : "../materials/spine_material_screen.json"
}
},
"spine_player.events" : {},
"spine_player.commands" : {}
}
}

View File

@@ -2,38 +2,5 @@
"prefab" : "../prefabs/scene_prefab.json",
"children" : [{
"prefab" : "../prefabs/camera_prefab.json"
},{
"prefab" : "../prefabs/coin_prefab.json",
"components" : {
"actor" : {
"translation" : [350,250],
"scale" : 0.25
},
"named" : {
"name" : "coin"
}
}
}, {
"prefab" : "../prefabs/raptor_prefab.json",
"components" : {
"actor" : {
"translation" : [300,-350],
"scale" : 0.25
},
"named" : {
"name" : "raptor"
}
}
}, {
"prefab" : "../prefabs/dragon_prefab.json",
"components" : {
"actor" : {
"translation" : [-100,0],
"scale" : 0.9
},
"named" : {
"name" : "dragon"
}
}
}]
}

View File

@@ -1,10 +0,0 @@
uniform sampler2D u_texture;
varying vec2 v_st0;
varying vec4 v_color0;
void main() {
vec4 c = texture2D(u_texture, v_st0) * v_color0;
c.rgb *= c.a;
gl_FragColor = c;
}

View File

@@ -1,4 +0,0 @@
{
"vertex" : "spine_shader.vert",
"fragment" : "spine_shader.frag"
}

View File

@@ -1,36 +0,0 @@
uniform vec2 u_screen_s;
uniform mat4 u_matrix_vp;
attribute vec3 a_vertex;
attribute vec2 a_st0;
attribute vec4 a_color0;
varying vec2 v_st0;
varying vec4 v_color0;
vec2 round(vec2 v) {
return vec2(
floor(v.x + 0.5),
floor(v.y + 0.5));
}
vec4 pixel_snap(vec4 pos) {
vec2 hpc = u_screen_s * 0.5;
vec2 pixel_pos = round((pos.xy / pos.w) * hpc);
pos.xy = pixel_pos / hpc * pos.w;
return pos;
}
vec4 vertex_to_homo(vec3 pos) {
return vec4(pos, 1.0) * u_matrix_vp;
}
void main() {
v_st0 = a_st0;
v_color0 = a_color0;
#ifndef VERTEX_SNAPPING_ON
gl_Position = vertex_to_homo(a_vertex);
#else
gl_Position = pixel_snap(vertex_to_homo(a_vertex));
#endif
}

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:772bf78b10c22079c0afb46edfff2eff789c4082cd52f4124868a04905b72bbf
size 2657

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:833bc92c2a02695c326080b72f8636206ec53f361010a6b02324565b470f690b
size 831

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:21bc65e3de306d698df5c6a6519786a430d8529bc58f01f87013c9927819f87a
size 402363

View File

@@ -1,5 +0,0 @@
{
"atlas" : "coin/coin.atlas",
"skeleton" : "coin/coin-pro.skel",
"skeleton_scale" : 1.0
}

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2d299087ede92db92396af117f151e8f50abecb57094f34b031d0a389a53d1ad
size 7073

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fa3a79e67d926b1e5f017ecd2f2bc2f73fa02a766f27cc83a6547f0c90556c58
size 4224

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a45ac203747ffc4802c83860b20bcb380b405ea55734992defb5fb64f49bbfa0
size 628537

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:86b1a3fcd15a538e08535f0fad4b6cc1c9595060aae0d558c0f2bcc1d09a2833
size 194829

View File

@@ -1,5 +0,0 @@
{
"atlas" : "dragon/dragon.atlas",
"skeleton" : "dragon/dragon-ess.skel",
"skeleton_scale" : 1.0
}

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c8d0e5057c92306900f39879e51642500b841825cedb47833bbfa129978b5757
size 172896

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:83c00999f00c4ac395ba063291d02131fb2a8869cb6d8f4fd20bc15131d52d54
size 63077

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dd001b421b535adb21c74148790d8b576d17aedaeecf0f2ac6318619b94a932b
size 4210

View File

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:879d84230024a1529ea994e7cd6416c5883f4b74b0d268b5e2dea1cbe2d00777
size 480392

View File

@@ -1,38 +0,0 @@
{
"atlas": "raptor/raptor.atlas",
"skeleton": "raptor/raptor-pro.json-large",
"skeleton_scale": 1.0,
"default_animation_mix" : 0.5,
"animation_mixes": [
{
"from": "walk",
"to": "roar",
"duration": 0.5
},
{
"from": "roar",
"to": "walk",
"duration": 0.5
},
{
"from": "walk",
"to": "jump",
"duration": 0.5
},
{
"from": "jump",
"to": "walk",
"duration": 0.5
},
{
"from": "roar",
"to": "jump",
"duration": 0.5
},
{
"from": "jump",
"to": "roar",
"duration": 0.5
}
]
}

View File

@@ -29,44 +29,6 @@ namespace
if ( k.is_key_pressed(keyboard_key::lsuper) && k.is_key_just_released(keyboard_key::enter) ) {
the<window>().toggle_fullscreen(!the<window>().fullscreen());
}
// use keys R, J, G to start animations
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);
if ( roar || jump || gun_grab ) {
owner.for_each_component<spine_player>([
roar, jump, gun_grab
](ecs::entity e, const spine_player& p) {
if ( roar && p.has_animation("roar") ) {
e.ensure_component<commands<spine_player_commands::command>>()
.add(spine_player_commands::set_anim_cmd(0, "roar")
.complete_message("to_walk"));
} else if ( jump && p.has_animation("jump") ) {
e.ensure_component<commands<spine_player_commands::command>>()
.add(spine_player_commands::set_anim_cmd(0, "jump")
.complete_message("to_walk"));
} else if ( gun_grab && p.has_animation("gun-grab") ) {
e.ensure_component<commands<spine_player_commands::command>>()
.add(spine_player_commands::set_anim_cmd(1, "gun-grab"))
.add(spine_player_commands::add_anim_cmd(1, "gun-holster").delay(3.f));
}
});
}
owner.for_joined_components<events<spine_player_events::event>>([
](ecs::entity e, const events<spine_player_events::event>& pe) {
for ( const auto& evt : pe.get() ) {
if ( auto complete = std::get_if<spine_player_events::complete_evt>(&evt);
complete && complete->message() == "to_walk" )
{
e.ensure_component<commands<spine_player_commands::command>>()
.add(spine_player_commands::add_anim_cmd(0, "walk")
.loop(true));
}
}
});
}
};

View File

@@ -1,415 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#include <enduro2d/high/assets/spine_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/spine.h>
#include <spine/extension.h>
namespace
{
using namespace e2d;
class spine_asset_loading_exception final : public asset_loading_exception {
const char* what() const noexcept final {
return "spine asset loading exception";
}
};
const char* spine_asset_schema_source = R"json({
"type" : "object",
"required" : [ "atlas", "skeleton" ],
"additionalProperties" : false,
"properties" : {
"atlas" : { "$ref" : "#/common_definitions/address" },
"skeleton" : { "$ref" : "#/common_definitions/address" },
"skeleton_scale" : { "type" : "number" },
"default_animation_mix" : { "type" : "number" },
"animation_mixes" : { "$ref": "#/definitions/animation_mixes" }
},
"definitions" : {
"animation_mixes" : {
"type" : "array",
"items" : { "$ref": "#/definitions/animation_mix" }
},
"animation_mix" : {
"type" : "object",
"required" : [ "from", "to", "duration" ],
"additionalProperties" : false,
"properties" : {
"from" : { "$ref": "#/common_definitions/name" },
"to" : { "$ref": "#/common_definitions/name" },
"duration" : { "type" : "number" }
}
}
}
})json";
const rapidjson::SchemaDocument& spine_asset_schema() {
static std::mutex mutex;
static std::unique_ptr<rapidjson::SchemaDocument> schema;
std::lock_guard<std::mutex> guard(mutex);
if ( !schema ) {
rapidjson::Document doc;
if ( doc.Parse(spine_asset_schema_source).HasParseError() ) {
the<debug>().error("ASSETS: Failed to parse spine asset schema");
throw spine_asset_loading_exception();
}
json_utils::add_common_schema_definitions(doc);
schema = std::make_unique<rapidjson::SchemaDocument>(doc);
}
return *schema;
}
struct animation_mix {
str from;
str to;
f32 duration{0.f};
};
animation_mix parse_animation_mix(const rapidjson::Value& root) {
animation_mix mix;
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_asset_loading_exception();
}
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_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_asset_loading_exception();
}
return mix;
}
struct atlas_internal_state {
asset_group loaded;
asset_dependencies loading;
};
stdex::promise<spine::atlas_ptr> load_atlas(
const library& library,
const str& parent_address,
const str& atlas_address)
{
const str atlas_path = path::combine(parent_address, atlas_address);
const str atlas_folder = path::parent_path(atlas_path);
return library.load_asset_async<binary_asset>(atlas_path)
.then([
&library,
atlas_path,
atlas_folder
](const binary_asset::load_result& atlas_data){
return the<deferrer>().do_in_worker_thread([](){})
.then([
&library,
atlas_data,
atlas_path,
atlas_folder
](){
auto atlas_internal = std::make_unique<atlas_internal_state>();
spine::atlas_ptr atlas(
spAtlas_create(
reinterpret_cast<const char*>(atlas_data->content().data()),
math::numeric_cast<int>(atlas_data->content().size()),
atlas_folder.c_str(),
atlas_internal.get()),
spAtlas_dispose);
if ( !atlas ) {
the<debug>().error("SPINE: Failed to create preload atlas");
throw spine_asset_loading_exception();
}
return atlas_internal->loading.load_async(library);
})
.then([
atlas_data,
atlas_path,
atlas_folder
](auto&& dependencies){
auto atlas_internal = std::make_unique<atlas_internal_state>();
atlas_internal->loaded = std::forward<decltype(dependencies)>(dependencies);
spine::atlas_ptr atlas(
spAtlas_create(
reinterpret_cast<const char*>(atlas_data->content().data()),
math::numeric_cast<int>(atlas_data->content().size()),
atlas_folder.c_str(),
atlas_internal.get()),
spAtlas_dispose);
if ( !atlas ) {
the<debug>().error("SPINE: Failed to create preloaded atlas");
throw spine_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_asset_loading_exception();
}
}
atlas->rendererObject = nullptr;
return atlas;
});
});
}
stdex::promise<spine::skeleton_data_ptr> load_skeleton_data(
const library& library,
const str& parent_address,
const str& skeleton_address,
f32 skeleton_scale,
const spine::atlas_ptr& atlas)
{
str address = path::combine(parent_address, skeleton_address);
return library.load_asset_async<binary_asset>(address)
.then([
atlas,
skeleton_scale,
address = std::move(address)
](const binary_asset::load_result& skeleton_data){
return the<deferrer>().do_in_worker_thread([
atlas,
skeleton_scale,
address = std::move(address),
skeleton_data
](){
if ( strings::ends_with(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_asset_loading_exception();
}
binary_skeleton->scale = skeleton_scale;
spine::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_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_asset_loading_exception();
}
json_skeleton->scale = skeleton_scale;
spine::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_asset_loading_exception();
}
return data_skeleton;
}
});
});
}
stdex::promise<spine> parse_spine(
const library& library,
const str& parent_address,
const rapidjson::Value& root)
{
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");
}
}
f32 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());
const str atlas_address = root["atlas"].GetString();
E2D_ASSERT(root.HasMember("skeleton") && root["skeleton"].IsString());
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::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::atlas_ptr,
spine::skeleton_data_ptr
>& results){
spine 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);
}
return content;
});
}
}
namespace e2d
{
spine_asset::load_async_result spine_asset::load_async(
const library& library, str_view address)
{
return library.load_asset_async<json_asset>(address)
.then([
&library,
address = str(address),
parent_address = path::parent_path(address)
](const json_asset::load_result& spine_data){
return the<deferrer>().do_in_worker_thread([address, spine_data](){
const rapidjson::Document& doc = *spine_data->content();
rapidjson::SchemaValidator validator(spine_asset_schema());
if ( doc.Accept(validator) ) {
return;
}
rapidjson::StringBuffer sb;
if ( validator.GetInvalidDocumentPointer().StringifyUriFragment(sb) ) {
the<debug>().error("ASSET: Failed to validate asset json:\n"
"--> Address: %0\n"
"--> Invalid schema keyword: %1\n"
"--> Invalid document pointer: %2",
address,
validator.GetInvalidSchemaKeyword(),
sb.GetString());
} else {
the<debug>().error("ASSET: Failed to validate asset json");
}
throw spine_asset_loading_exception();
})
.then([&library, parent_address, spine_data](){
return parse_spine(
library, parent_address, *spine_data->content());
})
.then([](auto&& content){
return spine_asset::create(
std::forward<decltype(content)>(content));
});
});
}
}
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

@@ -1,922 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/commands.hpp>
#include <enduro2d/high/components/events.hpp>
#include <spine/spine.h>
namespace
{
using namespace e2d;
std::optional<spine_player_events::custom_evt> parse_custom_evt(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
spine_player_events::custom_evt evt;
if ( root.HasMember("name") ) {
str evt_name = evt.name();
if ( !json_utils::try_parse_value(root["name"], evt_name) ) {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'custom_evt.name' property");
return std::nullopt;
}
evt.name(std::move(evt_name));
}
if ( root.HasMember("int_value") ) {
i32 evt_int_value = evt.int_value();
if ( !json_utils::try_parse_value(root["int_value"], evt_int_value) ) {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'custom_evt.int_value' property");
return std::nullopt;
}
evt.int_value(evt_int_value);
}
if ( root.HasMember("float_value") ) {
f32 evt_float_value = evt.float_value();
if ( !json_utils::try_parse_value(root["float_value"], evt_float_value) ) {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'custom_evt.float_value' property");
return std::nullopt;
}
evt.float_value(evt_float_value);
}
if ( root.HasMember("string_value") ) {
str evt_string_value = evt.string_value();
if ( !json_utils::try_parse_value(root["string_value"], evt_string_value) ) {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'custom_evt.string_value' property");
return std::nullopt;
}
evt.string_value(std::move(evt_string_value));
}
return evt;
}
std::optional<spine_player_events::end_evt> parse_end_evt(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
spine_player_events::end_evt evt;
if ( root.HasMember("message") ) {
str evt_message = evt.message();
if ( !json_utils::try_parse_value(root["message"], evt_message) ) {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'end_evt.message' property");
return std::nullopt;
}
evt.message(std::move(evt_message));
}
return evt;
}
std::optional<spine_player_events::complete_evt> parse_complete_evt(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
spine_player_events::complete_evt evt;
if ( root.HasMember("message") ) {
str evt_message = evt.message();
if ( !json_utils::try_parse_value(root["message"], evt_message) ) {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'complete_evt.message' property");
return std::nullopt;
}
evt.message(std::move(evt_message));
}
return evt;
}
std::optional<spine_player_events::event> parse_event(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
str_hash command_type;
E2D_ASSERT(root.HasMember("type") && root["type"].IsString());
if ( !json_utils::try_parse_value(root["type"], command_type) ) {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'event.type' property");
return std::nullopt;
}
E2D_ASSERT(root.HasMember("desc") && root["desc"].IsObject());
const auto& event_desc = root["desc"];
if ( command_type == make_hash("custom_evt") ) {
auto evt = parse_custom_evt(event_desc);
return evt ? evt : std::nullopt;
} else if ( command_type == make_hash("end_evt") ) {
auto evt = parse_end_evt(event_desc);
return evt ? evt : std::nullopt;
} else if ( command_type == make_hash("complete_evt") ) {
auto evt = parse_complete_evt(event_desc);
return evt ? evt : std::nullopt;
} else {
the<debug>().error("SPINE_PLAYER_EVENTS: Incorrect formatting of 'event.type' property");
return std::nullopt;
}
}
std::optional<vector<spine_player_events::event>> parse_events(const rapidjson::Value& root) {
E2D_ASSERT(root.IsArray());
vector<spine_player_events::event> evts;
evts.reserve(root.Size());
for ( rapidjson::SizeType i = 0; i < root.Size(); ++i ) {
auto evt = parse_event(root[i]);
if ( !evt ) {
return std::nullopt;
}
evts.push_back(std::move(*evt));
}
return evts;
}
}
namespace
{
using namespace e2d;
std::optional<spine_player_commands::clear_track_cmd> parse_clear_track_cmd(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
u32 cmd_track{0u};
if ( !json_utils::try_parse_value(root["track"], cmd_track) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'clear_track_cmd.track' property");
return std::nullopt;
}
return spine_player_commands::clear_track_cmd(cmd_track);
}
std::optional<spine_player_commands::set_anim_cmd> parse_set_anim_cmd(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
u32 cmd_track{0u};
E2D_ASSERT(root.HasMember("track"));
if ( !json_utils::try_parse_value(root["track"], cmd_track) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_anim_cmd.track' property");
return std::nullopt;
}
str cmd_name;
E2D_ASSERT(root.HasMember("name"));
if ( !json_utils::try_parse_value(root["name"], cmd_name) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_anim_cmd.name' property");
return std::nullopt;
}
spine_player_commands::set_anim_cmd cmd(cmd_track, std::move(cmd_name));
if ( root.HasMember("loop") ) {
bool cmd_loop = cmd.loop();
if ( !json_utils::try_parse_value(root["loop"], cmd_loop) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_anim_cmd.loop' property");
return std::nullopt;
}
cmd.loop(cmd_loop);
}
if ( root.HasMember("end_message") ) {
str cmd_end_message = cmd.end_message();
if ( !json_utils::try_parse_value(root["end_message"], cmd_end_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_anim_cmd.end_message' property");
return std::nullopt;
}
cmd.end_message(std::move(cmd_end_message));
}
if ( root.HasMember("complete_message") ) {
str cmd_complete_message = cmd.complete_message();
if ( !json_utils::try_parse_value(root["complete_message"], cmd_complete_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_anim_cmd.complete_message' property");
return std::nullopt;
}
cmd.complete_message(std::move(cmd_complete_message));
}
return cmd;
}
std::optional<spine_player_commands::add_anim_cmd> parse_add_anim_cmd(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
u32 cmd_track{0u};
E2D_ASSERT(root.HasMember("track"));
if ( !json_utils::try_parse_value(root["track"], cmd_track) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_anim_cmd.track' property");
return std::nullopt;
}
str cmd_name;
E2D_ASSERT(root.HasMember("name"));
if ( !json_utils::try_parse_value(root["name"], cmd_name) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_anim_cmd.name' property");
return std::nullopt;
}
spine_player_commands::add_anim_cmd cmd(cmd_track, std::move(cmd_name));
if ( root.HasMember("loop") ) {
bool cmd_loop = cmd.loop();
if ( !json_utils::try_parse_value(root["loop"], cmd_loop) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_anim_cmd.loop' property");
return std::nullopt;
}
cmd.loop(cmd_loop);
}
if ( root.HasMember("delay") ) {
f32 cmd_delay = cmd.delay();
if ( !json_utils::try_parse_value(root["delay"], cmd_delay) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_anim_cmd.delay' property");
return std::nullopt;
}
cmd.delay(cmd_delay);
}
if ( root.HasMember("end_message") ) {
str cmd_end_message = cmd.end_message();
if ( !json_utils::try_parse_value(root["end_message"], cmd_end_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_anim_cmd.end_message' property");
return std::nullopt;
}
cmd.end_message(std::move(cmd_end_message));
}
if ( root.HasMember("complete_message") ) {
str cmd_complete_message = cmd.complete_message();
if ( !json_utils::try_parse_value(root["complete_message"], cmd_complete_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_anim_cmd.complete_message' property");
return std::nullopt;
}
cmd.complete_message(std::move(cmd_complete_message));
}
return cmd;
}
std::optional<spine_player_commands::set_empty_anim_cmd> parse_set_empty_anim_cmd(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
u32 cmd_track{0u};
E2D_ASSERT(root.HasMember("track"));
if ( !json_utils::try_parse_value(root["track"], cmd_track) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_empty_anim_cmd.track' property");
return std::nullopt;
}
spine_player_commands::set_empty_anim_cmd cmd(cmd_track);
if ( root.HasMember("mix_duration") ) {
f32 cmd_mix_duration = cmd.mix_duration();
if ( !json_utils::try_parse_value(root["mix_duration"], cmd_mix_duration) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_empty_anim_cmd.mix_duration' property");
return std::nullopt;
}
cmd.mix_duration(cmd_mix_duration);
}
if ( root.HasMember("end_message") ) {
str cmd_end_message = cmd.end_message();
if ( !json_utils::try_parse_value(root["end_message"], cmd_end_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_empty_anim_cmd.end_message' property");
return std::nullopt;
}
cmd.end_message(std::move(cmd_end_message));
}
if ( root.HasMember("complete_message") ) {
str cmd_complete_message = cmd.complete_message();
if ( !json_utils::try_parse_value(root["complete_message"], cmd_complete_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'set_empty_anim_cmd.complete_message' property");
return std::nullopt;
}
cmd.complete_message(std::move(cmd_complete_message));
}
return cmd;
}
std::optional<spine_player_commands::add_empty_anim_cmd> parse_add_empty_anim_cmd(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
u32 cmd_track{0u};
E2D_ASSERT(root.HasMember("track"));
if ( !json_utils::try_parse_value(root["track"], cmd_track) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_empty_anim_cmd.track' property");
return std::nullopt;
}
spine_player_commands::add_empty_anim_cmd cmd(cmd_track);
if ( root.HasMember("delay") ) {
f32 cmd_delay = cmd.delay();
if ( !json_utils::try_parse_value(root["delay"], cmd_delay) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_empty_anim_cmd.delay' property");
return std::nullopt;
}
cmd.delay(cmd_delay);
}
if ( root.HasMember("mix_duration") ) {
f32 cmd_mix_duration = cmd.mix_duration();
if ( !json_utils::try_parse_value(root["mix_duration"], cmd_mix_duration) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_empty_anim_cmd.mix_duration' property");
return std::nullopt;
}
cmd.mix_duration(cmd_mix_duration);
}
if ( root.HasMember("end_message") ) {
str cmd_end_message = cmd.end_message();
if ( !json_utils::try_parse_value(root["end_message"], cmd_end_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_empty_anim_cmd.end_message' property");
return std::nullopt;
}
cmd.end_message(std::move(cmd_end_message));
}
if ( root.HasMember("complete_message") ) {
str cmd_complete_message = cmd.complete_message();
if ( !json_utils::try_parse_value(root["complete_message"], cmd_complete_message) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'add_empty_anim_cmd.complete_message' property");
return std::nullopt;
}
cmd.complete_message(std::move(cmd_complete_message));
}
return cmd;
}
std::optional<spine_player_commands::command> parse_command(const rapidjson::Value& root) {
E2D_ASSERT(root.IsObject());
str_hash command_type;
E2D_ASSERT(root.HasMember("type") && root["type"].IsString());
if ( !json_utils::try_parse_value(root["type"], command_type) ) {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'command.type' property");
return std::nullopt;
}
E2D_ASSERT(root.HasMember("desc") && root["desc"].IsObject());
const auto& command_desc = root["desc"];
if ( command_type == make_hash("clear_track_cmd") ) {
auto cmd = parse_clear_track_cmd(command_desc);
return cmd ? cmd : std::nullopt;
} else if ( command_type == make_hash("set_anim_cmd") ) {
auto cmd = parse_set_anim_cmd(command_desc);
return cmd ? cmd : std::nullopt;
} else if ( command_type == make_hash("add_anim_cmd") ) {
auto cmd = parse_add_anim_cmd(command_desc);
return cmd ? cmd : std::nullopt;
} else if ( command_type == make_hash("set_empty_anim_cmd") ) {
auto cmd = parse_set_empty_anim_cmd(command_desc);
return cmd ? cmd : std::nullopt;
} else if ( command_type == make_hash("add_empty_anim_cmd") ) {
auto cmd = parse_add_empty_anim_cmd(command_desc);
return cmd ? cmd : std::nullopt;
} else {
the<debug>().error("SPINE_PLAYER_COMMANDS: Incorrect formatting of 'command.type' property");
return std::nullopt;
}
}
std::optional<vector<spine_player_commands::command>> parse_commands(const rapidjson::Value& root) {
E2D_ASSERT(root.IsArray());
vector<spine_player_commands::command> cmds;
cmds.reserve(root.Size());
for ( rapidjson::SizeType i = 0; i < root.Size(); ++i ) {
auto cmd = parse_command(root[i]);
if ( !cmd ) {
return std::nullopt;
}
cmds.push_back(std::move(*cmd));
}
return cmds;
}
}
namespace e2d
{
spine_player::spine_player(const spine_asset::ptr& spine) {
this->spine(spine);
}
spine_player& spine_player::spine(const spine_asset::ptr& value) {
clipping_ptr new_clipping = clipping_ptr(
spSkeletonClipping_create(),
spSkeletonClipping_dispose);
if ( !new_clipping ) {
throw std::bad_alloc();
}
skeleton_ptr new_skeleton;
if ( value && value->content().skeleton() ) {
new_skeleton = skeleton_ptr(
spSkeleton_create(value->content().skeleton().get()),
spSkeleton_dispose);
if ( !new_skeleton ) {
throw std::bad_alloc();
}
}
animation_ptr new_animation;
if ( value && value->content().animation() ) {
new_animation = animation_ptr(
spAnimationState_create(value->content().animation().get()),
spAnimationState_dispose);
if ( !new_animation ) {
throw std::bad_alloc();
}
}
spine_ = value;
clipping_ = std::move(new_clipping);
skeleton_ = std::move(new_skeleton);
animation_ = std::move(new_animation);
return *this;
}
bool spine_player::skin(str_view name) {
if ( !skeleton_ ) {
return false;
}
static thread_local str skin_name;
skin_name = name;
return !!spSkeleton_setSkinByName(
skeleton_.get(), skin_name.c_str());
}
bool spine_player::attachment(str_view slot, str_view name) {
if ( !skeleton_ ) {
return false;
}
static thread_local str slot_name;
slot_name = slot;
static thread_local str attachment_name;
attachment_name = name;
return !!spSkeleton_setAttachment(
skeleton_.get(), slot_name.c_str(), attachment_name.c_str());
}
bool spine_player::has_skin(str_view name) const noexcept {
if ( !skeleton_ ) {
return false;
}
for ( int i = 0; i < skeleton_->data->skinsCount; ++i ) {
if ( name == skeleton_->data->skins[i]->name ) {
return true;
}
}
return false;
}
bool spine_player::has_animation(str_view name) const noexcept {
if ( !skeleton_ ) {
return false;
}
for ( int i = 0; i < skeleton_->data->animationsCount; ++i ) {
if ( name == skeleton_->data->animations[i]->name ) {
return true;
}
}
return false;
}
const spine_asset::ptr& spine_player::spine() const noexcept {
return spine_;
}
const spine_player::clipping_ptr& spine_player::clipper() const noexcept {
return clipping_;
}
const spine_player::skeleton_ptr& spine_player::skeleton() const noexcept {
return skeleton_;
}
const spine_player::animation_ptr& spine_player::animation() const noexcept {
return animation_;
}
spine_player& spine_player::materials(flat_map<str_hash, material_asset::ptr> value) noexcept {
materials_ = std::move(value);
return *this;
}
const flat_map<str_hash, material_asset::ptr>& spine_player::materials() const noexcept {
return materials_;
}
material_asset::ptr spine_player::find_material(str_hash name) const noexcept {
const auto iter = materials_.find(name);
return iter != materials_.end()
? iter->second
: nullptr;
}
}
namespace e2d
{
const char* factory_loader<spine_player>::schema_source = R"json({
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"spine" : { "$ref": "#/common_definitions/address" },
"materials" : { "$ref": "#/definitions/materials" },
"skin" : { "$ref": "#/common_definitions/name" },
"attachments" : { "$ref": "#/definitions/attachments" }
},
"definitions" : {
"materials" : {
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"normal" : { "$ref": "#/common_definitions/address" },
"additive" : { "$ref": "#/common_definitions/address" },
"multiply" : { "$ref": "#/common_definitions/address" },
"screen" : { "$ref": "#/common_definitions/address" }
}
},
"attachments" : {
"type" : "array",
"items" : { "$ref": "#/definitions/attachment" }
},
"attachment" : {
"type" : "object",
"required" : [ "slot", "name" ],
"additionalProperties" : false,
"properties" : {
"slot" : { "$ref": "#/common_definitions/name" },
"name" : { "$ref": "#/common_definitions/name" }
}
}
}
})json";
bool factory_loader<spine_player>::operator()(
spine_player& component,
const fill_context& ctx) const
{
if ( ctx.root.HasMember("spine") ) {
auto spine = ctx.dependencies.find_asset<spine_asset>(
path::combine(ctx.parent_address, ctx.root["spine"].GetString()));
if ( !spine ) {
the<debug>().error("SPINE_PLAYER: Dependency 'spine' is not found:\n"
"--> Parent address: %0\n"
"--> Dependency address: %1",
ctx.parent_address,
ctx.root["spine"].GetString());
return false;
}
component.spine(spine);
}
if ( ctx.root.HasMember("materials") ) {
const rapidjson::Value& materials_root = ctx.root["materials"];
flat_map<str_hash, material_asset::ptr> materials;
materials.reserve(materials_root.MemberCount());
for ( rapidjson::Value::ConstMemberIterator material_root = materials_root.MemberBegin();
material_root != materials_root.MemberEnd();
++material_root )
{
str_hash material_name;
if ( !json_utils::try_parse_value(material_root->name, material_name) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'material.name' property");
return false;
}
str material_address;
if ( !json_utils::try_parse_value(material_root->value, material_address) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'material.address' property");
return false;
}
auto material = ctx.dependencies.find_asset<material_asset>(
path::combine(ctx.parent_address, material_address));
if ( !material ) {
the<debug>().error("SPINE_PLAYER: Dependency 'material' is not found:\n"
"--> Parent address: %0\n"
"--> Dependency address: %1",
ctx.parent_address,
material_address);
return false;
}
materials.emplace(material_name, material);
}
component.materials(std::move(materials));
}
if ( ctx.root.HasMember("skin") ) {
str skin;
if ( !json_utils::try_parse_value(ctx.root["skin"], skin) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'skin' property");
return false;
}
component.skin(skin);
}
if ( ctx.root.HasMember("attachments") ) {
const auto& attachments_json = ctx.root["attachments"];
E2D_ASSERT(attachments_json.IsArray());
for ( rapidjson::SizeType i = 0; i < attachments_json.Size(); ++i ) {
E2D_ASSERT(attachments_json[i].IsObject());
const auto& attachment_json = attachments_json[i];
str slot;
E2D_ASSERT(attachment_json.HasMember("slot"));
if ( !json_utils::try_parse_value(attachment_json["slot"], slot) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'attachment.slot' property");
return false;
}
str name;
E2D_ASSERT(attachment_json.HasMember("name"));
if ( !json_utils::try_parse_value(attachment_json["name"], name) ) {
the<debug>().error("SPINE_PLAYER: Incorrect formatting of 'attachment.name' property");
return false;
}
component.attachment(slot, name);
}
}
return true;
}
bool factory_loader<spine_player>::operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const
{
if ( ctx.root.HasMember("spine") ) {
dependencies.add_dependency<spine_asset>(
path::combine(ctx.parent_address, ctx.root["spine"].GetString()));
}
if ( ctx.root.HasMember("materials") ) {
const rapidjson::Value& materials_root = ctx.root["materials"];
for ( rapidjson::Value::ConstMemberIterator material_root = materials_root.MemberBegin();
material_root != materials_root.MemberEnd();
++material_root )
{
dependencies.add_dependency<material_asset>(
path::combine(ctx.parent_address, material_root->value.GetString()));
}
}
return true;
}
}
namespace e2d
{
const char* factory_loader<events<spine_player_events::event>>::schema_source = R"json({
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"events" : { "$ref": "#/definitions/events" }
},
"definitions" : {
"events" : {
"type" : "array",
"items" : { "$ref": "#/definitions/event" }
},
"event" : {
"type" : "object",
"required" : [ "type", "desc" ],
"additionalProperties" : false,
"properties" : {
"type" : { "$ref": "#/common_definitions/name" },
"desc" : {
"anyOf" : [{
"$ref" : "#/definitions/custom_evt"
}, {
"$ref" : "#/definitions/end"
}, {
"$ref" : "#/definitions/complete"
}]
}
}
},
"custom_evt" : {
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"name" : { "type" : "string" },
"int_value" : { "type" : "integer" },
"float_value" : { "type" : "number" },
"string_value" : { "type" : "string" }
}
},
"end" : {
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"message" : { "type" : "string" }
}
},
"complete" : {
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"message" : { "type" : "string" }
}
}
}
})json";
bool factory_loader<events<spine_player_events::event>>::operator()(
events<spine_player_events::event>& component,
const fill_context& ctx) const
{
if ( ctx.root.HasMember("events") ) {
auto evts = parse_events(ctx.root["events"]);
if ( !evts ) {
return false;
}
for ( auto& evt : *evts ) {
if ( evt.valueless_by_exception() ) {
return false;
}
}
component.set(std::move(*evts));
}
return true;
}
bool factory_loader<events<spine_player_events::event>>::operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const
{
E2D_UNUSED(dependencies, ctx);
return true;
}
}
namespace e2d
{
const char* factory_loader<commands<spine_player_commands::command>>::schema_source = R"json({
"type" : "object",
"required" : [],
"additionalProperties" : false,
"properties" : {
"commands" : { "$ref": "#/definitions/commands" }
},
"definitions" : {
"commands" : {
"type" : "array",
"items" : { "$ref": "#/definitions/command" }
},
"command" : {
"type" : "object",
"required" : [ "type", "desc" ],
"additionalProperties" : false,
"properties" : {
"type" : { "$ref": "#/common_definitions/name" },
"desc" : {
"anyOf" : [{
"$ref" : "#/definitions/clear_track_cmd"
}, {
"$ref" : "#/definitions/set_anim_cmd"
}, {
"$ref" : "#/definitions/add_anim_cmd"
}, {
"$ref" : "#/definitions/set_empty_anim_cmd"
}, {
"$ref" : "#/definitions/add_empty_anim_cmd"
}]
}
}
},
"clear_track_cmd" : {
"type" : "object",
"required" : [ "track" ],
"additionalProperties" : false,
"properties" : {
"track" : { "type" : "integer", "minimum" : 0 }
}
},
"set_anim_cmd" : {
"type" : "object",
"required" : [ "track", "name" ],
"additionalProperties" : false,
"properties" : {
"track" : { "type" : "integer", "minimum" : 0 },
"name" : { "$ref": "#/common_definitions/name" },
"loop" : { "type" : "boolean" },
"end_message" : { "type" : "string" },
"complete_message" : { "type" : "string" }
}
},
"add_anim_cmd" : {
"type" : "object",
"required" : [ "track", "name" ],
"additionalProperties" : false,
"properties" : {
"track" : { "type" : "integer", "minimum" : 0 },
"name" : { "$ref": "#/common_definitions/name" },
"loop" : { "type" : "boolean" },
"delay" : { "type" : "number" },
"end_message" : { "type" : "string" },
"complete_message" : { "type" : "string" }
}
},
"set_empty_anim_cmd" : {
"type" : "object",
"required" : [ "track" ],
"additionalProperties" : false,
"properties" : {
"track" : { "type" : "integer", "minimum" : 0 },
"mix_duration" : { "type" : "number" },
"end_message" : { "type" : "string" },
"complete_message" : { "type" : "string" }
}
},
"add_empty_anim_cmd" : {
"type" : "object",
"required" : [ "track" ],
"additionalProperties" : false,
"properties" : {
"track" : { "type" : "integer", "minimum" : 0 },
"delay" : { "type" : "number" },
"mix_duration" : { "type" : "number" },
"end_message" : { "type" : "string" },
"complete_message" : { "type" : "string" }
}
}
}
})json";
bool factory_loader<commands<spine_player_commands::command>>::operator()(
commands<spine_player_commands::command>& component,
const fill_context& ctx) const
{
if ( ctx.root.HasMember("commands") ) {
auto cmds = parse_commands(ctx.root["commands"]);
if ( !cmds ) {
return false;
}
for ( auto& cmd : *cmds ) {
if ( cmd.valueless_by_exception() ) {
return false;
}
}
component.set(std::move(*cmds));
}
return true;
}
bool factory_loader<commands<spine_player_commands::command>>::operator()(
asset_dependencies& dependencies,
const collect_context& ctx) const
{
E2D_UNUSED(dependencies, ctx);
return true;
}
}
namespace e2d
{
const char* component_inspector<spine_player>::title = ICON_FA_PARAGRAPH " spine_player";
void component_inspector<spine_player>::operator()(gcomponent<spine_player>& c) const {
E2D_UNUSED(c);
///TODO(BlackMat): add 'spine' inspector
///TODO(BlackMat): add 'materials' inspector
}
}

View File

@@ -1,138 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#include <enduro2d/high/resources/spine.hpp>
#include <spine/spine.h>
namespace e2d
{
spine::spine(spine&& other) noexcept {
assign(std::move(other));
}
spine& spine::operator=(spine&& other) noexcept {
return assign(std::move(other));
}
spine::spine(const spine& other) {
assign(other);
}
spine& spine::operator=(const spine& other) {
return assign(other);
}
void spine::clear() noexcept {
atlas_.reset();
skeleton_.reset();
animation_.reset();
}
void spine::swap(spine& other) noexcept {
using std::swap;
swap(atlas_, other.atlas_);
swap(skeleton_, other.skeleton_);
swap(animation_, other.animation_);
}
spine& spine::assign(spine&& other) noexcept {
if ( this != &other ) {
swap(other);
other.clear();
}
return *this;
}
spine& spine::assign(const spine& other) {
if ( this != &other ) {
spine m;
m.atlas_ = other.atlas_;
m.skeleton_ = other.skeleton_;
m.animation_ = other.animation_;
swap(m);
}
return *this;
}
spine& spine::set_atlas(atlas_ptr atlas) {
atlas_ = std::move(atlas);
return *this;
}
spine& spine::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& spine::set_default_mix(f32 duration) {
if ( !animation_ ) {
throw bad_spine_access();
}
animation_->defaultMix = duration;
return *this;
}
spine& spine::set_animation_mix(
const str& from,
const str& to,
f32 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_access();
}
spAnimationStateData_setMix(animation_.get(), from_anim, to_anim, duration);
return *this;
}
const spine::atlas_ptr& spine::atlas() const noexcept {
return atlas_;
}
const spine::skeleton_data_ptr& spine::skeleton() const noexcept {
return skeleton_;
}
const spine::animation_data_ptr& spine::animation() const noexcept {
return animation_;
}
}
namespace e2d
{
void swap(spine& l, spine& r) noexcept {
l.swap(r);
}
bool operator==(const spine& l, const spine& r) noexcept {
return l.atlas() == r.atlas()
&& l.skeleton() == r.skeleton()
&& l.animation() == r.animation();
}
bool operator!=(const spine& l, const spine& r) noexcept {
return !(l == r);
}
}

View File

@@ -25,7 +25,6 @@
#include <enduro2d/high/components/named.hpp>
#include <enduro2d/high/components/renderer.hpp>
#include <enduro2d/high/components/scene.hpp>
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/sprite_renderer.hpp>
#include <enduro2d/high/components/touchable.hpp>
#include <enduro2d/high/components/widget.hpp>
@@ -37,7 +36,6 @@
#include <enduro2d/high/systems/label_system.hpp>
#include <enduro2d/high/systems/layout_system.hpp>
#include <enduro2d/high/systems/render_system.hpp>
#include <enduro2d/high/systems/spine_system.hpp>
#include <enduro2d/high/systems/touch_system.hpp>
#include <enduro2d/high/systems/widget_system.hpp>
#include <enduro2d/high/systems/world_system.hpp>
@@ -74,8 +72,6 @@ namespace
.add_system<layout_system>())
.feature<struct render_feature>(ecs::feature()
.add_system<render_system>())
.feature<struct spine_feature>(ecs::feature()
.add_system<spine_system>())
.feature<struct touch_feature>(ecs::feature()
.add_system<touch_system>())
.feature<struct widget_feature>(ecs::feature()
@@ -198,9 +194,6 @@ namespace e2d
.register_component<named>("named")
.register_component<renderer>("renderer")
.register_component<scene>("scene")
.register_component<spine_player>("spine_player")
.register_component<events<spine_player_events::event>>("spine_player.events")
.register_component<commands<spine_player_commands::command>>("spine_player.commands")
.register_component<sprite_renderer>("sprite_renderer")
.register_component<touchable>("touchable")
.register_component<events<touchable_events::event>>("touchable.events")
@@ -225,9 +218,6 @@ namespace e2d
.register_component<named>("named")
.register_component<renderer>("renderer")
.register_component<scene>("scene")
.register_component<spine_player>("spine_player")
//.register_component<events<spine_player_events::event>>("spine_player.events")
//.register_component<commands<spine_player_commands::command>>("spine_player.commands")
.register_component<sprite_renderer>("sprite_renderer")
.register_component<touchable>("touchable")
//.register_component<events<touchable_events::event>>("touchable.events")

View File

@@ -9,11 +9,8 @@
#include <enduro2d/high/components/disabled.hpp>
#include <enduro2d/high/components/model_renderer.hpp>
#include <enduro2d/high/components/renderer.hpp>
#include <enduro2d/high/components/spine_player.hpp>
#include <enduro2d/high/components/sprite_renderer.hpp>
#include <spine/spine.h>
namespace
{
using namespace e2d;
@@ -103,10 +100,6 @@ namespace e2d::render_system_impl
draw(model_m, *node_r, *mdl_r);
}
if ( auto spn_p = gcomponent<spine_player>{owner} ) {
draw(model_m, *node_r, *spn_p);
}
if ( auto spr_r = gcomponent<sprite_renderer>{owner} ) {
draw(model_m, *node_r, *spr_r);
}
@@ -155,276 +148,6 @@ namespace e2d::render_system_impl
}
}
void drawer::context::draw(
const m4f& model_m,
const renderer& node_r,
const spine_player& spine_r)
{
//TODO(BlackMat): replace it to frame allocator
static thread_local std::vector<float> temp_vertices(1000u, 0.f);
static thread_local std::vector<batcher_type::vertex_type> batch_vertices(1000u);
spSkeleton* skeleton = spine_r.skeleton().get();
spSkeletonClipping* clipper = spine_r.clipper().get();
if ( !skeleton || !clipper ) {
return;
}
if ( math::is_near_zero(skeleton->color.a) ) {
return;
}
material_asset::ptr normal_mat_a;
material_asset::ptr additive_mat_a;
material_asset::ptr multiply_mat_a;
material_asset::ptr screen_mat_a;
unsigned short quad_indices[6] = { 0, 1, 2, 2, 3, 0 };
DEFER([this, clipper](){
property_cache_.clear();
spSkeletonClipping_clipEnd2(clipper);
});
for ( int i = 0; i < skeleton->slotsCount; ++i ) {
spSlot* slot = skeleton->drawOrder[i];
auto slot_clipping_defer = defer_hpp::make_defer([clipper, slot](){
spSkeletonClipping_clipEnd(clipper, slot);
});
spAttachment* attachment = slot->attachment;
if ( !attachment || math::is_near_zero(slot->color.a) ) {
continue;
}
float* uvs = nullptr;
unsigned short* indices = nullptr;
int index_count = 0;
int vertex_count = 0;
const spAtlasPage* atlas_page = nullptr;
const spColor* attachment_color = nullptr;
if ( attachment->type == SP_ATTACHMENT_REGION ) {
spRegionAttachment* region = reinterpret_cast<spRegionAttachment*>(attachment);
attachment_color = &region->color;
if ( math::is_near_zero(attachment_color->a) ) {
continue;
}
{
vertex_count = 8;
if ( temp_vertices.size() < math::numeric_cast<std::size_t>(vertex_count) ) {
temp_vertices.resize(math::max(
temp_vertices.size() * 2u,
math::numeric_cast<std::size_t>(vertex_count)));
}
}
spRegionAttachment_computeWorldVertices(
region,
slot->bone,
temp_vertices.data(),
0, 2);
uvs = region->uvs;
indices = quad_indices;
index_count = 6;
atlas_page = static_cast<spAtlasRegion*>(region->rendererObject)->page;
} else if ( attachment->type == SP_ATTACHMENT_MESH ) {
spMeshAttachment* mesh = reinterpret_cast<spMeshAttachment*>(attachment);
attachment_color = &mesh->color;
if ( math::is_near_zero(attachment_color->a) ) {
continue;
}
{
vertex_count = mesh->super.worldVerticesLength;
if ( temp_vertices.size() < math::numeric_cast<std::size_t>(vertex_count) ) {
temp_vertices.resize(math::max(
temp_vertices.size() * 2u,
math::numeric_cast<std::size_t>(vertex_count)));
}
}
spVertexAttachment_computeWorldVertices(
&mesh->super,
slot,
0,
mesh->super.worldVerticesLength,
temp_vertices.data(),
0, 2);
uvs = mesh->uvs;
indices = mesh->triangles;
index_count = mesh->trianglesCount;
atlas_page = static_cast<spAtlasRegion*>(mesh->rendererObject)->page;
} else if ( attachment->type == SP_ATTACHMENT_CLIPPING ) {
spClippingAttachment* clip = reinterpret_cast<spClippingAttachment*>(attachment);
spSkeletonClipping_clipStart(clipper, slot, clip);
slot_clipping_defer.dismiss();
continue;
} else {
slot_clipping_defer.dismiss();
continue;
}
color32 vert_color = color32(
color(skeleton->color.r, skeleton->color.g, skeleton->color.b, skeleton->color.a) *
color(slot->color.r, slot->color.g, slot->color.b, slot->color.a) *
color(attachment_color->r, attachment_color->g, attachment_color->b, attachment_color->a));
texture_ptr tex_p;
const texture_asset* texture_asset_ptr = atlas_page
? static_cast<const texture_asset*>(atlas_page->rendererObject)
: nullptr;
if ( texture_asset_ptr ) {
tex_p = texture_asset_ptr->content();
}
render::sampler_min_filter tex_min_f = render::sampler_min_filter::linear;
if ( atlas_page ) {
switch ( atlas_page->minFilter ) {
case SP_ATLAS_NEAREST:
case SP_ATLAS_MIPMAP_NEAREST_LINEAR:
case SP_ATLAS_MIPMAP_NEAREST_NEAREST:
tex_min_f = render::sampler_min_filter::nearest;
break;
default:
tex_min_f = render::sampler_min_filter::linear;
break;
}
}
render::sampler_mag_filter tex_mag_f = render::sampler_mag_filter::linear;
if ( atlas_page ) {
switch ( atlas_page->magFilter ) {
case SP_ATLAS_NEAREST:
case SP_ATLAS_MIPMAP_NEAREST_LINEAR:
case SP_ATLAS_MIPMAP_NEAREST_NEAREST:
tex_mag_f = render::sampler_mag_filter::nearest;
break;
default:
tex_mag_f = render::sampler_mag_filter::linear;
break;
}
}
render::sampler_wrap tex_wrap_s = render::sampler_wrap::repeat;
if ( atlas_page ) {
switch ( atlas_page->uWrap ) {
case SP_ATLAS_MIRROREDREPEAT:
tex_wrap_s = render::sampler_wrap::mirror;
break;
case SP_ATLAS_CLAMPTOEDGE:
tex_wrap_s = render::sampler_wrap::clamp;
break;
case SP_ATLAS_REPEAT:
tex_wrap_s = render::sampler_wrap::repeat;
break;
default:
E2D_ASSERT_MSG(false, "unexpected wrap mode for slot");
break;
}
}
render::sampler_wrap tex_wrap_t = render::sampler_wrap::repeat;
if ( atlas_page ) {
switch ( atlas_page->vWrap ) {
case SP_ATLAS_MIRROREDREPEAT:
tex_wrap_t = render::sampler_wrap::mirror;
break;
case SP_ATLAS_CLAMPTOEDGE:
tex_wrap_t = render::sampler_wrap::clamp;
break;
case SP_ATLAS_REPEAT:
tex_wrap_t = render::sampler_wrap::repeat;
break;
default:
E2D_ASSERT_MSG(false, "unexpected wrap mode for slot");
break;
}
}
material_asset::ptr mat_a;
switch ( slot->data->blendMode ) {
case SP_BLEND_MODE_NORMAL:
mat_a = normal_mat_a
? normal_mat_a
: (normal_mat_a = spine_r.find_material(normal_material_hash));
break;
case SP_BLEND_MODE_ADDITIVE:
mat_a = additive_mat_a
? additive_mat_a
: (additive_mat_a = spine_r.find_material(additive_material_hash));
break;
case SP_BLEND_MODE_MULTIPLY:
mat_a = multiply_mat_a
? multiply_mat_a
: (multiply_mat_a = spine_r.find_material(multiply_material_hash));
break;
case SP_BLEND_MODE_SCREEN:
mat_a = screen_mat_a
? screen_mat_a
: (screen_mat_a = spine_r.find_material(screen_material_hash));
break;
default:
E2D_ASSERT_MSG(false, "unexpected blend mode for slot");
break;
}
if ( math::is_near_zero(vert_color.a) || !tex_p || !mat_a ) {
continue;
}
const float* vertices = temp_vertices.data();
if ( spSkeletonClipping_isClipping(clipper) ) {
spSkeletonClipping_clipTriangles(
clipper,
temp_vertices.data(), vertex_count,
indices, index_count,
uvs,
2);
vertices = clipper->clippedVertices->items;
vertex_count = clipper->clippedVertices->size;
uvs = clipper->clippedUVs->items;
indices = clipper->clippedTriangles->items;
index_count = clipper->clippedTriangles->size;
}
{
const std::size_t batch_vertex_count = vertex_count >> 1;
if ( batch_vertices.size() < batch_vertex_count ) {
batch_vertices.resize(math::max(batch_vertices.size() * 2u, batch_vertex_count));
}
for ( std::size_t j = 0; j < batch_vertex_count; ++j ) {
batcher_type::vertex_type& vert = batch_vertices[j];
vert.v = v3f(v4f(vertices[j * 2], vertices[j * 2 + 1], 0.f, 1.f) * model_m);
vert.t = v2f(uvs[j * 2], uvs[j * 2 + 1]);
vert.c = vert_color;
}
property_cache_
.sampler(texture_sampler_hash, render::sampler_state()
.texture(tex_p)
.filter(tex_min_f, tex_mag_f)
.wrap(tex_wrap_s, tex_wrap_t))
.merge(node_r.properties());
batcher_.batch(
mat_a,
property_cache_,
indices, math::numeric_cast<std::size_t>(index_count),
batch_vertices.data(), batch_vertex_count);
}
}
}
void drawer::context::draw(
const m4f& model_m,
const renderer& node_r,

View File

@@ -51,11 +51,6 @@ namespace e2d::render_system_impl
const m4f& model_m,
const renderer& node_r,
const sprite_renderer& spr_r);
void draw(
const m4f& model_m,
const renderer& node_r,
const spine_player& spine_r);
private:
render& render_;
batcher_type& batcher_;

View File

@@ -1,263 +0,0 @@
/*******************************************************************************
* This file is part of the "Enduro2D"
* For conditions of distribution and use, see copyright notice in LICENSE.md
* Copyright (C) 2018-2020, by Matvey Cherevko (blackmatov@gmail.com)
******************************************************************************/
#include <enduro2d/high/systems/spine_system.hpp>
#include <enduro2d/high/components/events.hpp>
#include <enduro2d/high/components/commands.hpp>
#include <enduro2d/high/components/spine_player.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<events<spine_player_events::event>>()
.add(spine_player_events::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<events<spine_player_events::event>>()
.add(spine_player_events::end_evt(entry_state.end_message));
} else if ( type == SP_ANIMATION_COMPLETE ) {
if ( entry_state.complete_message.empty() ) {
return;
}
entry_state.target
.ensure_component<events<spine_player_events::event>>()
.add(spine_player_events::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_commands::clear_track_cmd& cmd) const noexcept {
spAnimationState_clearTrack(
&animation_state_,
math::numeric_cast<int>(cmd.track()));
}
void operator()(const spine_player_commands::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_commands::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());
entry->listener = &entry_listener;
entry->userData = entry_state.release();
}
void operator()(const spine_player_commands::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());
entry->listener = &entry_listener;
entry->userData = entry_state.release();
}
void operator()(const spine_player_commands::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(),
cmd.delay());
entry->listener = &entry_listener;
entry->userData = entry_state.release();
}
private:
ecs::entity target_;
spSkeletonData& skeleton_data_;
spAnimationState& animation_state_;
};
void clear_events(ecs::registry& owner) {
owner.for_each_component<events<spine_player_events::event>>([
](const ecs::const_entity&, events<spine_player_events::event>& es) {
es.clear();
});
}
void update_animations(f32 dt, ecs::registry& owner) {
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);
});
}
void process_commands(ecs::registry& owner) {
owner.for_joined_components<commands<spine_player_commands::command>, spine_player>([](
ecs::entity e,
const commands<spine_player_commands::command>& cs,
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 : cs.get() ) {
std::visit(v, cmd);
}
});
}
void clear_commands(ecs::registry& owner) {
owner.for_each_component<commands<spine_player_commands::command>>([
](const ecs::const_entity&, commands<spine_player_commands::command>& cs) {
cs.clear();
});
}
}
namespace e2d
{
//
// internal_state
//
class spine_system::internal_state final {
public:
internal_state() = default;
~internal_state() noexcept = default;
void process_update(f32 dt, ecs::registry& owner) {
process_commands(owner);
clear_commands(owner);
clear_events(owner);
update_animations(dt, owner);
}
};
//
// spine_system
//
spine_system::spine_system()
: state_(new internal_state()) {}
spine_system::~spine_system() noexcept {
spAnimationState_disposeStatics();
}
void spine_system::process(
ecs::registry& owner,
const ecs::after<systems::update_event>& trigger)
{
state_->process_update(trigger.event.dt, owner);
}
}