spine-runtimes/spine-godot/SpineSkeletonDataResource_old.cpp
Luke Ingram b6d76309f3 [godot] Addresses #2899, #2980, #2985
Addresses the loader/saver registration issue in #2899.

Additionally, makes it such that you can adjust the SpineSkeletonData in
the inspector without crashes. This appears to have been caused by a
dangling pointer.

Finally, double-clicking on JSON in the inspector opens the text editor
and does not crash the Godot editor.
2025-12-11 23:43:40 -04:00

854 lines
28 KiB
C++

/******************************************************************************
* 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);
}