Skip to content

Commit

Permalink
Add support for bounding box, add process mode for SpineSprite
Browse files Browse the repository at this point in the history
  • Loading branch information
rayxuln committed Jul 14, 2021
1 parent 367d795 commit d305318
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 25 deletions.
1 change: 0 additions & 1 deletion SpineRuntimeEditorPlugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Created by Raiix on 2021/7/13.
//

#define TOOLS_ENABLED
#ifdef TOOLS_ENABLED
#include "SpineRuntimeEditorPlugin.h"

Expand Down
1 change: 0 additions & 1 deletion SpineRuntimeEditorPlugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
#ifndef GODOT_SPINERUNTIMEEDITORPLUGIN_H
#define GODOT_SPINERUNTIMEEDITORPLUGIN_H

#define TOOLS_ENABLED
#ifdef TOOLS_ENABLED
#include "editor/editor_node.h"

Expand Down
235 changes: 215 additions & 20 deletions SpineSprite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ void SpineSprite::_bind_methods() {
ClassDB::bind_method(D_METHOD("_on_skin_property_changed"), &SpineSprite::_on_skin_property_changed);
ClassDB::bind_method(D_METHOD("gen_spine_skin_from_packed_resource", "res"), &SpineSprite::gen_spine_skin_from_packed_resource);

ClassDB::bind_method(D_METHOD("set_process_mode", "v"), &SpineSprite::set_process_mode);
ClassDB::bind_method(D_METHOD("get_process_mode"), &SpineSprite::get_process_mode);

ClassDB::bind_method(D_METHOD("set_disable_collision_shapes", "v"), &SpineSprite::set_disable_collision_shapes);
ClassDB::bind_method(D_METHOD("get_disable_collision_shapes"), &SpineSprite::get_disable_collision_shapes);
ClassDB::bind_method(D_METHOD("set_display_collision_shapes", "v"), &SpineSprite::set_display_collision_shapes);
ClassDB::bind_method(D_METHOD("get_display_collision_shapes"), &SpineSprite::get_display_collision_shapes);
ClassDB::bind_method(D_METHOD("set_create_collision_shapes", "v"), &SpineSprite::set_create_collision_shapes);
ClassDB::bind_method(D_METHOD("get_create_collision_shapes"), &SpineSprite::get_create_collision_shapes);

ClassDB::bind_method(D_METHOD("manual_update", "delta"), &SpineSprite::_update_all);

ADD_SIGNAL(MethodInfo("animation_state_ready", PropertyInfo(Variant::OBJECT, "animation_state", PROPERTY_HINT_TYPE_STRING, "SpineAnimationState"), PropertyInfo(Variant::OBJECT, "skeleton", PROPERTY_HINT_TYPE_STRING, "SpineSkeleton")));
ADD_SIGNAL(MethodInfo("animation_start", PropertyInfo(Variant::OBJECT, "animation_state", PROPERTY_HINT_TYPE_STRING, "SpineAnimationState"), PropertyInfo(Variant::OBJECT, "track_entry", PROPERTY_HINT_TYPE_STRING, "SpineTrackEntry"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_TYPE_STRING, "SpineEvent")));
Expand All @@ -52,6 +63,13 @@ void SpineSprite::_bind_methods() {

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "animation_state_data_res", PropertyHint::PROPERTY_HINT_RESOURCE_TYPE, "SpineAnimationStateDataResource"), "set_animation_state_data_res", "get_animation_state_data_res");

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "overlap"), "set_overlap", "get_overlap");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bind_slot_nodes"), "set_bind_slot_nodes", "get_bind_slot_nodes");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packed_skin_resource", PropertyHint::PROPERTY_HINT_RESOURCE_TYPE, "PackedSpineSkinResource"), "set_skin", "get_skin");

ADD_GROUP("animation", "");
ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Process,Physics,Manually"), "set_process_mode", "get_process_mode");

