Browse Source

[godot] More GDExtension work. SpineAnimationTrack, animation mixes editor plugin and SpineMesh2D are non-functional due to missing APIs in godot-cpp

Mario Zechner 11 months ago
parent
commit
e9b43f9c73
100 changed files with 5672 additions and 38 deletions
  1. 7 0
      .gitignore
  2. 16 1
      spine-godot/.vscode/settings.json
  3. 99 0
      spine-godot/SConstruct
  4. 8 0
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus-data.tres
  5. 173 0
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.atlas
  6. 15 0
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.atlas.import
  7. BIN
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.png
  8. 34 0
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.png.import
  9. BIN
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.skel
  10. 14 0
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.skel.import
  11. 34 0
      spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus_2.png.import
  12. BIN
      spine-godot/example-v4-extension/assets/footstep.ogg
  13. 19 0
      spine-godot/example-v4-extension/assets/footstep.ogg.import
  14. 10 0
      spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match-data.tres
  15. 1487 0
      spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match-pro.spine-json
  16. 14 0
      spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match-pro.spine-json.import
  17. 358 0
      spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.atlas
  18. 15 0
      spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.atlas.import
  19. BIN
      spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.png
  20. 34 0
      spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.png.import
  21. 15 0
      spine-godot/example-v4-extension/assets/raggedyspineboy/Raggedy Spineboy.atlas.import
  22. 34 0
      spine-godot/example-v4-extension/assets/raggedyspineboy/Raggedy Spineboy.png.import
  23. 8 0
      spine-godot/example-v4-extension/assets/raggedyspineboy/raggedy spineboy-res.tres
  24. 14 0
      spine-godot/example-v4-extension/assets/raggedyspineboy/raggedy spineboy.spine-json.import
  25. BIN
      spine-godot/example-v4-extension/assets/raptor/light-sprite.png
  26. 34 0
      spine-godot/example-v4-extension/assets/raptor/light-sprite.png.import
  27. BIN
      spine-godot/example-v4-extension/assets/raptor/n_raptor.png
  28. 34 0
      spine-godot/example-v4-extension/assets/raptor/n_raptor.png.import
  29. 9 0
      spine-godot/example-v4-extension/assets/raptor/raptor-data.tres
  30. BIN
      spine-godot/example-v4-extension/assets/raptor/raptor-pro.skel
  31. 14 0
      spine-godot/example-v4-extension/assets/raptor/raptor-pro.skel.import
  32. 93 0
      spine-godot/example-v4-extension/assets/raptor/raptor.atlas
  33. 15 0
      spine-godot/example-v4-extension/assets/raptor/raptor.atlas.import
  34. BIN
      spine-godot/example-v4-extension/assets/raptor/raptor.png
  35. 34 0
      spine-godot/example-v4-extension/assets/raptor/raptor.png.import
  36. 20 0
      spine-godot/example-v4-extension/assets/spineboy/spineboy-data-res.tres
  37. BIN
      spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.skel
  38. 14 0
      spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.skel.import
  39. 557 0
      spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.spine-json
  40. 14 0
      spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.spine-json.import
  41. 94 0
      spine-godot/example-v4-extension/assets/spineboy/spineboy.atlas
  42. 15 0
      spine-godot/example-v4-extension/assets/spineboy/spineboy.atlas.import
  43. BIN
      spine-godot/example-v4-extension/assets/spineboy/spineboy.png
  44. 34 0
      spine-godot/example-v4-extension/assets/spineboy/spineboy.png.import
  45. BIN
      spine-godot/example-v4-extension/bin/macos/macos.framework/libspine_godot.macos.editor
  46. BIN
      spine-godot/example-v4-extension/bin/macos/macos.framework/libspine_godot.macos.template_debug
  47. 25 0
      spine-godot/example-v4-extension/bin/spine_godot_extension.gdextension
  48. 7 0
      spine-godot/example-v4-extension/default_env.tres
  49. 19 0
      spine-godot/example-v4-extension/examples/01-helloworld/helloworld.tscn
  50. 4 0
      spine-godot/example-v4-extension/examples/01-helloworld/spineboy-helloworld.gd
  51. 39 0
      spine-godot/example-v4-extension/examples/02-animation-state-listeners/animation-state-listeners.gd
  52. 20 0
      spine-godot/example-v4-extension/examples/02-animation-state-listeners/animation-state-listeners.tscn
  53. 22 0
      spine-godot/example-v4-extension/examples/03-mix-and-match/mix-and-match.gd
  54. 16 0
      spine-godot/example-v4-extension/examples/03-mix-and-match/mix-and-match.tscn
  55. 16 0
      spine-godot/example-v4-extension/examples/04-simple-input/simple-input.tscn
  56. 19 0
      spine-godot/example-v4-extension/examples/04-simple-input/spineboy-simple-input.gd
  57. 11 0
      spine-godot/example-v4-extension/examples/05-mouse-following/mouse-following.gd
  58. 23 0
      spine-godot/example-v4-extension/examples/05-mouse-following/mouse-following.tscn
  59. 5 0
      spine-godot/example-v4-extension/examples/06-bone-following/bone-following.gd
  60. 27 0
      spine-godot/example-v4-extension/examples/06-bone-following/bone-following.tscn
  61. 11 0
      spine-godot/example-v4-extension/examples/07-slot-node/slot-node.gd
  62. 61 0
      spine-godot/example-v4-extension/examples/07-slot-node/slot-node.tscn
  63. 45 0
      spine-godot/example-v4-extension/examples/09-custom-material/custom-material.tscn
  64. 4 0
      spine-godot/example-v4-extension/examples/10-2d-lighting/2d-lighting.gd
  65. 27 0
      spine-godot/example-v4-extension/examples/10-2d-lighting/2d-lighting.tscn
  66. 26 0
      spine-godot/example-v4-extension/examples/11-bone-node/bone-node.gd
  67. 53 0
      spine-godot/example-v4-extension/examples/11-bone-node/bone-node.tscn
  68. 32 0
      spine-godot/example-v4-extension/examples/12-physics/physics.gd
  69. 31 0
      spine-godot/example-v4-extension/examples/12-physics/physics.tscn
  70. 6 0
      spine-godot/example-v4-extension/examples/13-load-from-disk/load-from-disk.tscn
  71. 24 0
      spine-godot/example-v4-extension/examples/13-load-from-disk/load_from_disk.gd
  72. BIN
      spine-godot/example-v4-extension/icon.png
  73. 34 0
      spine-godot/example-v4-extension/icon.png.import
  74. 35 0
      spine-godot/example-v4-extension/project.godot
  75. 4 0
      spine-godot/example-v4-extension/tests/batch-test.gd
  76. 871 0
      spine-godot/example-v4-extension/tests/batch-test.tscn
  77. 53 0
      spine-godot/example-v4-extension/tests/ragdoll.tscn
  78. 26 0
      spine-godot/example-v4-extension/tests/transforms.tscn
  79. 39 0
      spine-godot/example-v4-extension/tests/unit-tests.gd
  80. 12 0
      spine-godot/example-v4-extension/tests/unit-tests.tscn
  81. 25 0
      spine-godot/gdextension.gd
  82. 52 0
      spine-godot/methods.py
  83. 4 0
      spine-godot/spine_godot/SpineAnimation.cpp
  84. 4 0
      spine-godot/spine_godot/SpineAnimationTrack.cpp
  85. 3 5
      spine-godot/spine_godot/SpineAnimationTrack.h
  86. 75 7
      spine-godot/spine_godot/SpineAtlasResource.cpp
  87. 2 0
      spine-godot/spine_godot/SpineAtlasResource.h
  88. 16 3
      spine-godot/spine_godot/SpineBoneNode.cpp
  89. 4 0
      spine-godot/spine_godot/SpineBoneNode.h
  90. 4 0
      spine-godot/spine_godot/SpineCommon.h
  91. 70 3
      spine-godot/spine_godot/SpineEditorPlugin.cpp
  92. 120 1
      spine-godot/spine_godot/SpineEditorPlugin.h
  93. 28 0
      spine-godot/spine_godot/SpineSkeletonDataResource.cpp
  94. 15 0
      spine-godot/spine_godot/SpineSkeletonDataResource.h
  95. 62 3
      spine-godot/spine_godot/SpineSkeletonFileResource.cpp
  96. 10 0
      spine-godot/spine_godot/SpineSkeletonFileResource.h
  97. 15 3
      spine-godot/spine_godot/SpineSlotNode.cpp
  98. 4 0
      spine-godot/spine_godot/SpineSlotNode.h
  99. 103 10
      spine-godot/spine_godot/SpineSprite.cpp
  100. 20 2
      spine-godot/spine_godot/SpineSprite.h

+ 7 - 0
.gitignore

@@ -205,3 +205,10 @@ spine-sdl/.vs
 spine-ts/spine-canvaskit/dist
 spine-ts/output.png
 spine-godot/godot-cpp
+spine-godot/__pycache__
+*.os
+spine-godot/.sconsign.dblite
+spine-godot/example-v4-extension/.godot
+spine-godot/src
+spine-godot/compile_commands.json
+spine-godot/bin

+ 16 - 1
spine-godot/.vscode/settings.json

@@ -1,5 +1,20 @@
 {
   "cmake.configureOnOpen": false,
   "C_Cpp.intelliSenseEngine": "default",
-  "dotnet.defaultSolution": "disable"
+  "dotnet.defaultSolution": "disable",
+  "files.associations": {
+    "__bit_reference": "cpp",
+    "__hash_table": "cpp",
+    "__tree": "cpp",
+    "map": "cpp",
+    "set": "cpp",
+    "string": "cpp",
+    "string_view": "cpp",
+    "unordered_map": "cpp",
+    "unordered_set": "cpp",
+    "vector": "cpp",
+    "__split_buffer": "cpp",
+    "deque": "cpp",
+    "list": "cpp"
+  }
 }

+ 99 - 0
spine-godot/SConstruct

@@ -0,0 +1,99 @@
+#!/usr/bin/env python
+import os
+import sys
+
+from methods import print_error
+
+
+def normalize_path(val, env):
+    return val if os.path.isabs(val) else os.path.join(env.Dir("#").abspath, val)
+
+
+def validate_parent_dir(key, val, env):
+    if not os.path.isdir(normalize_path(os.path.dirname(val), env)):
+        raise UserError("'%s' is not a directory: %s" % (key, os.path.dirname(val)))
+
+
+libname = "spine_godot"
+projectdir = "example-v4-extension"
+
+localEnv = Environment(tools=["default"], PLATFORM="")
+
+customs = ["custom.py"]
+customs = [os.path.abspath(path) for path in customs]
+
+opts = Variables(customs, ARGUMENTS)
+opts.Add(
+    BoolVariable(
+        key="compiledb",
+        help="Generate compilation DB (`compile_commands.json`) for external tools",
+        default=localEnv.get("compiledb", False),
+    )
+)
+opts.Add(
+    PathVariable(
+        key="compiledb_file",
+        help="Path to a custom `compile_commands.json` file",
+        default=localEnv.get("compiledb_file", "compile_commands.json"),
+        validator=validate_parent_dir,
+    )
+)
+opts.Update(localEnv)
+
+Help(opts.GenerateHelpText(localEnv))
+
+env = localEnv.Clone()
+env["compiledb"] = False
+
+env.Tool("compilation_db")
+compilation_db = env.CompilationDatabase(
+    normalize_path(localEnv["compiledb_file"], localEnv)
+)
+env.Alias("compiledb", compilation_db)
+
+submodule_initialized = False
+dir_name = 'godot-cpp'
+if os.path.isdir(dir_name):
+    if os.listdir(dir_name):
+        submodule_initialized = True
+
+if not submodule_initialized:
+    print_error("""godot-cpp is not available within this folder, as Git submodules haven't been initialized.
+Run the following command to download godot-cpp:
+
+    git submodule update --init --recursive""")
+    sys.exit(1)
+
+env = SConscript("godot-cpp/SConstruct", {"env": env, "customs": customs})
+
+env.Append(CPPDEFINES=["SPINE_GODOT_EXTENSION"])
+env.Append(CPPPATH=["spine_godot/", "spine_godot/spine-cpp/include"])
+
+sources = Glob("spine_godot/*.cpp") + Glob("spine_godot/spine-cpp/src/spine/*.cpp")
+
+if env["target"] in ["editor", "template_debug"]:
+    try:
+        doc_data = env.GodotCPPDocData("src/gen/doc_data.gen.cpp", source=Glob("spine_godot/docs/*.xml"))
+        sources.append(doc_data)
+    except AttributeError:
+        print("Not including class reference as we're targeting a pre-4.3 baseline.")
+
+file = "{}{}{}".format(libname, env["suffix"], env["SHLIBSUFFIX"])
+filepath = ""
+
+if env["platform"] == "macos" or env["platform"] == "ios":
+    filepath = "{}.framework/".format(env["platform"])
+    file = "{}.{}.{}".format(libname, env["platform"], env["target"])
+
+libraryfile = "bin/{}/{}{}".format(env["platform"], filepath, file)
+library = env.SharedLibrary(
+    libraryfile,
+    source=sources,
+)
+
+copy = env.InstallAs("{}/bin/{}/{}lib{}".format(projectdir, env["platform"], filepath, file), library)
+
+default_args = [library, copy]
+if localEnv.get("compiledb", False):
+    default_args += [compilation_db]
+Default(*default_args)

+ 8 - 0
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus-data.tres

@@ -0,0 +1,8 @@
+[gd_resource type="SpineSkeletonDataResource" load_steps=3 format=3 uid="uid://d1ordmjpe5kt7"]
+
+[ext_resource type="SpineAtlasResource" uid="uid://syvy0bxbnwgi" path="res://assets/celestial-circus/celestial-circus.atlas" id="1_fs1an"]
+[ext_resource type="SpineSkeletonFileResource" uid="uid://doan7yl2by7vm" path="res://assets/celestial-circus/celestial-circus.skel" id="2_sbom4"]
+
+[resource]
+atlas_res = ExtResource("1_fs1an")
+skeleton_file_res = ExtResource("2_sbom4")

+ 173 - 0
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.atlas

@@ -0,0 +1,173 @@
+celestial-circus.png
+	size: 1024, 1024
+	filter: Linear, Linear
+	scale: 0.4
+arm-back-down
+	bounds: 324, 401, 38, 82
+	rotate: 90
+arm-back-up
+	bounds: 290, 44, 83, 116
+	rotate: 90
+arm-front-down
+	bounds: 706, 2, 36, 78
+	rotate: 90
+arm-front-up
+	bounds: 860, 138, 77, 116
+bench
+	bounds: 725, 256, 189, 48
+body-bottom
+	bounds: 879, 868, 154, 124
+	rotate: 90
+body-top
+	bounds: 725, 128, 126, 133
+	rotate: 90
+chest
+	bounds: 408, 26, 104, 93
+cloud-back
+	bounds: 752, 378, 202, 165
+cloud-front
+	bounds: 2, 2, 325, 196
+	rotate: 90
+collar
+	bounds: 786, 13, 47, 26
+ear
+	bounds: 1002, 643, 20, 28
+eye-back-shadow
+	bounds: 428, 395, 14, 10
+eye-front-shadow
+	bounds: 704, 529, 24, 14
+eye-reflex-back
+	bounds: 860, 128, 8, 7
+	rotate: 90
+eye-reflex-front
+	bounds: 726, 386, 10, 7
+eye-white-back
+	bounds: 835, 23, 13, 16
+eye-white-front
+	bounds: 1005, 1000, 22, 17
+	rotate: 90
+eyelashes-down-back
+	bounds: 232, 329, 11, 6
+	rotate: 90
+eyelashes-down-front
+	bounds: 913, 851, 15, 6
+	rotate: 90
+eyelashes-top-back
+	bounds: 408, 395, 18, 10
+eyelashes-top-front
+	bounds: 702, 179, 30, 16
+	rotate: 90
+face
+	bounds: 514, 26, 93, 102
+	rotate: 90
+feathers-back
+	bounds: 954, 625, 46, 46
+feathers-front
+	bounds: 706, 40, 72, 86
+fringe-middle-back
+	bounds: 200, 6, 33, 52
+	rotate: 90
+fringe-middle-front
+	bounds: 878, 76, 60, 50
+	rotate: 90
+fringe-side-back
+	bounds: 780, 41, 27, 94
+	rotate: 90
+fringe-side-front
+	bounds: 939, 161, 26, 93
+glove-bottom-back
+	bounds: 954, 572, 51, 41
+	rotate: 90
+glove-bottom-front
+	bounds: 916, 256, 47, 48
+hair-back-1
+	bounds: 444, 395, 132, 306
+	rotate: 90
+hair-back-2
+	bounds: 438, 211, 80, 285
+	rotate: 90
+hair-back-3
+	bounds: 719, 306, 70, 268
+	rotate: 90
+hair-back-4
+	bounds: 438, 121, 88, 262
+	rotate: 90
+hair-back-5
+	bounds: 438, 293, 88, 279
+	rotate: 90
+hair-back-6
+	bounds: 200, 41, 88, 286
+hair-hat-shadow
+	bounds: 232, 398, 90, 41
+hand-back
+	bounds: 954, 673, 60, 47
+	rotate: 90
+hand-front
+	bounds: 967, 172, 53, 60
+hat-back
+	bounds: 954, 802, 64, 45
+	rotate: 90
+hat-front
+	bounds: 780, 70, 96, 56
+head-back
+	bounds: 618, 17, 102, 86
+	rotate: 90
+jabot
+	bounds: 967, 234, 70, 55
+	rotate: 90
+leg-back
+	bounds: 232, 441, 210, 333
+leg-front
+	bounds: 444, 529, 258, 320
+logo-brooch
+	bounds: 954, 545, 16, 25
+mouth
+	bounds: 408, 121, 22, 6
+neck
+	bounds: 232, 342, 39, 56
+	rotate: 90
+nose
+	bounds: 742, 529, 6, 7
+	rotate: 90
+nose-highlight
+	bounds: 719, 300, 4, 4
+nose-shadow
+	bounds: 869, 128, 7, 8
+pupil-back
+	bounds: 730, 529, 10, 14
+pupil-front
+	bounds: 254, 21, 12, 18
+rope-back
+	bounds: 232, 383, 10, 492
+	rotate: 90
+rope-front
+	bounds: 232, 383, 10, 492
+	rotate: 90
+rope-front-bottom
+	bounds: 954, 735, 42, 65
+skirt
+	bounds: 2, 776, 440, 246
+sock-bow
+	bounds: 408, 407, 33, 32
+spine-logo-body
+	bounds: 879, 853, 13, 32
+	rotate: 90
+star-big
+	bounds: 939, 141, 18, 24
+	rotate: 90
+star-medium
+	bounds: 742, 537, 6, 8
+	rotate: 90
+star-small
+	bounds: 719, 378, 3, 4
+	rotate: 90
+underskirt
+	bounds: 2, 329, 445, 228
+	rotate: 90
+underskirt-back
+	bounds: 444, 851, 433, 171
+wing-back
+	bounds: 290, 129, 146, 252
+wing-front
+	bounds: 704, 545, 304, 248
+	rotate: 90

+ 15 - 0
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.atlas.import

@@ -0,0 +1,15 @@
+[remap]
+
+importer="spine.atlas"
+type="SpineAtlasResource"
+uid="uid://syvy0bxbnwgi"
+path="res://.godot/imported/celestial-circus.atlas-8277f39286d729eed930dc8584632c9d.spatlas"
+
+[deps]
+
+source_file="res://assets/celestial-circus/celestial-circus.atlas"
+dest_files=["res://.godot/imported/celestial-circus.atlas-8277f39286d729eed930dc8584632c9d.spatlas"]
+
+[params]
+
+normal_map_prefix="n"

BIN
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.png


