diff --git a/.github/workflows/spine-godot.yml b/.github/workflows/spine-godot.yml index f0b0f746b..78a304dbc 100644 --- a/.github/workflows/spine-godot.yml +++ b/.github/workflows/spine-godot.yml @@ -33,7 +33,7 @@ jobs: ./spine-godot/build/build.sh release_debug - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-editor-windows.zip path: spine-godot/godot/bin/godot.windows.opt.tools.64.exe @@ -57,7 +57,7 @@ jobs: ./spine-godot/build/build.sh release_debug - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-editor-linux.zip path: spine-godot/godot/bin/godot.x11.opt.tools.64 @@ -82,7 +82,7 @@ jobs: popd - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-editor-macos.zip path: spine-godot/godot/bin/godot-editor-macos.zip @@ -103,7 +103,7 @@ jobs: ./spine-godot/build/build-templates.sh ios - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-ios.zip path: spine-godot/godot/bin/iphone.zip @@ -124,7 +124,7 @@ jobs: ./spine-godot/build/build-templates.sh macos - name: Upload artifacts - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-macos.zip path: spine-godot/godot/bin/osx.zip @@ -147,13 +147,13 @@ jobs: ./spine-godot/build/build-templates.sh linux - name: Upload artifacts debug - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-linux-debug.zip path: spine-godot/godot/bin/linux_x11_64_debug - name: Upload artifacts release - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-linux-release.zip path: spine-godot/godot/bin/linux_x11_64_release @@ -175,13 +175,13 @@ jobs: ./spine-godot/build/build-templates.sh windows - name: Upload artifacts debug - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-windows-debug.zip path: spine-godot/godot/bin/windows_64_debug.exe - name: Upload artifacts release - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-windows-release.zip path: spine-godot/godot/bin/windows_64_release.exe @@ -215,19 +215,19 @@ jobs: ./spine-godot/build/build-templates.sh android - name: Upload artifacts debug - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-android-debug.zip path: spine-godot/godot/bin/android_debug.apk - name: Upload artifacts release - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-android-release.zip path: spine-godot/godot/bin/android_release.apk - name: Upload artifacts source - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-android-source.zip path: spine-godot/godot/bin/android_source.zip @@ -257,13 +257,13 @@ jobs: ./spine-godot/build/build-templates.sh web - name: Upload artifacts debug - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-web-debug.zip path: spine-godot/godot/bin/webassembly_debug.zip - name: Upload artifacts release - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: godot-template-web-release.zip path: spine-godot/godot/bin/webassembly_release.zip diff --git a/spine-cpp/spine-cpp/include/spine/SpineString.h b/spine-cpp/spine-cpp/include/spine/SpineString.h index a11a77f8c..9036f7f57 100644 --- a/spine-cpp/spine-cpp/include/spine/SpineString.h +++ b/spine-cpp/spine-cpp/include/spine/SpineString.h @@ -179,7 +179,7 @@ namespace spine { return *this; } - bool startsWith(const String &needle) { + bool startsWith(const String &needle) const { if (needle.length() > length()) return false; for (int i = 0; i < (int)needle.length(); i++) { if (buffer()[i] != needle.buffer()[i]) return false; @@ -187,7 +187,7 @@ namespace spine { return true; } - int lastIndexOf(const char c) { + int lastIndexOf(const char c) const { for (int i = (int)length() - 1; i >= 0; i--) { if (buffer()[i] == c) return i; } diff --git a/spine-cpp/spine-cpp/src/spine/InheritTimeline.cpp b/spine-cpp/spine-cpp/src/spine/InheritTimeline.cpp index f3326d04c..f969ffc32 100644 --- a/spine-cpp/spine-cpp/src/spine/InheritTimeline.cpp +++ b/spine-cpp/spine-cpp/src/spine/InheritTimeline.cpp @@ -77,5 +77,5 @@ void InheritTimeline::apply(Skeleton &skeleton, float lastTime, float time, Vect return; } int idx = Animation::search(_frames, time, ENTRIES) + INHERIT; - bone->_inherit = (Inherit) _frames[idx]; + bone->_inherit = static_cast(_frames[idx]); } diff --git a/spine-godot/.vscode/settings.json b/spine-godot/.vscode/settings.json index 75a3d9303..30280d3e9 100644 --- a/spine-godot/.vscode/settings.json +++ b/spine-godot/.vscode/settings.json @@ -1,5 +1,5 @@ { "cmake.configureOnOpen": false, - "C_Cpp.intelliSenseEngine": "disabled", + "C_Cpp.intelliSenseEngine": "default", "dotnet.defaultSolution": "disable" } diff --git a/spine-godot/example-v4/examples/13-load-from-disk/load-from-disk.tscn b/spine-godot/example-v4/examples/13-load-from-disk/load-from-disk.tscn new file mode 100644 index 000000000..d67b0155d --- /dev/null +++ b/spine-godot/example-v4/examples/13-load-from-disk/load-from-disk.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://dr1u7vj8mm6ed"] + +[ext_resource type="Script" path="res://examples/13-load-from-disk/load_from_disk.gd" id="1_krs2n"] + +[node name="Load-from-disk" type="Node2D"] +script = ExtResource("1_krs2n") diff --git a/spine-godot/example-v4/examples/13-load-from-disk/load_from_disk.gd b/spine-godot/example-v4/examples/13-load-from-disk/load_from_disk.gd new file mode 100644 index 000000000..f56a0319f --- /dev/null +++ b/spine-godot/example-v4/examples/13-load-from-disk/load_from_disk.gd @@ -0,0 +1,24 @@ +extends Node2D + +func _ready(): + # Load the skeleton file + var skeleton_file_res = SpineSkeletonFileResource.new(); + skeleton_file_res.load_from_file("/Users/badlogic/workspaces/spine-runtimes/examples/coin/export/coin-pro.skel"); + + # Load the atlas file + var atlas_res = SpineAtlasResource.new(); + atlas_res.load_from_atlas_file("/Users/badlogic/workspaces/spine-runtimes/examples/coin/export/coin.atlas"); + + # Create a skeleton data resource, you can share this across multiple sprites + var skeleton_data_res = SpineSkeletonDataResource.new(); + skeleton_data_res.skeleton_file_res = skeleton_file_res; + skeleton_data_res.atlas_res = atlas_res + + # Create a sprite from the skeleton data and add it as a child + var sprite = SpineSprite.new(); + sprite.skeleton_data_res = skeleton_data_res; + sprite.position.x = 200; + sprite.position.y = 200; + sprite.get_animation_state().set_animation("animation", true, 0); + self.add_child(sprite) + pass diff --git a/spine-godot/spine_godot/SpineAnimationTrack.cpp b/spine-godot/spine_godot/SpineAnimationTrack.cpp index da0b80c5a..ff22de00a 100644 --- a/spine-godot/spine_godot/SpineAnimationTrack.cpp +++ b/spine-godot/spine_godot/SpineAnimationTrack.cpp @@ -37,7 +37,7 @@ #include "scene/resources/animation.h" #ifdef TOOLS_ENABLED -#include "godot/editor/editor_node.h" +#include "editor/editor_node.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/animation_tree_editor_plugin.h" #endif diff --git a/spine-godot/spine_godot/SpineAtlasResource.cpp b/spine-godot/spine_godot/SpineAtlasResource.cpp index 956c230fa..1531a8978 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.cpp +++ b/spine-godot/spine_godot/SpineAtlasResource.cpp @@ -30,45 +30,106 @@ #include "SpineAtlasResource.h" #include "SpineRendererObject.h" #include "core/io/json.h" +#include "core/io/image.h" +#include "scene/resources/image_texture.h" #include "scene/resources/texture.h" #include +#ifdef TOOLS_ENABLED +#include "editor/editor_file_system.h" +#endif + class GodotSpineTextureLoader : public spine::TextureLoader { Array *textures; Array *normal_maps; String normal_map_prefix; + bool is_importing; public: - GodotSpineTextureLoader(Array *_textures, Array *_normal_maps, const String &normal_map_prefix) : textures(_textures), normal_maps(_normal_maps), normal_map_prefix(normal_map_prefix) { + GodotSpineTextureLoader(Array *_textures, Array *_normal_maps, const String &normal_map_prefix, bool is_importing) : textures(_textures), normal_maps(_normal_maps), normal_map_prefix(normal_map_prefix), is_importing(is_importing) { } - static String fix_path(const String &path) { - if (path.size() > 5 && path[4] == '/' && path[5] == '/') return path; + static bool fix_path(String &path) { const String prefix = "res:/"; auto i = path.find(prefix); - auto sub_str_pos = i + prefix.size() - 1; - if (sub_str_pos < 0) return path; - auto res = path.substr(sub_str_pos); + if (i == std::string::npos) { + return false; + } + auto sub_str_pos = i + prefix.size() - 1; + auto res = path.substr(sub_str_pos); if (!EMPTY(res)) { if (res[0] != '/') { - return prefix + "/" + res; + path = prefix + "/" + res; } else { - return prefix + res; + path = prefix + res; } } - return path; + return true; + } + +#if VERSION_MAJOR > 3 + Ref get_texture_from_image(const String &path, bool is_resource) { + Error error = OK; + if (is_resource) { + return ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error); + } else { + Ref img; + img.instantiate(); + img = img->load_from_file(path); + return ImageTexture::create_from_image(img); + } + } +#else + Ref get_texture_from_image(const String &path, bool is_resource) { + Error error = OK; + if (is_resource) { + return ResourceLoader::load(path, "", false, &error); + } else { + Vector buf = FileAccess::get_file_as_array(path, &error); + if (error == OK) { + Ref img; + img.instantiate(); + String filename = path.get_filename().to_lower(); + if (filename.ends_with(".png")) { + img->load_png_from_buffer(buf); + } else if (filename_lower.ends_with(".jpg")) { + img->load_jpg_from_buffer(buf); + } + return ImageTexture::create_from_image(img); + } + } + return Ref(); + } +#endif + + void import_image_resource(const String &path) { +#ifdef VERSION_MAJOR> 4 +#ifdef TOOLS_ENABLED + // Required when importing into editor by e.g. drag & drop. The .png files + // of the atlas might not have been imported yet. + // See https://github.com/EsotericSoftware/spine-runtimes/issues/2385 + if (is_importing) { + HashMap custom_options; + Dictionary generator_parameters; + EditorFileSystem::get_singleton()->reimport_append(path, custom_options, "", generator_parameters); + } +#endif +#endif } void load(spine::AtlasPage &page, const spine::String &path) override { Error error = OK; - auto fixed_path = fix_path(String(path.buffer())); + String fixed_path = String(path.buffer()); + bool is_resource = fix_path(fixed_path); + + import_image_resource(fixed_path); #if VERSION_MAJOR > 3 - Ref texture = ResourceLoader::load(fixed_path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error); + Ref texture = get_texture_from_image(fixed_path, is_resource); #else - Ref texture = ResourceLoader::load(fixed_path, "", false, &error); + Ref texture = get_texture_from_image(fixed_path, is_resource); #endif if (error != OK || !texture.is_valid()) { ERR_PRINT(vformat("Can't load texture: \"%s\"", String(path.buffer()))); @@ -86,7 +147,8 @@ public: String new_path = vformat("%s/%s_%s", fixed_path.get_base_dir(), normal_map_prefix, fixed_path.get_file()); if (ResourceLoader::exists(new_path)) { - Ref normal_map = ResourceLoader::load(new_path); + import_image_resource(new_path); + Ref normal_map = get_texture_from_image(new_path, is_resource); normal_maps->append(normal_map); renderer_object->normal_map = normal_map; } @@ -156,13 +218,17 @@ String SpineAtlasResource::get_source_path() { } Error SpineAtlasResource::load_from_atlas_file(const String &path) { + load_from_atlas_file_internal(path, false); +} + +Error SpineAtlasResource::load_from_atlas_file_internal(const String &path, bool is_importing) { Error err; source_path = path; atlas_data = FileAccess::get_file_as_string(path, &err); if (err != OK) return err; clear(); - texture_loader = new GodotSpineTextureLoader(&textures, &normal_maps, normal_map_prefix); + texture_loader = new GodotSpineTextureLoader(&textures, &normal_maps, normal_map_prefix, is_importing); auto atlas_utf8 = atlas_data.utf8(); atlas = new spine::Atlas(atlas_utf8, atlas_utf8.length(), source_path.get_base_dir().utf8(), texture_loader); if (atlas) return OK; @@ -195,7 +261,7 @@ Error SpineAtlasResource::load_from_file(const String &path) { normal_map_prefix = content["normal_texture_prefix"]; clear(); - texture_loader = new GodotSpineTextureLoader(&textures, &normal_maps, normal_map_prefix); + texture_loader = new GodotSpineTextureLoader(&textures, &normal_maps, normal_map_prefix, false); auto utf8 = atlas_data.utf8(); atlas = new spine::Atlas(utf8.ptr(), utf8.size(), source_path.get_base_dir().utf8(), texture_loader); if (atlas) return OK; diff --git a/spine-godot/spine_godot/SpineAtlasResource.h b/spine-godot/spine_godot/SpineAtlasResource.h index 7c5bcfa6e..fb1003f39 100644 --- a/spine-godot/spine_godot/SpineAtlasResource.h +++ b/spine-godot/spine_godot/SpineAtlasResource.h @@ -65,6 +65,8 @@ public: Error load_from_atlas_file(const String &path);// .atlas + Error load_from_atlas_file_internal(const String &path, bool is_importing);// .atlas + Error load_from_file(const String &path);// .spatlas Error save_to_file(const String &path);// .spatlas diff --git a/spine-godot/spine_godot/SpineEditorPlugin.cpp b/spine-godot/spine_godot/SpineEditorPlugin.cpp index 3fa4fbd1a..446709e66 100644 --- a/spine-godot/spine_godot/SpineEditorPlugin.cpp +++ b/spine-godot/spine_godot/SpineEditorPlugin.cpp @@ -26,6 +26,7 @@ * (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. *****************************************************************************/ +#define VERSION_MAJOR 4 #ifdef TOOLS_ENABLED #include "SpineEditorPlugin.h" @@ -40,7 +41,7 @@ Error SpineAtlasResourceImportPlugin::import(const String &source_file, const St #endif Ref atlas(memnew(SpineAtlasResource)); atlas->set_normal_texture_prefix(options["normal_map_prefix"]); - atlas->load_from_atlas_file(source_file); + atlas->load_from_atlas_file_internal(source_file, true); String file_name = vformat("%s.%s", save_path, get_save_extension()); #if VERSION_MAJOR > 3 diff --git a/spine-godot/spine_godot/SpineSkeleton.cpp b/spine-godot/spine_godot/SpineSkeleton.cpp index cf709b930..4e5445a57 100644 --- a/spine-godot/spine_godot/SpineSkeleton.cpp +++ b/spine-godot/spine_godot/SpineSkeleton.cpp @@ -47,6 +47,7 @@ void SpineSkeleton::_bind_methods() { ClassDB::bind_method(D_METHOD("find_ik_constraint", "constraint_name"), &SpineSkeleton::find_ik_constraint); ClassDB::bind_method(D_METHOD("find_transform_constraint", "constraint_name"), &SpineSkeleton::find_transform_constraint); ClassDB::bind_method(D_METHOD("find_path_constraint", "constraint_name"), &SpineSkeleton::find_path_constraint); + ClassDB::bind_method(D_METHOD("find_physics_constraint", "constraint_name"), &SpineSkeleton::find_physics_constraint); ClassDB::bind_method(D_METHOD("get_bounds"), &SpineSkeleton::get_bounds); ClassDB::bind_method(D_METHOD("get_root_bone"), &SpineSkeleton::get_root_bone); ClassDB::bind_method(D_METHOD("get_data"), &SpineSkeleton::get_skeleton_data_res); diff --git a/spine-godot/spine_godot/SpineSkeletonDataResource.cpp b/spine-godot/spine_godot/SpineSkeletonDataResource.cpp index 41b10d689..bf51b1f9b 100644 --- a/spine-godot/spine_godot/SpineSkeletonDataResource.cpp +++ b/spine-godot/spine_godot/SpineSkeletonDataResource.cpp @@ -101,6 +101,8 @@ void SpineSkeletonDataResource::_bind_methods() { &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"), @@ -123,6 +125,8 @@ void SpineSkeletonDataResource::_bind_methods() { &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"), diff --git a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp index b9ae39a70..ae646dfc1 100644 --- a/spine-godot/spine_godot/SpineSkeletonFileResource.cpp +++ b/spine-godot/spine_godot/SpineSkeletonFileResource.cpp @@ -89,6 +89,7 @@ static char *readString(BinaryInput *input) { } void SpineSkeletonFileResource::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_from_file", "path"), &SpineSkeletonFileResource::load_from_file); ADD_SIGNAL(MethodInfo("skeleton_file_changed")); } diff --git a/spine-godot/spine_godot/docs/SpineSkeletonFileResource.xml b/spine-godot/spine_godot/docs/SpineSkeletonFileResource.xml index cbb0d9b3e..378c54208 100644 --- a/spine-godot/spine_godot/docs/SpineSkeletonFileResource.xml +++ b/spine-godot/spine_godot/docs/SpineSkeletonFileResource.xml @@ -9,6 +9,12 @@ + + + + + + diff --git a/spine-godot/spine_godot/icons/SpineAnimationTrack.svg b/spine-godot/spine_godot/icons/SpineAnimationTrack.svg new file mode 100644 index 000000000..705bfb161 --- /dev/null +++ b/spine-godot/spine_godot/icons/SpineAnimationTrack.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spine-godot/spine_godot/icons/SpineAtlasResource.svg b/spine-godot/spine_godot/icons/SpineAtlasResource.svg new file mode 100644 index 000000000..705bfb161 --- /dev/null +++ b/spine-godot/spine_godot/icons/SpineAtlasResource.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spine-godot/spine_godot/icons/SpineBoneNode.svg b/spine-godot/spine_godot/icons/SpineBoneNode.svg new file mode 100644 index 000000000..705bfb161 --- /dev/null +++ b/spine-godot/spine_godot/icons/SpineBoneNode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spine-godot/spine_godot/icons/SpineSkeletonDataResource.svg b/spine-godot/spine_godot/icons/SpineSkeletonDataResource.svg new file mode 100644 index 000000000..705bfb161 --- /dev/null +++ b/spine-godot/spine_godot/icons/SpineSkeletonDataResource.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spine-godot/spine_godot/icons/SpineSkeletonFileResource.svg b/spine-godot/spine_godot/icons/SpineSkeletonFileResource.svg new file mode 100644 index 000000000..705bfb161 --- /dev/null +++ b/spine-godot/spine_godot/icons/SpineSkeletonFileResource.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spine-godot/spine_godot/icons/SpineSlotNode.svg b/spine-godot/spine_godot/icons/SpineSlotNode.svg new file mode 100644 index 000000000..705bfb161 --- /dev/null +++ b/spine-godot/spine_godot/icons/SpineSlotNode.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spine-godot/spine_godot/icons/SpineSprite.svg b/spine-godot/spine_godot/icons/SpineSprite.svg new file mode 100644 index 000000000..705bfb161 --- /dev/null +++ b/spine-godot/spine_godot/icons/SpineSprite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/spine-godot/spine_godot/register_types.cpp b/spine-godot/spine_godot/register_types.cpp index 581ba4f39..549eef7c9 100644 --- a/spine-godot/spine_godot/register_types.cpp +++ b/spine-godot/spine_godot/register_types.cpp @@ -48,6 +48,7 @@ #include "SpineIkConstraintData.h" #include "SpineTransformConstraintData.h" #include "SpinePathConstraintData.h" +#include "SpinePhysicsConstraintData.h" #include "SpineTimeline.h" #include "SpineConstant.h" #include "SpineSlotNode.h" @@ -109,11 +110,13 @@ void register_spine_godot_types() { GDREGISTER_CLASS(SpineIkConstraintData); GDREGISTER_CLASS(SpineTransformConstraintData); GDREGISTER_CLASS(SpinePathConstraintData); + GDREGISTER_CLASS(SpinePhysicsConstraintData); GDREGISTER_CLASS(SpineBone); GDREGISTER_CLASS(SpineSlot); GDREGISTER_CLASS(SpineIkConstraint); GDREGISTER_CLASS(SpinePathConstraint); GDREGISTER_CLASS(SpineTransformConstraint); + GDREGISTER_CLASS(SpinePhysicsConstraint); GDREGISTER_CLASS(SpineTimeline); GDREGISTER_CLASS(SpineConstant);