ADD_PROPERTY(PropertyInfo(Variant::INT, "select_track_id"), "set_select_track_id", "get_select_track_id");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_track_trigger"), "set_clear_track", "get_clear_track");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clear_tracks_trigger"), "set_clear_tracks", "get_clear_tracks");
Expand All @@ -61,13 +79,25 @@ void SpineSprite::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "set_empty_animations_trigger"), "set_set_empty_animations", "get_set_empty_animations");

ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "current_animations"), "set_current_animations", "get_current_animations");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "bind_slot_nodes"), "set_bind_slot_nodes", "get_bind_slot_nodes");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "overlap"), "set_overlap", "get_overlap");
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "packed_skin_resource", PropertyHint::PROPERTY_HINT_RESOURCE_TYPE, "PackedSpineSkinResource"), "set_skin", "get_skin");

ADD_GROUP("collision", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "create_collision_shapes"), "set_create_collision_shapes", "get_create_collision_shapes");
ADD_PROPERTY_DEFAULT("create_collision_shapes", false);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disable_collision_shapes"), "set_disable_collision_shapes", "get_disable_collision_shapes");
ADD_PROPERTY_DEFAULT("disable_collision_shapes", false);
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "display_collision_shapes"), "set_display_collision_shapes", "get_display_collision_shapes");
ADD_PROPERTY_DEFAULT("display_collision_shapes", true);

BIND_ENUM_CONSTANT(ProcessMode::ProcessMode_Process);
BIND_ENUM_CONSTANT(ProcessMode::ProcessMode_Physics);
BIND_ENUM_CONSTANT(ProcessMode::ProcessMode_Manual);
}

SpineSprite::SpineSprite() :
select_track_id(0), empty_animation_duration(0.2f), skeleton_clipper(NULL) {
select_track_id(0), empty_animation_duration(0.2f), skeleton_clipper(NULL),
overlap(false),
process_mode(ProcessMode_Process),
create_collision_shapes(false), disable_collision_shapes(false), display_collision_shapes(true) {
skeleton_clipper = new spine::SkeletonClipping();
}
SpineSprite::~SpineSprite() {
Expand All @@ -77,28 +107,43 @@ SpineSprite::~SpineSprite() {
void SpineSprite::_notification(int p_what) {
switch (p_what) {
case NOTIFICATION_READY:{
set_process_internal(true);
set_process_internal(process_mode == ProcessMode_Process);
set_physics_process_internal(process_mode == ProcessMode_Physics);
remove_redundant_mesh_instances();
remove_redundant_collision_shapes();
} break;
case NOTIFICATION_INTERNAL_PROCESS: {
if (!(skeleton.is_valid() && animation_state.is_valid()) || mesh_instances.size() == 0)
return;
if (process_mode == ProcessMode_Process)
_update_all(get_process_delta_time());
} break;
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
if (process_mode == ProcessMode_Physics)
_update_all(get_physics_process_delta_time());
} break;
}
}

animation_state->update(get_process_delta_time());
if (!is_visible_in_tree())
return;

animation_state->apply(skeleton);
void SpineSprite::_update_all(float delta) {
if (!(skeleton.is_valid() && animation_state.is_valid()) || mesh_instances.empty())
return;

skeleton->update_world_transform();
animation_state->update(delta);
if (!is_visible_in_tree())
return;

update_mesh_from_skeleton(skeleton);
animation_state->apply(skeleton);

update();
skeleton->update_world_transform();

update_bind_slot_nodes();
} break;
}
update_mesh_from_skeleton(skeleton);

if (create_collision_shapes && !disable_collision_shapes && !collision_shapes.empty()) {
update_collision_shape_from_skeleton(skeleton);
}

update();

update_bind_slot_nodes();
}