+ 34 - 0
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://d27f2nxof5i18"
+path="res://.godot/imported/celestial-circus.png-362082edc9ee92c57423ee448ab8be8a.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/celestial-circus/celestial-circus.png"
+dest_files=["res://.godot/imported/celestial-circus.png-362082edc9ee92c57423ee448ab8be8a.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

BIN
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.skel


+ 14 - 0
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus.skel.import

@@ -0,0 +1,14 @@
+[remap]
+
+importer="spine.skel"
+type="SpineSkeletonFileResource"
+uid="uid://doan7yl2by7vm"
+path="res://.godot/imported/celestial-circus.skel-57e15df02c4ab2ec1bbb6a38273235f3.spskel"
+
+[deps]
+
+source_file="res://assets/celestial-circus/celestial-circus.skel"
+dest_files=["res://.godot/imported/celestial-circus.skel-57e15df02c4ab2ec1bbb6a38273235f3.spskel"]
+
+[params]
+

+ 34 - 0
spine-godot/example-v4-extension/assets/celestial-circus/celestial-circus_2.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://baym8211e1as0"
+path="res://.godot/imported/celestial-circus_2.png-10ce6d37d52bd6e8495aa1b68f8b3576.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/celestial-circus/celestial-circus_2.png"
+dest_files=["res://.godot/imported/celestial-circus_2.png-10ce6d37d52bd6e8495aa1b68f8b3576.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

BIN
spine-godot/example-v4-extension/assets/footstep.ogg


+ 19 - 0
spine-godot/example-v4-extension/assets/footstep.ogg.import

@@ -0,0 +1,19 @@
+[remap]
+
+importer="oggvorbisstr"
+type="AudioStreamOggVorbis"
+uid="uid://can7k84o8svum"
+path="res://.godot/imported/footstep.ogg-789c3f1e3c4e0ca3bebcb13f1160f623.oggvorbisstr"
+
+[deps]
+
+source_file="res://assets/footstep.ogg"
+dest_files=["res://.godot/imported/footstep.ogg-789c3f1e3c4e0ca3bebcb13f1160f623.oggvorbisstr"]
+
+[params]
+
+loop=false
+loop_offset=0
+bpm=0
+beat_count=0
+bar_beats=4

+ 10 - 0
spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match-data.tres

@@ -0,0 +1,10 @@
+[gd_resource type="SpineSkeletonDataResource" load_steps=3 format=2]
+
+[ext_resource path="res://assets/mix-and-match/mix-and-match.atlas" type="SpineAtlasResource" id=1]
+[ext_resource path="res://assets/mix-and-match/mix-and-match-pro.spine-json" type="SpineSkeletonFileResource" id=2]
+
+[resource]
+atlas_res = ExtResource( 1 )
+skeleton_file_res = ExtResource( 2 )
+animations = null
+skins = null

File diff suppressed because it is too large
+ 1487 - 0
spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match-pro.spine-json


+ 14 - 0
spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match-pro.spine-json.import

@@ -0,0 +1,14 @@
+[remap]
+
+importer="spine.json"
+type="SpineSkeletonFileResource"
+uid="uid://ckitsrtfxmo2w"
+path="res://.godot/imported/mix-and-match-pro.spine-json-3655503b0d08b0b0590c6310da1ac47b.spjson"
+
+[deps]
+
+source_file="res://assets/mix-and-match/mix-and-match-pro.spine-json"
+dest_files=["res://.godot/imported/mix-and-match-pro.spine-json-3655503b0d08b0b0590c6310da1ac47b.spjson"]
+
+[params]
+

+ 358 - 0
spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.atlas

@@ -0,0 +1,358 @@
+mix-and-match.png
+	size: 1024, 512
+	filter: Linear, Linear
+	scale: 0.5
+base-head
+	bounds: 118, 70, 95, 73
+boy/arm-front
+	bounds: 831, 311, 36, 115
+	rotate: 90
+boy/backpack
+	bounds: 249, 357, 119, 153
+boy/backpack-pocket
+	bounds: 628, 193, 34, 62
+	rotate: 90
+boy/backpack-strap-front
+	bounds: 330, 263, 38, 88
+	rotate: 90
+boy/backpack-up
+	bounds: 482, 171, 21, 70
+boy/body
+	bounds: 845, 413, 97, 132
+	rotate: 90
+boy/boot-ribbon-front
+	bounds: 234, 304, 9, 11
+boy/collar
+	bounds: 471, 243, 73, 29
+	rotate: 90
+boy/ear
+	bounds: 991, 352, 19, 23
+	rotate: 90
+boy/eye-back-low-eyelid
+	bounds: 66, 72, 17, 6
+boy/eye-back-pupil
+	bounds: 694, 279, 8, 9
+	rotate: 90
+boy/eye-back-up-eyelid
+	bounds: 460, 101, 23, 5
+	rotate: 90
+boy/eye-back-up-eyelid-back
+	bounds: 979, 414, 19, 10
+	rotate: 90
+boy/eye-front-low-eyelid
+	bounds: 1015, 203, 22, 7
+	rotate: 90
+boy/eye-front-pupil
+	bounds: 309, 50, 9, 9
+boy/eye-front-up-eyelid
+	bounds: 991, 373, 31, 6
+boy/eye-front-up-eyelid-back
+	bounds: 107, 76, 26, 9
+	rotate: 90
+boy/eye-iris-back
+	bounds: 810, 260, 17, 17
+boy/eye-iris-front
+	bounds: 902, 230, 18, 18
+boy/eye-white-back
+	bounds: 599, 179, 20, 12
+boy/eye-white-front
+	bounds: 544, 183, 27, 13
+boy/eyebrow-back
+	bounds: 1002, 225, 20, 11
+	rotate: 90
+boy/eyebrow-front
+	bounds: 975, 234, 25, 11
+boy/hair-back
+	bounds: 629, 289, 122, 81
+	rotate: 90
+boy/hair-bangs
+	bounds: 505, 180, 70, 37
+	rotate: 90
+boy/hair-side
+	bounds: 979, 435, 25, 43
+	rotate: 90
+boy/hand-backfingers
+	bounds: 858, 183, 19, 21
+boy/hand-front-fingers
+	bounds: 879, 183, 19, 21
+boy/hat
+	bounds: 218, 121, 93, 56
+boy/leg-front
+	bounds: 85, 104, 31, 158
+boy/mouth-close
+	bounds: 467, 100, 21, 5
+girl-blue-cape/mouth-close
+	bounds: 467, 100, 21, 5
+girl-spring-dress/mouth-close
+	bounds: 467, 100, 21, 5
+girl/mouth-close
+	bounds: 467, 100, 21, 5
+boy/mouth-smile
+	bounds: 1015, 258, 29, 7
+	rotate: 90
+boy/nose
+	bounds: 323, 79, 17, 10
+boy/pompom
+	bounds: 979, 462, 48, 43
+	rotate: 90
+boy/zip
+	bounds: 922, 231, 14, 23
+	rotate: 90
+girl-blue-cape/back-eyebrow
+	bounds: 527, 106, 18, 12
+	rotate: 90
+girl-blue-cape/body-dress
+	bounds: 2, 264, 109, 246
+girl-blue-cape/body-ribbon
+	bounds: 576, 193, 50, 38
+girl-blue-cape/cape-back
+	bounds: 113, 317, 134, 193
+girl-blue-cape/cape-back-up
+	bounds: 504, 305, 123, 106
+girl-blue-cape/cape-ribbon
+	bounds: 396, 118, 50, 18
+	rotate: 90
+girl-blue-cape/cape-shoulder-back
+	bounds: 420, 243, 49, 59
+girl-blue-cape/cape-shoulder-front
+	bounds: 2, 2, 62, 76
+girl-blue-cape/cape-up-front
+	bounds: 118, 145, 98, 117
+girl-blue-cape/ear
+	bounds: 837, 181, 19, 23
+girl-spring-dress/ear
+	bounds: 837, 181, 19, 23
+girl/ear
+	bounds: 837, 181, 19, 23
+girl-blue-cape/eye-back-low-eyelid
+	bounds: 810, 252, 17, 6
+girl-spring-dress/eye-back-low-eyelid
+	bounds: 810, 252, 17, 6
+girl/eye-back-low-eyelid
+	bounds: 810, 252, 17, 6
+girl-blue-cape/eye-back-pupil
+	bounds: 309, 40, 8, 9
+	rotate: 90
+girl-spring-dress/eye-back-pupil
+	bounds: 309, 40, 8, 9
+	rotate: 90
+girl/eye-back-pupil
+	bounds: 309, 40, 8, 9
+	rotate: 90
+girl-blue-cape/eye-back-up-eyelid
+	bounds: 573, 179, 24, 12
+girl-spring-dress/eye-back-up-eyelid
+	bounds: 573, 179, 24, 12
+girl/eye-back-up-eyelid
+	bounds: 573, 179, 24, 12
+girl-blue-cape/eye-back-up-eyelid-back
+	bounds: 380, 105, 17, 11
+	rotate: 90
+girl-spring-dress/eye-back-up-eyelid-back
+	bounds: 380, 105, 17, 11
+	rotate: 90
+girl/eye-back-up-eyelid-back
+	bounds: 380, 105, 17, 11
+	rotate: 90
+girl-blue-cape/eye-front-low-eyelid
+	bounds: 1016, 353, 18, 6
+	rotate: 90
+girl-spring-dress/eye-front-low-eyelid
+	bounds: 1016, 353, 18, 6
+	rotate: 90
+girl/eye-front-low-eyelid
+	bounds: 1016, 353, 18, 6
+	rotate: 90
+girl-blue-cape/eye-front-pupil
+	bounds: 363, 94, 9, 9
+girl-spring-dress/eye-front-pupil
+	bounds: 363, 94, 9, 9
+girl/eye-front-pupil
+	bounds: 363, 94, 9, 9
+girl-blue-cape/eye-front-up-eyelid
+	bounds: 679, 413, 30, 14
+	rotate: 90
+girl-spring-dress/eye-front-up-eyelid
+	bounds: 679, 413, 30, 14
+	rotate: 90
+girl/eye-front-up-eyelid
+	bounds: 679, 413, 30, 14
+	rotate: 90
+girl-blue-cape/eye-front-up-eyelid-back
+	bounds: 947, 234, 26, 11
+girl-spring-dress/eye-front-up-eyelid-back
+	bounds: 947, 234, 26, 11
+girl/eye-front-up-eyelid-back
+	bounds: 947, 234, 26, 11
+girl-blue-cape/eye-iris-back
+	bounds: 323, 105, 17, 17
+girl-blue-cape/eye-iris-front
+	bounds: 467, 107, 18, 18
+girl-blue-cape/eye-white-back
+	bounds: 621, 175, 20, 16
+girl-spring-dress/eye-white-back
+	bounds: 621, 175, 20, 16
+girl-blue-cape/eye-white-front
+	bounds: 643, 175, 20, 16
+girl-spring-dress/eye-white-front
+	bounds: 643, 175, 20, 16
+girl/eye-white-front
+	bounds: 643, 175, 20, 16
+girl-blue-cape/front-eyebrow
+	bounds: 309, 101, 18, 12
+	rotate: 90
+girl-blue-cape/hair-back
+	bounds: 712, 317, 117, 98
+girl-blue-cape/hair-bangs
+	bounds: 313, 170, 91, 40
+	rotate: 90
+girl-blue-cape/hair-head-side-back
+	bounds: 544, 198, 30, 52
+girl-blue-cape/hair-head-side-front
+	bounds: 466, 127, 41, 42
+girl-blue-cape/hair-side
+	bounds: 175, 2, 36, 71
+	rotate: 90
+girl-blue-cape/hand-front-fingers
+	bounds: 902, 207, 19, 21
+girl-spring-dress/hand-front-fingers
+	bounds: 902, 207, 19, 21
+girl-blue-cape/leg-front
+	bounds: 519, 413, 30, 158
+	rotate: 90
+girl-blue-cape/mouth-smile
+	bounds: 1015, 227, 29, 7
+	rotate: 90
+girl-spring-dress/mouth-smile
+	bounds: 1015, 227, 29, 7
+	rotate: 90
+girl/mouth-smile
+	bounds: 1015, 227, 29, 7
+	rotate: 90
+girl-blue-cape/nose
+	bounds: 342, 82, 11, 7
+girl-spring-dress/nose
+	bounds: 342, 82, 11, 7
+girl/nose
+	bounds: 342, 82, 11, 7
+girl-blue-cape/sleeve-back
+	bounds: 416, 95, 42, 29
+girl-blue-cape/sleeve-front
+	bounds: 249, 303, 52, 119
+	rotate: 90
+girl-spring-dress/arm-front
+	bounds: 829, 292, 17, 111
+	rotate: 90
+girl-spring-dress/back-eyebrow
+	bounds: 309, 81, 18, 12
+	rotate: 90
+girl-spring-dress/body-up
+	bounds: 66, 2, 64, 66
+girl-spring-dress/cloak-down
+	bounds: 758, 227, 50, 50
+girl-spring-dress/cloak-up
+	bounds: 628, 229, 64, 58
+girl-spring-dress/eye-iris-back
+	bounds: 342, 105, 17, 17
+girl-spring-dress/eye-iris-front
+	bounds: 487, 107, 18, 18
+girl-spring-dress/front-eyebrow
+	bounds: 323, 91, 18, 12
+girl-spring-dress/hair-back
+	bounds: 370, 417, 147, 93
+girl-spring-dress/hair-bangs
+	bounds: 829, 250, 91, 40
+girl-spring-dress/hair-head-side-back
+	bounds: 509, 126, 30, 52
+girl-spring-dress/hair-head-side-front
+	bounds: 816, 206, 41, 42
+girl-spring-dress/hair-side
+	bounds: 248, 2, 36, 71
+	rotate: 90
+girl-spring-dress/leg-front
+	bounds: 831, 381, 30, 158
+	rotate: 90
+girl-spring-dress/neck
+	bounds: 85, 70, 20, 32
+girl-spring-dress/shoulder-ribbon
+	bounds: 175, 44, 36, 24
+girl-spring-dress/skirt
+	bounds: 2, 80, 182, 81
+	rotate: 90
+girl-spring-dress/underskirt
+	bounds: 519, 445, 175, 65
+girl/arm-front
+	bounds: 712, 279, 36, 115
+	rotate: 90
+girl/back-eyebrow
+	bounds: 309, 61, 18, 12
+	rotate: 90
+girl/bag-base
+	bounds: 694, 219, 62, 58
+girl/bag-strap-front
+	bounds: 370, 304, 12, 96
+	rotate: 90
+girl/bag-top
+	bounds: 765, 175, 49, 50
+girl/body
+	bounds: 370, 318, 97, 132
+	rotate: 90
+girl/boot-ribbon-front
+	bounds: 323, 64, 13, 13
+girl/eye-iris-back
+	bounds: 361, 105, 17, 17
+girl/eye-iris-front
+	bounds: 507, 106, 18, 18
+girl/eye-white-back
+	bounds: 665, 175, 20, 16
+girl/front-eyebrow
+	bounds: 343, 91, 18, 12
+girl/hair-back
+	bounds: 696, 417, 147, 93
+girl/hair-bangs
+	bounds: 922, 247, 91, 40
+girl/hair-flap-down-front
+	bounds: 415, 171, 70, 65
+	rotate: 90
+girl/hair-head-side-back
+	bounds: 991, 381, 30, 52
+girl/hair-head-side-front
+	bounds: 859, 206, 41, 42
+girl/hair-patch
+	bounds: 132, 2, 66, 41
+	rotate: 90
+girl/hair-side
+	bounds: 692, 181, 36, 71
+	rotate: 90
+girl/hair-strand-back-1
+	bounds: 948, 289, 58, 74
+	rotate: 90
+girl/hair-strand-back-2
+	bounds: 355, 170, 91, 58
+	rotate: 90
+girl/hair-strand-back-3
+	bounds: 215, 40, 92, 79
+girl/hair-strand-front-1
+	bounds: 234, 263, 38, 94
+	rotate: 90
+girl/hair-strand-front-2
+	bounds: 576, 233, 70, 50
+	rotate: 90
+girl/hair-strand-front-3
+	bounds: 313, 124, 44, 81
+	rotate: 90
+girl/hand-front-fingers
+	bounds: 923, 208, 19, 21
+girl/hat
+	bounds: 218, 179, 93, 82
+girl/leg-front
+	bounds: 831, 349, 30, 158
+	rotate: 90
+girl/pompom
+	bounds: 416, 126, 48, 43
+girl/scarf
+	bounds: 113, 264, 119, 51
+girl/scarf-back
+	bounds: 502, 252, 72, 51
+girl/zip
+	bounds: 816, 179, 19, 25

+ 15 - 0
spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.atlas.import

@@ -0,0 +1,15 @@
+[remap]
+
+importer="spine.atlas"
+type="SpineAtlasResource"
+uid="uid://cvu8lycjkd245"
+path="res://.godot/imported/mix-and-match.atlas-3d349b543ecdcc01fb29033adaef0841.spatlas"
+
+[deps]
+
+source_file="res://assets/mix-and-match/mix-and-match.atlas"
+dest_files=["res://.godot/imported/mix-and-match.atlas-3d349b543ecdcc01fb29033adaef0841.spatlas"]
+
+[params]
+
+normal_map_prefix="n"

BIN
spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.png


+ 34 - 0
spine-godot/example-v4-extension/assets/mix-and-match/mix-and-match.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dkpeve6qcxa70"
+path="res://.godot/imported/mix-and-match.png-c2d8e28d9f2efc380ff8b95a22dadcc3.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/mix-and-match/mix-and-match.png"
+dest_files=["res://.godot/imported/mix-and-match.png-c2d8e28d9f2efc380ff8b95a22dadcc3.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 15 - 0
spine-godot/example-v4-extension/assets/raggedyspineboy/Raggedy Spineboy.atlas.import

@@ -0,0 +1,15 @@
+[remap]
+
+importer="spine.atlas"
+type="SpineAtlasResource"
+uid="uid://c75mtwn6dnkwp"
+path="res://.godot/imported/Raggedy Spineboy.atlas-07f7c6b30431397bcf472b18d48851e0.spatlas"
+
+[deps]
+
+source_file="res://assets/raggedyspineboy/Raggedy Spineboy.atlas"
+dest_files=["res://.godot/imported/Raggedy Spineboy.atlas-07f7c6b30431397bcf472b18d48851e0.spatlas"]
+
+[params]
+
+normal_map_prefix="n"

+ 34 - 0
spine-godot/example-v4-extension/assets/raggedyspineboy/Raggedy Spineboy.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://couyrsq850nh6"
+path="res://.godot/imported/Raggedy Spineboy.png-8982b59aad5b612b7ed1a5da8b447d00.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/raggedyspineboy/Raggedy Spineboy.png"
+dest_files=["res://.godot/imported/Raggedy Spineboy.png-8982b59aad5b612b7ed1a5da8b447d00.ctex"]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/bptc_ldr=0
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 8 - 0
spine-godot/example-v4-extension/assets/raggedyspineboy/raggedy spineboy-res.tres

@@ -0,0 +1,8 @@
+[gd_resource type="SpineSkeletonDataResource" load_steps=3 format=2]
+
+[ext_resource path="res://assets/raggedyspineboy/Raggedy Spineboy.atlas" type="SpineAtlasResource" id=1]
+[ext_resource path="res://assets/raggedyspineboy/raggedy spineboy.spine-json" type="SpineSkeletonFileResource" id=2]
+
+[resource]
+atlas_res = ExtResource( 1 )
+skeleton_file_res = ExtResource( 2 )

+ 14 - 0
spine-godot/example-v4-extension/assets/raggedyspineboy/raggedy spineboy.spine-json.import

@@ -0,0 +1,14 @@
+[remap]
+
+importer="spine.json"
+type="SpineSkeletonFileResource"
+uid="uid://2iqykoiop2tc"
+path="res://.godot/imported/raggedy spineboy.spine-json-d90ee4b829503274afa3dbf64c369460.spjson"
+
+[deps]
+
+source_file="res://assets/raggedyspineboy/raggedy spineboy.spine-json"
+dest_files=["res://.godot/imported/raggedy spineboy.spine-json-d90ee4b829503274afa3dbf64c369460.spjson"]
+
+[params]
+

BIN
spine-godot/example-v4-extension/assets/raptor/light-sprite.png


+ 34 - 0
spine-godot/example-v4-extension/assets/raptor/light-sprite.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://cggfs567rn1ay"
+path="res://.godot/imported/light-sprite.png-346e910021b52658f1de723e4c80e05e.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/raptor/light-sprite.png"
+dest_files=["res://.godot/imported/light-sprite.png-346e910021b52658f1de723e4c80e05e.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

BIN
spine-godot/example-v4-extension/assets/raptor/n_raptor.png


+ 34 - 0
spine-godot/example-v4-extension/assets/raptor/n_raptor.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://dnjt8gyvsw0l8"
+path="res://.godot/imported/n_raptor.png-d9af68b4a56af2b4319bb485e366dc90.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/raptor/n_raptor.png"
+dest_files=["res://.godot/imported/n_raptor.png-d9af68b4a56af2b4319bb485e366dc90.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 9 - 0
spine-godot/example-v4-extension/assets/raptor/raptor-data.tres

@@ -0,0 +1,9 @@
+[gd_resource type="SpineSkeletonDataResource" load_steps=3 format=2]
+
+[ext_resource path="res://assets/raptor/raptor.atlas" type="SpineAtlasResource" id=1]
+[ext_resource path="res://assets/raptor/raptor-pro.skel" type="SpineSkeletonFileResource" id=2]
+
+[resource]
+atlas_res = ExtResource( 1 )
+skeleton_file_res = ExtResource( 2 )
+default_mix = 0.2

BIN
spine-godot/example-v4-extension/assets/raptor/raptor-pro.skel


+ 14 - 0
spine-godot/example-v4-extension/assets/raptor/raptor-pro.skel.import

@@ -0,0 +1,14 @@
+[remap]
+
+importer="spine.skel"
+type="SpineSkeletonFileResource"
+uid="uid://dor8huwld4f84"
+path="res://.godot/imported/raptor-pro.skel-022e23a5f5581547a2d7faa9fb2c54e3.spskel"
+
+[deps]
+
+source_file="res://assets/raptor/raptor-pro.skel"
+dest_files=["res://.godot/imported/raptor-pro.skel-022e23a5f5581547a2d7faa9fb2c54e3.spskel"]
+
+[params]
+

+ 93 - 0
spine-godot/example-v4-extension/assets/raptor/raptor.atlas

@@ -0,0 +1,93 @@
+raptor.png
+	size: 1024, 512
+	filter: Linear, Linear
+	scale: 0.5
+back-arm
+	bounds: 895, 295, 46, 25
+back-bracer
+	bounds: 992, 216, 39, 28
+	rotate: 90
+back-hand
+	bounds: 594, 58, 36, 34
+back-knee
+	bounds: 729, 86, 49, 67
+	rotate: 90
+back-thigh
+	bounds: 379, 2, 39, 24
+eyes-open
+	bounds: 902, 194, 47, 45
+	rotate: 90
+front-arm
+	bounds: 945, 306, 48, 26
+front-bracer
+	bounds: 949, 197, 41, 29
+front-hand
+	bounds: 949, 266, 41, 38
+front-open-hand
+	bounds: 875, 148, 43, 44
+front-thigh
+	bounds: 793, 171, 57, 29
+	rotate: 90
+gun
+	bounds: 379, 28, 107, 103
+	rotate: 90
+gun-nohand
+	bounds: 487, 87, 105, 102
+head
+	bounds: 807, 361, 136, 149
+lower-leg
+	bounds: 827, 195, 73, 98
+mouth-grind
+	bounds: 920, 145, 47, 30
+	rotate: 90
+mouth-smile
+	bounds: 992, 257, 47, 30
+	rotate: 90
+neck
+	bounds: 359, 114, 18, 21
+raptor-back-arm
+	bounds: 653, 142, 82, 86
+raptor-body
+	bounds: 2, 277, 632, 233
+raptor-front-arm
+	bounds: 484, 4, 81, 102
+	rotate: 90
+raptor-front-leg
+	bounds: 2, 18, 191, 257
+raptor-hindleg-back
+	bounds: 636, 295, 169, 215
+raptor-horn
+	bounds: 195, 22, 182, 80
+raptor-horn-back
+	bounds: 945, 334, 176, 77
+	rotate: 90
+raptor-jaw
+	bounds: 359, 137, 126, 138
+raptor-jaw-tooth
+	bounds: 895, 322, 37, 48
+	rotate: 90
+raptor-mouth-inside
+	bounds: 949, 228, 36, 41
+	rotate: 90
+raptor-saddle-strap-back
+	bounds: 653, 86, 54, 74
+	rotate: 90
+raptor-saddle-strap-front
+	bounds: 594, 94, 57, 95
+raptor-saddle-w-shadow
+	bounds: 195, 104, 162, 171
+raptor-tail-shadow
+	bounds: 636, 230, 189, 63
+raptor-tongue
+	bounds: 807, 295, 86, 64
+stirrup-back
+	bounds: 952, 151, 44, 35
+	rotate: 90
+stirrup-front
+	bounds: 902, 243, 45, 50
+stirrup-strap
+	bounds: 824, 147, 49, 46
+torso
+	bounds: 737, 137, 54, 91
+visor
+	bounds: 487, 191, 131, 84

+ 15 - 0
spine-godot/example-v4-extension/assets/raptor/raptor.atlas.import

@@ -0,0 +1,15 @@
+[remap]
+
+importer="spine.atlas"
+type="SpineAtlasResource"
+uid="uid://dhtyf5vltqsjg"
+path="res://.godot/imported/raptor.atlas-66da4b831eebf404341993162ba3ddb8.spatlas"
+
+[deps]
+
+source_file="res://assets/raptor/raptor.atlas"
+dest_files=["res://.godot/imported/raptor.atlas-66da4b831eebf404341993162ba3ddb8.spatlas"]
+
+[params]
+
+normal_map_prefix="n"

BIN
spine-godot/example-v4-extension/assets/raptor/raptor.png


+ 34 - 0
spine-godot/example-v4-extension/assets/raptor/raptor.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://ucvdpil67hul"
+path="res://.godot/imported/raptor.png-505be50f63fd1d0fb9175a9efbb9776c.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/raptor/raptor.png"
+dest_files=["res://.godot/imported/raptor.png-505be50f63fd1d0fb9175a9efbb9776c.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 20 - 0
spine-godot/example-v4-extension/assets/spineboy/spineboy-data-res.tres

@@ -0,0 +1,20 @@
+[gd_resource type="SpineSkeletonDataResource" load_steps=5 format=3 uid="uid://cb6rn6vonqbx0"]
+
+[ext_resource type="SpineAtlasResource" uid="uid://dt2kctrit34y0" path="res://assets/spineboy/spineboy.atlas" id="1"]
+[ext_resource type="SpineSkeletonFileResource" uid="uid://bngulrxfavqyn" path="res://assets/spineboy/spineboy-pro.spine-json" id="2"]
+
+[sub_resource type="SpineAnimationMix" id="1"]
+from = "idle"
+to = "run"
+mix = 0.2
+
+[sub_resource type="SpineAnimationMix" id="2"]
+from = "run"
+to = "idle"
+mix = 0.2
+
+[resource]
+atlas_res = ExtResource("1")
+skeleton_file_res = ExtResource("2")
+default_mix = 0.1
+animation_mixes = [SubResource("1"), SubResource("2")]

BIN
spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.skel


+ 14 - 0
spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.skel.import

@@ -0,0 +1,14 @@
+[remap]
+
+importer="spine.skel"
+type="SpineSkeletonFileResource"
+uid="uid://cth0eypg16cgi"
+path="res://.godot/imported/spineboy-pro.skel-52781a88227740d2a80cb8d7636ea22e.spskel"
+
+[deps]
+
+source_file="res://assets/spineboy/spineboy-pro.skel"
+dest_files=["res://.godot/imported/spineboy-pro.skel-52781a88227740d2a80cb8d7636ea22e.spskel"]
+
+[params]
+

File diff suppressed because it is too large
+ 557 - 0
spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.spine-json


+ 14 - 0
spine-godot/example-v4-extension/assets/spineboy/spineboy-pro.spine-json.import

@@ -0,0 +1,14 @@
+[remap]
+
+importer="spine.json"
+type="SpineSkeletonFileResource"
+uid="uid://bngulrxfavqyn"
+path="res://.godot/imported/spineboy-pro.spine-json-f9f3900b0187b1b6e5b1abca50f5f02a.spjson"
+
+[deps]
+
+source_file="res://assets/spineboy/spineboy-pro.spine-json"
+dest_files=["res://.godot/imported/spineboy-pro.spine-json-f9f3900b0187b1b6e5b1abca50f5f02a.spjson"]
+
+[params]
+

+ 94 - 0
spine-godot/example-v4-extension/assets/spineboy/spineboy.atlas

@@ -0,0 +1,94 @@
+spineboy.png
+	size: 1024, 256
+	filter: Linear, Linear
+	scale: 0.5
+crosshair
+	bounds: 352, 7, 45, 45
+eye-indifferent
+	bounds: 862, 105, 47, 45
+eye-surprised
+	bounds: 505, 79, 47, 45
+front-bracer
+	bounds: 826, 66, 29, 40
+front-fist-closed
+	bounds: 786, 65, 38, 41
+front-fist-open
+	bounds: 710, 51, 43, 44
+	rotate: 90
+front-foot
+	bounds: 210, 6, 63, 35
+front-shin
+	bounds: 665, 128, 41, 92
+	rotate: 90
+front-thigh
+	bounds: 2, 2, 23, 56
+	rotate: 90
+front-upper-arm
+	bounds: 250, 205, 23, 49
+goggles
+	bounds: 665, 171, 131, 83
+gun
+	bounds: 798, 152, 105, 102
+head
+	bounds: 2, 27, 136, 149
+hoverboard-board
+	bounds: 2, 178, 246, 76
+hoverboard-thruster
+	bounds: 722, 96, 30, 32
+	rotate: 90
+hoverglow-small
+	bounds: 275, 81, 137, 38
+mouth-grind
+	bounds: 614, 97, 47, 30
+mouth-oooo
+	bounds: 612, 65, 47, 30
+mouth-smile
+	bounds: 661, 64, 47, 30
+muzzle-glow
+	bounds: 382, 54, 25, 25
+muzzle-ring
+	bounds: 275, 54, 25, 105
+	rotate: 90
+muzzle01
+	bounds: 911, 95, 67, 40
+	rotate: 90
+muzzle02
+	bounds: 792, 108, 68, 42
+muzzle03
+	bounds: 956, 171, 83, 53
+	rotate: 90
+muzzle04
+	bounds: 275, 7, 75, 45
+muzzle05
+	bounds: 140, 3, 68, 38
+neck
+	bounds: 250, 182, 18, 21
+portal-bg
+	bounds: 140, 43, 133, 133
+portal-flare1
+	bounds: 554, 65, 56, 30
+portal-flare2
+	bounds: 759, 112, 57, 31
+	rotate: 90
+portal-flare3
+	bounds: 554, 97, 58, 30
+portal-shade
+	bounds: 275, 121, 133, 133
+portal-streaks1
+	bounds: 410, 126, 126, 128
+portal-streaks2
+	bounds: 538, 129, 125, 125
+rear-bracer
+	bounds: 857, 67, 28, 36
+rear-foot
+	bounds: 663, 96, 57, 30
+rear-shin
+	bounds: 414, 86, 38, 89
+	rotate: 90
+rear-thigh
+	bounds: 756, 63, 28, 47
+rear-upper-arm
+	bounds: 60, 5, 20, 44
+	rotate: 90
+torso
+	bounds: 905, 164, 49, 90

+ 15 - 0
spine-godot/example-v4-extension/assets/spineboy/spineboy.atlas.import

@@ -0,0 +1,15 @@
+[remap]
+
+importer="spine.atlas"
+type="SpineAtlasResource"
+uid="uid://dt2kctrit34y0"
+path="res://.godot/imported/spineboy.atlas-54c12b5ff1cdaaa1b4e452a7d0d868c9.spatlas"
+
+[deps]
+
+source_file="res://assets/spineboy/spineboy.atlas"
+dest_files=["res://.godot/imported/spineboy.atlas-54c12b5ff1cdaaa1b4e452a7d0d868c9.spatlas"]
+
+[params]
+
+normal_map_prefix="n"

BIN
spine-godot/example-v4-extension/assets/spineboy/spineboy.png


+ 34 - 0
spine-godot/example-v4-extension/assets/spineboy/spineboy.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://bh5imhdfuc6m0"
+path="res://.godot/imported/spineboy.png-436dbd6da2b707b6828ede17b7871f43.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://assets/spineboy/spineboy.png"
+dest_files=["res://.godot/imported/spineboy.png-436dbd6da2b707b6828ede17b7871f43.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

BIN
spine-godot/example-v4-extension/bin/macos/macos.framework/libspine_godot.macos.editor


BIN
spine-godot/example-v4-extension/bin/macos/macos.framework/libspine_godot.macos.template_debug


+ 25 - 0
spine-godot/example-v4-extension/bin/spine_godot_extension.gdextension

@@ -0,0 +1,25 @@
+[configuration]
+
+entry_symbol = "spine_godot_library_init"
+compatibility_minimum = "4.1"
+
+[libraries]
+
+macos.debug = "res://bin/macos/macos.framework/libspine_godot.macos.template_debug"
+macos.release = "res://bin/macos/macos.framework/libspine_godot.macos.template_release"
+ios.debug = "res://bin/ios/ios.framework/libspine_godot.ios.template_debug"
+ios.release = "res://bin/ios/ios.framework/libspine_godot.ios.template_release"
+windows.debug.x86_32 = "res://bin/windows/libspine_godot.windows.template_debug.x86_32.dll"
+windows.release.x86_32 = "res://bin/windows/libspine_godot.windows.template_release.x86_32.dll"
+windows.debug.x86_64 = "res://bin/windows/libspine_godot.windows.template_debug.x86_64.dll"
+windows.release.x86_64 = "res://bin/windows/libspine_godot.windows.template_release.x86_64.dll"
+linux.debug.x86_64 = "res://bin/linux/libspine_godot.linux.template_debug.x86_64.so"
+linux.release.x86_64 = "res://bin/linux/libspine_godot.linux.template_release.x86_64.so"
+linux.debug.arm64 = "res://bin/linux/libspine_godot.linux.template_debug.arm64.so"
+linux.release.arm64 = "res://bin/linux/libspine_godot.linux.template_release.arm64.so"
+linux.debug.rv64 = "res://bin/linux/libspine_godot.linux.template_debug.rv64.so"
+linux.release.rv64 = "res://bin/linux/libspine_godot.linux.template_release.rv64.so"
+android.debug.x86_64 = "res://bin/android/libspine_godot.android.template_debug.x86_64.so"
+android.release.x86_64 = "res://bin/android/libspine_godot.android.template_release.x86_64.so"
+android.debug.arm64 = "res://bin/android/libspine_godot.android.template_debug.arm64.so"
+android.release.arm64 = "res://bin/android/libspine_godot.android.template_release.arm64.so"

+ 7 - 0
spine-godot/example-v4-extension/default_env.tres

@@ -0,0 +1,7 @@
+[gd_resource type="Environment" load_steps=2 format=2]
+
+[sub_resource type="ProceduralSky" id=1]
+
+[resource]
+background_mode = 2
+background_sky = SubResource( 1 )

+ 19 - 0
spine-godot/example-v4-extension/examples/01-helloworld/helloworld.tscn

@@ -0,0 +1,19 @@
+[gd_scene load_steps=3 format=3 uid="uid://d0v5rhv2ysej8"]
+
+[ext_resource type="Script" path="res://examples/01-helloworld/spineboy-helloworld.gd" id="1"]
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="3"]
+
+[node name="Node2D" type="Node2D"]
+
+[node name="Spineboy" type="SpineSprite" parent="."]
+position = Vector2(505, 466)
+scale = Vector2(0.466832, 0.466832)
+skeleton_data_res = ExtResource("3")
+bones_color = Color(0.968627, 1, 0, 0.501961)
+paths_color = Color(1, 0.498039, 0, 0.466667)
+paths_clipping = Color(0.8, 0, 0, 0.5)
+preview_skin = "Default"
+preview_animation = "run"
+preview_frame = true
+preview_time = 0.24
+script = ExtResource("1")

+ 4 - 0
spine-godot/example-v4-extension/examples/01-helloworld/spineboy-helloworld.gd

@@ -0,0 +1,4 @@
+extends SpineSprite
+
+func _ready():
+	get_animation_state().set_animation("walk", true, 0)

+ 39 - 0
spine-godot/example-v4-extension/examples/02-animation-state-listeners/animation-state-listeners.gd

@@ -0,0 +1,39 @@
+extends Node2D
+
+@onready var footstep_audio: AudioStreamPlayer = $FootstepAudio
+
+func _animation_started(sprite: SpineSprite, animation_state: SpineAnimationState, track_entry: SpineTrackEntry):
+	print("Animation started: " + track_entry.get_animation().get_name())
+
+func _animation_interrupted(sprite: SpineSprite, animation_state: SpineAnimationState, track_entry: SpineTrackEntry):
+	print("Animation interrupted: " + track_entry.get_animation().get_name())
+
+func _animation_ended(sprite: SpineSprite, animation_state: SpineAnimationState, track_entry: SpineTrackEntry):
+	print("Animation ended: " + track_entry.get_animation().get_name())
+
+func _animation_completed(sprite: SpineSprite, animation_state: SpineAnimationState, track_entry: SpineTrackEntry):
+	print("Animation completed: " + track_entry.get_animation().get_name())
+
+func _animation_disposed(sprite: SpineSprite, animation_state: SpineAnimationState, track_entry: SpineTrackEntry):
+	print("Animation disposed: " + track_entry.get_animation().get_name())
+	
+func _animation_event(sprite: SpineSprite, animation_state: SpineAnimationState, track_entry: SpineTrackEntry, event: SpineEvent):
+	print("Animation event: " + track_entry.get_animation().get_name() + ", " + event.get_data().get_event_name())
+	if (event.get_data().get_event_name() == "footstep"):		
+		footstep_audio.play()
+
+func _ready():
+	var spineboy = $Spineboy
+	spineboy.animation_started.connect(_animation_started)
+	spineboy.animation_interrupted.connect(_animation_interrupted)
+	spineboy.animation_ended.connect(_animation_ended)
+	spineboy.animation_completed.connect(_animation_completed)
+	spineboy.animation_disposed.connect(_animation_disposed)
+	spineboy.animation_event.connect(_animation_event)
+	
+	var animation_state = spineboy.get_animation_state()
+	animation_state.set_animation("jump", false, 0)
+	animation_state.add_animation("walk", 0, true, 0)
+	animation_state.add_animation("run", 2, true, 0)
+	
+	pass

+ 20 - 0
spine-godot/example-v4-extension/examples/02-animation-state-listeners/animation-state-listeners.tscn

@@ -0,0 +1,20 @@
+[gd_scene load_steps=4 format=3 uid="uid://c738i3nbdkn0h"]
+
+[ext_resource type="Script" path="res://examples/02-animation-state-listeners/animation-state-listeners.gd" id="1"]
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="2"]
+[ext_resource type="AudioStream" uid="uid://can7k84o8svum" path="res://assets/footstep.ogg" id="3"]
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("1")
+
+[node name="Spineboy" type="SpineSprite" parent="."]
+position = Vector2(473, 487)
+scale = Vector2(0.575051, 0.575051)
+skeleton_data_res = ExtResource("2")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+
+[node name="FootstepAudio" type="AudioStreamPlayer" parent="."]
+stream = ExtResource("3")

+ 22 - 0
spine-godot/example-v4-extension/examples/03-mix-and-match/mix-and-match.gd

@@ -0,0 +1,22 @@
+extends SpineSprite
+
+func _ready():
+	var data = get_skeleton().get_data()
+	var custom_skin = new_skin("custom-skin")
+	var skin_base = data.find_skin("skin-base")
+	custom_skin.add_skin(skin_base)
+	custom_skin.add_skin(data.find_skin("nose/short"))
+	custom_skin.add_skin(data.find_skin("eyelids/girly"))
+	custom_skin.add_skin(data.find_skin("eyes/violet"))
+	custom_skin.add_skin(data.find_skin("hair/brown"))
+	custom_skin.add_skin(data.find_skin("clothes/hoodie-orange"))
+	custom_skin.add_skin(data.find_skin("legs/pants-jeans"))
+	custom_skin.add_skin(data.find_skin("accessories/bag"))
+	custom_skin.add_skin(data.find_skin("accessories/hat-red-yellow"))
+	get_skeleton().set_skin(custom_skin);
+
+	for el in custom_skin.get_attachments():
+		var entry: SpineSkinEntry = el
+		print(str(entry.get_slot_index()) + " " + entry.get_name())
+
+	get_animation_state().set_animation("dance", true, 0)

+ 16 - 0
spine-godot/example-v4-extension/examples/03-mix-and-match/mix-and-match.tscn

@@ -0,0 +1,16 @@
+[gd_scene load_steps=3 format=3 uid="uid://dodvuj07fsynd"]
+
+[ext_resource type="SpineSkeletonDataResource" path="res://assets/mix-and-match/mix-and-match-data.tres" id="1"]
+[ext_resource type="Script" path="res://examples/03-mix-and-match/mix-and-match.gd" id="2"]
+
+[node name="Node2D" type="Node2D"]
+
+[node name="MixAndMatch" type="SpineSprite" parent="."]
+position = Vector2(532.982, 480.287)
+scale = Vector2(0.441932, 0.441932)
+skeleton_data_res = ExtResource("1")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+script = ExtResource("2")

+ 16 - 0
spine-godot/example-v4-extension/examples/04-simple-input/simple-input.tscn

@@ -0,0 +1,16 @@
+[gd_scene load_steps=3 format=3 uid="uid://bgdpghp11j3kg"]
+
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="1"]
+[ext_resource type="Script" path="res://examples/04-simple-input/spineboy-simple-input.gd" id="2"]
+
+[node name="Node2D" type="Node2D"]
+
+[node name="Spineboy" type="SpineSprite" parent="."]
+position = Vector2(501.503, 472.035)
+scale = Vector2(0.518624, 0.518624)
+skeleton_data_res = ExtResource("1")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+script = ExtResource("2")

+ 19 - 0
spine-godot/example-v4-extension/examples/04-simple-input/spineboy-simple-input.gd

@@ -0,0 +1,19 @@
+extends SpineSprite
+
+func _ready():
+	get_animation_state().set_animation("idle", true, 0)
+
+func _process(_delta):
+	if Input.is_action_just_pressed("ui_left"):
+		get_animation_state().set_animation("run", true, 0)
+		get_skeleton().set_scale_x(-1)
+		
+	if Input.is_action_just_released("ui_left"):
+		get_animation_state().set_animation("idle", true, 0)
+		
+	if (Input.is_action_just_pressed("ui_right")):
+		get_animation_state().set_animation("run", true, 0)
+		get_skeleton().set_scale_x(1)
+		
+	if Input.is_action_just_released("ui_right"):
+		get_animation_state().set_animation("idle", true, 0)

+ 11 - 0
spine-godot/example-v4-extension/examples/05-mouse-following/mouse-following.gd

@@ -0,0 +1,11 @@
+extends Node2D
+
+@onready var spineboy: SpineSprite = $Spineboy
+@onready var crosshair_bone: SpineBoneNode = $Spineboy/CrosshairBone
+
+func _ready():
+	spineboy.get_animation_state().set_animation("walk", true, 0)
+	spineboy.get_animation_state().set_animation("aim", true, 1)
+	
+func _process(_delta):
+	crosshair_bone.global_position = get_viewport().get_mouse_position()

+ 23 - 0
spine-godot/example-v4-extension/examples/05-mouse-following/mouse-following.tscn

@@ -0,0 +1,23 @@
+[gd_scene load_steps=3 format=3 uid="uid://borp2l17n5xw1"]
+
+[ext_resource type="Script" path="res://examples/05-mouse-following/mouse-following.gd" id="1"]
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="2"]
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("1")
+
+[node name="Spineboy" type="SpineSprite" parent="."]
+position = Vector2(147, 493)
+scale = Vector2(0.5, 0.5)
+skeleton_data_res = ExtResource("2")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+
+[node name="CrosshairBone" type="SpineBoneNode" parent="Spineboy"]
+show_behind_parent = true
+position = Vector2(302.333, -569.714)
+rotation = -0.000872665
+bone_name = "crosshair"
+bone_mode = 1

+ 5 - 0
spine-godot/example-v4-extension/examples/06-bone-following/bone-following.gd

@@ -0,0 +1,5 @@
+extends Node2D
+
+func _ready():
+	var spineboy: SpineSprite = $Spineboy
+	spineboy.get_animation_state().set_animation("walk", true, 0)	

+ 27 - 0
spine-godot/example-v4-extension/examples/06-bone-following/bone-following.tscn

@@ -0,0 +1,27 @@
+[gd_scene load_steps=4 format=3 uid="uid://bir7yvf0qwdge"]
+
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="1"]
+[ext_resource type="Script" path="res://examples/06-bone-following/bone-following.gd" id="2"]
+[ext_resource type="Texture2D" uid="uid://8ud5n2ywp5ba" path="res://icon.png" id="3"]
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("2")
+
+[node name="Spineboy" type="SpineSprite" parent="."]
+position = Vector2(478, 483)
+scale = Vector2(0.58461, 0.58461)
+skeleton_data_res = ExtResource("1")
+preview_skin = "Default"
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+
+[node name="GunTipBone" type="SpineBoneNode" parent="Spineboy"]
+show_behind_parent = true
+position = Vector2(214.298, -162.047)
+rotation = 0.713316
+scale = Vector2(1, 1)
+bone_name = "gun-tip"
+
+[node name="Sprite" type="Sprite2D" parent="Spineboy/GunTipBone"]
+texture = ExtResource("3")

+ 11 - 0
spine-godot/example-v4-extension/examples/07-slot-node/slot-node.gd

@@ -0,0 +1,11 @@
+extends Node2D
+
+@onready var spineboy: SpineSprite = $Spineboy
+@onready var raptor: SpineSprite = $Spineboy/GunSlot/Raptor
+@onready var tiny_spineboy: SpineSprite = $Spineboy/FrontFistSlot/TinySpineboy
+
+func _ready():	
+	var entry = spineboy.get_animation_state().set_animation("run", true, 0)
+	entry.set_time_scale(0.1)
+	raptor.get_animation_state().set_animation("walk", true, 0)
+	tiny_spineboy.get_animation_state().set_animation("walk", true, 0)

+ 61 - 0
spine-godot/example-v4-extension/examples/07-slot-node/slot-node.tscn

@@ -0,0 +1,61 @@
+[gd_scene load_steps=5 format=3 uid="uid://bhht8dees2pyq"]
+
+[ext_resource type="Script" path="res://examples/07-slot-node/slot-node.gd" id="1"]
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="2"]
+[ext_resource type="SpineSkeletonDataResource" path="res://assets/raptor/raptor-data.tres" id="3"]
+[ext_resource type="Texture2D" uid="uid://8ud5n2ywp5ba" path="res://icon.png" id="4"]
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("1")
+
+[node name="Spineboy" type="SpineSprite" parent="."]
+position = Vector2(474, 506)
+scale = Vector2(0.560712, 0.560712)
+skeleton_data_res = ExtResource("2")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+
+[node name="GunSlot" type="SpineSlotNode" parent="Spineboy"]
+show_behind_parent = true
+position = Vector2(40.8752, -276.036)
+rotation = 0.837234
+scale = Vector2(1, 1)
+slot_name = "gun"
+
+[node name="Raptor" type="SpineSprite" parent="Spineboy/GunSlot"]
+position = Vector2(84.6909, -67.9174)
+scale = Vector2(0.193472, 0.193472)
+skeleton_data_res = ExtResource("3")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+
+[node name="EyeSlot" type="SpineSlotNode" parent="Spineboy"]
+show_behind_parent = true
+position = Vector2(-23.4598, -402.301)
+rotation = -1.71793
+scale = Vector2(1, 1)
+slot_name = "eye"
+
+[node name="Sprite" type="Sprite2D" parent="Spineboy/EyeSlot"]
+position = Vector2(84.4734, 43.4469)
+rotation = 1.66344
+texture = ExtResource("4")
+
+[node name="FrontFistSlot" type="SpineSlotNode" parent="Spineboy"]
+show_behind_parent = true
+position = Vector2(-29.0298, -241.577)
+rotation = 0.995187
+slot_name = "front-fist"
+
+[node name="TinySpineboy" type="SpineSprite" parent="Spineboy/FrontFistSlot"]
+position = Vector2(-2.64624, -10.8111)
+scale = Vector2(0.193389, 0.193389)
+skeleton_data_res = ExtResource("2")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0

+ 45 - 0
spine-godot/example-v4-extension/examples/09-custom-material/custom-material.tscn

@@ -0,0 +1,45 @@
+[gd_scene load_steps=6 format=3 uid="uid://b4p2vn7bwm52a"]
+
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="1"]
+
+[sub_resource type="Shader" id="1"]
+code = "shader_type canvas_item;
+
+void fragment() {
+	COLOR = texture(TEXTURE, UV);
+	COLOR.r = 0.0;
+}"
+
+[sub_resource type="ShaderMaterial" id="2"]
+shader = SubResource("1")
+
+[sub_resource type="Shader" id="3"]
+code = "shader_type canvas_item;
+
+void fragment() {
+	COLOR = texture(TEXTURE, UV);
+	COLOR.b = 0.0;
+}"
+
+[sub_resource type="ShaderMaterial" id="4"]
+shader = SubResource("3")
+
+[node name="Node2D" type="Node2D"]
+
+[node name="SpineSprite" type="SpineSprite" parent="."]
+position = Vector2(501, 507)
+scale = Vector2(0.546374, 0.546373)
+skeleton_data_res = ExtResource("1")
+normal_material = SubResource("2")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+
+[node name="GunSlot" type="SpineSlotNode" parent="SpineSprite"]
+show_behind_parent = true
+position = Vector2(40.8753, -276.036)
+rotation = 0.837234
+scale = Vector2(1, 1)
+slot_name = "gun"
+normal_material = SubResource("4")

