diff --git a/spine-godot/example/assets/raptor/raptor-data-new.tres b/spine-godot/example/assets/raptor/raptor-data-new.tres new file mode 100644 index 000000000..18a042c08 --- /dev/null +++ b/spine-godot/example/assets/raptor/raptor-data-new.tres @@ -0,0 +1,10 @@ +[gd_resource type="SpineNewSkeletonDataResource" load_steps=3 format=2] + +[ext_resource path="res://assets/raptor/raptor.atlas" type="SpineAtlasResource" id=1] +[ext_resource path="res://assets/raptor/raptor-pro.skel" type="SpineSkeletonFileResource" id=2] + +[resource] +atlas_res = ExtResource( 1 ) +skeleton_file_res = ExtResource( 2 ) +animations = null +skins = null diff --git a/spine-godot/example/assets/spineboy/spinebody-data-new-res.tres b/spine-godot/example/assets/spineboy/spinebody-data-new-res.tres index 73189d31a..5ced328e4 100644 --- a/spine-godot/example/assets/spineboy/spinebody-data-new-res.tres +++ b/spine-godot/example/assets/spineboy/spinebody-data-new-res.tres @@ -1,7 +1,7 @@ [gd_resource type="SpineNewSkeletonDataResource" load_steps=3 format=2] [ext_resource path="res://assets/spineboy/spineboy.atlas" type="SpineAtlasResource" id=1] -[ext_resource path="res://assets/spineboy/spineboy-pro.skel" type="SpineSkeletonFileResource" id=2] +[ext_resource path="res://assets/spineboy/spineboy-pro.json" type="SpineSkeletonFileResource" id=2] [resource] atlas_res = ExtResource( 1 ) diff --git a/spine-godot/example/batch-test.tscn b/spine-godot/example/batch-test.tscn new file mode 100644 index 000000000..a89030628 --- /dev/null +++ b/spine-godot/example/batch-test.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://assets/spineboy/spinebody-data-new-res.tres" type="SpineNewSkeletonDataResource" id=1] + +[node name="Node2D" type="Node2D"] + +[node name="SpineNewSprite" type="SpineNewSprite" parent="."] +position = Vector2( 291.494, 476.479 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite2" type="SpineNewSprite" parent="."] +position = Vector2( 699.584, 371.093 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite3" type="SpineNewSprite" parent="."] +position = Vector2( 418.181, 256.739 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite4" type="SpineNewSprite" parent="."] +position = Vector2( 588.592, 489.933 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite5" type="SpineNewSprite" parent="."] +position = Vector2( 788.152, 515.719 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite6" type="SpineNewSprite" parent="."] +position = Vector2( 433.553, 554.793 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite7" type="SpineNewSprite" parent="."] +position = Vector2( 622.086, 573.354 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite8" type="SpineNewSprite" parent="."] +position = Vector2( 946.403, 559.678 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite9" type="SpineNewSprite" parent="."] +position = Vector2( 867.277, 378.471 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite10" type="SpineNewSprite" parent="."] +position = Vector2( 971.801, 339.885 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite11" type="SpineNewSprite" parent="."] +position = Vector2( 468.72, 379.936 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite12" type="SpineNewSprite" parent="."] +position = Vector2( 614.272, 317.418 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite13" type="SpineNewSprite" parent="."] +position = Vector2( 540.031, 309.114 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite14" type="SpineNewSprite" parent="."] +position = Vector2( 355.405, 362.353 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) + +[node name="SpineNewSprite15" type="SpineNewSprite" parent="."] +position = Vector2( 520.982, 532.326 ) +scale = Vector2( 0.2, 0.2 ) +skeleton_data_res = ExtResource( 1 ) diff --git a/spine-godot/example/project.godot b/spine-godot/example/project.godot index 725e4e0c7..8469864d0 100644 --- a/spine-godot/example/project.godot +++ b/spine-godot/example/project.godot @@ -11,10 +11,14 @@ config_version=4 [application] config/name="spine-godot-examples" -run/main_scene="res://spineboy.tscn" +run/main_scene="res://batch-test.tscn" run/low_processor_mode=true config/icon="res://icon.png" +[global] + +batch=false + [physics] common/enable_pause_aware_picking=true @@ -24,4 +28,7 @@ common/enable_pause_aware_picking=true quality/driver/driver_name="GLES2" vram_compression/import_etc=true vram_compression/import_etc2=false +batching/parameters/max_join_item_commands=100 +batching/parameters/item_reordering_lookahead=100 +batching/debug/diagnose_frame=true environment/default_environment="res://default_env.tres" diff --git a/spine-godot/godot/modules/spine_godot/SpineNewSprite.cpp b/spine-godot/godot/modules/spine_godot/SpineNewSprite.cpp index ccefade7a..a4fd347cd 100644 --- a/spine-godot/godot/modules/spine_godot/SpineNewSprite.cpp +++ b/spine-godot/godot/modules/spine_godot/SpineNewSprite.cpp @@ -33,6 +33,9 @@ #include "SpineTrackEntry.h" #include "SpineNewSkeleton.h" +Ref SpineNewSprite::materials[4] = {}; +static int sprite_count = 0; + void SpineNewSprite::_bind_methods() { ClassDB::bind_method(D_METHOD("set_skeleton_data_res", "skeleton_data_res"), &SpineNewSprite::set_skeleton_data_res); ClassDB::bind_method(D_METHOD("get_skeleton_data_res"), &SpineNewSprite::get_skeleton_data_res); @@ -74,10 +77,84 @@ void SpineNewSprite::_bind_methods() { SpineNewSprite::SpineNewSprite() : overlap(false), process_mode(ProcessMode_Process), skeleton_clipper(nullptr) { skeleton_clipper = new spine::SkeletonClipping(); + + // One material per blend mode, shared across all sprites. + if (!materials[0].is_valid()) { + Ref material_normal(memnew(CanvasItemMaterial)); + material_normal->set_blend_mode(CanvasItemMaterial::BLEND_MODE_MIX); + materials[spine::BlendMode_Normal] = material_normal; + + Ref material_additive(memnew(CanvasItemMaterial)); + material_additive->set_blend_mode(CanvasItemMaterial::BLEND_MODE_ADD); + materials[spine::BlendMode_Additive] = material_additive; + + Ref material_multiply(memnew(CanvasItemMaterial)); + material_multiply->set_blend_mode(CanvasItemMaterial::BLEND_MODE_MUL); + materials[spine::BlendMode_Multiply] = material_multiply; + + Ref material_screen(memnew(CanvasItemMaterial)); + material_screen->set_blend_mode(CanvasItemMaterial::BLEND_MODE_MIX); + materials[spine::BlendMode_Screen] = material_screen; + } + sprite_count++; } SpineNewSprite::~SpineNewSprite() { delete skeleton_clipper; + sprite_count--; + if (!sprite_count) { + for (int i = 0; i < 4; i++) materials[i].unref(); + } +} + +void SpineNewSprite::set_skeleton_data_res(const Ref &s) { + skeleton_data_res = s; + _on_skeleton_data_changed(); +} +Ref SpineNewSprite::get_skeleton_data_res() { + return skeleton_data_res; +} + +void SpineNewSprite::_on_skeleton_data_changed() { + remove_meshes(); + skeleton.unref(); + animation_state.unref(); + + if (skeleton_data_res.is_valid()) { + if (!skeleton_data_res->is_connected("skeleton_data_changed", this, "_on_skeleton_data_changed")) + skeleton_data_res->connect("skeleton_data_changed", this, "_on_skeleton_data_changed"); + } + + if (skeleton_data_res.is_valid() && skeleton_data_res->is_skeleton_data_loaded()) { + skeleton = Ref(memnew(SpineNewSkeleton)); + skeleton->set_skeleton_data_res(skeleton_data_res); + skeleton->set_spine_sprite(this); + + animation_state = Ref(memnew(SpineNewAnimationState)); + animation_state->set_skeleton_data_res(skeleton_data_res); + if (animation_state->get_spine_object()) animation_state->get_spine_object()->setListener(this); + + animation_state->update(0); + animation_state->apply(skeleton); + skeleton->update_world_transform(); + generate_meshes_for_slots(skeleton); + + if (process_mode == ProcessMode_Process) { + _notification(NOTIFICATION_INTERNAL_PROCESS); + } else if (process_mode == ProcessMode_Physics) { + _notification(NOTIFICATION_INTERNAL_PHYSICS_PROCESS); + } + } + + property_list_changed_notify(); +} + +Ref SpineNewSprite::get_skeleton() { + return skeleton; +} + +Ref SpineNewSprite::get_animation_state() { + return animation_state; } void SpineNewSprite::_notification(int p_what) { @@ -85,7 +162,6 @@ void SpineNewSprite::_notification(int p_what) { case NOTIFICATION_READY: { set_process_internal(process_mode == ProcessMode_Process); set_physics_process_internal(process_mode == ProcessMode_Physics); - remove_redundant_mesh_instances(); } break; case NOTIFICATION_INTERNAL_PROCESS: { if (process_mode == ProcessMode_Process) @@ -108,7 +184,7 @@ void SpineNewSprite::_update_all(float delta) { animation_state->apply(skeleton); skeleton->update_world_transform(); - update_mesh_from_skeleton(skeleton); + update_meshes(skeleton); update(); update_bind_slot_nodes(); } @@ -182,93 +258,21 @@ Node *SpineNewSprite::find_child_node_by_node(Node *node) { return node; } -void SpineNewSprite::set_skeleton_data_res(const Ref &s) { - skeleton_data_res = s; - _on_skeleton_data_changed(); -} -Ref SpineNewSprite::get_skeleton_data_res() { - return skeleton_data_res; -} +void SpineNewSprite::generate_meshes_for_slots(Ref skeleton_ref) { + auto skeleton = skeleton_ref->get_spine_object(); + for (int i = 0, n = skeleton->getSlots().size(); i < n; i++) { -void SpineNewSprite::_on_skeleton_data_changed() { - remove_mesh_instances(); - skeleton.unref(); - animation_state.unref(); + auto mesh_instance = memnew(MeshInstance2D); + mesh_instance->set_position(Vector2(0, 0)); + mesh_instance->set_material(materials[spine::BlendMode_Normal]); - if (skeleton_data_res.is_valid()) { - if (!skeleton_data_res->is_connected("skeleton_data_changed", this, "_on_skeleton_data_changed")) - skeleton_data_res->connect("skeleton_data_changed", this, "_on_skeleton_data_changed"); - } - - if (skeleton_data_res.is_valid() && skeleton_data_res->is_skeleton_data_loaded()) { - skeleton = Ref(memnew(SpineNewSkeleton)); - skeleton->set_skeleton_data_res(skeleton_data_res); - skeleton->set_spine_sprite(this); - - animation_state = Ref(memnew(SpineNewAnimationState)); - animation_state->set_skeleton_data_res(skeleton_data_res); - if (animation_state->get_spine_object()) animation_state->get_spine_object()->setListener(this); - - animation_state->update(0); - animation_state->apply(skeleton); - skeleton->update_world_transform(); - gen_mesh_from_skeleton(skeleton); - - if (process_mode == ProcessMode_Process) { - _notification(NOTIFICATION_INTERNAL_PROCESS); - } else if (process_mode == ProcessMode_Physics) { - _notification(NOTIFICATION_INTERNAL_PHYSICS_PROCESS); - } + add_child(mesh_instance); + mesh_instance->set_owner(this); + mesh_instances.push_back(mesh_instance); } } -Ref SpineNewSprite::get_skeleton() { - return skeleton; -} - -Ref SpineNewSprite::get_animation_state() { - return animation_state; -} - -void SpineNewSprite::gen_mesh_from_skeleton(Ref s) { - auto sk = s->get_spine_object(); - for (size_t i = 0, n = sk->getSlots().size(); i < n; ++i) { - auto mesh_ins = memnew(SpineSpriteMeshInstance2D); - add_child(mesh_ins); - mesh_ins->set_position(Vector2(0, 0)); - mesh_ins->set_owner(this); - mesh_instances.push_back(mesh_ins); - - spine::Slot *slot = sk->getDrawOrder()[i]; - mesh_ins->set_name(slot->getData().getName().buffer()); - Ref gd_slot(memnew(SpineSlot)); - gd_slot->set_spine_object(slot); - mesh_ins->set_slot(gd_slot); - - Ref mat(memnew(CanvasItemMaterial)); - CanvasItemMaterial::BlendMode blend_mode; - switch (slot->getData().getBlendMode()) { - case spine::BlendMode_Normal: - blend_mode = CanvasItemMaterial::BLEND_MODE_MIX; - break; - case spine::BlendMode_Additive: - blend_mode = CanvasItemMaterial::BLEND_MODE_ADD; - break; - case spine::BlendMode_Multiply: - blend_mode = CanvasItemMaterial::BLEND_MODE_MUL; - break; - case spine::BlendMode_Screen: - blend_mode = CanvasItemMaterial::BLEND_MODE_MIX; - break; - default: - blend_mode = CanvasItemMaterial::BLEND_MODE_MIX; - } - mat->set_blend_mode(blend_mode); - mesh_ins->set_material(mat); - } -} - -void SpineNewSprite::remove_mesh_instances() { +void SpineNewSprite::remove_meshes() { for (size_t i = 0; i < mesh_instances.size(); ++i) { remove_child(mesh_instances[i]); memdelete(mesh_instances[i]); @@ -276,24 +280,6 @@ void SpineNewSprite::remove_mesh_instances() { mesh_instances.clear(); } -void SpineNewSprite::remove_redundant_mesh_instances() { - Vector ms; - // remove the redundant mesh instances that added by duplicating - for (size_t i = 0, n = get_child_count(); i < n; ++i) { - auto node = get_child(i); - if (node && node->is_class("SpineNewSpriteMeshInstance2D")) { - if (mesh_instances.find((SpineSpriteMeshInstance2D *) node) == -1) { - ms.push_back(node); - } - } - } - for (size_t i = 0, n = ms.size(); i < n; ++i) { - remove_child(ms[i]); - memdelete(ms[i]); - } - ms.clear(); -} - #define TEMP_COPY(t, get_res) \ do { \ auto &temp_uvs = get_res; \ @@ -303,7 +289,7 @@ void SpineNewSprite::remove_redundant_mesh_instances() { } \ } while (false); -void SpineNewSprite::update_mesh_from_skeleton(Ref s) { +void SpineNewSprite::update_meshes(Ref s) { static const unsigned short VERTEX_STRIDE = 2; static unsigned short quad_indices[] = {0, 1, 2, 2, 3, 0}; @@ -461,25 +447,7 @@ void SpineNewSprite::update_mesh_from_skeleton(Ref s) { skeleton_clipper->clipEnd(*slot); if (mesh_ins->get_material()->is_class("CanvasItemMaterial")) { - Ref mat = mesh_ins->get_material(); - CanvasItemMaterial::BlendMode blend_mode; - switch (slot->getData().getBlendMode()) { - case spine::BlendMode_Normal: - blend_mode = CanvasItemMaterial::BLEND_MODE_MIX; - break; - case spine::BlendMode_Additive: - blend_mode = CanvasItemMaterial::BLEND_MODE_ADD; - break; - case spine::BlendMode_Multiply: - blend_mode = CanvasItemMaterial::BLEND_MODE_MUL; - break; - case spine::BlendMode_Screen: - blend_mode = CanvasItemMaterial::BLEND_MODE_MIX; - break; - default: - blend_mode = CanvasItemMaterial::BLEND_MODE_MIX; - } - mat->set_blend_mode(blend_mode); + mesh_ins->set_material(materials[slot->getData().getBlendMode()]); } } skeleton_clipper->clipEnd(); diff --git a/spine-godot/godot/modules/spine_godot/SpineNewSprite.h b/spine-godot/godot/modules/spine_godot/SpineNewSprite.h index d47ae7af0..12b4cee9b 100644 --- a/spine-godot/godot/modules/spine_godot/SpineNewSprite.h +++ b/spine-godot/godot/modules/spine_godot/SpineNewSprite.h @@ -30,11 +30,12 @@ #ifndef GODOT_SPINENEWSPRITE_H #define GODOT_SPINENEWSPRITE_H -#include +#include "scene/2d/node_2d.h" +#include "scene/resources/texture.h" #include "SpineNewSkeleton.h" #include "SpineNewAnimationState.h" -#include "SpineSpriteMeshInstance2D.h" +#include "scene/2d/mesh_instance_2d.h" class SpineNewSprite : public Node2D, public spine::AnimationStateListenerObject { GDCLASS(SpineNewSprite, Node2D); @@ -69,8 +70,9 @@ private: ProcessMode process_mode; - Vector mesh_instances; + Vector mesh_instances; spine::SkeletonClipping *skeleton_clipper; + static Ref materials[4]; public: SpineNewSprite(); @@ -82,11 +84,10 @@ public: Ref get_skeleton(); Ref get_animation_state(); - void gen_mesh_from_skeleton(Ref s); - void remove_mesh_instances(); - void remove_redundant_mesh_instances(); + void generate_meshes_for_slots(Ref skeleton_ref); + void remove_meshes(); - void update_mesh_from_skeleton(Ref s); + void update_meshes(Ref s); void update_bind_slot_nodes(); void update_bind_slot_node_transform(Ref bone, Node2D *node2d);