mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-20 01:06:00 +08:00
[godot] Removes unnecessary files and comments.
Additionally, CI builds were failing due to the commit where SpineSkin had an init() method added, but this caused Godot 3.x builds to fail. Our formatter was also run, so it should no longer fail the format check.
This commit is contained in:
parent
52b114d18a
commit
55d46f2019
@ -1,172 +0,0 @@
|
|||||||
# Crash Fix: SpineSkeletonDataResource Destructor (v2)
|
|
||||||
|
|
||||||
## TL;DR
|
|
||||||
|
|
||||||
Fixed segmentation fault during Godot editor shutdown by using `ObjectDB::get_instance()` to safely validate the `EditorFileSystem` pointer before attempting to disconnect the signal in the destructor. The original code used a raw pointer that could become dangling during shutdown; the fix stores the `ObjectID` at construction time and validates it in the destructor.
|
|
||||||
|
|
||||||
**Changed files:**
|
|
||||||
- `spine_godot/SpineSkeletonDataResource.h` - Added `ObjectID editor_file_system_id` member variable
|
|
||||||
- `spine_godot/SpineSkeletonDataResource.cpp` - Modified constructor and destructor
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Context
|
|
||||||
|
|
||||||
Mario requested a reference for the claim that removing the disconnect code entirely was safe. Upon investigation, that claim was not fully accurate. Godot's automatic signal cleanup in `Object::~Object()` also calls methods on the signal owner, which could crash if the owner is already destroyed.
|
|
||||||
|
|
||||||
The proper fix is to retain the disconnect logic but use Godot's `ObjectDB` system to safely validate that `EditorFileSystem` still exists before calling any methods on it.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The Problem
|
|
||||||
|
|
||||||
The Godot editor crashed with signal 11 (SIGSEGV) during shutdown. The crash occurred in the `SpineSkeletonDataResource` destructor when attempting to disconnect from the `EditorFileSystem` singleton's "resources_reimported" signal.
|
|
||||||
|
|
||||||
**Original crash backtrace:**
|
|
||||||
```
|
|
||||||
[14] SpineSkeletonDataResource::~SpineSkeletonDataResource()
|
|
||||||
(spine_godot/SpineSkeletonDataResource.cpp:255)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Root cause:** During editor shutdown, singletons are destroyed in an undefined order. The destructor used `get_editor_file_system()` which returns a raw pointer. If `EditorFileSystem` was destroyed before `SpineSkeletonDataResource` objects, the pointer became dangling. Calling `efs->is_connected()` on a dangling pointer caused the segfault.
|
|
||||||
|
|
||||||
The check `if (efs)` only verifies the pointer is non-null, not that the memory it points to is valid.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The Solution
|
|
||||||
|
|
||||||
Use Godot's `ObjectDB` system to safely validate object existence:
|
|
||||||
|
|
||||||
1. Store the `ObjectID` of `EditorFileSystem` when connecting the signal (in constructor)
|
|
||||||
2. Use `ObjectDB::get_instance(id)` in the destructor to check if the object still exists
|
|
||||||
3. Only proceed with disconnect if the object is valid
|
|
||||||
|
|
||||||
`ObjectDB::get_instance()` is safe to call even if the object has been destroyed - it returns `nullptr` in that case, rather than accessing freed memory.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Code Changes
|
|
||||||
|
|
||||||
### Header file (SpineSkeletonDataResource.h)
|
|
||||||
|
|
||||||
**Added member variable (inside private section, under TOOLS_ENABLED):**
|
|
||||||
```cpp
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
// Store the ObjectID of EditorFileSystem to safely validate it in destructor.
|
|
||||||
// Raw pointers to singletons can become dangling during editor shutdown,
|
|
||||||
// but ObjectID can be safely validated via ObjectDB::get_instance().
|
|
||||||
ObjectID editor_file_system_id;
|
|
||||||
#endif
|
|
||||||
```
|
|
||||||
|
|
||||||
### Source file (SpineSkeletonDataResource.cpp)
|
|
||||||
|
|
||||||
**Constructor - store the ObjectID when connecting:**
|
|
||||||
```cpp
|
|
||||||
SpineSkeletonDataResource::SpineSkeletonDataResource()
|
|
||||||
: default_mix(0), skeleton_data(nullptr), animation_state_data(nullptr) {
|
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
EditorFileSystem *efs = get_editor_file_system();
|
|
||||||
if (efs) {
|
|
||||||
// Store the ObjectID for safe validation in destructor
|
|
||||||
editor_file_system_id = efs->get_instance_id();
|
|
||||||
efs->connect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
EditorFileSystem *efs = EditorFileSystem::get_singleton();
|
|
||||||
if (efs) {
|
|
||||||
// Store the ObjectID for safe validation in destructor
|
|
||||||
editor_file_system_id = efs->get_instance_id();
|
|
||||||
efs->connect("resources_reimported", this, "_on_resources_reimported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Destructor - validate via ObjectDB before disconnecting:**
|
|
||||||
```cpp
|
|
||||||
SpineSkeletonDataResource::~SpineSkeletonDataResource() {
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
// Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists.
|
|
||||||
// This avoids the dangling pointer problem during editor shutdown where
|
|
||||||
// EditorFileSystem may be destroyed before SpineSkeletonDataResource objects.
|
|
||||||
EditorFileSystem *efs = Object::cast_to<EditorFileSystem>(ObjectDB::get_instance(editor_file_system_id));
|
|
||||||
if (efs && efs->is_connected("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported))) {
|
|
||||||
efs->disconnect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
// Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists.
|
|
||||||
EditorFileSystem *efs = Object::cast_to<EditorFileSystem>(ObjectDB::get_instance(editor_file_system_id));
|
|
||||||
if (efs && efs->is_connected("resources_reimported", this, "_on_resources_reimported")) {
|
|
||||||
efs->disconnect("resources_reimported", this, "_on_resources_reimported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
delete skeleton_data;
|
|
||||||
delete animation_state_data;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why This Works
|
|
||||||
|
|
||||||
1. **ObjectID is just a uint64_t** - It's safe to store and doesn't hold a reference to the object
|
|
||||||
2. **ObjectDB::get_instance() is safe** - It performs a lookup in Godot's object registry and returns `nullptr` if the object no longer exists, without accessing any potentially-freed memory
|
|
||||||
3. **The pattern is used throughout Godot** - See `godot/core/object/undo_redo.cpp`, `godot/core/object/message_queue.cpp`, and `godot/core/object/callable_method_pointer.h` for examples
|
|
||||||
|
|
||||||
**From godot/core/object/undo_redo.cpp:355:**
|
|
||||||
```cpp
|
|
||||||
Object *obj = ObjectDB::get_instance(op.object);
|
|
||||||
if (!obj) { //may have been deleted and this is fine
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Why Not Just Remove the Disconnect?
|
|
||||||
|
|
||||||
Mario correctly questioned whether removing the disconnect entirely was safe. Investigation revealed:
|
|
||||||
|
|
||||||
1. Godot's `Object::~Object()` does perform automatic signal cleanup (lines 2186-2198 in object.cpp)
|
|
||||||
2. However, that cleanup also calls `c.signal.get_object()->_disconnect(...)` which accesses the signal owner
|
|
||||||
3. If the signal owner is destroyed first, the automatic cleanup would also crash
|
|
||||||
|
|
||||||
The automatic cleanup uses `c.callable.get_object()` which does perform ObjectDB validation, but the crash was happening in our custom destructor code before the automatic cleanup ran.
|
|
||||||
|
|
||||||
Retaining explicit disconnect with proper validation is the safer approach and follows patterns used elsewhere in the Godot codebase.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
- [ ] Editor starts without errors
|
|
||||||
- [ ] Editor closes cleanly without crashes
|
|
||||||
- [ ] Hot-reload works (modify and reimport a Spine asset while editor is running)
|
|
||||||
- [ ] Normal gameplay with Spine assets works
|
|
||||||
- [ ] Test with both module build and GDExtension build
|
|
||||||
- [ ] Test with C# build if applicable
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- Godot ObjectDB implementation: `godot/core/object/object.h:1044-1064`
|
|
||||||
- Godot Object destructor signal cleanup: `godot/core/object/object.cpp:2135-2198`
|
|
||||||
- Example usage in undo_redo: `godot/core/object/undo_redo.cpp:355`
|
|
||||||
- GitHub Issue on signal disconnection: https://github.com/godotengine/godot/issues/70414
|
|
||||||
@ -1,853 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Spine Runtimes License Agreement
|
|
||||||
* Last updated April 5, 2025. Replaces all prior versions.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2025, Esoteric Software LLC
|
|
||||||
*
|
|
||||||
* Integration of the Spine Runtimes into software or otherwise creating
|
|
||||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
|
||||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
|
||||||
* http://esotericsoftware.com/spine-editor-license
|
|
||||||
*
|
|
||||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
|
||||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
|
||||||
* "Products"), provided that each user of the Products must obtain their own
|
|
||||||
* Spine Editor license and redistribution of the Products in any form must
|
|
||||||
* include this license and copyright notice.
|
|
||||||
*
|
|
||||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
|
||||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#include "SpineSkeletonDataResource.h"
|
|
||||||
#include "SpineCommon.h"
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
#include <godot_cpp/classes/encoded_object_as_id.hpp>
|
|
||||||
#include <godot_cpp/classes/engine.hpp>
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#include <godot_cpp/classes/editor_interface.hpp>
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
#include "core/config/engine.h"
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#include "editor/editor_interface.h"
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#include "core/engine.h"
|
|
||||||
#endif
|
|
||||||
#include <core/io/marshalls.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
#include <godot_cpp/classes/editor_file_system.hpp>
|
|
||||||
#else
|
|
||||||
#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5)
|
|
||||||
#include "editor/file_system/editor_file_system.h"
|
|
||||||
#else
|
|
||||||
#include "editor/editor_file_system.h"
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SpineAnimationMix::_bind_methods() {
|
|
||||||
ClassDB::bind_method(D_METHOD("set_from", "from"),
|
|
||||||
&SpineAnimationMix::set_from);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_from"), &SpineAnimationMix::get_from);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_to", "to"), &SpineAnimationMix::set_to);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_to"), &SpineAnimationMix::get_to);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_mix", "mix"), &SpineAnimationMix::set_mix);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_mix"), &SpineAnimationMix::get_mix);
|
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "from"), "set_from", "get_from");
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::STRING, "to"), "set_to", "get_to");
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mix"), "set_mix", "get_mix");
|
|
||||||
#else
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "mix"), "set_mix", "get_mix");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SpineAnimationMix::SpineAnimationMix() : from(""), to(""), mix(0) {}
|
|
||||||
|
|
||||||
void SpineAnimationMix::set_from(const String &_from) { this->from = _from; }
|
|
||||||
|
|
||||||
String SpineAnimationMix::get_from() { return from; }
|
|
||||||
|
|
||||||
void SpineAnimationMix::set_to(const String &_to) { this->to = _to; }
|
|
||||||
|
|
||||||
String SpineAnimationMix::get_to() { return to; }
|
|
||||||
|
|
||||||
void SpineAnimationMix::set_mix(float _mix) { this->mix = _mix; }
|
|
||||||
|
|
||||||
float SpineAnimationMix::get_mix() { return mix; }
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::_bind_methods() {
|
|
||||||
ClassDB::bind_method(D_METHOD("is_skeleton_data_loaded"),
|
|
||||||
&SpineSkeletonDataResource::is_skeleton_data_loaded);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_atlas_res", "atlas_res"),
|
|
||||||
&SpineSkeletonDataResource::set_atlas_res);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_atlas_res"),
|
|
||||||
&SpineSkeletonDataResource::get_atlas_res);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_skeleton_file_res", "skeleton_file_res"),
|
|
||||||
&SpineSkeletonDataResource::set_skeleton_file_res);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_skeleton_file_res"),
|
|
||||||
&SpineSkeletonDataResource::get_skeleton_file_res);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_default_mix", "default_mix"),
|
|
||||||
&SpineSkeletonDataResource::set_default_mix);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_default_mix"),
|
|
||||||
&SpineSkeletonDataResource::get_default_mix);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_animation_mixes", "mixes"),
|
|
||||||
&SpineSkeletonDataResource::set_animation_mixes);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_animation_mixes"),
|
|
||||||
&SpineSkeletonDataResource::get_animation_mixes);
|
|
||||||
|
|
||||||
// Spine API
|
|
||||||
ClassDB::bind_method(D_METHOD("find_bone", "bone_name"),
|
|
||||||
&SpineSkeletonDataResource::find_bone);
|
|
||||||
ClassDB::bind_method(D_METHOD("find_slot", "slot_name"),
|
|
||||||
&SpineSkeletonDataResource::find_slot);
|
|
||||||
ClassDB::bind_method(D_METHOD("find_skin", "skin_name"),
|
|
||||||
&SpineSkeletonDataResource::find_skin);
|
|
||||||
ClassDB::bind_method(D_METHOD("find_event", "event_data_name"),
|
|
||||||
&SpineSkeletonDataResource::find_event);
|
|
||||||
ClassDB::bind_method(D_METHOD("find_animation", "animation_name"),
|
|
||||||
&SpineSkeletonDataResource::find_animation);
|
|
||||||
ClassDB::bind_method(D_METHOD("find_ik_constraint_data", "constraint_name"),
|
|
||||||
&SpineSkeletonDataResource::find_ik_constraint);
|
|
||||||
ClassDB::bind_method(
|
|
||||||
D_METHOD("find_transform_constraint_data", "constraint_name"),
|
|
||||||
&SpineSkeletonDataResource::find_transform_constraint);
|
|
||||||
ClassDB::bind_method(D_METHOD("find_path_constraint_data", "constraint_name"),
|
|
||||||
&SpineSkeletonDataResource::find_path_constraint);
|
|
||||||
ClassDB::bind_method(D_METHOD("find_physics_constraint_data", "constraint_name"),
|
|
||||||
&SpineSkeletonDataResource::find_physics_constraint);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_skeleton_name"),
|
|
||||||
&SpineSkeletonDataResource::get_skeleton_name);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_bones"),
|
|
||||||
&SpineSkeletonDataResource::get_bones);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_slots"),
|
|
||||||
&SpineSkeletonDataResource::get_slots);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_skins"),
|
|
||||||
&SpineSkeletonDataResource::get_skins);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_default_skin"),
|
|
||||||
&SpineSkeletonDataResource::get_default_skin);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_default_skin", "skin"),
|
|
||||||
&SpineSkeletonDataResource::set_default_skin);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_events"),
|
|
||||||
&SpineSkeletonDataResource::get_events);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_animations"),
|
|
||||||
&SpineSkeletonDataResource::get_animations);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_ik_constraints"),
|
|
||||||
&SpineSkeletonDataResource::get_ik_constraints);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_transform_constraints"),
|
|
||||||
&SpineSkeletonDataResource::get_transform_constraints);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_path_constraints"),
|
|
||||||
&SpineSkeletonDataResource::get_path_constraints);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_physics_constraints"),
|
|
||||||
&SpineSkeletonDataResource::get_physics_constraints);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_x"), &SpineSkeletonDataResource::get_x);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_y"), &SpineSkeletonDataResource::get_y);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_width"),
|
|
||||||
&SpineSkeletonDataResource::get_width);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_height"),
|
|
||||||
&SpineSkeletonDataResource::get_height);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_version"),
|
|
||||||
&SpineSkeletonDataResource::get_version);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_hash"),
|
|
||||||
&SpineSkeletonDataResource::get_hash);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_images_path"),
|
|
||||||
&SpineSkeletonDataResource::get_images_path);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_audio_path"),
|
|
||||||
&SpineSkeletonDataResource::get_audio_path);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_fps"),
|
|
||||||
&SpineSkeletonDataResource::get_fps);
|
|
||||||
ClassDB::bind_method(D_METHOD("get_reference_scale"),
|
|
||||||
&SpineSkeletonDataResource::get_reference_scale);
|
|
||||||
ClassDB::bind_method(D_METHOD("set_reference_scale", "reference_scale"),
|
|
||||||
&SpineSkeletonDataResource::set_reference_scale);
|
|
||||||
ClassDB::bind_method(D_METHOD("update_skeleton_data"),
|
|
||||||
&SpineSkeletonDataResource::update_skeleton_data);
|
|
||||||
|
|
||||||
ADD_SIGNAL(MethodInfo("skeleton_data_changed"));
|
|
||||||
ADD_SIGNAL(MethodInfo("_internal_spine_objects_invalidated"));
|
|
||||||
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "atlas_res",
|
|
||||||
PropertyHint::PROPERTY_HINT_RESOURCE_TYPE,
|
|
||||||
"SpineAtlasResource"),
|
|
||||||
"set_atlas_res", "get_atlas_res");
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "skeleton_file_res",
|
|
||||||
PropertyHint::PROPERTY_HINT_RESOURCE_TYPE,
|
|
||||||
"SpineSkeletonFileResource"),
|
|
||||||
"set_skeleton_file_res", "get_skeleton_file_res");
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "default_mix"), "set_default_mix",
|
|
||||||
"get_default_mix");
|
|
||||||
#else
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::REAL, "default_mix"), "set_default_mix",
|
|
||||||
"get_default_mix");
|
|
||||||
#endif
|
|
||||||
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animation_mixes"),
|
|
||||||
"set_animation_mixes", "get_animation_mixes");
|
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
ClassDB::bind_method(D_METHOD("_on_resources_reimported", "resources"),
|
|
||||||
&SpineSkeletonDataResource::_on_resources_reimported);
|
|
||||||
#else
|
|
||||||
ClassDB::bind_method(D_METHOD("_on_resources_reimported", "resources"),
|
|
||||||
&SpineSkeletonDataResource::_on_resources_reimported);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
EditorFileSystem *get_editor_file_system() {
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
EditorInterface *editor_interface = EditorInterface::get_singleton();
|
|
||||||
if (editor_interface) {
|
|
||||||
return editor_interface->get_resource_filesystem();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
#else
|
|
||||||
return EditorFileSystem::get_singleton();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
SpineSkeletonDataResource::SpineSkeletonDataResource()
|
|
||||||
: default_mix(0), skeleton_data(nullptr), animation_state_data(nullptr) {
|
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
EditorFileSystem *efs = get_editor_file_system();
|
|
||||||
if (efs) {
|
|
||||||
efs->connect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
EditorFileSystem *efs = EditorFileSystem::get_singleton();
|
|
||||||
if (efs) {
|
|
||||||
efs->connect("resources_reimported", this, "_on_resources_reimported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
SpineSkeletonDataResource::~SpineSkeletonDataResource() {
|
|
||||||
/*#ifdef TOOLS_ENABLED
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
EditorFileSystem *efs = get_editor_file_system();
|
|
||||||
if (efs && efs->is_connected("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported))) {
|
|
||||||
efs->disconnect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
|
||||||
EditorFileSystem *efs = EditorFileSystem::get_singleton();
|
|
||||||
if (efs && efs->is_connected("resources_reimported", this, "_on_resources_reimported")) {
|
|
||||||
efs->disconnect("resources_reimported", this, "_on_resources_reimported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
*/
|
|
||||||
delete skeleton_data;
|
|
||||||
delete animation_state_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
void SpineSkeletonDataResource::_on_resources_reimported(const PackedStringArray &resources) {
|
|
||||||
for (int i = 0; i < resources.size(); i++) {
|
|
||||||
if (atlas_res.is_valid() && atlas_res->get_path() == resources[i]) {
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
atlas_res = ResourceLoader::get_singleton()->load(resources[i], "SpineAtlasResource", ResourceLoader::CACHE_MODE_IGNORE);
|
|
||||||
#else
|
|
||||||
atlas_res = ResourceLoader::load(resources[i], "SpineAtlasResource", ResourceFormatLoader::CACHE_MODE_IGNORE);
|
|
||||||
#endif
|
|
||||||
update_skeleton_data();
|
|
||||||
} else if (skeleton_file_res.is_valid() && skeleton_file_res->get_path() == resources[i]) {
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
skeleton_file_res = ResourceLoader::get_singleton()->load(resources[i], "SpineSkeletonFileResource", ResourceLoader::CACHE_MODE_IGNORE);
|
|
||||||
#else
|
|
||||||
skeleton_file_res = ResourceLoader::load(resources[i], "SpineSkeletonFileResource", ResourceFormatLoader::CACHE_MODE_IGNORE);
|
|
||||||
#endif
|
|
||||||
update_skeleton_data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void SpineSkeletonDataResource::_on_resources_reimported(const PoolStringArray &resources) {
|
|
||||||
for (int i = 0; i < resources.size(); i++) {
|
|
||||||
if (atlas_res.is_valid() && atlas_res->get_path() == resources[i]) {
|
|
||||||
atlas_res = ResourceLoader::load(resources[i]);
|
|
||||||
update_skeleton_data();
|
|
||||||
} else if (skeleton_file_res.is_valid() && skeleton_file_res->get_path() == resources[i]) {
|
|
||||||
skeleton_file_res = ResourceLoader::load(resources[i]);
|
|
||||||
update_skeleton_data();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::update_skeleton_data() {
|
|
||||||
if (skeleton_data) {
|
|
||||||
delete skeleton_data;
|
|
||||||
skeleton_data = nullptr;
|
|
||||||
}
|
|
||||||
if (animation_state_data) {
|
|
||||||
delete animation_state_data;
|
|
||||||
animation_state_data = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit_signal(SNAME("_internal_spine_objects_invalidated"));
|
|
||||||
|
|
||||||
if (atlas_res.is_valid() && skeleton_file_res.is_valid()) {
|
|
||||||
load_resources(atlas_res->get_spine_atlas(), skeleton_file_res->get_json(),
|
|
||||||
skeleton_file_res->get_binary());
|
|
||||||
}
|
|
||||||
emit_signal(SNAME("skeleton_data_changed"));
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
NOTIFY_PROPERTY_LIST_CHANGED();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
void SpineSkeletonDataResource::load_resources(spine::Atlas *atlas,
|
|
||||||
const String &json,
|
|
||||||
const PackedByteArray &binary) {
|
|
||||||
#else
|
|
||||||
void SpineSkeletonDataResource::load_resources(spine::Atlas *atlas,
|
|
||||||
const String &json,
|
|
||||||
const Vector<uint8_t> &binary) {
|
|
||||||
#endif
|
|
||||||
if ((EMPTY(json) && EMPTY(binary)) || atlas == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
spine::SkeletonData *data;
|
|
||||||
if (!EMPTY(json)) {
|
|
||||||
spine::SkeletonJson skeletonJson(atlas);
|
|
||||||
data = skeletonJson.readSkeletonData(json.utf8().ptr());
|
|
||||||
if (!data) {
|
|
||||||
ERR_PRINT(String("Error while loading skeleton data: ") + get_path());
|
|
||||||
ERR_PRINT(String("Error message: ") + skeletonJson.getError().buffer());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
spine::SkeletonBinary skeletonBinary(atlas);
|
|
||||||
data = skeletonBinary.readSkeletonData(binary.ptr(), binary.size());
|
|
||||||
if (!data) {
|
|
||||||
ERR_PRINT(String("Error while loading skeleton data: ") + get_path());
|
|
||||||
ERR_PRINT(String("Error message: ") + skeletonBinary.getError().buffer());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
skeleton_data = data;
|
|
||||||
animation_state_data = new spine::AnimationStateData(data);
|
|
||||||
update_mixes();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SpineSkeletonDataResource::is_skeleton_data_loaded() const {
|
|
||||||
return skeleton_data != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::set_atlas_res(
|
|
||||||
const Ref<SpineAtlasResource> &atlas) {
|
|
||||||
atlas_res = atlas;
|
|
||||||
update_skeleton_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineAtlasResource> SpineSkeletonDataResource::get_atlas_res() {
|
|
||||||
return atlas_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::set_skeleton_file_res(
|
|
||||||
const Ref<SpineSkeletonFileResource> &skeleton_file) {
|
|
||||||
skeleton_file_res = skeleton_file;
|
|
||||||
update_skeleton_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineSkeletonFileResource>
|
|
||||||
SpineSkeletonDataResource::get_skeleton_file_res() {
|
|
||||||
return skeleton_file_res;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
void SpineSkeletonDataResource::get_animation_names(PackedStringArray &animation_names) const {
|
|
||||||
#else
|
|
||||||
void SpineSkeletonDataResource::get_animation_names(Vector<String> &animation_names) const {
|
|
||||||
#endif
|
|
||||||
animation_names.clear();
|
|
||||||
if (!is_skeleton_data_loaded())
|
|
||||||
return;
|
|
||||||
auto animations = skeleton_data->getAnimations();
|
|
||||||
for (size_t i = 0; i < animations.size(); ++i) {
|
|
||||||
auto animation = animations[i];
|
|
||||||
String name;
|
|
||||||
#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5)
|
|
||||||
name = String::utf8(animation->getName().buffer());
|
|
||||||
#else
|
|
||||||
name.parse_utf8(animation->getName().buffer());
|
|
||||||
#endif
|
|
||||||
animation_names.push_back(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
void SpineSkeletonDataResource::get_skin_names(PackedStringArray &skin_names) const {
|
|
||||||
#else
|
|
||||||
void SpineSkeletonDataResource::get_skin_names(Vector<String> &skin_names) const {
|
|
||||||
#endif
|
|
||||||
skin_names.clear();
|
|
||||||
if (!is_skeleton_data_loaded())
|
|
||||||
return;
|
|
||||||
auto skins = skeleton_data->getSkins();
|
|
||||||
for (size_t i = 0; i < skins.size(); ++i) {
|
|
||||||
auto skin = skins[i];
|
|
||||||
String name;
|
|
||||||
#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5)
|
|
||||||
name = String::utf8(skin->getName().buffer());
|
|
||||||
#else
|
|
||||||
name.parse_utf8(skin->getName().buffer());
|
|
||||||
#endif
|
|
||||||
skin_names.push_back(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
void SpineSkeletonDataResource::get_slot_names(PackedStringArray &slot_names) {
|
|
||||||
#else
|
|
||||||
void SpineSkeletonDataResource::get_slot_names(Vector<String> &slot_names) {
|
|
||||||
#endif
|
|
||||||
slot_names.clear();
|
|
||||||
if (!is_skeleton_data_loaded())
|
|
||||||
return;
|
|
||||||
auto slots = skeleton_data->getSlots();
|
|
||||||
for (size_t i = 0; i < slots.size(); ++i) {
|
|
||||||
auto slot = slots[i];
|
|
||||||
String name;
|
|
||||||
#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5)
|
|
||||||
name = String::utf8(slot->getName().buffer());
|
|
||||||
#else
|
|
||||||
name.parse_utf8(slot->getName().buffer());
|
|
||||||
#endif
|
|
||||||
slot_names.push_back(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
void SpineSkeletonDataResource::get_bone_names(PackedStringArray &bone_names) {
|
|
||||||
#else
|
|
||||||
void SpineSkeletonDataResource::get_bone_names(Vector<String> &bone_names) {
|
|
||||||
#endif
|
|
||||||
bone_names.clear();
|
|
||||||
if (!is_skeleton_data_loaded())
|
|
||||||
return;
|
|
||||||
auto bones = skeleton_data->getBones();
|
|
||||||
for (size_t i = 0; i < bones.size(); ++i) {
|
|
||||||
auto bone = bones[i];
|
|
||||||
String name;
|
|
||||||
#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5)
|
|
||||||
name = String::utf8(bone->getName().buffer());
|
|
||||||
#else
|
|
||||||
name.parse_utf8(bone->getName().buffer());
|
|
||||||
#endif
|
|
||||||
bone_names.push_back(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::set_default_mix(float _default_mix) {
|
|
||||||
this->default_mix = _default_mix;
|
|
||||||
update_mixes();
|
|
||||||
}
|
|
||||||
|
|
||||||
float SpineSkeletonDataResource::get_default_mix() { return default_mix; }
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::set_animation_mixes(Array _animation_mixes) {
|
|
||||||
for (int i = 0; i < _animation_mixes.size(); i++) {
|
|
||||||
auto objectId = Object::cast_to<EncodedObjectAsID>(_animation_mixes[0]);
|
|
||||||
if (objectId) {
|
|
||||||
ERR_PRINT("Live-editing of animation mixes is not supported.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this->animation_mixes = _animation_mixes;
|
|
||||||
update_mixes();
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_animation_mixes() {
|
|
||||||
return animation_mixes;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::update_mixes() {
|
|
||||||
if (!is_skeleton_data_loaded())
|
|
||||||
return;
|
|
||||||
animation_state_data->clear();
|
|
||||||
animation_state_data->setDefaultMix(default_mix);
|
|
||||||
for (int i = 0; i < animation_mixes.size(); i++) {
|
|
||||||
Ref<SpineAnimationMix> mix = animation_mixes[i];
|
|
||||||
spine::Animation *from =
|
|
||||||
skeleton_data->findAnimation(mix->get_from().utf8().ptr());
|
|
||||||
spine::Animation *to =
|
|
||||||
skeleton_data->findAnimation(mix->get_to().utf8().ptr());
|
|
||||||
if (!from) {
|
|
||||||
ERR_PRINT(vformat("Failed to set animation mix %s->%s. Animation %s does "
|
|
||||||
"not exist in skeleton.",
|
|
||||||
from, to, from));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!to) {
|
|
||||||
ERR_PRINT(vformat("Failed to set animation mix %s->%s. Animation %s does "
|
|
||||||
"not exist in skeleton.",
|
|
||||||
from, to, to));
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
animation_state_data->setMix(from, to, mix->get_mix());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineAnimation>
|
|
||||||
SpineSkeletonDataResource::find_animation(const String &animation_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(animation_name))
|
|
||||||
return nullptr;
|
|
||||||
auto animation =
|
|
||||||
skeleton_data->findAnimation(SPINE_STRING_TMP(animation_name));
|
|
||||||
if (!animation)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineAnimation> animation_ref(memnew(SpineAnimation));
|
|
||||||
animation_ref->set_spine_object(this, animation);
|
|
||||||
return animation_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineBoneData>
|
|
||||||
SpineSkeletonDataResource::find_bone(const String &bone_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(bone_name))
|
|
||||||
return nullptr;
|
|
||||||
auto bone = skeleton_data->findBone(SPINE_STRING_TMP(bone_name));
|
|
||||||
if (!bone)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineBoneData> bone_ref(memnew(SpineBoneData));
|
|
||||||
bone_ref->set_spine_object(this, bone);
|
|
||||||
return bone_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineSlotData>
|
|
||||||
SpineSkeletonDataResource::find_slot(const String &slot_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(slot_name))
|
|
||||||
return nullptr;
|
|
||||||
auto slot = skeleton_data->findSlot(SPINE_STRING_TMP(slot_name));
|
|
||||||
if (!slot)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineSlotData> slot_ref(memnew(SpineSlotData));
|
|
||||||
slot_ref->set_spine_object(this, slot);
|
|
||||||
return slot_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineSkin>
|
|
||||||
SpineSkeletonDataResource::find_skin(const String &skin_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(skin_name))
|
|
||||||
return nullptr;
|
|
||||||
auto skin = skeleton_data->findSkin(SPINE_STRING_TMP(skin_name));
|
|
||||||
if (!skin)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineSkin> skin_ref(memnew(SpineSkin));
|
|
||||||
skin_ref->set_spine_object(this, skin);
|
|
||||||
return skin_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineEventData>
|
|
||||||
SpineSkeletonDataResource::find_event(const String &event_data_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(event_data_name))
|
|
||||||
return nullptr;
|
|
||||||
auto event = skeleton_data->findEvent(SPINE_STRING_TMP(event_data_name));
|
|
||||||
if (!event)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineEventData> event_ref(memnew(SpineEventData));
|
|
||||||
event_ref->set_spine_object(this, event);
|
|
||||||
return event_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineIkConstraintData> SpineSkeletonDataResource::find_ik_constraint(
|
|
||||||
const String &constraint_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(constraint_name))
|
|
||||||
return nullptr;
|
|
||||||
auto constraint =
|
|
||||||
skeleton_data->findIkConstraint(SPINE_STRING_TMP(constraint_name));
|
|
||||||
if (!constraint)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineIkConstraintData> constraint_ref(memnew(SpineIkConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraint);
|
|
||||||
return constraint_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineTransformConstraintData>
|
|
||||||
SpineSkeletonDataResource::find_transform_constraint(
|
|
||||||
const String &constraint_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(constraint_name))
|
|
||||||
return nullptr;
|
|
||||||
auto constraint =
|
|
||||||
skeleton_data->findTransformConstraint(SPINE_STRING_TMP(constraint_name));
|
|
||||||
if (!constraint)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineTransformConstraintData> constraint_ref(
|
|
||||||
memnew(SpineTransformConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraint);
|
|
||||||
return constraint_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpinePathConstraintData> SpineSkeletonDataResource::find_path_constraint(
|
|
||||||
const String &constraint_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(constraint_name))
|
|
||||||
return nullptr;
|
|
||||||
auto constraint =
|
|
||||||
skeleton_data->findPathConstraint(SPINE_STRING_TMP(constraint_name));
|
|
||||||
if (constraint == nullptr)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpinePathConstraintData> constraint_ref(memnew(SpinePathConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraint);
|
|
||||||
return constraint_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpinePhysicsConstraintData>
|
|
||||||
SpineSkeletonDataResource::find_physics_constraint(
|
|
||||||
const String &constraint_name) const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
if (EMPTY(constraint_name))
|
|
||||||
return nullptr;
|
|
||||||
auto constraint =
|
|
||||||
skeleton_data->findPhysicsConstraint(SPINE_STRING_TMP(constraint_name));
|
|
||||||
if (constraint == nullptr)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpinePhysicsConstraintData> constraint_ref(
|
|
||||||
memnew(SpinePhysicsConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraint);
|
|
||||||
return constraint_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
String SpineSkeletonDataResource::get_skeleton_name() const {
|
|
||||||
SPINE_CHECK(skeleton_data, "")
|
|
||||||
String name;
|
|
||||||
#if (VERSION_MAJOR >= 4 && VERSION_MINOR >= 5)
|
|
||||||
name = String::utf8(skeleton_data->getName().buffer());
|
|
||||||
#else
|
|
||||||
name.parse_utf8(skeleton_data->getName().buffer());
|
|
||||||
#endif
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_bones() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto bones = skeleton_data->getBones();
|
|
||||||
result.resize((int) bones.size());
|
|
||||||
for (int i = 0; i < bones.size(); ++i) {
|
|
||||||
Ref<SpineBoneData> bone_ref(memnew(SpineBoneData));
|
|
||||||
bone_ref->set_spine_object(this, bones[i]);
|
|
||||||
result[i] = bone_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_slots() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto slots = skeleton_data->getSlots();
|
|
||||||
result.resize((int) slots.size());
|
|
||||||
for (int i = 0; i < slots.size(); ++i) {
|
|
||||||
Ref<SpineSlotData> slot_ref(memnew(SpineSlotData));
|
|
||||||
slot_ref->set_spine_object(this, slots[i]);
|
|
||||||
result[i] = slot_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_skins() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto skins = skeleton_data->getSkins();
|
|
||||||
result.resize((int) skins.size());
|
|
||||||
for (int i = 0; i < skins.size(); ++i) {
|
|
||||||
Ref<SpineSkin> skin_ref(memnew(SpineSkin));
|
|
||||||
skin_ref->set_spine_object(this, skins[i]);
|
|
||||||
result[i] = skin_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ref<SpineSkin> SpineSkeletonDataResource::get_default_skin() const {
|
|
||||||
SPINE_CHECK(skeleton_data, nullptr)
|
|
||||||
auto skin = skeleton_data->getDefaultSkin();
|
|
||||||
if (skin)
|
|
||||||
return nullptr;
|
|
||||||
Ref<SpineSkin> skin_ref(memnew(SpineSkin));
|
|
||||||
skin_ref->set_spine_object(this, skin);
|
|
||||||
return skin_ref;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::set_default_skin(Ref<SpineSkin> skin) {
|
|
||||||
SPINE_CHECK(skeleton_data, )
|
|
||||||
skeleton_data->setDefaultSkin(skin.is_valid() && skin->get_spine_object()
|
|
||||||
? skin->get_spine_object()
|
|
||||||
: nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_events() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto events = skeleton_data->getEvents();
|
|
||||||
result.resize((int) events.size());
|
|
||||||
for (int i = 0; i < events.size(); ++i) {
|
|
||||||
Ref<SpineEventData> event_ref(memnew(SpineEventData));
|
|
||||||
event_ref->set_spine_object(this, events[i]);
|
|
||||||
result[i] = event_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_animations() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto animations = skeleton_data->getAnimations();
|
|
||||||
result.resize((int) animations.size());
|
|
||||||
for (int i = 0; i < animations.size(); ++i) {
|
|
||||||
Ref<SpineAnimation> animation_ref(memnew(SpineAnimation));
|
|
||||||
animation_ref->set_spine_object(this, animations[i]);
|
|
||||||
result[i] = animation_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_ik_constraints() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto constraints = skeleton_data->getIkConstraints();
|
|
||||||
result.resize((int) constraints.size());
|
|
||||||
for (int i = 0; i < constraints.size(); ++i) {
|
|
||||||
Ref<SpineIkConstraintData> constraint_ref(memnew(SpineIkConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraints[i]);
|
|
||||||
result[i] = constraint_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_transform_constraints() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto constraints = skeleton_data->getTransformConstraints();
|
|
||||||
result.resize((int) constraints.size());
|
|
||||||
for (int i = 0; i < constraints.size(); ++i) {
|
|
||||||
Ref<SpineTransformConstraintData> constraint_ref(
|
|
||||||
memnew(SpineTransformConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraints[i]);
|
|
||||||
result[i] = constraint_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_path_constraints() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto constraints = skeleton_data->getPathConstraints();
|
|
||||||
result.resize((int) constraints.size());
|
|
||||||
for (int i = 0; i < constraints.size(); ++i) {
|
|
||||||
Ref<SpinePathConstraintData> constraint_ref(
|
|
||||||
memnew(SpinePathConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraints[i]);
|
|
||||||
result[i] = constraint_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Array SpineSkeletonDataResource::get_physics_constraints() const {
|
|
||||||
Array result;
|
|
||||||
SPINE_CHECK(skeleton_data, result)
|
|
||||||
auto constraints = skeleton_data->getPhysicsConstraints();
|
|
||||||
result.resize((int) constraints.size());
|
|
||||||
for (int i = 0; i < constraints.size(); ++i) {
|
|
||||||
Ref<SpinePhysicsConstraintData> constraint_ref(
|
|
||||||
memnew(SpinePhysicsConstraintData));
|
|
||||||
constraint_ref->set_spine_object(this, constraints[i]);
|
|
||||||
result[i] = constraint_ref;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
float SpineSkeletonDataResource::get_x() const {
|
|
||||||
SPINE_CHECK(skeleton_data, 0)
|
|
||||||
return skeleton_data->getX();
|
|
||||||
}
|
|
||||||
|
|
||||||
float SpineSkeletonDataResource::get_y() const {
|
|
||||||
SPINE_CHECK(skeleton_data, 0)
|
|
||||||
return skeleton_data->getY();
|
|
||||||
}
|
|
||||||
|
|
||||||
float SpineSkeletonDataResource::get_width() const {
|
|
||||||
SPINE_CHECK(skeleton_data, 0)
|
|
||||||
return skeleton_data->getWidth();
|
|
||||||
}
|
|
||||||
|
|
||||||
float SpineSkeletonDataResource::get_height() const {
|
|
||||||
SPINE_CHECK(skeleton_data, 0)
|
|
||||||
return skeleton_data->getHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
String SpineSkeletonDataResource::get_version() const {
|
|
||||||
SPINE_CHECK(skeleton_data, "")
|
|
||||||
return skeleton_data->getVersion().buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
String SpineSkeletonDataResource::get_hash() const {
|
|
||||||
SPINE_CHECK(skeleton_data, "")
|
|
||||||
return skeleton_data->getHash().buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
String SpineSkeletonDataResource::get_images_path() const {
|
|
||||||
SPINE_CHECK(skeleton_data, "")
|
|
||||||
return skeleton_data->getImagesPath().buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
String SpineSkeletonDataResource::get_audio_path() const {
|
|
||||||
SPINE_CHECK(skeleton_data, "")
|
|
||||||
return skeleton_data->getAudioPath().buffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
float SpineSkeletonDataResource::get_fps() const {
|
|
||||||
SPINE_CHECK(skeleton_data, 0)
|
|
||||||
return skeleton_data->getFps();
|
|
||||||
}
|
|
||||||
|
|
||||||
float SpineSkeletonDataResource::get_reference_scale() const {
|
|
||||||
SPINE_CHECK(skeleton_data, 100);
|
|
||||||
return skeleton_data->getReferenceScale();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SpineSkeletonDataResource::set_reference_scale(float reference_scale) {
|
|
||||||
SPINE_CHECK(skeleton_data, )
|
|
||||||
skeleton_data->setReferenceScale(reference_scale);
|
|
||||||
}
|
|
||||||
@ -1,220 +0,0 @@
|
|||||||
/******************************************************************************
|
|
||||||
* Spine Runtimes License Agreement
|
|
||||||
* Last updated April 5, 2025. Replaces all prior versions.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2013-2025, Esoteric Software LLC
|
|
||||||
*
|
|
||||||
* Integration of the Spine Runtimes into software or otherwise creating
|
|
||||||
* derivative works of the Spine Runtimes is permitted under the terms and
|
|
||||||
* conditions of Section 2 of the Spine Editor License Agreement:
|
|
||||||
* http://esotericsoftware.com/spine-editor-license
|
|
||||||
*
|
|
||||||
* Otherwise, it is permitted to integrate the Spine Runtimes into software
|
|
||||||
* or otherwise create derivative works of the Spine Runtimes (collectively,
|
|
||||||
* "Products"), provided that each user of the Products must obtain their own
|
|
||||||
* Spine Editor license and redistribution of the Products in any form must
|
|
||||||
* include this license and copyright notice.
|
|
||||||
*
|
|
||||||
* THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY
|
|
||||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
* DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY
|
|
||||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES,
|
|
||||||
* BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND
|
|
||||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
*****************************************************************************/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "SpineAnimation.h"
|
|
||||||
#include "SpineAtlasResource.h"
|
|
||||||
#include "SpineBoneData.h"
|
|
||||||
#include "SpineEventData.h"
|
|
||||||
#include "SpineIkConstraintData.h"
|
|
||||||
#include "SpinePathConstraintData.h"
|
|
||||||
#include "SpinePhysicsConstraintData.h"
|
|
||||||
#include "SpineSkeletonFileResource.h"
|
|
||||||
#include "SpineSkin.h"
|
|
||||||
#include "SpineSlotData.h"
|
|
||||||
#include "SpineTransformConstraintData.h"
|
|
||||||
|
|
||||||
class SpineAnimationMix : public Resource {
|
|
||||||
GDCLASS(SpineAnimationMix, Resource)
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static void _bind_methods();
|
|
||||||
|
|
||||||
String from;
|
|
||||||
String to;
|
|
||||||
float mix;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SpineAnimationMix();
|
|
||||||
|
|
||||||
void set_from(const String &from);
|
|
||||||
|
|
||||||
String get_from();
|
|
||||||
|
|
||||||
void set_to(const String &to);
|
|
||||||
|
|
||||||
String get_to();
|
|
||||||
|
|
||||||
void set_mix(float mix);
|
|
||||||
|
|
||||||
float get_mix();
|
|
||||||
};
|
|
||||||
|
|
||||||
class SpineSkeletonDataResource : public Resource {
|
|
||||||
GDCLASS(SpineSkeletonDataResource, Resource)
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static void _bind_methods();
|
|
||||||
|
|
||||||
private:
|
|
||||||
Ref<SpineAtlasResource> atlas_res;
|
|
||||||
Ref<SpineSkeletonFileResource> skeleton_file_res;
|
|
||||||
float default_mix;
|
|
||||||
Array animation_mixes;
|
|
||||||
|
|
||||||
spine::SkeletonData *skeleton_data;
|
|
||||||
spine::AnimationStateData *animation_state_data;
|
|
||||||
|
|
||||||
void update_skeleton_data();
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
void load_resources(spine::Atlas *atlas, const String &json,
|
|
||||||
const PackedByteArray &binary);
|
|
||||||
#else
|
|
||||||
void load_resources(spine::Atlas *atlas, const String &json,
|
|
||||||
const Vector<uint8_t> &binary);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public:
|
|
||||||
SpineSkeletonDataResource();
|
|
||||||
virtual ~SpineSkeletonDataResource();
|
|
||||||
|
|
||||||
bool is_skeleton_data_loaded() const;
|
|
||||||
|
|
||||||
void set_atlas_res(const Ref<SpineAtlasResource> &atlas);
|
|
||||||
Ref<SpineAtlasResource> get_atlas_res();
|
|
||||||
|
|
||||||
void
|
|
||||||
set_skeleton_file_res(const Ref<SpineSkeletonFileResource> &skeleton_file);
|
|
||||||
Ref<SpineSkeletonFileResource> get_skeleton_file_res();
|
|
||||||
|
|
||||||
spine::SkeletonData *get_skeleton_data() const { return skeleton_data; }
|
|
||||||
|
|
||||||
spine::AnimationStateData *get_animation_state_data() const {
|
|
||||||
return animation_state_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
|
||||||
void get_animation_names(PackedStringArray &animation_names) const;
|
|
||||||
|
|
||||||
void get_skin_names(PackedStringArray &l) const;
|
|
||||||
|
|
||||||
void get_slot_names(PackedStringArray &slot_names);
|
|
||||||
|
|
||||||
void get_bone_names(PackedStringArray &bone_names);
|
|
||||||
#else
|
|
||||||
void get_animation_names(Vector<String> &animation_names) const;
|
|
||||||
|
|
||||||
void get_skin_names(Vector<String> &l) const;
|
|
||||||
|
|
||||||
void get_slot_names(Vector<String> &slot_names);
|
|
||||||
|
|
||||||
void get_bone_names(Vector<String> &bone_names);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void set_default_mix(float default_mix);
|
|
||||||
|
|
||||||
float get_default_mix();
|
|
||||||
|
|
||||||
void set_animation_mixes(Array animation_mixes);
|
|
||||||
|
|
||||||
Array get_animation_mixes();
|
|
||||||
|
|
||||||
// Used by SpineEditorPropertyAnimationMix(es) to update the underlying
|
|
||||||
// AnimationState
|
|
||||||
void update_mixes();
|
|
||||||
|
|
||||||
// Spine API
|
|
||||||
Ref<SpineBoneData> find_bone(const String &bone_name) const;
|
|
||||||
|
|
||||||
Ref<SpineSlotData> find_slot(const String &slot_name) const;
|
|
||||||
|
|
||||||
Ref<SpineSkin> find_skin(const String &skin_name) const;
|
|
||||||
|
|
||||||
Ref<SpineEventData> find_event(const String &event_data_name) const;
|
|
||||||
|
|
||||||
Ref<SpineAnimation> find_animation(const String &animation_name) const;
|
|
||||||
|
|
||||||
Ref<SpineIkConstraintData>
|
|
||||||
find_ik_constraint(const String &constraint_name) const;
|
|
||||||
|
|
||||||
Ref<SpineTransformConstraintData>
|
|
||||||
find_transform_constraint(const String &constraint_name) const;
|
|
||||||
|
|
||||||
Ref<SpinePathConstraintData>
|
|
||||||
find_path_constraint(const String &constraint_name) const;
|
|
||||||
|
|
||||||
Ref<SpinePhysicsConstraintData>
|
|
||||||
find_physics_constraint(const String &constraint_name) const;
|
|
||||||
|
|
||||||
String get_skeleton_name() const;
|
|
||||||
|
|
||||||
Array get_bones() const;
|
|
||||||
|
|
||||||
Array get_slots() const;
|
|
||||||
|
|
||||||
Array get_skins() const;
|
|
||||||
|
|
||||||
Ref<SpineSkin> get_default_skin() const;
|
|
||||||
|
|
||||||
void set_default_skin(Ref<SpineSkin> skin);
|
|
||||||
|
|
||||||
Array get_events() const;
|
|
||||||
|
|
||||||
Array get_animations() const;
|
|
||||||
|
|
||||||
Array get_ik_constraints() const;
|
|
||||||
|
|
||||||
Array get_transform_constraints() const;
|
|
||||||
|
|
||||||
Array get_path_constraints() const;
|
|
||||||
|
|
||||||
Array get_physics_constraints() const;
|
|
||||||
|
|
||||||
float get_x() const;
|
|
||||||
|
|
||||||
float get_y() const;
|
|
||||||
|
|
||||||
float get_width() const;
|
|
||||||
|
|
||||||
float get_height() const;
|
|
||||||
|
|
||||||
String get_version() const;
|
|
||||||
|
|
||||||
String get_hash() const;
|
|
||||||
|
|
||||||
String get_images_path() const;
|
|
||||||
|
|
||||||
String get_audio_path() const;
|
|
||||||
|
|
||||||
float get_fps() const;
|
|
||||||
|
|
||||||
float get_reference_scale() const;
|
|
||||||
|
|
||||||
void set_reference_scale(float reference_scale);
|
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
|
||||||
#if VERSION_MAJOR > 3
|
|
||||||
void _on_resources_reimported(const PackedStringArray &resources);
|
|
||||||
#else
|
|
||||||
void _on_resources_reimported(const PoolStringArray &resources);
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
@ -1,4 +1,3 @@
|
|||||||
// this is my version (v1), generated with Claude Code
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Spine Runtimes License Agreement
|
* Spine Runtimes License Agreement
|
||||||
* Last updated April 5, 2025. Replaces all prior versions.
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
@ -256,9 +255,6 @@ SpineSkeletonDataResource::~SpineSkeletonDataResource() {
|
|||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
#if VERSION_MAJOR > 3
|
#if VERSION_MAJOR > 3
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
// Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists.
|
|
||||||
// This avoids the dangling pointer problem during editor shutdown where
|
|
||||||
// EditorFileSystem may be destroyed before SpineSkeletonDataResource objects.
|
|
||||||
EditorFileSystem *efs = Object::cast_to<EditorFileSystem>(ObjectDB::get_instance(editor_file_system_id));
|
EditorFileSystem *efs = Object::cast_to<EditorFileSystem>(ObjectDB::get_instance(editor_file_system_id));
|
||||||
if (efs && efs->is_connected("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported))) {
|
if (efs && efs->is_connected("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported))) {
|
||||||
efs->disconnect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported));
|
efs->disconnect("resources_reimported", callable_mp(this, &SpineSkeletonDataResource::_on_resources_reimported));
|
||||||
@ -266,7 +262,6 @@ SpineSkeletonDataResource::~SpineSkeletonDataResource() {
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
if (Engine::get_singleton()->is_editor_hint()) {
|
if (Engine::get_singleton()->is_editor_hint()) {
|
||||||
// Use ObjectDB::get_instance() to safely check if EditorFileSystem still exists.
|
|
||||||
EditorFileSystem *efs = Object::cast_to<EditorFileSystem>(ObjectDB::get_instance(editor_file_system_id));
|
EditorFileSystem *efs = Object::cast_to<EditorFileSystem>(ObjectDB::get_instance(editor_file_system_id));
|
||||||
if (efs && efs->is_connected("resources_reimported", this, "_on_resources_reimported")) {
|
if (efs && efs->is_connected("resources_reimported", this, "_on_resources_reimported")) {
|
||||||
efs->disconnect("resources_reimported", this, "_on_resources_reimported");
|
efs->disconnect("resources_reimported", this, "_on_resources_reimported");
|
||||||
|
|||||||
@ -1,4 +1,3 @@
|
|||||||
// this is my version (v1), generated with Claude Code
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
* Spine Runtimes License Agreement
|
* Spine Runtimes License Agreement
|
||||||
* Last updated April 5, 2025. Replaces all prior versions.
|
* Last updated April 5, 2025. Replaces all prior versions.
|
||||||
@ -84,9 +83,6 @@ private:
|
|||||||
spine::AnimationStateData *animation_state_data;
|
spine::AnimationStateData *animation_state_data;
|
||||||
|
|
||||||
#ifdef TOOLS_ENABLED
|
#ifdef TOOLS_ENABLED
|
||||||
// Store the ObjectID of EditorFileSystem to safely validate it in destructor.
|
|
||||||
// Raw pointers to singletons can become dangling during editor shutdown,
|
|
||||||
// but ObjectID can be safely validated via ObjectDB::get_instance().
|
|
||||||
ObjectID editor_file_system_id;
|
ObjectID editor_file_system_id;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@ -45,7 +45,9 @@ void SpineSkin::_bind_methods() {
|
|||||||
ClassDB::bind_method(D_METHOD("get_attachments"), &SpineSkin::get_attachments);
|
ClassDB::bind_method(D_METHOD("get_attachments"), &SpineSkin::get_attachments);
|
||||||
ClassDB::bind_method(D_METHOD("get_bones"), &SpineSkin::get_bones);
|
ClassDB::bind_method(D_METHOD("get_bones"), &SpineSkin::get_bones);
|
||||||
ClassDB::bind_method(D_METHOD("get_constraints"), &SpineSkin::get_constraints);
|
ClassDB::bind_method(D_METHOD("get_constraints"), &SpineSkin::get_constraints);
|
||||||
|
#if VERSION_MAJOR >= 4
|
||||||
ClassDB::bind_method(D_METHOD("init", "name", "sprite"), &SpineSkin::init);
|
ClassDB::bind_method(D_METHOD("init", "name", "sprite"), &SpineSkin::init);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
SpineSkin::SpineSkin() : owns_skin(false) {
|
SpineSkin::SpineSkin() : owns_skin(false) {
|
||||||
|
|||||||
@ -116,12 +116,12 @@ void register_spine_godot_types() {
|
|||||||
#endif
|
#endif
|
||||||
spine::Bone::setYDown(true);
|
spine::Bone::setYDown(true);
|
||||||
|
|
||||||
#ifndef SPINE_GODOT_EXTENSION
|
#ifndef SPINE_GODOT_EXTENSION
|
||||||
GDREGISTER_CLASS(SpineAtlasResourceFormatLoader);
|
GDREGISTER_CLASS(SpineAtlasResourceFormatLoader);
|
||||||
GDREGISTER_CLASS(SpineAtlasResourceFormatSaver);
|
GDREGISTER_CLASS(SpineAtlasResourceFormatSaver);
|
||||||
GDREGISTER_CLASS(SpineSkeletonFileResourceFormatLoader);
|
GDREGISTER_CLASS(SpineSkeletonFileResourceFormatLoader);
|
||||||
GDREGISTER_CLASS(SpineSkeletonFileResourceFormatSaver);
|
GDREGISTER_CLASS(SpineSkeletonFileResourceFormatSaver);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
GDREGISTER_CLASS(SpineObjectWrapper);
|
GDREGISTER_CLASS(SpineObjectWrapper);
|
||||||
GDREGISTER_CLASS(SpineAtlasResource);
|
GDREGISTER_CLASS(SpineAtlasResource);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user