+ 4 - 0
spine-godot/example-v4-extension/examples/10-2d-lighting/2d-lighting.gd

@@ -0,0 +1,4 @@
+extends Node2D
+
+func _ready():
+	$SpineSprite.get_animation_state().set_animation("walk");

+ 27 - 0
spine-godot/example-v4-extension/examples/10-2d-lighting/2d-lighting.tscn

@@ -0,0 +1,27 @@
+[gd_scene load_steps=4 format=3 uid="uid://rt2llskmx7xm"]
+
+[ext_resource type="SpineSkeletonDataResource" path="res://assets/raptor/raptor-data.tres" id="1"]
+[ext_resource type="Texture2D" uid="uid://cggfs567rn1ay" path="res://assets/raptor/light-sprite.png" id="2"]
+[ext_resource type="Script" path="res://examples/10-2d-lighting/2d-lighting.gd" id="3"]
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("3")
+
+[node name="SpineSprite" type="SpineSprite" parent="."]
+position = Vector2(576, 506)
+scale = Vector2(0.458967, 0.458967)
+skeleton_data_res = ExtResource("1")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+
+[node name="Light2D" type="PointLight2D" parent="."]
+position = Vector2(691, 283)
+scale = Vector2(1.51563, 1.51563)
+color = Color(1, 0.0117647, 0.0117647, 1)
+texture = ExtResource("2")
+height = 100.0
+
+[node name="CanvasModulate" type="CanvasModulate" parent="."]
+color = Color(0.223529, 0.160784, 0.160784, 1)