void SpineSprite::update_bind_slot_nodes(){
Expand Down Expand Up @@ -199,13 +244,22 @@ void SpineSprite::_on_animation_data_created(){
skeleton->update_world_transform();
gen_mesh_from_skeleton(skeleton);

_notification(NOTIFICATION_INTERNAL_PROCESS);
remove_collision_shapes();
if (create_collision_shapes)
gen_collision_shape_from_skeleton(skeleton);

if (process_mode == ProcessMode_Process) {
_notification(NOTIFICATION_INTERNAL_PROCESS);
} else if (process_mode == ProcessMode_Physics) {
_notification(NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
}

emit_signal("animation_state_ready", animation_state, skeleton);
}
void SpineSprite::_on_animation_data_changed() {
// print_line("_on_animation_data_changed");
remove_mesh_instances();
remove_collision_shapes();
skeleton.unref();
animation_state.unref();
if(!animation_state_data_res.is_null())
Expand Down Expand Up @@ -499,7 +553,6 @@ void SpineSprite::update_mesh_from_skeleton(Ref<SpineSkeleton> s) {
}
skeleton_clipper->clipEnd();
}
#undef TEMP_COPY

void SpineSprite::callback(spine::AnimationState *state, spine::EventType type, spine::TrackEntry *entry, spine::Event *event) {
Ref<SpineTrackEntry> gd_entry(NULL);
Expand Down Expand Up @@ -773,3 +826,145 @@ void SpineSprite::unbind_slot_with_node_2d(const String &slot_name, Node2D *n){
}
}

void SpineSprite::gen_collision_shape_from_skeleton(Ref<SpineSkeleton> s) {
auto sk = s->get_spine_object();
// create collision shape for every slot
for(size_t i=0, n = sk->getSlots().size(); i < n; ++i)
{
auto collision_shape = memnew(CollisionPolygon2D);
add_child(collision_shape);
collision_shape->set_position(Vector2(0, 0));
collision_shape->set_owner(this);
collision_shapes.push_back(collision_shape);

spine::Slot *slot = sk->getDrawOrder()[i];
collision_shape->set_name(vformat("c_%s", slot->getData().getName().buffer()));

collision_shape->set_disabled(disable_collision_shapes);
collision_shape->set_visible(display_collision_shapes);
}
}

void SpineSprite::remove_redundant_collision_shapes() {
Vector<Node*> ms;

for (size_t i=0; i<get_child_count(); ++i) {
auto child = get_child(i);
if (child->get_owner() != this || !child->is_class("CollisionPolygon2D")) {
continue;
}
if (collision_shapes.find((CollisionPolygon2D*)child) >= 0) {
continue;
}
ms.push_back(child);
}

for (size_t i=0; i<ms.size(); ++i) {
remove_child(ms[i]);
memdelete(ms[i]);
}
}

void SpineSprite::remove_collision_shapes() {
for (size_t i=0; i<collision_shapes.size(); ++i) {
remove_child(collision_shapes[i]);
memdelete(collision_shapes[i]);
}
collision_shapes.clear();
}

void SpineSprite::update_collision_shape_from_skeleton(Ref<SpineSkeleton> s) {
auto sk = s->get_spine_object();
for(size_t i=0, n = sk->getSlots().size(); i < n; ++i)
{
spine::Vector<float> vertices;

spine::Slot *slot = sk->getDrawOrder()[i];

spine::Attachment *attachment = slot->getAttachment();
if(!attachment){
collision_shapes[i]->get_polygon().clear();
continue;
}

// spine::Color skeleton_color = sk->getColor();
// spine::Color slot_color = slot->getColor();
// spine::Color tint(skeleton_color.r * slot_color.r, skeleton_color.g * slot_color.g, skeleton_color.b * slot_color.b, skeleton_color.a * slot_color.a);

if (attachment->getRTTI().isExactly(spine::BoundingBoxAttachment::rtti)) {
auto *box = (spine::BoundingBoxAttachment*) attachment;

vertices.setSize(box->getWorldVerticesLength(), 0);
// print_line(vformat("vert: %d", vertices.size()));

box->computeWorldVertices(*slot, vertices);

// auto attachment_color = box->getColor();
// tint.r *= attachment_color.r;
// tint.g *= attachment_color.g;
// tint.b *= attachment_color.b;
// tint.a *= attachment_color.a;
} else {
continue;
}

Vector<Vector2> vs;
vs.resize(vertices.size()/2);
for (size_t j=0; j < vertices.size(); j+=2) {
vs.set(j/2, Vector2(vertices[j], -vertices[j + 1]));
}
auto collision_shape = collision_shapes[i];
collision_shape->set_polygon(vs);
}
}

bool SpineSprite::get_disable_collision_shapes() {
return disable_collision_shapes;
}

void SpineSprite::set_disable_collision_shapes(bool v) {
if (disable_collision_shapes != v) {
for (size_t i=0; i<collision_shapes.size(); ++i) {
collision_shapes[i]->set_disabled(v);
}
}
disable_collision_shapes = v;
}

bool SpineSprite::get_display_collision_shapes() {
return display_collision_shapes;
}

void SpineSprite::set_display_collision_shapes(bool v) {
if (display_collision_shapes != v) {
for (size_t i=0; i<collision_shapes.size(); ++i) {
collision_shapes[i]->set_visible(v);
}
}
display_collision_shapes = v;
}

bool SpineSprite::get_create_collision_shapes() {
return create_collision_shapes;
}

void SpineSprite::set_create_collision_shapes(bool v) {
if (create_collision_shapes != v) {
remove_collision_shapes();
if (v) {
gen_collision_shape_from_skeleton(skeleton);
}
}
create_collision_shapes = v;
}

SpineSprite::ProcessMode SpineSprite::get_process_mode() {
return process_mode;
}

void SpineSprite::set_process_mode(SpineSprite::ProcessMode v) {
process_mode = v;
set_process_internal(process_mode == ProcessMode_Process);
set_physics_process_internal(process_mode == ProcessMode_Physics);
}

37 changes: 35 additions & 2 deletions SpineSprite.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

#include <scene/resources/texture.h>

#include <scene/2d/collision_polygon_2d.h>

#include "SpineAnimationStateDataResource.h"
#include "SpineSkeleton.h"
#include "SpineAnimationState.h"
Expand All @@ -19,6 +21,13 @@ class SpineSprite : public Node2D, public spine::AnimationStateListenerObject {
static void _bind_methods();

void _notification(int p_what);

public:
enum ProcessMode {
ProcessMode_Process,
ProcessMode_Physics,
ProcessMode_Manual
};
private:

Ref<SpineAnimationStateDataResource> animation_state_data_res;
Expand All @@ -27,14 +36,21 @@ class SpineSprite : public Node2D, public spine::AnimationStateListenerObject {
Ref<SpineAnimationState> animation_state;

Vector<SpineSpriteMeshInstance2D*> mesh_instances;
Vector<CollisionPolygon2D*> collision_shapes;

Array current_animations;
int select_track_id;
float empty_animation_duration;
Array bind_slot_nodes;
bool overlap = false;
bool overlap;
Ref<PackedSpineSkinResource> skin;

bool disable_collision_shapes;
bool display_collision_shapes;
bool create_collision_shapes;

ProcessMode process_mode;

spine::SkeletonClipping *skeleton_clipper;

public:
Expand All @@ -53,6 +69,11 @@ class SpineSprite : public Node2D, public spine::AnimationStateListenerObject {

void update_mesh_from_skeleton(Ref<SpineSkeleton> s);

void gen_collision_shape_from_skeleton(Ref<SpineSkeleton> s);
void update_collision_shape_from_skeleton(Ref<SpineSkeleton> s);
void remove_collision_shapes();
void remove_redundant_collision_shapes();

void update_bind_slot_nodes();
void update_bind_slot_node_transform(Ref<SpineBone> bone, Node2D *node2d);
void update_bind_slot_node_draw_order(const String &slot_name, Node2D *node2d);
Expand All @@ -63,6 +84,7 @@ class SpineSprite : public Node2D, public spine::AnimationStateListenerObject {
void _on_animation_data_created();
void _on_animation_data_changed();

void _update_all(float delta);

// External feature functions
Array get_current_animations();
Expand Down Expand Up @@ -103,7 +125,18 @@ class SpineSprite : public Node2D, public spine::AnimationStateListenerObject {

Ref<SpineSkin> gen_spine_skin_from_packed_resource(Ref<PackedSpineSkinResource> res);

};
bool get_disable_collision_shapes();
void set_disable_collision_shapes(bool v);

bool get_display_collision_shapes();
void set_display_collision_shapes(bool v);

bool get_create_collision_shapes();
void set_create_collision_shapes(bool v);

ProcessMode get_process_mode();
void set_process_mode(ProcessMode v);
};

VARIANT_ENUM_CAST(SpineSprite::ProcessMode);
#endif //GODOT_SPINESPRITE_H
Loading

0 comments on commit d305318

Please sign in to comment.