+ 26 - 0
spine-godot/example-v4-extension/examples/11-bone-node/bone-node.gd

@@ -0,0 +1,26 @@
+extends Node2D
+
+@onready var spineboy = $SpineSprite
+@onready var center_bone = $SpineSprite/HoverboardCenterBone
+@onready var center_ray = $SpineSprite/HoverboardCenterBone/CenterRay
+@onready var target_bone = $SpineSprite/HoverboardTargetBone
+@onready var target_ray = $SpineSprite/HoverboardTargetBone/TargetRay
+@onready var hip_bone = $SpineSprite/HipBone
+var center_hip_distance = 0
+
+func _ready():
+	spineboy.get_animation_state().set_animation("hoverboard", true, 0)
+	spineboy.update_skeleton(0);
+	center_hip_distance = hip_bone.global_position.y - center_bone.global_position.y
+
+func _physics_process(delta):
+	if target_ray.is_colliding():
+		target_bone.global_position.y = target_ray.get_collision_point().y - 30
+
+	if center_ray.is_colliding():
+		center_bone.global_position.y = center_ray.get_collision_point().y - 30
+		
+	if abs(hip_bone.global_position.y - center_bone.global_position.y) - abs(center_hip_distance) < 20:
+		hip_bone.global_position.y = center_bone.global_position.y + center_hip_distance
+		
+	spineboy.global_position.x += delta * 150;

+ 53 - 0
spine-godot/example-v4-extension/examples/11-bone-node/bone-node.tscn

@@ -0,0 +1,53 @@
+[gd_scene load_steps=3 format=3 uid="uid://chrw4i0nksphn"]
+
+[ext_resource type="Script" path="res://examples/11-bone-node/bone-node.gd" id="1"]
+[ext_resource type="SpineSkeletonDataResource" uid="uid://cb6rn6vonqbx0" path="res://assets/spineboy/spineboy-data-res.tres" id="2"]
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("1")
+
+[node name="SpineSprite" type="SpineSprite" parent="."]
+position = Vector2(13, 569)
+scale = Vector2(0.278096, 0.278096)
+skeleton_data_res = ExtResource("2")
+update_mode = 1
+preview_skin = "Default"
+preview_animation = "hoverboard"
+preview_frame = true
+preview_time = 0.0
+
+[node name="HoverboardCenterBone" type="SpineBoneNode" parent="SpineSprite"]
+show_behind_parent = true
+position = Vector2(-10.1996, -68.2213)
+rotation = -0.00403465
+bone_name = "hoverboard-controller"
+bone_mode = 1
+
+[node name="CenterRay" type="RayCast2D" parent="SpineSprite/HoverboardCenterBone"]
+target_position = Vector2(0, 10000)
+
+[node name="HoverboardTargetBone" type="SpineBoneNode" parent="SpineSprite"]
+show_behind_parent = true
+position = Vector2(258.903, -71.9176)
+rotation = -0.000872665
+bone_name = "board-ik"
+bone_mode = 1
+
+[node name="TargetRay" type="RayCast2D" parent="SpineSprite/HoverboardTargetBone"]
+position = Vector2(-3.59872, 3.59277)
+target_position = Vector2(0, 10000)
+
+[node name="HipBone" type="SpineBoneNode" parent="SpineSprite"]
+show_behind_parent = true
+position = Vector2(-53.7338, -279.363)
+rotation = -0.000872665
+bone_name = "hip"
+bone_mode = 1
+
+[node name="Polygon2D" type="Polygon2D" parent="."]
+polygon = PackedVector2Array(0, 596, 309, 471, 516, 515, 762, 447, 984, 504, 1023, 505, 1024, 596)
+
+[node name="StaticBody2D" type="StaticBody2D" parent="Polygon2D"]
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Polygon2D/StaticBody2D"]
+polygon = PackedVector2Array(3, 592, 304, 469, 516, 514, 762, 443, 984, 499, 1252, 498, 1280, 596)

+ 32 - 0
spine-godot/example-v4-extension/examples/12-physics/physics.gd

@@ -0,0 +1,32 @@
+extends Node2D
+
+@onready var celestial_circus: SpineSprite = $"celestial-circus"
+
+var last_x = -1
+var last_y = -1
+var isMouseOver = false
+
+func _ready():
+	celestial_circus.get_animation_state().set_animation("wind-idle", true, 0)
+	celestial_circus.get_animation_state().set_animation("eyeblink-long", true, 1)
+	celestial_circus.get_animation_state().set_animation("stars", true, 2)
+	
+func _process(_delta):
+	if (Input.is_mouse_button_pressed(MOUSE_BUTTON_LEFT) and isMouseOver):
+		var pos = get_viewport().get_mouse_position()
+		if (last_x != -1):
+			var dx = pos.x - last_x
+			var dy = pos.y - last_y
+			celestial_circus.global_position += Vector2(dx, dy)
+			celestial_circus.get_skeleton().physics_translate(dx * 1 / celestial_circus.scale.x, dy * 1 / celestial_circus.scale.y)
+		last_x = pos.x
+		last_y = pos.y
+	else:
+		last_x = -1
+		last_y = -1
+
+func _on_area_2d_mouse_entered():
+	isMouseOver = true
+
+func _on_area_2d_mouse_exited():
+	isMouseOver = false

+ 31 - 0
spine-godot/example-v4-extension/examples/12-physics/physics.tscn

@@ -0,0 +1,31 @@
+[gd_scene load_steps=4 format=3 uid="uid://re2mg31p3vln"]
+
+[ext_resource type="Script" path="res://examples/12-physics/physics.gd" id="1_7guqn"]
+[ext_resource type="SpineSkeletonDataResource" uid="uid://d1ordmjpe5kt7" path="res://assets/celestial-circus/celestial-circus-data.tres" id="2_t2blj"]
+
+[sub_resource type="RectangleShape2D" id="RectangleShape2D_do8w3"]
+size = Vector2(270, 573)
+
+[node name="Node2D" type="Node2D"]
+script = ExtResource("1_7guqn")
+
+[node name="celestial-circus" type="SpineSprite" parent="."]
+position = Vector2(600, 485)
+scale = Vector2(0.25, 0.25)
+skeleton_data_res = ExtResource("2_t2blj")
+preview_skin = "Default"
+preview_animation = "-- Empty --"
+preview_frame = false
+preview_time = 0.0
+
+[node name="Area2D" type="Area2D" parent="celestial-circus"]
+show_behind_parent = true
+scale = Vector2(4, 4)
+
+[node name="CollisionShape2D" type="CollisionShape2D" parent="celestial-circus/Area2D"]
+show_behind_parent = true
+position = Vector2(0, -158.5)
+shape = SubResource("RectangleShape2D_do8w3")
+
+[connection signal="mouse_entered" from="celestial-circus/Area2D" to="." method="_on_area_2d_mouse_entered"]
+[connection signal="mouse_exited" from="celestial-circus/Area2D" to="." method="_on_area_2d_mouse_exited"]

+ 6 - 0
spine-godot/example-v4-extension/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")

+ 24 - 0
spine-godot/example-v4-extension/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

BIN
spine-godot/example-v4-extension/icon.png


+ 34 - 0
spine-godot/example-v4-extension/icon.png.import

@@ -0,0 +1,34 @@
+[remap]
+
+importer="texture"
+type="CompressedTexture2D"
+uid="uid://8ud5n2ywp5ba"
+path="res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://icon.png"
+dest_files=["res://.godot/imported/icon.png-487276ed1e3a0c39cad0279d744ee560.ctex"]
+
+[params]
+
+compress/mode=0
+compress/high_quality=false
+compress/lossy_quality=0.7
+compress/hdr_compression=1
+compress/normal_map=0
+compress/channel_pack=0
+mipmaps/generate=false
+mipmaps/limit=-1
+roughness/mode=0
+roughness/src_normal=""
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/normal_map_invert_y=false
+process/hdr_as_srgb=false
+process/hdr_clamp_exposure=false
+process/size_limit=0
+detect_3d/compress_to=1

+ 35 - 0
spine-godot/example-v4-extension/project.godot

@@ -0,0 +1,35 @@
+; Engine configuration file.
+; It's best edited using the editor UI and not directly,
+; since the parameters that go here are not all obvious.
+;
+; Format:
+;   [section] ; section goes between []
+;   param=value ; assign values to parameters
+
+config_version=5
+
+[application]
+
+config/name="spine-godot-examples"
+run/main_scene="res://examples/08-animation-player/animation-player.tscn"
+config/features=PackedStringArray("4.3")
+run/low_processor_mode=true
+config/icon="res://icon.png"
+
+[global]
+
+batch=false
+
+[physics]
+
+common/enable_pause_aware_picking=true
+
+[rendering]
+
+batching/parameters/max_join_item_commands=100
+batching/parameters/batch_buffer_size=65535
+batching/parameters/item_reordering_lookahead=100
+quality/driver/driver_name="GLES2"
+vram_compression/import_etc=true
+vram_compression/import_etc2=false
+environment/default_environment="res://default_env.tres"

+ 4 - 0
spine-godot/example-v4-extension/tests/batch-test.gd

@@ -0,0 +1,4 @@
+extends SpineSprite
+
+func _ready():
+	get_animation_state().set_animation("walk", true)

+ 871 - 0
spine-godot/example-v4-extension/tests/batch-test.tscn

@@ -0,0 +1,871 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://assets/spineboy/spineboy-data-res.tres" type="SpineSkeletonDataResource" id=1]
+[ext_resource path="res://tests/batch-test.gd" type="Script" id=2]
+
+[node name="Node2D" type="Node2D"]
+rotation = -3.67884e-05
+
+[node name="SpineSprite" type="SpineSprite" parent="."]
+position = Vector2( 53.8037, 119.483 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite2" type="SpineSprite" parent="."]
+position = Vector2( 111.488, 119.485 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite3" type="SpineSprite" parent="."]
+position = Vector2( 164.678, 119.487 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = true
+preview_time = 2.36477e-39
+script = ExtResource( 2 )
+
+[node name="SpineSprite4" type="SpineSprite" parent="."]
+position = Vector2( 217.119, 121.736 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = true
+preview_time = 6.85235e-43
+script = ExtResource( 2 )
+
+[node name="SpineSprite5" type="SpineSprite" parent="."]
+position = Vector2( 277.051, 120.989 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite6" type="SpineSprite" parent="."]
+position = Vector2( 337.733, 118.744 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite7" type="SpineSprite" parent="."]
+position = Vector2( 403.658, 120.994 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite8" type="SpineSprite" parent="."]
+position = Vector2( 464.34, 120.996 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite9" type="SpineSprite" parent="."]
+position = Vector2( 525.021, 120.998 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 51.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite10" type="SpineSprite" parent="."]
+position = Vector2( 589.448, 121.001 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite11" type="SpineSprite" parent="."]
+position = Vector2( 649.381, 121.752 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite12" type="SpineSprite" parent="."]
+position = Vector2( 709.313, 123.253 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite13" type="SpineSprite" parent="."]
+position = Vector2( 769.245, 119.509 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite14" type="SpineSprite" parent="."]
+position = Vector2( 830.676, 119.511 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite15" type="SpineSprite" parent="."]
+position = Vector2( 892.106, 121.761 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite16" type="SpineSprite" parent="."]
+position = Vector2( 953.537, 121.763 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite17" type="SpineSprite" parent="."]
+position = Vector2( 56.0478, 207.883 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite18" type="SpineSprite" parent="."]
+position = Vector2( 113.733, 207.885 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite19" type="SpineSprite" parent="."]
+position = Vector2( 166.923, 207.887 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite20" type="SpineSprite" parent="."]
+position = Vector2( 219.363, 210.136 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite21" type="SpineSprite" parent="."]
+position = Vector2( 279.295, 209.389 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite22" type="SpineSprite" parent="."]
+position = Vector2( 339.977, 207.144 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite23" type="SpineSprite" parent="."]
+position = Vector2( 405.902, 209.394 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite24" type="SpineSprite" parent="."]
+position = Vector2( 466.584, 209.396 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite25" type="SpineSprite" parent="."]
+position = Vector2( 527.265, 209.399 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite26" type="SpineSprite" parent="."]
+position = Vector2( 591.692, 209.401 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite27" type="SpineSprite" parent="."]
+position = Vector2( 651.625, 210.152 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite28" type="SpineSprite" parent="."]
+position = Vector2( 711.557, 211.653 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite29" type="SpineSprite" parent="."]
+position = Vector2( 771.489, 207.909 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite30" type="SpineSprite" parent="."]
+position = Vector2( 832.92, 207.911 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite31" type="SpineSprite" parent="."]
+position = Vector2( 894.351, 210.161 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite32" type="SpineSprite" parent="."]
+position = Vector2( 955.781, 210.163 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite33" type="SpineSprite" parent="."]
+position = Vector2( 60.5394, 300.778 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite34" type="SpineSprite" parent="."]
+position = Vector2( 118.224, 300.78 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite35" type="SpineSprite" parent="."]
+position = Vector2( 171.414, 300.782 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite36" type="SpineSprite" parent="."]
+position = Vector2( 223.855, 303.032 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite37" type="SpineSprite" parent="."]
+position = Vector2( 283.787, 302.285 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite38" type="SpineSprite" parent="."]
+position = Vector2( 344.468, 300.039 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite39" type="SpineSprite" parent="."]
+position = Vector2( 410.394, 302.289 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite40" type="SpineSprite" parent="."]
+position = Vector2( 471.075, 302.292 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite41" type="SpineSprite" parent="."]
+position = Vector2( 531.757, 302.294 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite42" type="SpineSprite" parent="."]
+position = Vector2( 596.184, 302.296 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite43" type="SpineSprite" parent="."]
+position = Vector2( 656.116, 303.047 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite44" type="SpineSprite" parent="."]
+position = Vector2( 716.048, 304.548 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite45" type="SpineSprite" parent="."]
+position = Vector2( 775.981, 300.804 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite46" type="SpineSprite" parent="."]
+position = Vector2( 837.411, 300.807 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite47" type="SpineSprite" parent="."]
+position = Vector2( 898.842, 303.056 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite48" type="SpineSprite" parent="."]
+position = Vector2( 960.273, 303.059 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite49" type="SpineSprite" parent="."]
+position = Vector2( 60.5361, 389.178 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite50" type="SpineSprite" parent="."]
+position = Vector2( 118.221, 389.18 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite51" type="SpineSprite" parent="."]
+position = Vector2( 171.411, 389.182 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite52" type="SpineSprite" parent="."]
+position = Vector2( 223.851, 391.432 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite53" type="SpineSprite" parent="."]
+position = Vector2( 283.784, 390.685 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite54" type="SpineSprite" parent="."]
+position = Vector2( 344.465, 388.44 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite55" type="SpineSprite" parent="."]
+position = Vector2( 410.391, 390.689 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite56" type="SpineSprite" parent="."]
+position = Vector2( 471.072, 390.692 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite57" type="SpineSprite" parent="."]
+position = Vector2( 531.753, 390.694 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite58" type="SpineSprite" parent="."]
+position = Vector2( 596.181, 390.696 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite59" type="SpineSprite" parent="."]
+position = Vector2( 656.113, 391.448 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite60" type="SpineSprite" parent="."]
+position = Vector2( 716.045, 392.948 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite61" type="SpineSprite" parent="."]
+position = Vector2( 775.978, 389.204 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite62" type="SpineSprite" parent="."]
+position = Vector2( 837.408, 389.207 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite63" type="SpineSprite" parent="."]
+position = Vector2( 898.839, 391.456 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite64" type="SpineSprite" parent="."]
+position = Vector2( 960.269, 391.459 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite65" type="SpineSprite" parent="."]
+position = Vector2( 62.0311, 477.578 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite66" type="SpineSprite" parent="."]
+position = Vector2( 119.716, 477.581 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite67" type="SpineSprite" parent="."]
+position = Vector2( 172.906, 477.583 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite68" type="SpineSprite" parent="."]
+position = Vector2( 225.346, 479.832 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite69" type="SpineSprite" parent="."]
+position = Vector2( 285.279, 479.085 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite70" type="SpineSprite" parent="."]
+position = Vector2( 345.96, 476.84 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite71" type="SpineSprite" parent="."]
+position = Vector2( 411.886, 479.09 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite72" type="SpineSprite" parent="."]
+position = Vector2( 472.567, 479.092 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite73" type="SpineSprite" parent="."]
+position = Vector2( 533.248, 479.094 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite74" type="SpineSprite" parent="."]
+position = Vector2( 597.676, 479.096 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite75" type="SpineSprite" parent="."]
+position = Vector2( 657.608, 479.848 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite76" type="SpineSprite" parent="."]
+position = Vector2( 717.54, 481.348 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite77" type="SpineSprite" parent="."]
+position = Vector2( 777.473, 477.605 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite78" type="SpineSprite" parent="."]
+position = Vector2( 838.903, 477.607 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite79" type="SpineSprite" parent="."]
+position = Vector2( 900.334, 479.857 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite80" type="SpineSprite" parent="."]
+position = Vector2( 961.765, 479.859 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite81" type="SpineSprite" parent="."]
+position = Vector2( 66.5229, 562.233 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite82" type="SpineSprite" parent="."]
+position = Vector2( 124.208, 562.235 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite83" type="SpineSprite" parent="."]
+position = Vector2( 177.398, 562.237 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite84" type="SpineSprite" parent="."]
+position = Vector2( 229.838, 564.486 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite85" type="SpineSprite" parent="."]
+position = Vector2( 289.77, 563.74 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite86" type="SpineSprite" parent="."]
+position = Vector2( 350.452, 561.494 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite87" type="SpineSprite" parent="."]
+position = Vector2( 416.377, 563.744 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite88" type="SpineSprite" parent="."]
+position = Vector2( 477.059, 563.746 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite89" type="SpineSprite" parent="."]
+position = Vector2( 537.74, 563.749 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite90" type="SpineSprite" parent="."]
+position = Vector2( 602.167, 563.751 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite91" type="SpineSprite" parent="."]
+position = Vector2( 662.1, 564.502 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite92" type="SpineSprite" parent="."]
+position = Vector2( 722.032, 566.003 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite93" type="SpineSprite" parent="."]
+position = Vector2( 781.964, 562.259 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite94" type="SpineSprite" parent="."]
+position = Vector2( 843.395, 562.261 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite95" type="SpineSprite" parent="."]
+position = Vector2( 904.826, 564.511 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )
+
+[node name="SpineSprite96" type="SpineSprite" parent="."]
+position = Vector2( 966.256, 564.513 )
+scale = Vector2( 0.1, 0.1 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = ""
+preview_frame = false
+preview_time = 0.0
+script = ExtResource( 2 )

+ 53 - 0
spine-godot/example-v4-extension/tests/ragdoll.tscn

@@ -0,0 +1,53 @@
+[gd_scene format=2]
+
+[node name="Node2D" type="Node2D"]
+
+[node name="A" type="RigidBody2D" parent="."]
+position = Vector2( 483, 158 )
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="A"]
+position = Vector2( 6, 17 )
+polygon = PoolVector2Array( -38, 6, 28, 6, 28, -45, -40, -45 )
+__meta__ = {
+"_edit_lock_": true
+}
+
+[node name="B" type="RigidBody2D" parent="."]
+position = Vector2( 484, 228 )
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="B"]
+position = Vector2( 6, 17 )
+polygon = PoolVector2Array( -38, 6, 28, 6, 28, -45, -40, -45 )
+__meta__ = {
+"_edit_lock_": true
+}
+
+[node name="C" type="RigidBody2D" parent="."]
+position = Vector2( 485, 296 )
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="C"]
+position = Vector2( 6, 17 )
+polygon = PoolVector2Array( -38, 6, 28, 6, 28, -45, -40, -45 )
+__meta__ = {
+"_edit_lock_": true
+}
+
+[node name="PinJoint2D" type="PinJoint2D" parent="."]
+position = Vector2( 484, 189 )
+node_a = NodePath("../A")
+node_b = NodePath("../B")
+bias = 0.9
+disable_collision = false
+
+[node name="PinJoint2D2" type="PinJoint2D" parent="."]
+position = Vector2( 486, 257 )
+node_a = NodePath("../B")
+node_b = NodePath("../C")
+bias = 0.9
+disable_collision = false
+
+[node name="Ground" type="StaticBody2D" parent="."]
+position = Vector2( 489, 478 )
+
+[node name="CollisionPolygon2D" type="CollisionPolygon2D" parent="Ground"]
+polygon = PoolVector2Array( -116, -4, 128, -100, 204, 34, -156, 48 )

+ 26 - 0
spine-godot/example-v4-extension/tests/transforms.tscn

@@ -0,0 +1,26 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://assets/spineboy/spineboy-data-res.tres" type="SpineSkeletonDataResource" id=1]
+[ext_resource path="res://icon.png" type="Texture" id=2]
+
+[node name="Node2D" type="Node2D"]
+
+[node name="SpineSprite" type="SpineSprite" parent="."]
+position = Vector2( 459.397, 501.236 )
+scale = Vector2( 0.742335, 0.742335 )
+skeleton_data_res = ExtResource( 1 )
+preview_animation = "aim"
+preview_frame = true
+preview_time = 0.0
+
+[node name="SpineBoneNode" type="SpineBoneNode" parent="SpineSprite"]
+position = Vector2( 40.8752, -276.036 )
+rotation = 0.837234
+bone_name = "gun"
+bone_mode = 1
+enabled = false
+color = Color( 0, 1, 0.0627451, 0.466667 )
+
+[node name="Sprite" type="Sprite" parent="SpineSprite/SpineBoneNode"]
+visible = false
+texture = ExtResource( 2 )

+ 39 - 0
spine-godot/example-v4-extension/tests/unit-tests.gd

@@ -0,0 +1,39 @@
+extends SpineSprite
+
+func test_spine_animation():
+	var walkAnim: SpineAnimation = get_skeleton().get_data().find_animation("walk")
+	assert(walkAnim.get_name() == "walk")
+	var duration = walkAnim.get_duration()
+	walkAnim.set_duration(duration + 1)
+	assert(walkAnim.get_duration() == duration + 1)
+	assert(walkAnim.get_timelines().size() == 39)
+	var timeline: SpineTimeline = walkAnim.get_timelines()[0]
+	var propertyIds = timeline.get_property_ids()
+	assert(walkAnim.has_timeline(propertyIds))
+	assert(!walkAnim.has_timeline([0]))
+	
+func test_spine_timeline():
+	var walkAnim: SpineAnimation = get_skeleton().get_data().find_animation("walk")
+	var timeline: SpineTimeline = walkAnim.get_timelines()[0]
+	assert(timeline.get_duration() == 1)
+	assert(timeline.get_property_ids() == [4294967300])
+	assert(timeline.get_type() == "RotateTimeline")
+
+func test_spine_object_invalidation():
+	var skeleton_data = get_skeleton().get_data()
+	var bone_data = skeleton_data.find_bone("gun");
+	var old_bone_data_x = bone_data.get_x();
+	var bone = get_skeleton().find_bone("gun")
+	var old_bone_x = bone.get_x()
+	skeleton_data_res = null
+	assert(old_bone_x != bone.get_x())
+	assert(old_bone_data_x == bone_data.get_x())
+	skeleton_data.atlas_res = null;
+	assert(old_bone_data_x != bone_data.get_x())
+
+func _ready():
+	
+	test_spine_animation()
+	test_spine_timeline()
+	test_spine_object_invalidation()
+	print("All tests passed")

+ 12 - 0
spine-godot/example-v4-extension/tests/unit-tests.tscn

@@ -0,0 +1,12 @@
+[gd_scene load_steps=3 format=2]
+
+[ext_resource path="res://tests/unit-tests.gd" type="Script" id=1]
+[ext_resource path="res://assets/spineboy/spineboy-data-res.tres" type="SpineSkeletonDataResource" id=2]
+
+[node name="Node2D" type="Node2D"]
+position = Vector2( 496.659, 431.634 )
+scale = Vector2( 0.7, 0.7 )
+
+[node name="SpineSprite" type="SpineSprite" parent="."]
+skeleton_data_res = ExtResource( 2 )
+script = ExtResource( 1 )

+ 25 - 0
spine-godot/gdextension.gd

@@ -0,0 +1,25 @@
+[configuration]
+
+entry_symbol = "spine_godot_library_init"
+compatibility_minimum = "4.1"
+
+[libraries]
+
+macos.debug = "res://bin/macos/macos.framework/libspine_godot.macos.template_debug"
+macos.release = "res://bin/macos/macos.framework/libspine_godot.macos.template_release"
+ios.debug = "res://bin/ios/ios.framework/libspine_godot.ios.template_debug"
+ios.release = "res://bin/ios/ios.framework/libspine_godot.ios.template_release"
+windows.debug.x86_32 = "res://bin/windows/libspine_godot.windows.template_debug.x86_32.dll"
+windows.release.x86_32 = "res://bin/windows/libspine_godot.windows.template_release.x86_32.dll"
+windows.debug.x86_64 = "res://bin/windows/libspine_godot.windows.template_debug.x86_64.dll"
+windows.release.x86_64 = "res://bin/windows/libspine_godot.windows.template_release.x86_64.dll"
+linux.debug.x86_64 = "res://bin/linux/libspine_godot.linux.template_debug.x86_64.so"
+linux.release.x86_64 = "res://bin/linux/libspine_godot.linux.template_release.x86_64.so"
+linux.debug.arm64 = "res://bin/linux/libspine_godot.linux.template_debug.arm64.so"
+linux.release.arm64 = "res://bin/linux/libspine_godot.linux.template_release.arm64.so"
+linux.debug.rv64 = "res://bin/linux/libspine_godot.linux.template_debug.rv64.so"
+linux.release.rv64 = "res://bin/linux/libspine_godot.linux.template_release.rv64.so"
+android.debug.x86_64 = "res://bin/android/libspine_godot.android.template_debug.x86_64.so"
+android.release.x86_64 = "res://bin/android/libspine_godot.android.template_release.x86_64.so"
+android.debug.arm64 = "res://bin/android/libspine_godot.android.template_debug.arm64.so"
+android.release.arm64 = "res://bin/android/libspine_godot.android.template_release.arm64.so"

+ 52 - 0
spine-godot/methods.py

@@ -0,0 +1,52 @@
+import os
+import sys
+from enum import Enum
+
+# Colors are disabled in non-TTY environments such as pipes. This means
+# that if output is redirected to a file, it won't contain color codes.
+# Colors are always enabled on continuous integration.
+_colorize = bool(sys.stdout.isatty() or os.environ.get("CI"))
+
+
+class ANSI(Enum):
+    """
+    Enum class for adding ansi colorcodes directly into strings.
+    Automatically converts values to strings representing their
+    internal value, or an empty string in a non-colorized scope.
+    """
+
+    RESET = "\x1b[0m"
+
+    BOLD = "\x1b[1m"
+    ITALIC = "\x1b[3m"
+    UNDERLINE = "\x1b[4m"
+    STRIKETHROUGH = "\x1b[9m"
+    REGULAR = "\x1b[22;23;24;29m"
+
+    BLACK = "\x1b[30m"
+    RED = "\x1b[31m"
+    GREEN = "\x1b[32m"
+    YELLOW = "\x1b[33m"
+    BLUE = "\x1b[34m"
+    MAGENTA = "\x1b[35m"
+    CYAN = "\x1b[36m"
+    WHITE = "\x1b[37m"
+
+    PURPLE = "\x1b[38;5;93m"
+    PINK = "\x1b[38;5;206m"
+    ORANGE = "\x1b[38;5;214m"
+    GRAY = "\x1b[38;5;244m"
+
+    def __str__(self) -> str:
+        global _colorize
+        return str(self.value) if _colorize else ""
+
+
+def print_warning(*values: object) -> None:
+    """Prints a warning message with formatting."""
+    print(f"{ANSI.YELLOW}{ANSI.BOLD}WARNING:{ANSI.REGULAR}", *values, ANSI.RESET, file=sys.stderr)
+
+
+def print_error(*values: object) -> None:
+    """Prints an error message with formatting."""
+    print(f"{ANSI.RED}{ANSI.BOLD}ERROR:{ANSI.REGULAR}", *values, ANSI.RESET, file=sys.stderr)

+ 4 - 0
spine-godot/spine_godot/SpineAnimation.cpp

@@ -82,7 +82,11 @@ Array SpineAnimation::get_timelines() {
 	for (int i = 0; i < (int) result.size(); ++i) {
 		auto timeline_ref = Ref<SpineTimeline>(memnew(SpineTimeline));
 		timeline_ref->set_spine_object(get_spine_owner(), timelines[i]);
+#ifdef SPINE_GODOT_EXTENSION
+		result[i] = timeline_ref;
+#else
 		result.set(i, timeline_ref);
+#endif
 	}
 	return result;
 }

+ 4 - 0
spine-godot/spine_godot/SpineAnimationTrack.cpp

@@ -27,6 +27,8 @@
  * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *****************************************************************************/
 
+#ifndef SPINE_GODOT_EXTENSION
+
 #include "SpineAnimationTrack.h"
 #if VERSION_MAJOR > 3
 #include "core/config/engine.h"
@@ -554,3 +556,5 @@ void SpineAnimationTrack::set_debug(bool _debug) {
 bool SpineAnimationTrack::get_debug() {
 	return debug;
 }
+
+#endif

+ 3 - 5
spine-godot/spine_godot/SpineAnimationTrack.h

@@ -29,14 +29,10 @@
 
 #pragma once
 
+#ifndef SPINE_GODOT_EXTENSION
 #include "SpineSprite.h"
-#ifdef SPINE_GODOT_EXTENSION
-#include <godot_cpp/classes/animation_player.hpp>
-#include <godot_cpp/classes/animation.hpp>
-#else
 #include "scene/animation/animation_player.h"
 #include "scene/resources/animation.h"
-#endif
 
 class SpineAnimationTrack : public Node {
 	GDCLASS(SpineAnimationTrack, Node)
@@ -138,3 +134,5 @@ public:
 
 	bool get_debug();
 };
+
+#endif

+ 75 - 7
spine-godot/spine_godot/SpineAtlasResource.cpp

@@ -34,22 +34,28 @@
 #include <godot_cpp/classes/json.hpp>
 #include <godot_cpp/classes/texture.hpp>
 #include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/image.hpp>
+#include <godot_cpp/classes/image_texture.hpp>
 #else
 #include "core/io/json.h"
 #include "scene/resources/texture.h"
-#endif
-#include <spine/TextureLoader.h>
-
 #if VERSION_MAJOR > 3
 #include "core/io/image.h"
 #include "scene/resources/image_texture.h"
 #else
 #include "core/image.h"
 #endif
+#endif
 
 #ifdef TOOLS_ENABLED
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/editor_file_system.hpp>
+#else
 #include "editor/editor_file_system.h"
 #endif
+#endif
+
+#include <spine/TextureLoader.h>
 
 class GodotSpineTextureLoader : public spine::TextureLoader {
 
@@ -69,11 +75,11 @@ public:
 			return false;
 		}
 
-		auto sub_str_pos = i + prefix.size() - 1;
+		auto sub_str_pos = i + SSIZE(prefix) - 1;
 		auto res = path.substr(sub_str_pos);
 		if (!EMPTY(res)) {
 			if (res[0] != '/') {
-				path = prefix + "/" + res;
+				path = prefix + String("/") + res;
 			} else {
 				path = prefix + res;
 			}
@@ -85,7 +91,11 @@ public:
 	Ref<Texture2D> get_texture_from_image(const String &path, bool is_resource) {
 		Error error = OK;
 		if (is_resource) {
+#ifdef SPINE_GODOT_EXTENSION
+			return ResourceLoader::get_singleton()->load(path, "", ResourceLoader::CACHE_MODE_REUSE);
+#else
 			return ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REUSE, &error);
+#endif
 		} else {
 			Ref<Image> img;
 			img.instantiate();
@@ -246,10 +256,15 @@ Error SpineAtlasResource::load_from_atlas_file(const String &path) {
 }
 
 Error SpineAtlasResource::load_from_atlas_file_internal(const String &path, bool is_importing) {
-	Error err;
 	source_path = path;
+#ifdef SPINE_GODOT_EXTENSION
+	atlas_data = FileAccess::get_file_as_string(path);
+	if (SSIZE(atlas_data) == 0) return ERR_FILE_UNRECOGNIZED;
+#else
+	Error err;
 	atlas_data = FileAccess::get_file_as_string(path, &err);
 	if (err != OK) return err;
+#endif
 
 	clear();
 	texture_loader = new GodotSpineTextureLoader(&textures, &normal_maps, normal_map_prefix, is_importing);
@@ -263,8 +278,13 @@ Error SpineAtlasResource::load_from_atlas_file_internal(const String &path, bool
 
 Error SpineAtlasResource::load_from_file(const String &path) {
 	Error error;
+#ifdef SPINE_GODOT_EXTENSION
+	String json_string = FileAccess::get_file_as_string(path);
+	if (SSIZE(json_string) == 0) return ERR_FILE_UNRECOGNIZED;
+#else
 	String json_string = FileAccess::get_file_as_string(path, &error);
 	if (error != OK) return error;
+#endif
 
 #if VERSION_MAJOR > 3
 	JSON json;
@@ -297,8 +317,13 @@ Error SpineAtlasResource::load_from_file(const String &path) {
 Error SpineAtlasResource::save_to_file(const String &path) {
 	Error err;
 #if VERSION_MAJOR > 3
+#if SPINE_GODOT_EXTENSION
+	Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE);
+	if (file.is_null()) return ERR_FILE_UNRECOGNIZED;
+#else
 	Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE, &err);
 	if (err != OK) return err;
+#endif
 #else
 	FileAccess *file = FileAccess::open(path, FileAccess::WRITE, &err);
 	if (err != OK) {
@@ -322,6 +347,7 @@ Error SpineAtlasResource::save_to_file(const String &path) {
 	return OK;
 }
 
+#ifndef SPINE_GODOT_EXTENSION
 #if VERSION_MAJOR > 3
 Error SpineAtlasResource::copy_from(const Ref<Resource> &p_resource) {
 	auto error = Resource::copy_from(p_resource);
@@ -343,7 +369,11 @@ Error SpineAtlasResource::copy_from(const Ref<Resource> &p_resource) {
 	return OK;
 }
 #endif
+#endif
 
+#ifdef SPINE_GODOT_EXTENSION
+Variant SpineAtlasResourceFormatLoader::_load(const String &path, const String &original_path, bool use_sub_threads, int32_t cache_mode) {
+#else
 #if VERSION_MAJOR > 3
 RES SpineAtlasResourceFormatLoader::load(const String &path, const String &original_path, Error *error, bool use_sub_threads, float *progress, CacheMode cache_mode) {
 #else
@@ -352,41 +382,79 @@ RES SpineAtlasResourceFormatLoader::load(const String &path, const String &origi
 #else
 RES SpineAtlasResourceFormatLoader::load(const String &path, const String &original_path, Error *error) {
 #endif
+#endif
 #endif
 	Ref<SpineAtlasResource> atlas = memnew(SpineAtlasResource);
 	atlas->load_from_file(path);
+#ifndef SPINE_GODOT_EXTENSION
 	if (error) *error = OK;
+#endif
 	return atlas;
 }
 
+
+#ifdef SPINE_GODOT_EXTENSION
+PackedStringArray SpineAtlasResourceFormatLoader::_get_recognized_extensions() {
+	PackedStringArray extensions;
+	extensions.push_back("spatlas");
+	return extensions;
+}
+#else
 void SpineAtlasResourceFormatLoader::get_recognized_extensions(List<String> *extensions) const {
 	const char atlas_ext[] = "spatlas";
 	if (!extensions->find(atlas_ext))
 		extensions->push_back(atlas_ext);
 }
+#endif
 
+#ifdef SPINE_GODOT_EXTENSION
+String SpineAtlasResourceFormatLoader::_get_resource_type(const String &path) {
+#else
 String SpineAtlasResourceFormatLoader::get_resource_type(const String &path) const {
+#endif
 	return path.ends_with("spatlas") || path.ends_with(".atlas") ? "SpineAtlasResource" : "";
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+bool SpineAtlasResourceFormatLoader::_handles_type(const StringName &type) {
+#else
 bool SpineAtlasResourceFormatLoader::handles_type(const String &type) const {
-	return type == "SpineAtlasResource" || ClassDB::is_parent_class(type, "SpineAtlasResource");
+#endif
+	return type == StringName("SpineAtlasResource") || ClassDB::is_parent_class(type, "SpineAtlasResource");
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+Error SpineAtlasResourceFormatSaver::_save(const Ref<Resource> &resource, const String &path, uint32_t flags) {
+#else
 #if VERSION_MAJOR > 3
 Error SpineAtlasResourceFormatSaver::save(const RES &resource, const String &path, uint32_t flags) {
 #else
 Error SpineAtlasResourceFormatSaver::save(const String &path, const RES &resource, uint32_t flags) {
+#endif
 #endif
 	Ref<SpineAtlasResource> res = resource;
 	return res->save_to_file(path);
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+PackedStringArray SpineAtlasResourceFormatSaver::_get_recognized_extensions(const Ref<Resource> &resource) {
+	PackedStringArray extensions;
+	if (Object::cast_to<SpineAtlasResource>(*resource)) {
+		extensions.push_back("spatlas");
+	}
+	return extensions;
+}
+#else
 void SpineAtlasResourceFormatSaver::get_recognized_extensions(const RES &resource, List<String> *extensions) const {
 	if (Object::cast_to<SpineAtlasResource>(*resource))
 		extensions->push_back("spatlas");
 }
+#endif
 
+#ifdef SPINE_GODOT_EXTENSION
+bool SpineAtlasResourceFormatSaver::_recognize(const RES &resource) {
+#else
 bool SpineAtlasResourceFormatSaver::recognize(const RES &resource) const {
+#endif
 	return Object::cast_to<SpineAtlasResource>(*resource) != nullptr;
 }

+ 2 - 0
spine-godot/spine_godot/SpineAtlasResource.h

@@ -80,8 +80,10 @@ public:
 
 	Error save_to_file(const String &path);// .spatlas
 
+#ifndef SPINE_GODOT_EXTENSION
 #if VERSION_MAJOR > 3
 	virtual Error copy_from(const Ref<Resource> &p_resource);
+#endif
 #endif
 
 	String get_source_path();

+ 16 - 3
spine-godot/spine_godot/SpineBoneNode.cpp

@@ -29,11 +29,16 @@
 
 #include "SpineBoneNode.h"
 
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/engine.hpp>
+#include <godot_cpp/classes/scene_tree.hpp>
+#else
 #if VERSION_MAJOR > 3
 #include "core/config/engine.h"
 #else
 #include "core/engine.h"
 #endif
+#endif
 
 void SpineBoneNode::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_bone_mode"), &SpineBoneNode::set_bone_mode);
@@ -102,7 +107,11 @@ void SpineBoneNode::_notification(int what) {
 }
 
 void SpineBoneNode::_get_property_list(List<PropertyInfo> *list) const {
+#ifdef SPINE_GODOT_EXTENSION
+	PackedStringArray bone_names;
+#else
 	Vector<String> bone_names;
+#endif
 	SpineSprite *sprite = find_parent_sprite();
 	if (sprite) sprite->get_skeleton_data_res()->get_bone_names(bone_names);
 	else
@@ -110,7 +119,7 @@ void SpineBoneNode::_get_property_list(List<PropertyInfo> *list) const {
 	auto element = list->front();
 	while (element) {
 		auto property_info = element->get();
-		if (property_info.name == "SpineBoneNode") break;
+		if (property_info.name == StringName("SpineBoneNode")) break;
 		element = element->next();
 	}
 	PropertyInfo slot_name_property;
@@ -123,7 +132,7 @@ void SpineBoneNode::_get_property_list(List<PropertyInfo> *list) const {
 }
 
 bool SpineBoneNode::_get(const StringName &property, Variant &value) const {
-	if (property == "bone_name") {
+	if (property == StringName("bone_name")) {
 		value = bone_name;
 		return true;
 	}
@@ -131,7 +140,7 @@ bool SpineBoneNode::_get(const StringName &property, Variant &value) const {
 }
 
 bool SpineBoneNode::_set(const StringName &property, const Variant &value) {
-	if (property == "bone_name") {
+	if (property == StringName("bone_name")) {
 		bone_name = value;
 		SpineSprite *sprite = find_parent_sprite();
 		init_transform(sprite);
@@ -218,7 +227,11 @@ void SpineBoneNode::draw() {
 	if (bone_length == 0) {
 		draw_circle(Vector2(0, 0), debug_thickness, debug_color);
 	} else {
+		#ifdef SPINE_GODOT_EXTENSION
+		PackedVector2Array points;
+		#else
 		Vector<Vector2> points;
+		#endif
 		points.push_back(Vector2(-debug_thickness, 0));
 		points.push_back(Vector2(0, debug_thickness));
 		points.push_back(Vector2(bone_length, 0));

+ 4 - 0
spine-godot/spine_godot/SpineBoneNode.h

@@ -32,7 +32,11 @@
 #include "SpineCommon.h"
 #include "SpineSkeleton.h"
 #include "SpineSprite.h"
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/node2d.hpp>
+#else
 #include "scene/2d/node_2d.h"
+#endif
 
 class SpineBoneNode : public Node2D {
 	GDCLASS(SpineBoneNode, Node2D)

+ 4 - 0
spine-godot/spine_godot/SpineCommon.h

@@ -46,8 +46,12 @@ using namespace godot;
 #define RES Ref<Resource>
 #define REF Ref<RefCounted>
 #define GEOMETRY2D Geometry2D
+#ifndef VERSION_MAJOR
 #define VERSION_MAJOR GODOT_VERSION_MAJOR
+#endif
+#ifndef VERSION_MINOR
 #define VERSION_MINOR GODOT_VERSION_MINOR
+#endif
 // FIXME this doesn't do the same as the engine SNAME in terms of caching
 #define SNAME(name) StringName(name)
 #define RS RenderingServer

+ 70 - 3
spine-godot/spine_godot/SpineEditorPlugin.cpp

@@ -34,8 +34,16 @@
 #include "SpineSkeletonFileResource.h"
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
+#else
 #include "editor/editor_undo_redo_manager.h"
+#endif
+#ifdef SPINE_GODOT_EXTENSION
+Error SpineAtlasResourceImportPlugin::_import(const String &source_file, const String &save_path, const Dictionary &options, const TypedArray<String> &platform_variants, const TypedArray<String> &gen_files) const {
+#else
 Error SpineAtlasResourceImportPlugin::import(const String &source_file, const String &save_path, const HashMap<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) {
+#endif
 #else
 Error SpineAtlasResourceImportPlugin::import(const String &source_file, const String &save_path, const Map<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) {
 #endif
@@ -43,15 +51,21 @@ Error SpineAtlasResourceImportPlugin::import(const String &source_file, const St
 	atlas->set_normal_texture_prefix(options["normal_map_prefix"]);
 	atlas->load_from_atlas_file_internal(source_file, true);
 
-	String file_name = vformat("%s.%s", save_path, get_save_extension());
+	String file_name = save_path + String(".") + _get_save_extension();
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	auto error = ResourceSaver::get_singleton()->save(atlas, file_name);
+#else
 	auto error = ResourceSaver::save(atlas, file_name);
+#endif
 #else
 	auto error = ResourceSaver::save(file_name, atlas);
 #endif
 	return error;
 }
 
+// FIXME get_import_options is not available in godot-cpp
+#ifndef SPINE_GODOT_EXTENSION
 #if VERSION_MAJOR > 3
 void SpineAtlasResourceImportPlugin::get_import_options(const String &path, List<ImportOption> *options, int preset) const {
 #else
@@ -66,9 +80,14 @@ void SpineAtlasResourceImportPlugin::get_import_options(List<ImportOption> *opti
 		options->push_back(op);
 	}
 }
+#endif
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+Error SpineJsonResourceImportPlugin::_import(const String &source_file, const String &save_path, const Dictionary &options, const TypedArray<String> &platform_variants, const TypedArray<String> &gen_files) const {
+#else
 Error SpineJsonResourceImportPlugin::import(const String &source_file, const String &save_path, const HashMap<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) {
+#endif
 #else
 Error SpineJsonResourceImportPlugin::import(const String &source_file, const String &save_path, const Map<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) {
 #endif
@@ -76,17 +95,27 @@ Error SpineJsonResourceImportPlugin::import(const String &source_file, const Str
 	Error error = skeleton_file_res->load_from_file(source_file);
 	if (error != OK) return error;
 
-	String file_name = vformat("%s.%s", save_path, get_save_extension());
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	String file_name = save_path + String(".") + _get_save_extension();
+	error = ResourceSaver::get_singleton()->save(skeleton_file_res, file_name);
+#else
+	String file_name = save_path + String(".") + get_save_extension();
 	error = ResourceSaver::save(skeleton_file_res, file_name);
+#endif
 #else
+	String file_name = save_path + String(".") + get_save_extension();
 	error = ResourceSaver::save(file_name, skeleton_file_res);
 #endif
 	return error;
 }
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+Error SpineBinaryResourceImportPlugin::_import(const String &source_file, const String &save_path, const Dictionary &options, const TypedArray<String> &platform_variants, const TypedArray<String> &gen_files) const {
+#else
 Error SpineBinaryResourceImportPlugin::import(const String &source_file, const String &save_path, const HashMap<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) {
+#endif
 #else
 Error SpineBinaryResourceImportPlugin::import(const String &source_file, const String &save_path, const Map<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) {
 #endif
@@ -94,15 +123,30 @@ Error SpineBinaryResourceImportPlugin::import(const String &source_file, const S
 	Error error = skeleton_file_res->load_from_file(source_file);
 	if (error != OK) return error;
 
-	String file_name = vformat("%s.%s", save_path, get_save_extension());
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	String file_name = save_path + String(".") + _get_save_extension();
+	error = ResourceSaver::get_singleton()->save(skeleton_file_res, file_name);
+#else
+	String file_name = save_path + String(".") + get_save_extension();
 	error = ResourceSaver::save(skeleton_file_res, file_name);
+#endif
 #else
+	String file_name = save_path + String(".") + get_save_extension();
 	error = ResourceSaver::save(file_name, skeleton_file_res);
 #endif
 	return error;
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+SpineEditorPlugin::SpineEditorPlugin() {
+	add_import_plugin(memnew(SpineAtlasResourceImportPlugin));
+	add_import_plugin(memnew(SpineJsonResourceImportPlugin));
+	add_import_plugin(memnew(SpineBinaryResourceImportPlugin));
+	add_inspector_plugin(memnew(SpineSkeletonDataResourceInspectorPlugin));
+	add_inspector_plugin(memnew(SpineSpriteInspectorPlugin));
+}
+#else
 SpineEditorPlugin::SpineEditorPlugin(EditorNode *node) {
 	add_import_plugin(memnew(SpineAtlasResourceImportPlugin));
 	add_import_plugin(memnew(SpineJsonResourceImportPlugin));
@@ -110,17 +154,28 @@ SpineEditorPlugin::SpineEditorPlugin(EditorNode *node) {
 	add_inspector_plugin(memnew(SpineSkeletonDataResourceInspectorPlugin));
 	add_inspector_plugin(memnew(SpineSpriteInspectorPlugin));
 }
+#endif
 
+#ifdef SPINE_GODOT_EXTENSION
+bool SpineSkeletonDataResourceInspectorPlugin::_can_handle(Object *object) const {
+#else
 bool SpineSkeletonDataResourceInspectorPlugin::can_handle(Object *object) {
+#endif
 	return object->is_class("SpineSkeletonDataResource");
 }
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+bool SpineSkeletonDataResourceInspectorPlugin::_parse_property(Object *object, Variant::Type type, const String &path, PropertyHint hint, const String &hint_text, const BitField<PropertyUsageFlags> p_usage, bool wide) {
+#else
 bool SpineSkeletonDataResourceInspectorPlugin::parse_property(Object *object, const Variant::Type type, const String &path, const PropertyHint hint, const String &hint_text, const BitField<PropertyUsageFlags> p_usage, const bool wide) {
+#endif
 #else
 bool SpineSkeletonDataResourceInspectorPlugin::parse_property(Object *object, Variant::Type type, const String &path,
 															  PropertyHint hint, const String &hint_text, int usage) {
 #endif
+// FIXME can't do this in godot-cpp
+#ifndef SPINE_GODOT_EXTENSION
 	if (path == "animation_mixes") {
 		Ref<SpineSkeletonDataResource> skeleton_data = Object::cast_to<SpineSkeletonDataResource>(object);
 		if (!skeleton_data.is_valid() || !skeleton_data->is_skeleton_data_loaded()) return true;
@@ -129,9 +184,12 @@ bool SpineSkeletonDataResourceInspectorPlugin::parse_property(Object *object, Va
 		add_property_editor(path, mixes_property);
 		return true;
 	}
+#endif
 	return false;
 }
 
+// FIXME can't do this in godot-cpp
+#ifndef SPINE_GODOT_EXTENSION
 SpineEditorPropertyAnimationMixes::SpineEditorPropertyAnimationMixes() : skeleton_data(nullptr), container(nullptr), updating(false) {
 	INSTANTIATE(array_object);
 }
@@ -349,6 +407,7 @@ void SpineEditorPropertyAnimationMix::update_property() {
 
 	updating = false;
 }
+#endif
 
 void SpineSpriteInspectorPlugin::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("button_clicked"), &SpineSpriteInspectorPlugin::button_clicked);
@@ -357,11 +416,19 @@ void SpineSpriteInspectorPlugin::_bind_methods() {
 void SpineSpriteInspectorPlugin::button_clicked(const String &button_name) {
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+bool SpineSpriteInspectorPlugin::_can_handle(Object *object) const {
+#else
 bool SpineSpriteInspectorPlugin::can_handle(Object *object) {
+#endif
 	return Object::cast_to<SpineSprite>(object) != nullptr;
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+void SpineSpriteInspectorPlugin::_parse_begin(Object *object) {
+#else
 void SpineSpriteInspectorPlugin::parse_begin(Object *object) {
+#endif
 	sprite = Object::cast_to<SpineSprite>(object);
 	if (!sprite) return;
 	if (!sprite->get_skeleton_data_res().is_valid() || !sprite->get_skeleton_data_res()->is_skeleton_data_loaded()) return;

+ 120 - 1
spine-godot/spine_godot/SpineEditorPlugin.h

@@ -30,19 +30,47 @@
 #pragma once
 
 #ifdef TOOLS_ENABLED
-#include "SpineCommon.h"
 #include "SpineSprite.h"
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/editor_import_plugin.hpp>
+#else
 #include "editor/import/editor_import_plugin.h"
 #endif
+#endif
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/editor_plugin.hpp>
+#include <godot_cpp/classes/editor_inspector_plugin.hpp>
+#include <godot_cpp/classes/editor_property.hpp>
+#else
 #include "editor/editor_node.h"
 #include "editor/editor_properties.h"
 #include "editor/editor_properties_array_dict.h"
+#endif
 
 class SpineAtlasResourceImportPlugin : public EditorImportPlugin {
 	GDCLASS(SpineAtlasResourceImportPlugin, EditorImportPlugin)
 
 public:
+#ifdef SPINE_GODOT_EXTENSION
+	String _get_importer_name() const override { return "spine.atlas"; }
+
+	String _get_visible_name() const override { return "Spine Runtime Atlas"; }
+
+	PackedStringArray _get_recognized_extensions() const override {
+		PackedStringArray extensions;
+		extensions.push_back("atlas");
+		return extensions;
+	}
+
+	String _get_preset_name(int idx) const override { return idx == 0 ? "Default" : "Unknown"; }
+
+	int _get_preset_count() const override { return 1; }
+
+	String _get_save_extension() const override { return "spatlas"; }
+
+	String _get_resource_type() const override { return "SpineAtlasResource"; }
+#else
 	String get_importer_name() const override { return "spine.atlas"; }
 
 	String get_visible_name() const override { return "Spine Runtime Atlas"; }
@@ -56,8 +84,18 @@ public:
 	String get_save_extension() const override { return "spatlas"; }
 
 	String get_resource_type() const override { return "SpineAtlasResource"; }
+#endif
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	virtual int _get_import_order() const override { return IMPORT_ORDER_DEFAULT; }
+
+	virtual float _get_priority() const override { return 1; }
+
+	virtual bool _get_option_visibility(const String &p_path, const StringName &p_option_name, const Dictionary &p_options) const override { return true; };
+
+	virtual Error _import(const String &p_source_file, const String &p_save_path, const Dictionary &p_options, const TypedArray<String> &p_platform_variants, const TypedArray<String> &p_gen_files) const override;
+#else
 	int get_import_order() const override { return IMPORT_ORDER_DEFAULT; }
 
 	float get_priority() const override { return 1; }
@@ -67,6 +105,7 @@ public:
 	virtual bool get_option_visibility(const String &path, const String &option, const HashMap<StringName, Variant> &options) const override { return true; }
 
 	Error import(const String &source_file, const String &save_path, const HashMap<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) override;
+#endif
 #else
 	void get_import_options(List<ImportOption> *options, int preset) const override;
 
@@ -80,6 +119,25 @@ class SpineJsonResourceImportPlugin : public EditorImportPlugin {
 	GDCLASS(SpineJsonResourceImportPlugin, EditorImportPlugin)
 
 public:
+#ifdef SPINE_GODOT_EXTENSION
+	String _get_importer_name() const override { return "spine.json"; }
+
+	String _get_visible_name() const override { return "Spine Skeleton Json"; }
+
+	PackedStringArray _get_recognized_extensions() const override {
+		PackedStringArray extensions;
+		extensions.push_back("spine-json");
+		return extensions;
+	}
+
+	String _get_preset_name(int idx) const override { return idx == 0 ? "Default" : "Unknown"; }
+
+	int _get_preset_count() const override { return 1; }
+
+	String _get_save_extension() const override { return "spjson"; }
+
+	String _get_resource_type() const override { return "SpineSkeletonFileResource"; }
+#else
 	String get_importer_name() const override { return "spine.json"; }
 
 	String get_visible_name() const override { return "Spine Skeleton Json"; }
@@ -93,8 +151,18 @@ public:
 	String get_save_extension() const override { return "spjson"; }
 
 	String get_resource_type() const override { return "SpineSkeletonFileResource"; }
+#endif
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	int _get_import_order() const override { return IMPORT_ORDER_DEFAULT; }
+
+	float _get_priority() const override { return 1; }
+
+	virtual bool _get_option_visibility(const String &p_path, const StringName &p_option_name, const Dictionary &p_options) const override { return true; };
+
+	virtual Error _import(const String &p_source_file, const String &p_save_path, const Dictionary &p_options, const TypedArray<String> &p_platform_variants, const TypedArray<String> &p_gen_files) const override;
+#else
 	int get_import_order() const override { return IMPORT_ORDER_DEFAULT; }
 
 	float get_priority() const override { return 1; }
@@ -104,6 +172,7 @@ public:
 	bool get_option_visibility(const String &path, const String &option, const HashMap<StringName, Variant> &options) const override { return true; }
 
 	Error import(const String &source_file, const String &save_path, const HashMap<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) override;
+#endif
 #else
 	void get_import_options(List<ImportOption> *options, int preset) const override {}
 
@@ -117,6 +186,25 @@ class SpineBinaryResourceImportPlugin : public EditorImportPlugin {
 	GDCLASS(SpineBinaryResourceImportPlugin, EditorImportPlugin);
 
 public:
+#ifdef SPINE_GODOT_EXTENSION
+	String _get_importer_name() const override { return "spine.skel"; }
+
+	String _get_visible_name() const override { return "Spine Skeleton Binary"; }
+
+	PackedStringArray _get_recognized_extensions() const override {
+		PackedStringArray extensions;
+		extensions.push_back("skel");
+		return extensions;
+	}
+
+	String _get_preset_name(int idx) const override { return idx == 0 ? "Default" : "Unknown"; }
+
+	int _get_preset_count() const override { return 1; }
+
+	String _get_save_extension() const override { return "spskel"; }
+
+	String _get_resource_type() const override { return "SpineSkeletonFileResource"; }
+#else
 	String get_importer_name() const override { return "spine.skel"; }
 
 	String get_visible_name() const override { return "Spine Skeleton Binary"; }
@@ -130,8 +218,18 @@ public:
 	String get_save_extension() const override { return "spskel"; }
 
 	String get_resource_type() const override { return "SpineSkeletonFileResource"; }
+#endif
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	int _get_import_order() const override { return IMPORT_ORDER_DEFAULT; }
+
+	float _get_priority() const override { return 1; }
+
+	virtual bool _get_option_visibility(const String &p_path, const StringName &p_option_name, const Dictionary &p_options) const override { return true; };
+
+	virtual Error _import(const String &p_source_file, const String &p_save_path, const Dictionary &p_options, const TypedArray<String> &p_platform_variants, const TypedArray<String> &p_gen_files) const override;
+#else
 	int get_import_order() const override { return IMPORT_ORDER_DEFAULT; }
 
 	float get_priority() const override { return 1; }
@@ -141,6 +239,7 @@ public:
 	bool get_option_visibility(const String &path, const String &option, const HashMap<StringName, Variant> &options) const override { return true; }
 
 	Error import(const String &source_file, const String &save_path, const HashMap<StringName, Variant> &options, List<String> *platform_variants, List<String> *gen_files, Variant *metadata) override;
+#endif
 #else
 	void get_import_options(List<ImportOption> *options, int preset) const override {}
 
@@ -153,24 +252,38 @@ public:
 class SpineEditorPlugin : public EditorPlugin {
 	GDCLASS(SpineEditorPlugin, EditorPlugin)
 
+	static void _bind_methods() {}
+
 public:
+#ifdef SPINE_GODOT_EXTENSION
+	explicit SpineEditorPlugin();
+
+	String _get_plugin_name() const override { return "SpineEditorPlugin"; }
+#else
 	explicit SpineEditorPlugin(EditorNode *node);
 
 	String get_name() const override { return "SpineEditorPlugin"; }
+#endif
 };
 
 class SpineSkeletonDataResourceInspectorPlugin : public EditorInspectorPlugin {
 	GDCLASS(SpineSkeletonDataResourceInspectorPlugin, EditorInspectorPlugin)
 
 public:
+#ifdef SPINE_GODOT_EXTENSION
+	bool _can_handle(Object *p_object) const override;
+	bool _parse_property(Object *object, Variant::Type type, const String &path, PropertyHint hint, const String &hint_text, const BitField<PropertyUsageFlags> p_usage, bool wide) override;
+#else
 	bool can_handle(Object *object) override;
 #if VERSION_MAJOR > 3
 	bool parse_property(Object *object, Variant::Type type, const String &path, PropertyHint hint, const String &hint_text, const BitField<PropertyUsageFlags> p_usage, bool wide) override;
 #else
 	bool parse_property(Object *object, Variant::Type type, const String &path, PropertyHint hint, const String &hint_text, int usage) override;
 #endif
+#endif
 };
 
+#ifndef SPINE_GODOT_EXTENSION
 class SpineEditorPropertyAnimationMix;
 
 class SpineEditorPropertyAnimationMixes : public EditorProperty {
@@ -210,6 +323,7 @@ public:
 	void setup(SpineEditorPropertyAnimationMixes *mixes_property, const Ref<SpineSkeletonDataResource> &skeleton_data, int index);
 	void update_property() override;
 };
+#endif
 
 class SpineSpriteInspectorPlugin : public EditorInspectorPlugin {
 	GDCLASS(SpineSpriteInspectorPlugin, EditorInspectorPlugin)
@@ -220,8 +334,13 @@ class SpineSpriteInspectorPlugin : public EditorInspectorPlugin {
 	void button_clicked(const String &button_name);
 
 public:
+#ifdef SPINE_GODOT_EXTENSION
+	virtual bool _can_handle(Object *object) const override;
+	virtual void _parse_begin(Object *object) override;
+#else
 	bool can_handle(Object *object) override;
 	void parse_begin(Object *object) override;
+#endif
 };
 
 #endif

+ 28 - 0
spine-godot/spine_godot/SpineSkeletonDataResource.cpp

@@ -30,6 +30,10 @@
 #include "SpineSkeletonDataResource.h"
 #include "SpineCommon.h"
 
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/encoded_object_as_id.hpp>
+#endif
+
 void SpineAnimationMix::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("set_from", "from"),
 						 &SpineAnimationMix::set_from);
@@ -201,9 +205,15 @@ void SpineSkeletonDataResource::update_skeleton_data() {
 #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;
 
@@ -287,8 +297,13 @@ 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;
@@ -299,8 +314,13 @@ void SpineSkeletonDataResource::get_animation_names(
 	}
 }
 
+#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;
@@ -311,7 +331,11 @@ void SpineSkeletonDataResource::get_skin_names(
 	}
 }
 
+#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;
@@ -322,7 +346,11 @@ void SpineSkeletonDataResource::get_slot_names(Vector<String> &slot_names) {
 	}
 }
 
+#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;

+ 15 - 0
spine-godot/spine_godot/SpineSkeletonDataResource.h

@@ -84,8 +84,13 @@ private:
 
 	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();
@@ -106,6 +111,15 @@ public:
 		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;
@@ -113,6 +127,7 @@ public:
 	void get_slot_names(Vector<String> &slot_names);
 
 	void get_bone_names(Vector<String> &bone_names);
+#endif
 
 	void set_default_mix(float default_mix);
 

+ 62 - 3
spine-godot/spine_godot/SpineSkeletonFileResource.cpp

@@ -129,16 +129,26 @@ static bool checkBinary(const char *binaryData, int length) {
 Error SpineSkeletonFileResource::load_from_file(const String &path) {
 	Error error = OK;
 	if (path.ends_with(".spjson") || path.ends_with(".spine-json")) {
+#ifdef SPINE_GODOT_EXTENSION
+		json = FileAccess::get_file_as_string(path);
+		if (SSIZE(json) == 0) return ERR_INVALID_DATA;
+#else
 		json = FileAccess::get_file_as_string(path, &error);
 		if (error != OK) return error;
+#endif
 		if (!checkJson(json.utf8())) return ERR_INVALID_DATA;
 	} else {
+#ifdef SPINE_GODOT_EXTENSION
+		binary = FileAccess::get_file_as_bytes(path);
+		if (binary.size() == 0) return ERR_INVALID_DATA;
+#else
 #if VERSION_MAJOR > 3
 		binary = FileAccess::get_file_as_bytes(path, &error);
 #else
 		binary = FileAccess::get_file_as_array(path, &error);
 #endif
 		if (error != OK) return error;
+#endif
 		if (!checkBinary((const char *) binary.ptr(), binary.size())) return ERR_INVALID_DATA;
 	}
 	return error;
@@ -146,6 +156,10 @@ Error SpineSkeletonFileResource::load_from_file(const String &path) {
 
 Error SpineSkeletonFileResource::save_to_file(const String &path) {
 	Error error;
+#ifdef SPINE_GODOT_EXTENSION
+	Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE);
+	if (!file.is_valid()) return ERR_FILE_CANT_OPEN;
+#else
 #if VERSION_MAJOR > 3
 	Ref<FileAccess> file = FileAccess::open(path, FileAccess::WRITE, &error);
 	if (error != OK) return error;
@@ -155,6 +169,7 @@ Error SpineSkeletonFileResource::save_to_file(const String &path) {
 		if (file) file->close();
 		return error;
 	}
+#endif
 #endif
 	if (!is_binary())
 		file->store_string(json);
@@ -168,6 +183,7 @@ Error SpineSkeletonFileResource::save_to_file(const String &path) {
 	return OK;
 }
 
+#ifndef SPINE_GODOT_EXTENSION
 #if VERSION_MAJOR > 3
 Error SpineSkeletonFileResource::copy_from(const Ref<Resource> &p_resource) {
 	auto error = Resource::copy_from(p_resource);
@@ -179,7 +195,11 @@ Error SpineSkeletonFileResource::copy_from(const Ref<Resource> &p_resource) {
 	return OK;
 }
 #endif
+#endif
 
+#ifdef SPINE_GODOT_EXTENSION
+Variant SpineSkeletonFileResourceFormatLoader::_load(const String &path, const String &original_path, bool use_sub_threads, int32_t cache_mode) {
+#else
 #if VERSION_MAJOR > 3
 RES SpineSkeletonFileResourceFormatLoader::load(const String &path, const String &original_path, Error *error, bool use_sub_threads, float *progress, CacheMode cache_mode) {
 #else
@@ -188,43 +208,82 @@ RES SpineSkeletonFileResourceFormatLoader::load(const String &path, const String
 #else
 RES SpineSkeletonFileResourceFormatLoader::load(const String &path, const String &original_path, Error *error) {
 #endif
+#endif
 #endif
 	Ref<SpineSkeletonFileResource> skeleton_file = memnew(SpineSkeletonFileResource);
 	skeleton_file->load_from_file(path);
+#ifndef SPINE_GODOT_EXTENSION
 	if (error) *error = OK;
+#endif
 	return skeleton_file;
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+PackedStringArray SpineSkeletonFileResourceFormatLoader::_get_recognized_extensions() {
+	PackedStringArray extensions;
+	extensions.push_back("spjson");
+	extensions.push_back("spskel");
+	return extensions;
+}
+#else
 void SpineSkeletonFileResourceFormatLoader::get_recognized_extensions(List<String> *extensions) const {
 	extensions->push_back("spjson");
 	extensions->push_back("spskel");
 }
+#endif
 
+#ifdef SPINE_GODOT_EXTENSION
+String SpineSkeletonFileResourceFormatLoader::_get_resource_type(const String &path) {
+#else
 String SpineSkeletonFileResourceFormatLoader::get_resource_type(const String &path) const {
+#endif
 	return path.ends_with(".spjson") || path.ends_with(".spskel") || path.ends_with(".spine-json") || path.ends_with(".skel") ? "SpineSkeletonFileResource" : "";
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+bool SpineSkeletonFileResourceFormatLoader::_handles_type(const StringName &type) {
+#else
 bool SpineSkeletonFileResourceFormatLoader::handles_type(const String &type) const {
-	return type == "SpineSkeletonFileResource" || ClassDB::is_parent_class(type, "SpineSkeletonFileResource");
+#endif
+	return type == StringName("SpineSkeletonFileResource") || ClassDB::is_parent_class(type, "SpineSkeletonFileResource");
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+Error SpineSkeletonFileResourceFormatSaver::_save(const Ref<Resource> &resource, const String &path, uint32_t flags) {
+#else
 #if VERSION_MAJOR > 3
 Error SpineSkeletonFileResourceFormatSaver::save(const RES &resource, const String &path, uint32_t flags) {
 #else
 Error SpineSkeletonFileResourceFormatSaver::save(const String &path, const RES &resource, uint32_t flags) {
+#endif
 #endif
 	Ref<SpineSkeletonFileResource> res = resource;
 	Error error = res->save_to_file(path);
 	return error;
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+PackedStringArray SpineSkeletonFileResourceFormatSaver::_get_recognized_extensions(const Ref<Resource> &resource) {
+	PackedStringArray extensions;
+	if (Object::cast_to<SpineSkeletonFileResource>(*resource)) {
+		extensions.push_back("spjson");
+		extensions.push_back("spskel");
+	}
+	return extensions;
+}
+#else
 void SpineSkeletonFileResourceFormatSaver::get_recognized_extensions(const RES &resource, List<String> *p_extensions) const {
 	if (Object::cast_to<SpineSkeletonFileResource>(*resource)) {
 		p_extensions->push_back("spjson");
 		p_extensions->push_back("spskel");
 	}
 }
+#endif
 
-bool SpineSkeletonFileResourceFormatSaver::recognize(const RES &p_resource) const {
-	return Object::cast_to<SpineSkeletonFileResource>(*p_resource) != nullptr;
+#ifdef SPINE_GODOT_EXTENSION
+bool SpineSkeletonFileResourceFormatSaver::_recognize(const Ref<Resource> &resource) {
+#else
+bool SpineSkeletonFileResourceFormatSaver::recognize(const RES &resource) const {
+#endif
+	return Object::cast_to<SpineSkeletonFileResource>(*resource) != nullptr;
 }

+ 10 - 0
spine-godot/spine_godot/SpineSkeletonFileResource.h

@@ -50,12 +50,20 @@ protected:
 	static void _bind_methods();
 
 	String json;
+#ifdef SPINE_GODOT_EXTENSION
+	PackedByteArray binary;
+#else
 	Vector<uint8_t> binary;
+#endif
 
 public:
 	bool is_binary() { return binary.size() > 0; }
 
+#ifdef SPINE_GODOT_EXTENSION
+	const PackedByteArray &get_binary() { return binary; }
+#else
 	const Vector<uint8_t> &get_binary() { return binary; }
+#endif
 
 	const String &get_json() { return json; }
 
@@ -63,9 +71,11 @@ public:
 
 	Error save_to_file(const String &path);
 
+#ifndef SPINE_GODOT_EXTENSION
 #if VERSION_MAJOR > 3
 	virtual Error copy_from(const Ref<Resource> &p_resource);
 #endif
+#endif
 };
 
 class SpineSkeletonFileResourceFormatLoader : public ResourceFormatLoader {

+ 15 - 3
spine-godot/spine_godot/SpineSlotNode.cpp

@@ -30,9 +30,17 @@
 #include "SpineSlotNode.h"
 
 #ifdef TOOLS_ENABLED
+#ifdef SPINE_GODOT_EXTENSION
+// FIXME
+#else
 #include "editor/editor_node.h"
 #endif
+#endif
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/viewport.hpp>
+#else
 #include "scene/main/viewport.h"
+#endif
 
 void SpineSlotNode::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("_on_world_transforms_changed", "spine_sprite"), &SpineSlotNode::on_world_transforms_changed);
@@ -99,7 +107,11 @@ void SpineSlotNode::_notification(int what) {
 }
 
 void SpineSlotNode::_get_property_list(List<PropertyInfo> *list) const {
+#ifdef SPINE_GODOT_EXTENSION
+	PackedStringArray slot_names;
+#else
 	Vector<String> slot_names;
+#endif
 	SpineSprite *sprite = cast_to<SpineSprite>(get_parent());
 	if (sprite && sprite->get_skeleton_data_res().is_valid()) sprite->get_skeleton_data_res()->get_slot_names(slot_names);
 	else
@@ -107,7 +119,7 @@ void SpineSlotNode::_get_property_list(List<PropertyInfo> *list) const {
 	auto element = list->front();
 	while (element) {
 		auto property_info = element->get();
-		if (property_info.name == "SpineSlotNode") break;
+		if (property_info.name == StringName("SpineSlotNode")) break;
 		element = element->next();
 	}
 	PropertyInfo slot_name_property;
@@ -120,7 +132,7 @@ void SpineSlotNode::_get_property_list(List<PropertyInfo> *list) const {
 }
 
 bool SpineSlotNode::_get(const StringName &property, Variant &value) const {
-	if (property == "slot_name") {
+	if (property == StringName("slot_name")) {
 		value = slot_name;
 		return true;
 	}
@@ -128,7 +140,7 @@ bool SpineSlotNode::_get(const StringName &property, Variant &value) const {
 }
 
 bool SpineSlotNode::_set(const StringName &property, const Variant &value) {
-	if (property == "slot_name") {
+	if (property == StringName("slot_name")) {
 		slot_name = value;
 		SpineSprite *sprite = cast_to<SpineSprite>(get_parent());
 		update_transform(sprite);

+ 4 - 0
spine-godot/spine_godot/SpineSlotNode.h

@@ -31,7 +31,11 @@
 
 #include "SpineCommon.h"
 #include "SpineSprite.h"
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/classes/node2d.hpp>
+#else
 #include "scene/2d/node_2d.h"
+#endif
 
 class SpineSlotNode : public Node2D {
 	GDCLASS(SpineSlotNode, Node2D)

+ 103 - 10
spine-godot/spine_godot/SpineSprite.cpp

@@ -33,6 +33,23 @@
 #include "SpineSkeleton.h"
 #include "SpineRendererObject.h"
 #include "SpineSlotNode.h"
+
+#ifdef SPINE_GODOT_EXTENSION
+#include <godot_cpp/core/memory.hpp>
+#include <godot_cpp/classes/engine.hpp>
+#include <godot_cpp/classes/geometry2d.hpp>
+#include <godot_cpp/variant/array.hpp>
+#include <godot_cpp/classes/mesh.hpp>
+#include <godot_cpp/classes/rendering_server.hpp>
+#include <godot_cpp/classes/editor_interface.hpp>
+#include <godot_cpp/classes/control.hpp>
+#include <godot_cpp/classes/viewport.hpp>
+#include <godot_cpp/classes/scene_tree.hpp>
+#if TOOLS_ENABLED
+#include <godot_cpp/classes/editor_plugin.hpp>
+#include <godot_cpp/classes/font.hpp>
+#endif
+#else
 #include "core/os/memory.h"
 
 #if VERSION_MAJOR > 3
@@ -64,6 +81,7 @@
 #include "editor/editor_plugin.h"
 #endif
 
+#endif
 #endif
 
 Ref<CanvasItemMaterial>
@@ -71,7 +89,11 @@ Ref<CanvasItemMaterial>
 static int sprite_count = 0;
 static spine::Vector<unsigned short> quad_indices;
 static spine::Vector<float> scratch_vertices;
+#ifdef SPINE_GODOT_EXTENSION
+static PackedVector2Array scratch_points;
+#else
 static Vector<Vector2> scratch_points;
+#endif
 
 
 static void clear_triangles(SpineMesh2D *mesh_instance) {
@@ -82,12 +104,21 @@ static void clear_triangles(SpineMesh2D *mesh_instance) {
 #endif
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+static void add_triangles(SpineMesh2D *mesh_instance,
+						  const PackedVector2Array &vertices,
+						  const PackedVector2Array &uvs,
+						  const PackedColorArray &colors,
+						  const PackedInt32Array &indices,
+						  SpineRendererObject *renderer_object) {
+#else
 static void add_triangles(SpineMesh2D *mesh_instance,
 						  const Vector<Point2> &vertices,
 						  const Vector<Point2> &uvs,
 						  const Vector<Color> &colors,
 						  const Vector<int> &indices,
 						  SpineRendererObject *renderer_object) {
+#endif
 #if VERSION_MAJOR > 3
 	mesh_instance->update_mesh(vertices, uvs, colors, indices, renderer_object);
 #else
@@ -137,15 +168,29 @@ void SpineMesh2D::_notification(int what) {
 void SpineMesh2D::_bind_methods() {
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+void SpineMesh2D::update_mesh(const PackedVector2Array &vertices,
+							  const PackedVector2Array &uvs,
+							  const PackedColorArray &colors,
+							  const PackedInt32Array &indices,
+							  SpineRendererObject *renderer_object) {
+#else
 void SpineMesh2D::update_mesh(const Vector<Point2> &vertices,
 							  const Vector<Point2> &uvs,
 							  const Vector<Color> &colors,
 							  const Vector<int> &indices,
 							  SpineRendererObject *renderer_object) {
+#endif
+#if SPINE_GODOT_EXTENSION
+#else
 #if VERSION_MAJOR > 3
 	if (!mesh.is_valid() || vertices.size() != num_vertices || indices.size() != num_indices || indices_changed) {
 		if (mesh.is_valid()) {
+#ifdef SPINE_GODOT_EXTENSION
+			RS::get_singleton()->free_rid(mesh);
+#else
 			RS::get_singleton()->free(mesh);
+#endif
 		}
 		mesh = RS::get_singleton()->mesh_create();
 		Array arrays;
@@ -257,6 +302,7 @@ void SpineMesh2D::update_mesh(const Vector<Point2> &vertices,
 			renderer_object->texture.is_null() ? RID() : renderer_object->texture->get_rid(),
 			renderer_object->normal_map.is_null() ? RID() : renderer_object->normal_map->get_rid());
 #endif
+#endif
 }
 
 void SpineSprite::_bind_methods() {
@@ -555,8 +601,13 @@ void SpineSprite::_notification(int what) {
 
 void SpineSprite::_get_property_list(List<PropertyInfo> *list) const {
 	if (!skeleton_data_res.is_valid() || !skeleton_data_res->is_skeleton_data_loaded()) return;
+#ifdef SPINE_GODOT_EXTENSION
+	PackedStringArray animation_names;
+	PackedStringArray skin_names;
+#else
 	Vector<String> animation_names;
 	Vector<String> skin_names;
+#endif
 	skeleton_data_res->get_animation_names(animation_names);
 	skeleton_data_res->get_skin_names(skin_names);
 	animation_names.insert(0, "-- Empty --");
@@ -592,28 +643,32 @@ void SpineSprite::_get_property_list(List<PropertyInfo> *list) const {
 		auto animation = skeleton_data_res->find_animation(preview_animation);
 		if (animation.is_valid()) animation_duration = animation->get_duration();
 	}
+#ifdef SPINE_GODOT_EXTENSION
+	preview_time_property.hint_string = String("0.0,") + String::num(animation_duration) + String(",0.01");
+#else
 	preview_time_property.hint_string = String("0.0,{0},0.01").format(varray(animation_duration));
+#endif
 	preview_time_property.hint = PROPERTY_HINT_RANGE;
 	list->push_back(preview_time_property);
 }
 
 bool SpineSprite::_get(const StringName &property, Variant &value) const {
-	if (property == "preview_skin") {
+	if (property == StringName("preview_skin")) {
 		value = preview_skin;
 		return true;
 	}
 
-	if (property == "preview_animation") {
+	if (property == StringName("preview_animation")) {
 		value = preview_animation;
 		return true;
 	}
 
-	if (property == "preview_frame") {
+	if (property == StringName("preview_frame")) {
 		value = preview_frame;
 		return true;
 	}
 
-	if (property == "preview_time") {
+	if (property == StringName("preview_time")) {
 		value = preview_time;
 		return true;
 	}
@@ -644,27 +699,27 @@ static void update_preview_animation(SpineSprite *sprite, const String &skin, co
 }
 
 bool SpineSprite::_set(const StringName &property, const Variant &value) {
-	if (property == "preview_skin") {
+	if (property == StringName("preview_skin")) {
 		preview_skin = value;
 		update_preview_animation(this, preview_skin, preview_animation, preview_frame, preview_time);
 		NOTIFY_PROPERTY_LIST_CHANGED();
 		return true;
 	}
 
-	if (property == "preview_animation") {
+	if (property == StringName("preview_animation")) {
 		preview_animation = value;
 		update_preview_animation(this, preview_skin, preview_animation, preview_frame, preview_time);
 		NOTIFY_PROPERTY_LIST_CHANGED();
 		return true;
 	}
 
-	if (property == "preview_frame") {
+	if (property == StringName("preview_frame")) {
 		preview_frame = value;
 		update_preview_animation(this, preview_skin, preview_animation, preview_frame, preview_time);
 		return true;
 	}
 
-	if (property == "preview_time") {
+	if (property == StringName("preview_time")) {
 		preview_time = value;
 		update_preview_animation(this, preview_skin, preview_animation, preview_frame, preview_time);
 		return true;
@@ -865,7 +920,11 @@ void SpineSprite::update_meshes(Ref<SpineSkeleton> skeleton_ref) {
 	skeleton_clipper->clipEnd();
 }
 
+#ifdef SPINE_GODOT_EXTENSION
+void createLinesFromMesh(PackedVector2Array &scratch_points, spine::Vector<unsigned short> &triangles, spine::Vector<float> *vertices) {
+#else
 void createLinesFromMesh(Vector<Vector2> &scratch_points, spine::Vector<unsigned short> &triangles, spine::Vector<float> *vertices) {
+#endif
 	scratch_points.resize(0);
 	for (int i = 0; i < triangles.size(); i += 3) {
 		int i1 = triangles[i];
@@ -924,7 +983,11 @@ void SpineSprite::draw() {
 			scratch_points.push_back(Vector2(vertices->buffer()[0], vertices->buffer()[1]));
 
 			Color color = debug_regions_color;
+#ifdef SPINE_GODOT_EXTENSION
+			if (GEOMETRY2D::get_singleton()->is_point_in_polygon(mouse_position, scratch_points)) {
+#else
 			if (GEOMETRY2D::is_point_in_polygon(mouse_position, scratch_points)) {
+#endif
 				hovered_slot = slot;
 				color = Color(1, 1, 1, 1);
 			}
@@ -960,7 +1023,11 @@ void SpineSprite::draw() {
 			}
 
 			Color color = debug_meshes_color;
+#ifdef SPINE_GODOT_EXTENSION
+			if (GEOMETRY2D::get_singleton()->is_point_in_polygon(mouse_position, scratch_points)) {
+#else
 			if (GEOMETRY2D::is_point_in_polygon(mouse_position, scratch_points)) {
+#endif
 				hovered_slot = slot;
 				color = Color(1, 1, 1, 1);
 			}
@@ -1029,7 +1096,11 @@ void SpineSprite::draw() {
 		Transform2D bone_transform(spine::MathUtil::Deg_Rad * bone->getWorldRotationX(), Vector2(bone->getWorldX(), bone->getWorldY()));
 		bone_transform.scale_basis(Vector2(bone->getWorldScaleX(), bone->getWorldScaleY()));
 		auto mouse_local_position = bone_transform.affine_inverse().xform(mouse_position);
+#ifdef SPINE_GODOT_EXTENSION
+		if (GEOMETRY2D::get_singleton()->is_point_in_polygon(mouse_local_position, scratch_points)) {
+#else
 		if (GEOMETRY2D::is_point_in_polygon(mouse_local_position, scratch_points)) {
+#endif
 			hovered_bone = bone;
 		}
 	}
@@ -1053,7 +1124,11 @@ void SpineSprite::draw() {
 			Transform2D bone_transform(spine::MathUtil::Deg_Rad * bone->getWorldRotationX(), Vector2(bone->getWorldX(), bone->getWorldY()));
 			bone_transform.scale_basis(Vector2(bone->getWorldScaleX(), bone->getWorldScaleY()));
 			auto mouse_local_position = bone_transform.affine_inverse().xform(mouse_position);
+#ifdef SPINE_GODOT_EXTENSION
+			if (GEOMETRY2D::get_singleton()->is_point_in_polygon(mouse_local_position, scratch_points)) {
+#else
 			if (GEOMETRY2D::is_point_in_polygon(mouse_local_position, scratch_points)) {
+#endif
 				hovered_bone = bone;
 			}
 		}
@@ -1089,7 +1164,12 @@ void SpineSprite::draw() {
 	memdelete(control);
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	// FIXME possibly wrong
+	float line_height = default_font->get_height() + default_font->get_descent();
+#else
 	float line_height = default_font->get_height(Font::DEFAULT_FONT_SIZE) + default_font->get_descent(Font::DEFAULT_FONT_SIZE);
+#endif
 #else
 	float line_height = default_font->get_height() + default_font->get_descent();
 #endif
@@ -1099,14 +1179,22 @@ void SpineSprite::draw() {
 	}
 
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+	Rect2 background_rect(0, -default_font->get_height() - 5, rect_width + 20, line_height * hover_text_lines.size() + 10);
+#else
 	Rect2 background_rect(0, -default_font->get_height(Font::DEFAULT_FONT_SIZE) - 5, rect_width + 20, line_height * hover_text_lines.size() + 10);
+#endif
 #else
 	Rect2 background_rect(0, -default_font->get_height() - 5, rect_width + 20, line_height * hover_text_lines.size() + 10);
 #endif
 	if (hover_text_lines.size() > 0) draw_rect(background_rect, Color(0, 0, 0, 0.8));
 	for (int i = 0; i < hover_text_lines.size(); i++) {
 #if VERSION_MAJOR > 3
+#ifdef SPINE_GODOT_EXTENSION
+		draw_string(default_font, Vector2(10, 0 + i * default_font->get_height()), hover_text_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, 16, Color(1, 1, 1, 1));
+#else
 		draw_string(default_font, Vector2(10, 0 + i * default_font->get_height(Font::DEFAULT_FONT_SIZE)), hover_text_lines[i], HORIZONTAL_ALIGNMENT_LEFT, -1, Font::DEFAULT_FONT_SIZE, Color(1, 1, 1, 1));
+#endif
 #else
 		draw_string(default_font, Vector2(10, 0 + i * default_font->get_height()), hover_text_lines[i], Color(1, 1, 1, 1));
 #endif
@@ -1118,7 +1206,11 @@ void SpineSprite::draw_bone(spine::Bone *bone, const Color &color) {
 	draw_set_transform(Vector2(bone->getWorldX(), bone->getWorldY()), spine::MathUtil::Deg_Rad * bone->getWorldRotationX(), Vector2(bone->getWorldScaleX(), bone->getWorldScaleY()));
 	float bone_length = bone->getData().getLength();
 	if (bone_length == 0) bone_length = debug_bones_thickness * 2;
+#ifdef SPINE_GODOT_EXTENSION
+	PackedVector2Array points;
+#else
 	Vector<Vector2> points;
+#endif
 	points.push_back(Vector2(-debug_bones_thickness, 0));
 	points.push_back(Vector2(0, debug_bones_thickness));
 	points.push_back(Vector2(bone_length, 0));
@@ -1162,7 +1254,6 @@ Transform2D SpineSprite::get_global_bone_transform(const String &bone_name) {
 	if (!animation_state.is_valid() && !skeleton.is_valid()) return get_global_transform();
 	auto bone = skeleton->find_bone(bone_name);
 	if (!bone.is_valid()) {
-		print_error(vformat("Bone: '%s' not found.", bone_name));
 		return get_global_transform();
 	}
 	return bone->get_global_transform();
@@ -1223,13 +1314,14 @@ void SpineSprite::set_screen_material(Ref<Material> material) {
 	screen_material = material;
 }
 
+#ifndef SPINE_GODOT_EXTENSION
+// FIXME
 #ifdef TOOLS_ENABLED
 Rect2 SpineSprite::_edit_get_rect() const {
 	if (skeleton_data_res.is_valid() && skeleton_data_res->is_skeleton_data_loaded()) {
 		auto data = skeleton_data_res->get_skeleton_data();
 		return Rect2(data->getX(), -data->getY() - data->getHeight(), data->getWidth(), data->getHeight());
 	}
-
 	return Node2D::_edit_get_rect();
 }
 
@@ -1237,3 +1329,4 @@ bool SpineSprite::_edit_use_rect() const {
 	return skeleton_data_res.is_valid() && skeleton_data_res->is_skeleton_data_loaded();
 }
 #endif
+#endif

+ 20 - 2
spine-godot/spine_godot/SpineSprite.h

@@ -58,10 +58,17 @@ protected:
 	void _notification(int what);
 	static void _bind_methods();
 
+#ifdef SPINE_GODOT_EXTENSION
+	PackedVector2Array vertices;
+	PackedVector2Array uvs;
+	PackedColorArray colors;
+	PackedInt32Array indices;
+#else
 	Vector<Vector2> vertices;
 	Vector<Vector2> uvs;
 	Vector<Color> colors;
 	Vector<int> indices;
+#endif
 	SpineRendererObject *renderer_object;
 
 	bool indices_changed;
@@ -89,7 +96,7 @@ protected:
 
 public:
 #if VERSION_MAJOR > 3
-	SpineMesh2D() : renderer_object(nullptr), indices_changed(true), num_vertices(0), num_indices(0), vertex_stride(0), normal_tangent_stride(0), attribute_stride(0){};
+	SpineMesh2D() : renderer_object(nullptr), indices_changed(true), num_vertices(0), num_indices(0), vertex_stride(0), normal_tangent_stride(0), attribute_stride(0) {};
 	~SpineMesh2D() {
 		if (mesh.is_valid()) {
 #ifdef SPINE_GODOT_EXTENSION
@@ -100,7 +107,7 @@ public:
 		}
 	}
 #else
-	SpineMesh2D() : renderer_object(nullptr), indices_changed(true), num_vertices(0), num_indices(0){};
+	SpineMesh2D() : renderer_object(nullptr), indices_changed(true), num_vertices(0), num_indices(0) {};
 	~SpineMesh2D() {
 		if (mesh.is_valid()) {
 			VS::get_singleton()->free(mesh);
@@ -108,11 +115,19 @@ public:
 	}
 #endif
 
+#ifdef SPINE_GODOT_EXTENSION
+	void update_mesh(const PackedVector2Array &vertices,
+					 const PackedVector2Array &uvs,
+					 const PackedColorArray &colors,
+					 const PackedInt32Array &indices,
+					 SpineRendererObject *renderer_object);
+#else
 	void update_mesh(const Vector<Point2> &vertices,
 					 const Vector<Point2> &uvs,
 					 const Vector<Color> &colors,
 					 const Vector<int> &indices,
 					 SpineRendererObject *renderer_object);
+#endif
 };
 
 class SpineSprite : public Node2D,
@@ -276,8 +291,11 @@ public:
 
 	void set_debug_clipping_color(const Color &color) { debug_clipping_color = color; }
 
+#ifndef SPINE_GODOT_EXTENSION
+// FIXME
 #ifdef TOOLS_ENABLED
 	virtual Rect2 _edit_get_rect() const;
 	virtual bool _edit_use_rect() const;
 #endif
+#endif
 };

Some files were not shown because too many files changed in this diff