diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c3a6ef55..8888e77fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -79,6 +79,8 @@ * Added example component `SkeletonRenderTexture` to render a `SkeletonRenderer` to a `RenderTexture`, mainly for proper transparency. Added an example scene named `RenderTexture FadeOut Transparency` that demonstrates usage for a fadeout transparency effect. * Added another fadeout example component named `SkeletonRenderTextureFadeout` which takes over transparency fadeout when enabled. You can use this component as-is, attach it in disabled state and enable it to start a fadeout effect. * Timeline clips now offer an additional `Alpha` parameter for setting a custom constant mix alpha value other than 1.0, just as `TrackEntry.Alpha`. Defaults to 1.0. + * `GetRemappedClone` copying from `Sprite` now provides additional `pmaCloneTextureFormat` and `pmaCloneMipmaps` parameters to explicitly specify the texture format of a newly created PMA texture. + * Spine property Inspector fields (`Animation Name`, `Bone Name`, `Slot` and similar) now display the name in red when the respective animation/bone/etc no longer exists at the skeleton data. This may be helpful when such items have been renamed or deleted. * **Breaking changes** diff --git a/examples/export/export.sh b/examples/export/export.sh index 3099af86b..07e803138 100755 --- a/examples/export/export.sh +++ b/examples/export/export.sh @@ -16,7 +16,7 @@ fi echo "Spine exe: $SPINE_EXE" if [ "$#" -eq 0 ]; then - echo "Enter the Spine editor version to use for the export (eg 3.8.99):" + echo "Enter the Spine editor version to use for the export (eg 4.1.xx):" read version else version=${1%/} @@ -79,6 +79,11 @@ echo "Exporting assets..." -i ../mix-and-match/images -o ../mix-and-match/export -n mix-and-match -p atlas-0.5.json \ -i ../mix-and-match/images -o ../mix-and-match/export -n mix-and-match-pma -p atlas-0.5-pma.json \ \ +-i ../owl/owl-pro.spine -o ../owl/export -e json.json \ +-i ../owl/owl-pro.spine -o ../owl/export -e binary.json \ +-i ../owl/images -o ../owl/export -n owl -p atlas-0.5.json \ +-i ../owl/images -o ../owl/export -n owl-pma -p atlas-0.5-pma.json \ +\ -i ../powerup/powerup-ess.spine -o ../powerup/export -e json.json \ -i ../powerup/powerup-ess.spine -o ../powerup/export -e binary.json \ -i ../powerup/powerup-pro.spine -o ../powerup/export -e json.json \ @@ -100,6 +105,7 @@ echo "Exporting assets..." -i ../spineboy/spineboy-ess.spine -o ../spineboy/export -e binary.json \ -i ../spineboy/spineboy-pro.spine -o ../spineboy/export -e json.json \ -i ../spineboy/spineboy-pro.spine -o ../spineboy/export -e binary.json \ +-i ../spineboy/spineboy-pro.spine -o ../spineboy/export/spineboy-run.atlas -e png-0.5-frame-by-frame.json \ -i ../spineboy/images -o ../spineboy/export -n spineboy -p atlas-0.5.json \ -i ../spineboy/images -o ../spineboy/export -n spineboy-pma -p atlas-0.5-pma.json \ \ @@ -126,14 +132,10 @@ echo "Exporting assets..." -i ../windmill/images -o ../windmill/export -n windmill -p atlas-0.5.json \ -i ../windmill/images -o ../windmill/export -n windmill-pma -p atlas-0.5-pma.json -# Owl needs separate export, as cleaning would kill keys in idle animation, which -# would lead to incorrect additive animation blending. +# spineboy-old.spine needs separate export, as its images are in an atlas. "$SPINE_EXE" \ -u $version ${@:2} \ --i ../owl/owl-pro.spine -o ../owl/export -e json.json \ --i ../owl/owl-pro.spine -o ../owl/export -e binary.json \ --i ../owl/images -o ../owl/export -n owl -p atlas-0.5.json \ --i ../owl/images -o ../owl/export -n owl-pma -p atlas-0.5-pma.json \ +-i ../../spine-libgdx/spine-libgdx-tests/assets/spineboy-old/spineboy-old.spine -o ../../spine-libgdx/spine-libgdx-tests/assets/spineboy-old -e json.json # Export Unity Assets UNITY_BASE_DIR=../spine-unity diff --git a/examples/export/png-0.5-frame-by-frame.json b/examples/export/png-0.5-frame-by-frame.json new file mode 100644 index 000000000..16cca03cf --- /dev/null +++ b/examples/export/png-0.5-frame-by-frame.json @@ -0,0 +1,87 @@ +{ +"class": "export-png", +"exportType": "animation", +"skeletonType": "single", +"skeleton": "spineboy-pro", +"animationType": "single", +"animation": "run", +"skinType": "current", +"skinNone": false, +"skin": null, +"maxBounds": false, +"renderImages": true, +"renderBones": false, +"renderOthers": false, +"scale": 50, +"fitWidth": 0, +"fitHeight": 0, +"enlarge": false, +"background": null, +"fps": 15, +"lastFrame": false, +"cropWidth": 0, +"cropHeight": 0, +"rangeStart": -1, +"rangeEnd": -1, +"packAtlas": { + "stripWhitespaceX": true, + "stripWhitespaceY": true, + "rotation": true, + "alias": true, + "ignoreBlankImages": false, + "alphaThreshold": 3, + "minWidth": 16, + "minHeight": 16, + "maxWidth": 2048, + "maxHeight": 2048, + "pot": false, + "multipleOfFour": false, + "square": false, + "outputFormat": "png", + "jpegQuality": 0.9, + "premultiplyAlpha": true, + "bleed": false, + "scale": [ 1 ], + "scaleSuffix": [ "" ], + "scaleResampling": [ "bicubic" ], + "paddingX": 2, + "paddingY": 2, + "edgePadding": true, + "duplicatePadding": false, + "filterMin": "Linear", + "filterMag": "Linear", + "wrapX": "ClampToEdge", + "wrapY": "ClampToEdge", + "format": "RGBA8888", + "atlasExtension": ".atlas", + "combineSubdirectories": false, + "flattenPaths": false, + "useIndexes": true, + "debug": false, + "fast": false, + "limitMemory": true, + "currentProject": true, + "packing": "rectangles", + "prettyPrint": true, + "legacyOutput": false, + "webp": null, + "bleedIterations": 2, + "ignore": false, + "separator": "_", + "silent": false +}, +"compression": 9, +"bruteForce": true, +"quantize": true, +"quality": 100, +"speed": 1, +"dither": 0, +"maxColors": 256, +"pad": false, +"msaa": 4, +"smoothing": 8, +"renderSelection": false, +"cropX": 0, +"cropY": 0, +"open": false +} \ No newline at end of file diff --git a/examples/owl/owl-pro.spine b/examples/owl/owl-pro.spine index 85defbf62..317b0c361 100755 Binary files a/examples/owl/owl-pro.spine and b/examples/owl/owl-pro.spine differ diff --git a/examples/spineboy/export/spineboy-run.atlas b/examples/spineboy/export/spineboy-run.atlas new file mode 100644 index 000000000..ce32001a8 --- /dev/null +++ b/examples/spineboy/export/spineboy-run.atlas @@ -0,0 +1,60 @@ +spineboy-run.png + size: 1181, 687 + filter: Linear, Linear + pma: true +spineboy-pro-run + index: 8 + bounds: 2, 371, 303, 314 + offsets: 11, 22, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 9 + bounds: 2, 44, 278, 325 + offsets: 30, 2, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 0 + bounds: 307, 439, 246, 320 + offsets: 67, 1, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 3 + bounds: 629, 443, 242, 320 + offsets: 12, 20, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 7 + bounds: 951, 389, 228, 296 + offsets: 36, 33, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 4 + bounds: 307, 195, 242, 320 + offsets: 2, 4, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 5 + bounds: 629, 200, 241, 316 + offsets: 8, 3, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 1 + bounds: 282, 2, 191, 318 + offsets: 70, 3, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 2 + bounds: 947, 81, 226, 306 + offsets: 34, 26, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 6 + bounds: 629, 4, 194, 316 + offsets: 68, 4, 316, 341 + rotate: 90 + origin: 142, 3 diff --git a/examples/spineboy/export/spineboy-run.png b/examples/spineboy/export/spineboy-run.png new file mode 100644 index 000000000..e28e0c6ba Binary files /dev/null and b/examples/spineboy/export/spineboy-run.png differ diff --git a/spine-libgdx/spine-libgdx-tests/.settings/org.eclipse.core.resources.prefs b/spine-libgdx/spine-libgdx-tests/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..42a944b8f --- /dev/null +++ b/spine-libgdx/spine-libgdx-tests/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=Cp1252 diff --git a/spine-libgdx/spine-libgdx-tests/assets/skin/skin.json b/spine-libgdx/spine-libgdx-tests/assets/skin/skin.json index d686f5763..80f12107d 100644 --- a/spine-libgdx/spine-libgdx-tests/assets/skin/skin.json +++ b/spine-libgdx/spine-libgdx-tests/assets/skin/skin.json @@ -1,5 +1,5 @@ { -com.badlogic.gdx.graphics.g2d.BitmapFont: { default-font: { file: com/badlogic/gdx/utils/arial-15.fnt } }, +com.badlogic.gdx.graphics.g2d.BitmapFont: { default-font: { file: com/badlogic/gdx/utils/lsans-15.fnt } }, com.badlogic.gdx.graphics.Color: { green: { a: 1, b: 0, g: 1, r: 0 }, white: { a: 1, b: 1, g: 1, r: 1 }, diff --git a/spine-libgdx/spine-libgdx-tests/assets/spineboy-old/spineboy-old.json b/spine-libgdx/spine-libgdx-tests/assets/spineboy-old/spineboy-old.json index aa4b25342..8f0efc842 100644 --- a/spine-libgdx/spine-libgdx-tests/assets/spineboy-old/spineboy-old.json +++ b/spine-libgdx/spine-libgdx-tests/assets/spineboy-old/spineboy-old.json @@ -1,7 +1,7 @@ { "skeleton": { - "hash": "L56RnW+mbehRLXsZX4oI9CbSsr8", - "spine": "3.8.75", + "hash": "F9MaKeu7FF4", + "spine": "4.1.26-beta", "x": -88.95, "y": -2.26, "width": 161.04, @@ -53,12 +53,6 @@ { "name": "default", "attachments": { - "left arm": { - "left-arm": { "x": 15.11, "y": -0.44, "rotation": 33.85, "width": 35, "height": 29 } - }, - "right upper leg": { - "right-upper-leg": { "x": 23.03, "y": 0.26, "rotation": 101.14, "width": 44, "height": 70 } - }, "bb-head": { "bb-head": { "type": "boundingbox", @@ -67,30 +61,15 @@ "color": "60ef00ff" } }, - "left lower leg": { - "left-lower-leg": { "x": 24.56, "y": -1.92, "rotation": 105.76, "width": 49, "height": 64 } - }, - "right arm": { - "right-arm": { "x": 18.34, "y": -2.65, "rotation": 94.32, "width": 21, "height": 45 } - }, - "left shoulder": { - "left-shoulder": { "x": 23.74, "y": 0.12, "rotation": 62.01, "width": 34, "height": 53 } - }, - "neck": { - "neck": { "x": 9.43, "y": -3.66, "rotation": -100.16, "width": 34, "height": 28 } - }, - "head": { - "head": { "x": 53.95, "y": -5.75, "rotation": -86.9, "width": 121, "height": 132 } - }, "eyes": { "eyes": { "x": 28.94, "y": -32.92, "rotation": -86.9, "width": 34, "height": 27 }, "eyes-closed": { "x": 28.78, "y": -32.86, "rotation": -86.9, "width": 34, "height": 27 } }, - "right hand": { - "right-hand": { "x": 6.83, "y": 1.26, "rotation": 91.96, "width": 32, "height": 32 } + "head": { + "head": { "x": 53.95, "y": -5.75, "rotation": -86.9, "width": 121, "height": 132 } }, - "right shoulder": { - "right-shoulder": { "x": 25.87, "y": 0.04, "rotation": 134.45, "width": 52, "height": 51 } + "left arm": { + "left-arm": { "x": 15.11, "y": -0.44, "rotation": 33.85, "width": 35, "height": 29 } }, "left foot": { "left-foot": { "x": 24.35, "y": 8.89, "rotation": 3.32, "width": 65, "height": 30 } @@ -98,20 +77,41 @@ "left hand": { "left-hand": { "x": 0.75, "y": 1.86, "rotation": 31.14, "width": 35, "height": 38 } }, + "left lower leg": { + "left-lower-leg": { "x": 24.56, "y": -1.92, "rotation": 105.76, "width": 49, "height": 64 } + }, + "left shoulder": { + "left-shoulder": { "x": 23.74, "y": 0.12, "rotation": 62.01, "width": 34, "height": 53 } + }, + "left upper leg": { + "left-upper-leg": { "x": 26.12, "y": -1.86, "rotation": 89.1, "width": 33, "height": 67 } + }, + "neck": { + "neck": { "x": 9.43, "y": -3.66, "rotation": -100.16, "width": 34, "height": 28 } + }, "pelvis": { "pelvis": { "x": -4.83, "y": 10.63, "width": 63, "height": 47 } }, - "right lower leg": { - "right-lower-leg": { "x": 23.28, "y": -2.59, "rotation": 111.84, "width": 51, "height": 64 } + "right arm": { + "right-arm": { "x": 18.34, "y": -2.65, "rotation": 94.32, "width": 21, "height": 45 } }, "right foot": { "right-foot": { "x": 19.03, "y": 8.47, "rotation": 1.53, "width": 67, "height": 30 } }, + "right hand": { + "right-hand": { "x": 6.83, "y": 1.26, "rotation": 91.96, "width": 32, "height": 32 } + }, + "right lower leg": { + "right-lower-leg": { "x": 23.28, "y": -2.59, "rotation": 111.84, "width": 51, "height": 64 } + }, + "right shoulder": { + "right-shoulder": { "x": 25.87, "y": 0.04, "rotation": 134.45, "width": 52, "height": 51 } + }, + "right upper leg": { + "right-upper-leg": { "x": 23.03, "y": 0.26, "rotation": 101.14, "width": 44, "height": 70 } + }, "torso": { "torso": { "x": 44.58, "y": -7.08, "rotation": -94.95, "width": 68, "height": 92 } - }, - "left upper leg": { - "left-upper-leg": { "x": 26.12, "y": -1.86, "rotation": 89.1, "width": 33, "height": 67 } } } } @@ -127,158 +127,140 @@ "head": { "rotate": [ {}, - { "time": 0.9615, "angle": -23.11 }, - { "time": 1.7856, "angle": -56.45 }, - { "time": 2.6098, "angle": 1.39 }, - { "time": 3.5713, "angle": 36.12 }, - { "time": 4.258, "angle": 1.24 }, - { "time": 5.2195, "angle": -37.13 }, - { "time": 5.9086, "angle": 2.07 }, - { "time": 6.9044, "angle": 34.72 }, - { "time": 7.7675, "angle": 360 } + { "time": 0.9615, "value": -23.11 }, + { "time": 1.7856, "value": -56.45 }, + { "time": 2.6098, "value": 1.39 }, + { "time": 3.5713, "value": 36.12 }, + { "time": 4.258, "value": 1.24 }, + { "time": 5.2195, "value": -37.13 }, + { "time": 5.9086, "value": 2.07 }, + { "time": 6.9044, "value": 34.72 }, + { "time": 7.7675 } ], "translate": [ - { "curve": 0.191, "c2": 0.41, "c3": 0.586, "c4": 0.76 }, + { + "curve": [ 0.105, 23.73, 0.322, 44, 0.105, -14.65, 0.322, -27.15 ] + }, { "time": 0.5494, "x": 57.89, "y": -35.72, - "curve": 0.39, - "c2": 0.54, - "c3": 0.633, - "c4": 0.73 + "curve": [ 0.71, 73.75, 0.81, 79.33, 0.71, -63.9, 0.81, -73.81 ] }, { "time": 0.9615, "x": 87.26, "y": -87.9, - "curve": 0.325, - "c2": 0.24, - "c3": 0.588, - "c4": 0.37 + "curve": [ 1.095, 73.25, 1.204, 65.66, 1.095, -94.31, 1.204, -97.79 ] }, { "time": 1.3736, "x": 28.89, "y": -114.62, - "curve": 0.383, - "c2": 0.23, - "c3": 0.736, - "c4": 0.56 + "curve": [ 1.531, 4.63, 1.677, -30.17, 1.531, -117.01, 1.677, -120.43 ] }, { "time": 1.7856, "x": -76.58, "y": -124.99, - "curve": 0.129, - "c2": 0.21, - "c3": 0.547, - "c4": 0.64 + "curve": [ 1.839, -92.92, 2.011, -126.37, 1.839, -114.94, 2.011, -94.37 ] }, { "time": 2.1977, "x": -154.38, "y": -77.14, - "curve": 0.354, - "c2": 0.49, - "c3": 0.73, - "c4": 0.9 + "curve": [ 2.344, -167.43, 2.499, -178.36, 2.344, -30.24, 2.499, 9 ] }, { "time": 2.6098, "x": -181.02, "y": 18.57, - "curve": 0.064, - "c2": 0.16, - "c3": 0.521, - "c4": 0.62 + "curve": [ 2.645, -176.12, 2.896, -162.02, 2.645, 36.19, 2.896, 86.84 ] + }, + { + "time": 3.1592, + "x": -150.38, + "y": 128.68, + "curve": [ 3.317, -129.32, 3.48, -112.08, 3.317, 138.36, 3.48, 146.28 ] + }, + { + "time": 3.5713, + "x": -112.08, + "y": 146.28, + "curve": [ 3.638, -112.08, 3.743, -89.83, 3.638, 146.28, 3.743, 130.15 ] }, - { "time": 3.1592, "x": -150.38, "y": 128.68, "curve": 0.382, "c2": 0.55, "c3": 0.778 }, - { "time": 3.5713, "x": -112.08, "y": 146.28, "curve": 0.242, "c3": 0.626, "c4": 0.46 }, { "time": 3.846, "x": -63.71, "y": 111.22, - "curve": 0.399, - "c2": 0.36, - "c3": 0.786, - "c4": 0.77 + "curve": [ 4.01, -58.39, 4.17, -52.34, 4.01, 70.62, 4.17, 24.39 ] }, { "time": 4.258, "x": -48.94, "y": -1.55, - "curve": 0.189, - "c2": 0.21, - "c3": 0.575, - "c4": 0.62 + "curve": [ 4.336, -57.92, 4.495, -75.45, 4.336, -20.53, 4.495, -57.59 ] + }, + { + "time": 4.6701, + "x": -91.7, + "y": -91.93, + "curve": [ 4.87, -118.27, 5.091, -142.8, 4.87, -110.08, 5.091, -126.83 ] }, - { "time": 4.6701, "x": -91.7, "y": -91.93, "curve": 0.363, "c2": 0.52, "c3": 0.766 }, { "time": 5.2195, "x": -142.8, "y": -126.83, - "curve": 0.228, - "c2": 0.35, - "c3": 0.594, - "c4": 0.76 + "curve": [ 5.282, -154.67, 5.383, -168.56, 5.282, -116.86, 5.383, -105.17 ] }, { "time": 5.4943, "x": -176.7, "y": -98.33, - "curve": 0.26, - "c2": 0.4, - "c3": 0.612, - "c4": 0.72 + "curve": [ 5.566, -171.6, 5.662, -167.52, 5.566, -68.62, 5.662, -44.85 ] }, { "time": 5.769, "x": -163.95, "y": -24.05, - "curve": 0.339, - "c2": 0.37, - "c3": 0.676, - "c4": 0.71 + "curve": [ 5.816, -158.85, 5.862, -154.17, 5.816, -11.19, 5.862, 0.64 ] + }, + { + "time": 5.9063, + "x": -150.17, + "y": 10.72, + "curve": [ 6.013, -121.06, 6.11, -102.45, 6.013, 32.19, 6.11, 45.92 ] }, - { "time": 5.9063, "x": -150.17, "y": 10.72, "curve": 0.388, "c2": 0.61, "c3": 0.742 }, { "time": 6.181, "x": -102.45, "y": 45.92, - "curve": 0.31, - "c2": 0.24, - "c3": 0.648, - "c4": 0.59 + "curve": [ 6.266, -90.82, 6.359, -73.86, 6.266, 51.8, 6.359, 60.36 ] }, { "time": 6.4557, "x": -54, "y": 70.4, - "curve": 0.326, - "c2": 0.29, - "c3": 0.664, - "c4": 0.63 + "curve": [ 6.545, -37.79, 6.638, -18.79, 6.545, 66.09, 6.638, 61.04 ] }, { "time": 6.7305, "x": 1.89, "y": 55.55, - "curve": 0.387, - "c2": 0.33, - "c3": 0.769, - "c4": 0.73 + "curve": [ 6.89, 12.58, 7.047, 25.53, 6.89, 49.14, 7.047, 41.38 ] }, { "time": 7.1425, "x": 34.27, "y": 36.14, - "curve": 0.206, - "c2": 0.28, - "c3": 0.596, - "c4": 0.68 + "curve": [ 7.227, 31.38, 7.388, 27.25, 7.227, 26.3, 7.388, 12.25 ] + }, + { + "time": 7.5546, + "x": 23.95, + "y": 1.01, + "curve": [ 7.708, 10.54, 7.867, 0, 7.708, 0.44, 7.867, 0 ] }, - { "time": 7.5546, "x": 23.95, "y": 1.01, "curve": 0.373, "c2": 0.56, "c3": 0.759 }, { "time": 7.9667 } ], "scale": [ @@ -334,10 +316,20 @@ "translate": [ { "x": -11.57, "y": -3.01 }, { "time": 0.2333, "x": -16.2, "y": -19.44 }, - { "time": 0.3333, "x": 7.67, "y": -8.49, "curve": 0.057, "c2": 0.07, "c3": 0.713 }, + { + "time": 0.3333, + "x": 7.67, + "y": -8.49, + "curve": [ 0.335, 8.21, 0.357, 15.39, 0.335, -7.54, 0.357, 5.02 ] + }, { "time": 0.3667, "x": 15.39, "y": 5.02 }, { "time": 0.4667, "x": -7.85, "y": 57.22 }, - { "time": 0.6, "x": -10.82, "y": 96.34, "curve": 0.241, "c2": -0.01 }, + { + "time": 0.6, + "x": -10.82, + "y": 96.34, + "curve": [ 0.632, -10.86, 0.733, -7.02, 0.632, 96.76, 0.733, 54.71 ] + }, { "time": 0.7333, "x": -7.02, "y": 54.71 }, { "time": 0.8, "x": -10.58, "y": 32.2 }, { "time": 0.9333, "x": -31.99, "y": 0.45 }, @@ -352,16 +344,16 @@ }, "left upper leg": { "rotate": [ - { "angle": 17.14 }, - { "time": 0.2333, "angle": 44.35 }, - { "time": 0.3333, "angle": 16.47 }, - { "time": 0.4, "angle": -9.88 }, - { "time": 0.4667, "angle": -11.42 }, - { "time": 0.5667, "angle": 23.47 }, - { "time": 0.7667, "angle": 71.83 }, - { "time": 0.9333, "angle": 65.53 }, - { "time": 1.0667, "angle": 51.01 }, - { "time": 1.3667, "angle": 17.14 } + { "value": 17.14 }, + { "time": 0.2333, "value": 44.35 }, + { "time": 0.3333, "value": 16.47 }, + { "time": 0.4, "value": -9.88 }, + { "time": 0.4667, "value": -11.42 }, + { "time": 0.5667, "value": 23.47 }, + { "time": 0.7667, "value": 71.83 }, + { "time": 0.9333, "value": 65.53 }, + { "time": 1.0667, "value": 51.01 }, + { "time": 1.3667, "value": 17.14 } ], "translate": [ { "x": -3, "y": -2.25, "curve": "stepped" }, @@ -376,15 +368,15 @@ }, "left lower leg": { "rotate": [ - { "angle": -16.25 }, - { "time": 0.2333, "angle": -52.21 }, - { "time": 0.4, "angle": 15.05 }, - { "time": 0.4667, "angle": -8.96 }, - { "time": 0.5667, "angle": -39.53 }, - { "time": 0.7667, "angle": -27.28 }, - { "time": 0.9333, "angle": -3.52 }, - { "time": 1.0667, "angle": -61.92 }, - { "time": 1.3667, "angle": -16.25 } + { "value": -16.25 }, + { "time": 0.2333, "value": -52.21 }, + { "time": 0.4, "value": 15.05 }, + { "time": 0.4667, "value": -8.96 }, + { "time": 0.5667, "value": -39.53 }, + { "time": 0.7667, "value": -27.28 }, + { "time": 0.9333, "value": -3.52 }, + { "time": 1.0667, "value": -61.92 }, + { "time": 1.3667, "value": -16.25 } ], "translate": [ { "curve": "stepped" }, @@ -399,16 +391,16 @@ }, "left foot": { "rotate": [ - { "angle": 0.34 }, - { "time": 0.2333, "angle": 6.2 }, - { "time": 0.3333, "angle": 14.73 }, - { "time": 0.4, "angle": -15.54 }, - { "time": 0.4333, "angle": -21.2 }, - { "time": 0.5667, "angle": -7.56 }, - { "time": 0.7667, "angle": -0.68 }, - { "time": 0.9333, "angle": -0.59 }, - { "time": 1.0667, "angle": 14.65 }, - { "time": 1.3667, "angle": 0.34 } + { "value": 0.34 }, + { "time": 0.2333, "value": 6.2 }, + { "time": 0.3333, "value": 14.73 }, + { "time": 0.4, "value": -15.54 }, + { "time": 0.4333, "value": -21.2 }, + { "time": 0.5667, "value": -7.56 }, + { "time": 0.7667, "value": -0.68 }, + { "time": 0.9333, "value": -0.59 }, + { "time": 1.0667, "value": 14.65 }, + { "time": 1.3667, "value": 0.34 } ], "translate": [ { "curve": "stepped" }, @@ -423,16 +415,20 @@ }, "right upper leg": { "rotate": [ - { "angle": 25.97 }, - { "time": 0.2333, "angle": 46.43 }, - { "time": 0.3333, "angle": 22.62 }, - { "time": 0.4, "angle": 2.13 }, - { "time": 0.4667, "angle": 0.05, "curve": 0, "c3": 0.638, "c4": 0.99 }, - { "time": 0.6, "angle": 65.55 }, - { "time": 0.7667, "angle": 64.93 }, - { "time": 0.9333, "angle": 41.08 }, - { "time": 1.0667, "angle": 66.26 }, - { "time": 1.3667, "angle": 25.97 } + { "value": 25.97 }, + { "time": 0.2333, "value": 46.43 }, + { "time": 0.3333, "value": 22.62 }, + { "time": 0.4, "value": 2.13 }, + { + "time": 0.4667, + "value": 0.05, + "curve": [ 0.467, 0.05, 0.552, 64.9 ] + }, + { "time": 0.6, "value": 65.55 }, + { "time": 0.7667, "value": 64.93 }, + { "time": 0.9333, "value": 41.08 }, + { "time": 1.0667, "value": 66.26 }, + { "time": 1.3667, "value": 25.97 } ], "translate": [ { "x": 5.75, "y": 0.61 }, @@ -450,15 +446,15 @@ }, "right lower leg": { "rotate": [ - { "angle": -27.46 }, - { "time": 0.2333, "angle": -64.04 }, - { "time": 0.4, "angle": -48.36 }, - { "time": 0.5667, "angle": -76.86 }, - { "time": 0.7667, "angle": -26.89 }, - { "time": 0.9, "angle": -18.98 }, - { "time": 0.9333, "angle": -14.19 }, - { "time": 1.0667, "angle": -80.45 }, - { "time": 1.3667, "angle": -27.46 } + { "value": -27.46 }, + { "time": 0.2333, "value": -64.04 }, + { "time": 0.4, "value": -48.36 }, + { "time": 0.5667, "value": -76.86 }, + { "time": 0.7667, "value": -26.89 }, + { "time": 0.9, "value": -18.98 }, + { "time": 0.9333, "value": -14.19 }, + { "time": 1.0667, "value": -80.45 }, + { "time": 1.3667, "value": -27.46 } ], "translate": [ { "curve": "stepped" }, @@ -473,19 +469,19 @@ }, "right foot": { "rotate": [ - { "angle": 1.08 }, - { "time": 0.2333, "angle": 16.03 }, - { "time": 0.3, "angle": 12.95 }, - { "time": 0.3333, "angle": 15.17 }, - { "time": 0.4, "angle": -14.71 }, - { "time": 0.4333, "angle": -12.86 }, - { "time": 0.4667, "angle": -19.18 }, - { "time": 0.5667, "angle": -15.82 }, - { "time": 0.6, "angle": -3.6 }, - { "time": 0.7667, "angle": -3.56 }, - { "time": 0.9333, "angle": 1.86 }, - { "time": 1.0667, "angle": 16.03 }, - { "time": 1.3667, "angle": 1.08 } + { "value": 1.08 }, + { "time": 0.2333, "value": 16.03 }, + { "time": 0.3, "value": 12.95 }, + { "time": 0.3333, "value": 15.17 }, + { "time": 0.4, "value": -14.71 }, + { "time": 0.4333, "value": -12.86 }, + { "time": 0.4667, "value": -19.18 }, + { "time": 0.5667, "value": -15.82 }, + { "time": 0.6, "value": -3.6 }, + { "time": 0.7667, "value": -3.56 }, + { "time": 0.9333, "value": 1.86 }, + { "time": 1.0667, "value": 16.03 }, + { "time": 1.3667, "value": 1.08 } ], "translate": [ { "curve": "stepped" }, @@ -500,14 +496,14 @@ }, "torso": { "rotate": [ - { "angle": -13.35 }, - { "time": 0.2333, "angle": -48.95 }, - { "time": 0.4333, "angle": -35.77 }, - { "time": 0.6, "angle": -4.59 }, - { "time": 0.7667, "angle": 14.61 }, - { "time": 0.9333, "angle": 15.74 }, - { "time": 1.0667, "angle": -32.45 }, - { "time": 1.3667, "angle": -13.35 } + { "value": -13.35 }, + { "time": 0.2333, "value": -48.95 }, + { "time": 0.4333, "value": -35.77 }, + { "time": 0.6, "value": -4.59 }, + { "time": 0.7667, "value": 14.61 }, + { "time": 0.9333, "value": 15.74 }, + { "time": 1.0667, "value": -32.45 }, + { "time": 1.3667, "value": -13.35 } ], "translate": [ { "x": -3.67, "y": 1.69, "curve": "stepped" }, @@ -522,15 +518,15 @@ }, "neck": { "rotate": [ - { "angle": 12.79 }, - { "time": 0.2333, "angle": 16.46 }, - { "time": 0.4, "angle": 26.49 }, - { "time": 0.6, "angle": 15.51 }, - { "time": 0.7667, "angle": 1.35 }, - { "time": 0.9333, "angle": 2.36 }, - { "time": 1.0667, "angle": 6.09 }, - { "time": 1.3, "angle": 21.24 }, - { "time": 1.3667, "angle": 12.79 } + { "value": 12.79 }, + { "time": 0.2333, "value": 16.46 }, + { "time": 0.4, "value": 26.49 }, + { "time": 0.6, "value": 15.51 }, + { "time": 0.7667, "value": 1.35 }, + { "time": 0.9333, "value": 2.36 }, + { "time": 1.0667, "value": 6.09 }, + { "time": 1.3, "value": 21.24 }, + { "time": 1.3667, "value": 12.79 } ], "translate": [ { "curve": "stepped" }, @@ -545,15 +541,15 @@ }, "head": { "rotate": [ - { "angle": 5.2 }, - { "time": 0.2333, "angle": 20.28 }, - { "time": 0.4, "angle": 15.28 }, - { "time": 0.6, "angle": -24.7 }, - { "time": 0.7667, "angle": -11.02 }, - { "time": 0.9333, "angle": -24.38 }, - { "time": 1.0667, "angle": 12 }, - { "time": 1.3, "angle": 4.86 }, - { "time": 1.3667, "angle": 5.2 } + { "value": 5.2 }, + { "time": 0.2333, "value": 20.28 }, + { "time": 0.4, "value": 15.28 }, + { "time": 0.6, "value": -24.7 }, + { "time": 0.7667, "value": -11.02 }, + { "time": 0.9333, "value": -24.38 }, + { "time": 1.0667, "value": 12 }, + { "time": 1.3, "value": 4.86 }, + { "time": 1.3667, "value": 5.2 } ], "translate": [ { "curve": "stepped" }, @@ -568,13 +564,24 @@ }, "left shoulder": { "rotate": [ - { "angle": 0.05, "curve": 0, "c3": 0.621 }, - { "time": 0.2333, "angle": 279.66, "curve": 0.218, "c2": 0.67, "c3": 0.661, "c4": 0.99 }, - { "time": 0.5, "angle": 62.27, "curve": 0.463, "c3": 0.764, "c4": 0.58 }, - { "time": 0.9333, "angle": 28.91 }, - { "time": 1.0667, "angle": -8.63 }, - { "time": 1.1667, "angle": -18.43 }, - { "time": 1.3667, "angle": 0.05 } + { + "value": 0.05, + "curve": [ 0, 0.05, 0.145, -80.34 ] + }, + { + "time": 0.2333, + "value": -80.34, + "curve": [ 0.291, 15.21, 0.41, 60.84 ] + }, + { + "time": 0.5, + "value": 62.27, + "curve": [ 0.701, 62.27, 0.831, 42.92 ] + }, + { "time": 0.9333, "value": 28.91 }, + { "time": 1.0667, "value": -8.63 }, + { "time": 1.1667, "value": -18.43 }, + { "time": 1.3667, "value": 0.05 } ], "translate": [ { "x": -1.77, "y": 0.57, "curve": "stepped" }, @@ -589,9 +596,9 @@ }, "left hand": { "rotate": [ - { "angle": 11.59, "curve": "stepped" }, - { "time": 0.9333, "angle": 11.59, "curve": "stepped" }, - { "time": 1.3667, "angle": 11.59 } + { "value": 11.59, "curve": "stepped" }, + { "time": 0.9333, "value": 11.59, "curve": "stepped" }, + { "time": 1.3667, "value": 11.59 } ], "translate": [ { "curve": "stepped" }, @@ -606,12 +613,12 @@ }, "left arm": { "rotate": [ - { "angle": 0.52 }, - { "time": 0.4333, "angle": 12.82 }, - { "time": 0.6, "angle": 47.56 }, - { "time": 0.9333, "angle": 12.82 }, - { "time": 1.1667, "angle": -6.5 }, - { "time": 1.3667, "angle": 0.52 } + { "value": 0.52 }, + { "time": 0.4333, "value": 12.82 }, + { "time": 0.6, "value": 47.56 }, + { "time": 0.9333, "value": 12.82 }, + { "time": 1.1667, "value": -6.5 }, + { "time": 1.3667, "value": 0.52 } ], "translate": [ { "curve": "stepped" }, @@ -626,13 +633,24 @@ }, "right shoulder": { "rotate": [ - { "angle": 43.82, "curve": 0, "c3": 0.621 }, - { "time": 0.2333, "angle": -8.74, "curve": 0.305, "c2": 0.58, "c3": 0.71, "c4": 0.97 }, - { "time": 0.5333, "angle": -208.03, "curve": 0.463, "c3": 0.764, "c4": 0.58 }, - { "time": 0.9333, "angle": -246.72 }, - { "time": 1.0667, "angle": -307.14 }, - { "time": 1.1667, "angle": 37.15 }, - { "time": 1.3667, "angle": 43.82 } + { + "value": 43.82, + "curve": [ 0, 43.82, 0.145, -8.74 ] + }, + { + "time": 0.2333, + "value": -8.74, + "curve": [ 0.325, 84.47, 0.446, 147.15 ] + }, + { + "time": 0.5333, + "value": 151.97, + "curve": [ 0.719, 151.97, 0.839, 129.53 ] + }, + { "time": 0.9333, "value": 113.28 }, + { "time": 1.0667, "value": 52.86 }, + { "time": 1.1667, "value": 37.15 }, + { "time": 1.3667, "value": 43.82 } ], "translate": [ { "x": -7.84, "y": 7.19, "curve": "stepped" }, @@ -647,11 +665,11 @@ }, "right arm": { "rotate": [ - { "angle": -4.03 }, - { "time": 0.6, "angle": 17.51 }, - { "time": 0.9333, "angle": -4.03 }, - { "time": 1.1667, "angle": -16.73 }, - { "time": 1.3667, "angle": -4.03 } + { "value": -4.03 }, + { "time": 0.6, "value": 17.51 }, + { "time": 0.9333, "value": -4.03 }, + { "time": 1.1667, "value": -16.73 }, + { "time": 1.3667, "value": -4.03 } ], "translate": [ { "curve": "stepped" }, @@ -666,9 +684,9 @@ }, "right hand": { "rotate": [ - { "angle": 22.92, "curve": "stepped" }, - { "time": 0.9333, "angle": 22.92, "curve": "stepped" }, - { "time": 1.3667, "angle": 22.92 } + { "value": 22.92, "curve": "stepped" }, + { "time": 0.9333, "value": 22.92, "curve": "stepped" }, + { "time": 1.3667, "value": 22.92 } ], "translate": [ { "curve": "stepped" }, @@ -684,8 +702,8 @@ "root": { "rotate": [ {}, - { "time": 0.4333, "angle": -14.53 }, - { "time": 0.8, "angle": 9.87 }, + { "time": 0.4333, "value": -14.53 }, + { "time": 0.8, "value": 9.87 }, { "time": 1.3667 } ], "scale": [ @@ -699,15 +717,15 @@ "bones": { "left upper leg": { "rotate": [ - { "angle": -26.56 }, - { "time": 0.1333, "angle": -8.79 }, - { "time": 0.2667, "angle": 9.51 }, - { "time": 0.4, "angle": 30.74 }, - { "time": 0.5333, "angle": 25.34 }, - { "time": 0.6667, "angle": 26.12 }, - { "time": 0.8, "angle": -7.71 }, - { "time": 0.9333, "angle": -21.19 }, - { "time": 1.0667, "angle": -26.56 } + { "value": -26.56 }, + { "time": 0.1333, "value": -8.79 }, + { "time": 0.2667, "value": 9.51 }, + { "time": 0.4, "value": 30.74 }, + { "time": 0.5333, "value": 25.34 }, + { "time": 0.6667, "value": 26.12 }, + { "time": 0.8, "value": -7.71 }, + { "time": 0.9333, "value": -21.19 }, + { "time": 1.0667, "value": -26.56 } ], "translate": [ { "x": -3, "y": -2.25 }, @@ -717,14 +735,22 @@ }, "right upper leg": { "rotate": [ - { "angle": 42.45 }, - { "time": 0.1333, "angle": 52.11 }, - { "time": 0.2667, "angle": 5.96 }, - { "time": 0.5333, "angle": -16.94 }, - { "time": 0.6667, "angle": 1.9 }, - { "time": 0.8, "angle": 28.06, "curve": 0.463, "c2": 0.12 }, - { "time": 0.9333, "angle": 58.69, "curve": 0.5, "c2": 0.02 }, - { "time": 1.0667, "angle": 42.45 } + { "value": 42.45 }, + { "time": 0.1333, "value": 52.11 }, + { "time": 0.2667, "value": 5.96 }, + { "time": 0.5333, "value": -16.94 }, + { "time": 0.6667, "value": 1.9 }, + { + "time": 0.8, + "value": 28.06, + "curve": [ 0.862, 31.74, 0.933, 58.69 ] + }, + { + "time": 0.9333, + "value": 58.69, + "curve": [ 1, 58.37, 1.067, 42.45 ] + }, + { "time": 1.0667, "value": 42.45 } ], "translate": [ { "x": 8.12, "y": -2.36 }, @@ -737,35 +763,42 @@ }, "left lower leg": { "rotate": [ - { "angle": -10.22 }, - { "time": 0.1333, "angle": -55.65 }, - { "time": 0.2667, "angle": -68.13 }, - { "time": 0.5333, "angle": 5.12 }, - { "time": 0.6667, "angle": -28.3 }, - { "time": 0.8, "angle": 4.08 }, - { "time": 0.9333, "angle": 3.53 }, - { "time": 1.0667, "angle": -10.22 } + { "value": -10.22 }, + { "time": 0.1333, "value": -55.65 }, + { "time": 0.2667, "value": -68.13 }, + { "time": 0.5333, "value": 5.12 }, + { "time": 0.6667, "value": -28.3 }, + { "time": 0.8, "value": 4.08 }, + { "time": 0.9333, "value": 3.53 }, + { "time": 1.0667, "value": -10.22 } ] }, "left foot": { "rotate": [ - { "angle": -3.69 }, - { "time": 0.1333, "angle": -10.42 }, - { "time": 0.2667, "angle": -17.15 }, - { "time": 0.4, "angle": -2.84 }, - { "time": 0.5333, "angle": -3.88 }, - { "time": 0.6667, "angle": 2.78 }, - { "time": 0.8, "angle": 1.68 }, - { "time": 0.9333, "angle": -8.54 }, - { "time": 1.0667, "angle": -3.69 } + { "value": -3.69 }, + { "time": 0.1333, "value": -10.42 }, + { "time": 0.2667, "value": -17.15 }, + { "time": 0.4, "value": -2.84 }, + { "time": 0.5333, "value": -3.88 }, + { "time": 0.6667, "value": 2.78 }, + { "time": 0.8, "value": 1.68 }, + { "time": 0.9333, "value": -8.54 }, + { "time": 1.0667, "value": -3.69 } ] }, "right shoulder": { "rotate": [ - { "angle": 20.9, "curve": 0.264, "c3": 0.75 }, - { "time": 0.1333, "angle": 3.72, "curve": 0.273, "c3": 0.842 }, - { "time": 0.6667, "angle": -278.28 }, - { "time": 1.0667, "angle": 20.9 } + { + "value": 20.9, + "curve": [ 0.035, 20.9, 0.1, 3.72 ] + }, + { + "time": 0.1333, + "value": 3.72, + "curve": [ 0.279, 3.72, 0.582, 81.72 ] + }, + { "time": 0.6667, "value": 81.72 }, + { "time": 1.0667, "value": 20.9 } ], "translate": [ { "x": -7.84, "y": 7.19 }, @@ -776,26 +809,37 @@ }, "right arm": { "rotate": [ - { "angle": -4.03, "curve": 0.267, "c2": 0.01, "c3": 0.805, "c4": 0.99 }, - { "time": 0.1333, "angle": -14, "curve": 0.342 }, - { "time": 0.6667, "angle": 36.55, "curve": 0.307, "c3": 0.787, "c4": 0.99 }, - { "time": 1.0667, "angle": -4.03 } + { + "value": -4.03, + "curve": [ 0.036, -4.13, 0.107, -13.9 ] + }, + { + "time": 0.1333, + "value": -14, + "curve": [ 0.316, -14, 0.667, 36.55 ] + }, + { + "time": 0.6667, + "value": 36.55, + "curve": [ 0.789, 36.55, 0.981, -3.62 ] + }, + { "time": 1.0667, "value": -4.03 } ] }, "right hand": { "rotate": [ - { "angle": 22.92 }, - { "time": 0.4, "angle": -8.97 }, - { "time": 0.6667, "angle": 0.51 }, - { "time": 1.0667, "angle": 22.92 } + { "value": 22.92 }, + { "time": 0.4, "value": -8.97 }, + { "time": 0.6667, "value": 0.51 }, + { "time": 1.0667, "value": 22.92 } ] }, "left shoulder": { "rotate": [ - { "angle": -1.48 }, - { "time": 0.1333, "angle": 13.61 }, - { "time": 0.6667, "angle": 280.75 }, - { "time": 1.0667, "angle": -1.48 } + { "value": -1.48 }, + { "time": 0.1333, "value": 13.61 }, + { "time": 0.6667, "value": -79.25 }, + { "time": 1.0667, "value": -1.48 } ], "translate": [ { "x": -1.77, "y": 0.57 }, @@ -805,29 +849,48 @@ }, "left hand": { "rotate": [ - { "angle": 11.59, "curve": 0.17, "c2": 0.37, "c3": 0.632, "c4": 1.55 }, - { "time": 0.1333, "angle": 28.13, "curve": 0.693, "c2": 0.01, "c3": 0.693, "c4": 0.99 }, - { "time": 0.6667, "angle": -27.43, "curve": 0.118, "c2": 0.41, "c3": 0.739, "c4": 1.77 }, - { "time": 0.8, "angle": -36.33 }, - { "time": 1.0667, "angle": 11.59 } + { + "value": 11.59, + "curve": [ 0.023, 17.71, 0.084, 37.23 ] + }, + { + "time": 0.1333, + "value": 28.13, + "curve": [ 0.503, 27.57, 0.503, -26.87 ] + }, + { + "time": 0.6667, + "value": -27.43, + "curve": [ 0.682, -31.08, 0.765, -43.18 ] + }, + { "time": 0.8, "value": -36.33 }, + { "time": 1.0667, "value": 11.59 } ] }, "left arm": { "rotate": [ - { "angle": -8.28 }, - { "time": 0.1333, "angle": 18.43 }, - { "time": 0.6667, "angle": 0.88 }, - { "time": 1.0667, "angle": -8.28 } + { "value": -8.28 }, + { "time": 0.1333, "value": 18.43 }, + { "time": 0.6667, "value": 0.88 }, + { "time": 1.0667, "value": -8.28 } ] }, "torso": { "rotate": [ - { "angle": -10.28 }, - { "time": 0.1333, "angle": -15.39, "curve": 0.546, "c2": 0.01 }, - { "time": 0.4, "angle": -9.78, "curve": 0.58, "c2": 0.17 }, - { "time": 0.6667, "angle": -15.75 }, - { "time": 0.9333, "angle": -7.07 }, - { "time": 1.0667, "angle": -10.28 } + { "value": -10.28 }, + { + "time": 0.1333, + "value": -15.39, + "curve": [ 0.279, -15.33, 0.4, -9.78 ] + }, + { + "time": 0.4, + "value": -9.78, + "curve": [ 0.555, -10.79, 0.667, -15.75 ] + }, + { "time": 0.6667, "value": -15.75 }, + { "time": 0.9333, "value": -7.07 }, + { "time": 1.0667, "value": -10.28 } ], "translate": [ { "x": -3.67, "y": 1.69 }, @@ -839,24 +902,28 @@ }, "right foot": { "rotate": [ - { "angle": -5.25 }, - { "time": 0.2667, "angle": -4.08 }, - { "time": 0.4, "angle": -6.45 }, - { "time": 0.5333, "angle": -5.4 }, - { "time": 0.8, "angle": -11.69 }, - { "time": 0.9333, "angle": 0.46 }, - { "time": 1.0667, "angle": -5.25 } + { "value": -5.25 }, + { "time": 0.2667, "value": -4.08 }, + { "time": 0.4, "value": -6.45 }, + { "time": 0.5333, "value": -5.4 }, + { "time": 0.8, "value": -11.69 }, + { "time": 0.9333, "value": 0.46 }, + { "time": 1.0667, "value": -5.25 } ] }, "right lower leg": { "rotate": [ - { "angle": -3.39 }, - { "time": 0.1333, "angle": -45.53 }, - { "time": 0.2667, "angle": -2.6 }, - { "time": 0.5333, "angle": -19.53 }, - { "time": 0.6667, "angle": -64.8 }, - { "time": 0.8, "angle": -82.56, "curve": 0.557, "c2": 0.18 }, - { "time": 1.0667, "angle": -3.39 } + { "value": -3.39 }, + { "time": 0.1333, "value": -45.53 }, + { "time": 0.2667, "value": -2.6 }, + { "time": 0.5333, "value": -19.53 }, + { "time": 0.6667, "value": -64.8 }, + { + "time": 0.8, + "value": -82.56, + "curve": [ 0.949, -68.31, 1.067, -3.39 ] + }, + { "time": 1.0667, "value": -3.39 } ] }, "hip": { @@ -866,10 +933,18 @@ ], "translate": [ {}, - { "time": 0.1333, "y": -7.62, "curve": 0.273, "c2": 0.86 }, + { + "time": 0.1333, + "y": -7.62, + "curve": [ 0.206, 0, 0.4, 0, 0.206, 6.42, 0.4, 8.7 ] + }, { "time": 0.4, "y": 8.7 }, { "time": 0.5333, "y": -0.42 }, - { "time": 0.6667, "y": -7.06, "curve": 0.236, "c2": 0.9 }, + { + "time": 0.6667, + "y": -7.06, + "curve": [ 0.698, 0, 0.8, 0, 0.698, 1.92, 0.8, 2.92 ] + }, { "time": 0.8, "y": 2.92 }, { "time": 0.9333, "y": 6.78 }, { "time": 1.0667 } @@ -877,28 +952,35 @@ }, "neck": { "rotate": [ - { "angle": 3.6 }, - { "time": 0.1333, "angle": 17.5 }, - { "time": 0.2667, "angle": 6.11 }, - { "time": 0.4, "angle": 3.46 }, - { "time": 0.5333, "angle": 5.18 }, - { "time": 0.6667, "angle": 18.36 }, - { "time": 0.8, "angle": 6.09 }, - { "time": 0.9333, "angle": 2.29 }, - { "time": 1.0667, "angle": 3.6 } + { "value": 3.6 }, + { "time": 0.1333, "value": 17.5 }, + { "time": 0.2667, "value": 6.11 }, + { "time": 0.4, "value": 3.46 }, + { "time": 0.5333, "value": 5.18 }, + { "time": 0.6667, "value": 18.36 }, + { "time": 0.8, "value": 6.09 }, + { "time": 0.9333, "value": 2.29 }, + { "time": 1.0667, "value": 3.6 } ] }, "head": { "rotate": [ - { "angle": 3.6, "curve": 0, "c3": 0.704, "c4": 1.62 }, - { "time": 0.1667, "angle": -0.21 }, - { "time": 0.2667, "angle": 6.11 }, - { "time": 0.4, "angle": 3.46 }, - { "time": 0.5333, "angle": 5.18, "curve": 0, "c3": 0.704, "c4": 1.62 }, - { "time": 0.7, "angle": 1.11 }, - { "time": 0.8, "angle": 6.09 }, - { "time": 0.9333, "angle": 2.29 }, - { "time": 1.0667, "angle": 3.6 } + { + "value": 3.6, + "curve": [ 0, 3.6, 0.117, -2.57 ] + }, + { "time": 0.1667, "value": -0.21 }, + { "time": 0.2667, "value": 6.11 }, + { "time": 0.4, "value": 3.46 }, + { + "time": 0.5333, + "value": 5.18, + "curve": [ 0.533, 5.18, 0.651, -1.41 ] + }, + { "time": 0.7, "value": 1.11 }, + { "time": 0.8, "value": 6.09 }, + { "time": 0.9333, "value": 2.29 }, + { "time": 1.0667, "value": 3.6 } ] }, "root": { diff --git a/spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.atlas b/spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.atlas new file mode 100644 index 000000000..ce32001a8 --- /dev/null +++ b/spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.atlas @@ -0,0 +1,60 @@ +spineboy-run.png + size: 1181, 687 + filter: Linear, Linear + pma: true +spineboy-pro-run + index: 8 + bounds: 2, 371, 303, 314 + offsets: 11, 22, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 9 + bounds: 2, 44, 278, 325 + offsets: 30, 2, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 0 + bounds: 307, 439, 246, 320 + offsets: 67, 1, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 3 + bounds: 629, 443, 242, 320 + offsets: 12, 20, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 7 + bounds: 951, 389, 228, 296 + offsets: 36, 33, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 4 + bounds: 307, 195, 242, 320 + offsets: 2, 4, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 5 + bounds: 629, 200, 241, 316 + offsets: 8, 3, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 1 + bounds: 282, 2, 191, 318 + offsets: 70, 3, 316, 341 + rotate: 90 + origin: 142, 3 +spineboy-pro-run + index: 2 + bounds: 947, 81, 226, 306 + offsets: 34, 26, 316, 341 + origin: 142, 3 +spineboy-pro-run + index: 6 + bounds: 629, 4, 194, 316 + offsets: 68, 4, 316, 341 + rotate: 90 + origin: 142, 3 diff --git a/spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.png b/spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.png new file mode 100644 index 000000000..e28e0c6ba Binary files /dev/null and b/spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.png differ diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java index 2109b4dad..3b2014498 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java @@ -50,6 +50,7 @@ import com.esotericsoftware.spine.attachments.PointAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.Sequence; +/** Unit tests to ensure {@link AnimationState} is working as expected. */ public class AnimationStateTests { final SkeletonJson json = new SkeletonJson(new AttachmentLoader() { public RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence) { diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java index 5f0fd3447..7f4261175 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java @@ -35,7 +35,7 @@ import com.esotericsoftware.spine.Animation.AttachmentTimeline; import com.esotericsoftware.spine.Animation.Timeline; import com.esotericsoftware.spine.attachments.Attachment; -/** Unit tests for {@link AttachmentTimeline}. */ +/** Unit tests to ensure {@link AttachmentTimeline} is working as expected. */ public class AttachmentTimelineTests { private final SkeletonData skeletonData; private final Skeleton skeleton; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java index e492bbf94..475b6460f 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java @@ -43,9 +43,10 @@ import com.esotericsoftware.spine.attachments.PointAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.Sequence; +/** Demonstrates loading skeleton data without an atlas and plotting bone transform for each animation. */ public class BonePlotting { static public void main (String[] args) throws Exception { - // This example shows how to load skeleton data and plot a bone transform for each animation. + // Create a skeleton loader that doesn't use an atlas and doesn't create any attachments. SkeletonJson json = new SkeletonJson(new AttachmentLoader() { public RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence) { return null; @@ -71,17 +72,22 @@ public class BonePlotting { return null; } }); + SkeletonData skeletonData = json.readSkeletonData(new FileHandle("assets/spineboy/spineboy-ess.json")); Skeleton skeleton = new Skeleton(skeletonData); Bone bone = skeleton.findBone("gun-tip"); + + // Pose the skeleton at regular intervals throughout each animation. float fps = 1 / 15f; for (Animation animation : skeletonData.getAnimations()) { float time = 0; while (time < animation.getDuration()) { animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in); skeleton.updateWorldTransform(); - System.out - .println(animation.getName() + "," + bone.getWorldX() + "," + bone.getWorldY() + "," + bone.getWorldRotationX()); + + System.out.println(animation.getName() + "," // + + bone.getWorldX() + "," + bone.getWorldY() + "," + bone.getWorldRotationX()); + time += fps; } } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java index b0d9e831b..1f57b4143 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java @@ -58,6 +58,7 @@ import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader; import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.Sequence; +/** Demonstrates positioning physics bodies for a skeleton. */ public class Box2DExample extends ApplicationAdapter { SpriteBatch batch; ShapeRenderer renderer; @@ -84,8 +85,8 @@ public class Box2DExample extends ApplicationAdapter { atlas = new TextureAtlas(Gdx.files.internal("spineboy/spineboy-pma.atlas")); - // This loader creates Box2dAttachments instead of RegionAttachments for an easy way to keep - // track of the Box2D body for each attachment. + // This loader creates Box2dAttachments instead of RegionAttachments for an easy way to keep track of the Box2D body for + // each attachment. AtlasAttachmentLoader atlasLoader = new AtlasAttachmentLoader(atlas) { public RegionAttachment newRegionAttachment (Skin skin, String name, String path, @Null Sequence sequence) { Box2dAttachment attachment = new Box2dAttachment(name); @@ -154,7 +155,7 @@ public class Box2DExample extends ApplicationAdapter { batch.end(); - // Position each attachment body. + // Position the physics body for each attachment. for (Slot slot : skeleton.getSlots()) { if (!(slot.getAttachment() instanceof Box2dAttachment)) continue; Box2dAttachment attachment = (Box2dAttachment)slot.getAttachment(); @@ -182,25 +183,19 @@ public class Box2DExample extends ApplicationAdapter { PolygonShape shape = new PolygonShape(); shape.set(vertices); - // next we create a static ground platform. This platform - // is not moveable and will not react to any influences from - // outside. It will however influence other bodies. First we - // create a PolygonShape that holds the form of the platform. - // it will be 100 meters wide and 2 meters high, centered - // around the origin + // Next we create a static ground platform. This platform is not moveable and will not react to any outside influences. It + // will however influence other bodies. First we create a PolygonShape that holds the form of the platform. It will be + // 100 meters wide and 2 meters high, centered around the origin. PolygonShape groundPoly = new PolygonShape(); groundPoly.setAsBox(50, 1); - // next we create the body for the ground platform. It's - // simply a static body. + // Next we create the body for the ground platform. It's simply a static body. BodyDef groundBodyDef = new BodyDef(); groundBodyDef.type = BodyType.StaticBody; groundBody = world.createBody(groundBodyDef); - // finally we add a fixture to the body using the polygon - // defined above. Note that we have to dispose PolygonShapes - // and CircleShapes once they are no longer used. This is the - // only time you have to care explicitely for memomry managment. + // Finally we add a fixture to the body using the polygon defined above. Note that we have to dispose PolygonShapes and + // CircleShapes once they are no longer used. This is the only time you have to care explicitely for memomry managment. FixtureDef fixtureDef = new FixtureDef(); fixtureDef.shape = groundPoly; fixtureDef.filter.groupIndex = 0; @@ -210,12 +205,10 @@ public class Box2DExample extends ApplicationAdapter { PolygonShape boxPoly = new PolygonShape(); boxPoly.setAsBox(1, 1); - // Next we create the 50 box bodies using the PolygonShape we just - // defined. This process is similar to the one we used for the ground - // body. Note that we reuse the polygon for each body fixture. + // Next we create the 50 box bodies using the PolygonShape we just defined. This process is similar to the one we used for + // the ground body. Note that we reuse the polygon for each body fixture. for (int i = 0; i < 45; i++) { - // Create the BodyDef, set a random position above the - // ground and create a new body + // Create the BodyDef, set a random position above the ground and create a new body. BodyDef boxBodyDef = new BodyDef(); boxBodyDef.type = BodyType.DynamicBody; boxBodyDef.position.x = -24 + (float)(Math.random() * 48); @@ -225,7 +218,7 @@ public class Box2DExample extends ApplicationAdapter { boxBody.createFixture(boxPoly, 1); } - // we are done, all that's left is disposing the boxPoly + // We are done, all that's left is disposing the boxPoly. boxPoly.dispose(); } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/EventTimelineTests.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/EventTimelineTests.java index ceb8ec1ea..4a992376c 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/EventTimelineTests.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/EventTimelineTests.java @@ -38,7 +38,7 @@ import com.esotericsoftware.spine.Animation.EventTimeline; import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixDirection; -/** Unit tests for {@link EventTimeline}. */ +/** Unit tests to ensure {@link EventTimeline} is working as expected. */ public class EventTimelineTests { private final SkeletonData skeletonData; private final Skeleton skeleton; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FboTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FboTest.java index eb17d3a74..53d07b4a6 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FboTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FboTest.java @@ -44,6 +44,7 @@ import com.badlogic.gdx.utils.ScreenUtils; import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; +/** Demonstrates rendering an animation to a frame buffer (FBO) and then rendering the FBO to the screen. */ public class FboTest extends ApplicationAdapter { OrthographicCamera camera; TwoColorPolygonBatch batch; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FrameByFrameTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FrameByFrameTest.java index 958d36fbd..3b292c688 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FrameByFrameTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FrameByFrameTest.java @@ -46,32 +46,22 @@ public class FrameByFrameTest extends ApplicationAdapter { TextureAtlas atlas; float time; - Animation walkAnimation, deathAnimation, current; + Animation walkAnimation, current; public void create () { camera = new OrthographicCamera(); batch = new PolygonSpriteBatch(); - atlas = new TextureAtlas("spineboy/frame-by-frame.atlas"); + atlas = new TextureAtlas("spineboy/spineboy-run.atlas"); - walkAnimation = new Animation(1 / 20f, atlas.createSprites("spineboy-pro-walk")); + walkAnimation = new Animation(1 / 15f, atlas.createSprites("spineboy-pro-run")); walkAnimation.setPlayMode(PlayMode.LOOP); - deathAnimation = new Animation(1 / 20f, atlas.createSprites("spineboy-pro-death")); - current = walkAnimation; } public void render () { time += Gdx.graphics.getDeltaTime(); - if (current == deathAnimation && current.isAnimationFinished(time)) { - current = walkAnimation; - time = 0; - } - if (Gdx.input.justTouched()) { - current = deathAnimation; - time = 0; - } AtlasSprite frame = current.getKeyFrame(time); float x = Math.round(Gdx.graphics.getWidth() / 2), y = 25; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkinBonesMixAndMatchTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixAndMatchTest.java similarity index 91% rename from spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkinBonesMixAndMatchTest.java rename to spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixAndMatchTest.java index d3b8108d1..bbfce39f1 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkinBonesMixAndMatchTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixAndMatchTest.java @@ -37,7 +37,8 @@ import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.utils.ScreenUtils; -public class SkinBonesMixAndMatchTest extends ApplicationAdapter { +/** Demonstrates creating and configuring a new skin at runtime. */ +public class MixAndMatchTest extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; SkeletonRenderer renderer; @@ -67,13 +68,11 @@ public class SkinBonesMixAndMatchTest extends ApplicationAdapter { AnimationStateData stateData = new AnimationStateData(skeletonData); // Defines mixing (crossfading) between animations. state = new AnimationState(stateData); // Holds the animation state for a skeleton (current animation, time, etc). - // Queue animations on track 0. + // Set animations on track 0. state.setAnimation(0, "dance", true); - // Create a new skin, by mixing and matching other skins - // that fit together. Items making up the girl are individual - // skins. Using the skin API, a new skin is created which is - // a combination of all these individual item skins. + // Create a new skin, by mixing and matching other skins that fit together. Items making up the girl are individual skins. + // Using the skin API, a new skin is created which is a combination of all these individual item skins. Skin mixAndMatchSkin = new Skin("custom-girl"); mixAndMatchSkin.addSkin(skeletonData.findSkin("skin-base")); mixAndMatchSkin.addSkin(skeletonData.findSkin("nose/short")); @@ -113,6 +112,6 @@ public class SkinBonesMixAndMatchTest extends ApplicationAdapter { } public static void main (String[] args) throws Exception { - new Lwjgl3Application(new SkinBonesMixAndMatchTest()); + new Lwjgl3Application(new MixAndMatchTest()); } } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java index ba59dc774..7a404ad57 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java @@ -59,6 +59,9 @@ import com.badlogic.gdx.utils.ScreenUtils; import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixDirection; +/** Demonstrates simplistic usage of lighting with normal maps. + *

+ * Note the normals are not rotated when bones are rotated, making lighting incorrect. */ public class NormalMapTest extends ApplicationAdapter { String skeletonPath, animationName; SpriteBatch batch; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/PngExportTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/PngExportTest.java new file mode 100644 index 000000000..dabab7b15 --- /dev/null +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/PngExportTest.java @@ -0,0 +1,138 @@ +/****************************************************************************** + * Spine Runtimes License Agreement + * Last updated September 24, 2021. Replaces all prior versions. + * + * Copyright (c) 2013-2021, Esoteric Software LLC + * + * Integration of the Spine Runtimes into software or otherwise creating + * derivative works of the Spine Runtimes is permitted under the terms and + * conditions of Section 2 of the Spine Editor License Agreement: + * http://esotericsoftware.com/spine-editor-license + * + * Otherwise, it is permitted to integrate the Spine Runtimes into software + * or otherwise create derivative works of the Spine Runtimes (collectively, + * "Products"), provided that each user of the Products must obtain their own + * Spine Editor license and redistribution of the Products in any form must + * include this license and copyright notice. + * + * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, + * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.esotericsoftware.spine; + +import com.badlogic.gdx.ApplicationAdapter; +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; +import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; +import com.badlogic.gdx.files.FileHandle; +import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.OrthographicCamera; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.graphics.Pixmap.Format; +import com.badlogic.gdx.graphics.PixmapIO; +import com.badlogic.gdx.graphics.g2d.BitmapFont; +import com.badlogic.gdx.graphics.g2d.TextureAtlas; +import com.badlogic.gdx.graphics.g2d.TextureRegion; +import com.badlogic.gdx.graphics.glutils.FrameBuffer; +import com.badlogic.gdx.utils.ScreenUtils; + +import com.esotericsoftware.spine.Animation.MixBlend; +import com.esotericsoftware.spine.Animation.MixDirection; +import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; + +/** Demonstrates rendering an animation to a frame buffer (FBO) and then writing each frame as a PNG. */ +public class PngExportTest extends ApplicationAdapter { + OrthographicCamera camera; + TwoColorPolygonBatch batch; + SkeletonRenderer renderer; + BitmapFont font; + + TextureAtlas atlas; + Skeleton skeleton; + + FrameBuffer fbo; + TextureRegion fboRegion; + boolean drawFbo = true; + + public void create () { + camera = new OrthographicCamera(); + batch = new TwoColorPolygonBatch(); + renderer = new SkeletonRenderer(); + renderer.setPremultipliedAlpha(true); + font = new BitmapFont(); + font.setColor(Color.BLACK); + + // Load the atlas and skeleton. + atlas = new TextureAtlas(Gdx.files.internal("spineboy/spineboy-pma.atlas")); + SkeletonJson json = new SkeletonJson(atlas); + json.setScale(0.66f); + SkeletonData skeletonData = json.readSkeletonData(Gdx.files.internal("spineboy/spineboy-ess.json")); + + // Create a skeleton instance, set the position of its root bone, and update its world transform. + skeleton = new Skeleton(skeletonData); + skeleton.setPosition(250, 20); + skeleton.updateWorldTransform(); + + // Create an FBO and a texture region. + fbo = new FrameBuffer(Pixmap.Format.RGBA8888, 512, 512, false); + fboRegion = new TextureRegion(fbo.getColorBufferTexture()); + + // Create a pixmap of the same size. + Pixmap pixmap = new Pixmap(fbo.getWidth(), fbo.getHeight(), Format.RGBA8888); + + // Configure the camera and batch for rendering to the FBO's size. + camera.setToOrtho(true, fbo.getWidth(), fbo.getHeight()); + camera.update(); + batch.getProjectionMatrix().set(camera.combined); + + // Start rendering to the FBO. + fbo.begin(); + + // Pose the skeleton at regular intervals throughout the animation. + Animation animation = skeletonData.findAnimation("run"); + float fps = 1 / 15f, time = 0; + int frame = 1; + while (time < animation.getDuration()) { + animation.apply(skeleton, time, time, false, null, 1, MixBlend.first, MixDirection.in); + skeleton.updateWorldTransform(); + + // Render the skeleton to the FBO. + ScreenUtils.clear(0, 0, 0, 0); + batch.begin(); + renderer.draw(batch, skeleton); + batch.end(); + + // Copy the FBO to the pixmap and write it to a PNG file. + String name = animation.getName() + "_" + frame + ".png"; + System.out.println(name); + Gdx.gl.glPixelStorei(GL20.GL_PACK_ALIGNMENT, 1); // Have glReadPixels use byte alignment for each pixel row. + Gdx.gl.glReadPixels(0, 0, fbo.getWidth(), fbo.getHeight(), GL20.GL_RGBA, GL20.GL_UNSIGNED_BYTE, pixmap.getPixels()); + PixmapIO.writePNG(new FileHandle(name), pixmap); + + frame++; + time += fps; + } + + pixmap.dispose(); + fbo.end(); + + // Terminate without showing a window. + System.exit(0); + } + + static public void main (String[] args) throws Exception { + Lwjgl3ApplicationConfiguration config = new Lwjgl3ApplicationConfiguration(); + config.setInitialVisible(false); + new Lwjgl3Application(new PngExportTest(), config); + } +} diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java index 9dd01ba5e..46ca59ba8 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java @@ -35,8 +35,11 @@ import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.TextureAtlas; + import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; +/** Demonstrates loading, animating, and rendering a skeleton. + * @see SkeletonAssetManagerTest */ public class SimpleTest1 extends ApplicationAdapter { OrthographicCamera camera; TwoColorPolygonBatch batch; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java index a581ab74e..3580b9209 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java @@ -38,11 +38,13 @@ import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.math.Vector3; + import com.esotericsoftware.spine.AnimationState.AnimationStateListener; import com.esotericsoftware.spine.AnimationState.TrackEntry; import com.esotericsoftware.spine.attachments.BoundingBoxAttachment; import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; +/** Demonstrates loading, animating, and rendering a skeleton with hit detectiong using a bounding box attachment. */ public class SimpleTest2 extends ApplicationAdapter { OrthographicCamera camera; TwoColorPolygonBatch batch; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java index 44a65a94c..bc3094a09 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java @@ -37,6 +37,7 @@ import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.utils.ScreenUtils; +/** Demonstrates applying multiple animations at once using {@link AnimationState} tracks. */ public class SimpleTest3 extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; @@ -61,7 +62,7 @@ public class SimpleTest3 extends ApplicationAdapter { SkeletonJson loader = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. // SkeletonLoader loader = new SkeletonBinary(atlas); // Or use SkeletonBinary to load binary data. - loader.setScale(0.1f); // Load the skeleton at 50% the size it was in Spine. + loader.setScale(0.5f); // Load the skeleton at 50% the size it was in Spine. SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("raptor/raptor-pro.json")); skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java deleted file mode 100644 index a18402f8d..000000000 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java +++ /dev/null @@ -1,116 +0,0 @@ -/****************************************************************************** - * Spine Runtimes License Agreement - * Last updated September 24, 2021. Replaces all prior versions. - * - * Copyright (c) 2013-2021, Esoteric Software LLC - * - * Integration of the Spine Runtimes into software or otherwise creating - * derivative works of the Spine Runtimes is permitted under the terms and - * conditions of Section 2 of the Spine Editor License Agreement: - * http://esotericsoftware.com/spine-editor-license - * - * Otherwise, it is permitted to integrate the Spine Runtimes into software - * or otherwise create derivative works of the Spine Runtimes (collectively, - * "Products"), provided that each user of the Products must obtain their own - * Spine Editor license and redistribution of the Products in any form must - * include this license and copyright notice. - * - * THE SPINE RUNTIMES ARE PROVIDED BY ESOTERIC SOFTWARE LLC "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL ESOTERIC SOFTWARE LLC BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, - * BUSINESS INTERRUPTION, OR LOSS OF USE, DATA, OR PROFITS) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -package com.esotericsoftware.spine; - -import com.badlogic.gdx.ApplicationAdapter; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.utils.ScreenUtils; - -import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; - -public class SimpleTest4 extends ApplicationAdapter { - OrthographicCamera camera; - TwoColorPolygonBatch batch; - SkeletonRenderer renderer; - SkeletonRendererDebug debugRenderer; - - TextureAtlas atlas; - Skeleton skeleton; - AnimationState state; - - public void create () { - camera = new OrthographicCamera(); - batch = new TwoColorPolygonBatch(); - renderer = new SkeletonRenderer(); - renderer.setPremultipliedAlpha(true); // PMA results in correct blending without outlines. - debugRenderer = new SkeletonRendererDebug(); - debugRenderer.setBoundingBoxes(false); - debugRenderer.setRegionAttachments(false); - - atlas = new TextureAtlas(Gdx.files.internal("goblins/goblins-pma.atlas")); - - SkeletonJson loader = new SkeletonJson(atlas); // This loads skeleton JSON data, which is stateless. - // SkeletonLoader loader = new SkeletonBinary(atlas); // Or use SkeletonBinary to load binary data. - loader.setScale(1.3f); // Load the skeleton at 130% the size it was in Spine. - SkeletonData skeletonData = loader.readSkeletonData(Gdx.files.internal("goblins/goblins-pro.json")); - - skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). - skeleton.setPosition(250, 20); - - AnimationStateData stateData = new AnimationStateData(skeletonData); // Defines mixing (crossfading) between animations. - - state = new AnimationState(stateData); // Holds the animation state for a skeleton (current animation, time, etc). - state.setTimeScale(0.5f); // Slow all animations down to 50% speed. - - // Queue animations on track 0. - state.setAnimation(0, "walk", true); - - // Create an empty skin and copy the goblingirl skin into it. - Skin skin = new Skin("test"); - skin.copySkin(skeletonData.findSkin("goblingirl")); - skeleton.setSkin(skin); - skeleton.setSlotsToSetupPose(); - } - - public void render () { - state.update(Gdx.graphics.getDeltaTime()); // Update the animation time. - - ScreenUtils.clear(0, 0, 0, 0); - - state.apply(skeleton); // Poses skeleton using current animations. This sets the bones' local SRT. - skeleton.updateWorldTransform(); // Uses the bones' local SRT to compute their world SRT. - - // Configure the camera, SpriteBatch, and SkeletonRendererDebug. - camera.update(); - batch.getProjectionMatrix().set(camera.combined); - debugRenderer.getShapeRenderer().setProjectionMatrix(camera.combined); - - batch.begin(); - renderer.draw(batch, skeleton); // Draw the skeleton images. - batch.end(); - - debugRenderer.draw(skeleton); // Draw debug lines. - } - - public void resize (int width, int height) { - camera.setToOrtho(false); // Update camera with new size. - } - - public void dispose () { - atlas.dispose(); - } - - public static void main (String[] args) throws Exception { - new Lwjgl3Application(new SimpleTest4()); - } -} diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAssetManagerTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAssetManagerTest.java index 3c48cf77b..64d49f031 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAssetManagerTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAssetManagerTest.java @@ -41,7 +41,7 @@ import com.badlogic.gdx.utils.ScreenUtils; import com.esotericsoftware.spine.utils.SkeletonDataLoader; import com.esotericsoftware.spine.utils.SkeletonDataLoader.SkeletonDataParameter; -/** Like {@link SimpleTest1}, but using {@link AssetManager} to load the atlas and skeleton data. */ +/** Demonstrates loading an atlas and skeleton using {@link AssetManager}. */ public class SkeletonAssetManagerTest extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; @@ -85,8 +85,8 @@ public class SkeletonAssetManagerTest extends ApplicationAdapter { skeleton = new Skeleton(skeletonData); // Skeleton holds skeleton state (bone positions, slot attachments, etc). skeleton.setPosition(250, 20); - AnimationStateData stateData = new AnimationStateData(skeletonData); // Defines mixing (crossfading) between - // animations. + // Define the default mixing (crossfading) between animations. + AnimationStateData stateData = new AnimationStateData(skeletonData); stateData.setMix("run", "jump", 0.2f); stateData.setMix("jump", "run", 0.2f); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java index 430231722..38e138812 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java @@ -39,6 +39,7 @@ import com.badlogic.gdx.utils.ScreenUtils; import com.esotericsoftware.spine.attachments.SkeletonAttachment; +/** Demonstrates using {@link SkeletonAttachment} to use an entire skeleton as an attachment. */ public class SkeletonAttachmentTest extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java index 81301a5eb..94678ed81 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java @@ -42,6 +42,7 @@ import com.badlogic.gdx.utils.ScreenUtils; import com.esotericsoftware.spine.vertexeffects.SwirlEffect; +/** Boilerplate for basic skeleton rendering, used for various testing. */ public class TestHarness extends ApplicationAdapter { // static String JSON = "coin/coin-pro.json"; // static String ATLAS = "coin/coin-pma.atlas"; diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TimelineApiTest.java similarity index 94% rename from spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java rename to spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TimelineApiTest.java index ef663cd78..5c233c797 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TimelineApiTest.java @@ -40,7 +40,10 @@ import com.badlogic.gdx.utils.ScreenUtils; import com.esotericsoftware.spine.Animation.MixBlend; import com.esotericsoftware.spine.Animation.MixDirection; -public class MixTest extends ApplicationAdapter { +/** Demonstrates using the timeline API. See {@link SimpleTest1} for a higher level API using {@link AnimationState}. + *

+ * See: http://esotericsoftware.com/spine-applying-animations */ +public class TimelineApiTest extends ApplicationAdapter { SpriteBatch batch; float time; Array events = new Array(); @@ -139,6 +142,6 @@ public class MixTest extends ApplicationAdapter { } public static void main (String[] args) throws Exception { - new Lwjgl3Application(new MixTest()); + new Lwjgl3Application(new TimelineApiTest()); } } diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java index c63fa059d..0175a7dda 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java @@ -39,8 +39,10 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.math.Interpolation; import com.badlogic.gdx.utils.ScreenUtils; +import com.esotericsoftware.spine.SkeletonRenderer.VertexEffect; import com.esotericsoftware.spine.vertexeffects.SwirlEffect; +/** Demonstrates applying a {@link VertexEffect}. */ public class VertexEffectTest extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; diff --git a/spine-libgdx/spine-libgdx/.settings/org.eclipse.core.resources.prefs b/spine-libgdx/spine-libgdx/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..42a944b8f --- /dev/null +++ b/spine-libgdx/spine-libgdx/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=Cp1252 diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index a246752dc..d1ea1206b 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -364,6 +364,18 @@ public class Skeleton { public void updateWorldTransform (Bone parent) { if (parent == null) throw new IllegalArgumentException("parent cannot be null."); + Object[] bones = this.bones.items; + for (int i = 1, n = this.bones.size; i < n; i++) { // Skip root bone. + Bone bone = (Bone)bones[i]; + bone.ax = bone.x; + bone.ay = bone.y; + bone.arotation = bone.rotation; + bone.ascaleX = bone.scaleX; + bone.ascaleY = bone.scaleY; + bone.ashearX = bone.shearX; + bone.ashearY = bone.shearY; + } + // Apply the parent bone transform to the root bone. The root bone always inherits scale, rotation and reflection. Bone rootBone = getRootBone(); float pa = parent.a, pb = parent.b, pc = parent.c, pd = parent.d; diff --git a/spine-libgdx/spine-skeletonviewer/.settings/org.eclipse.core.resources.prefs b/spine-libgdx/spine-skeletonviewer/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..42a944b8f --- /dev/null +++ b/spine-libgdx/spine-skeletonviewer/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=Cp1252 diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs index bf3a76b56..0a1312791 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs @@ -455,7 +455,7 @@ namespace Spine.Unity.Editor { } if (cc.changed) { - targetSkeletonDataAsset.FillStateData(); + targetSkeletonDataAsset.FillStateData(quiet: true); EditorUtility.SetDirty(targetSkeletonDataAsset); serializedObject.ApplyModifiedProperties(); } diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs index 0a41cd479..7bfbbce03 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs @@ -61,11 +61,34 @@ namespace Spine.Unity.Editor { return noneLabel; } + static GUIStyle errorPopupStyle; + GUIStyle ErrorPopupStyle { + get { + if (errorPopupStyle == null) errorPopupStyle = new GUIStyle(EditorStyles.popup); + errorPopupStyle.normal.textColor = Color.red; + errorPopupStyle.hover.textColor = Color.red; + errorPopupStyle.focused.textColor = Color.red; + errorPopupStyle.active.textColor = Color.red; + return errorPopupStyle; + } + } + protected T TargetAttribute { get { return (T)attribute; } } protected SerializedProperty SerializedProperty { get; private set; } protected abstract Texture2D Icon { get; } + protected bool IsValueValid (SerializedProperty property) { + if (skeletonDataAsset != null) { + SkeletonData skeletonData = skeletonDataAsset.GetSkeletonData(true); + if (skeletonData != null && !string.IsNullOrEmpty(property.stringValue)) + return IsValueValid(skeletonData, property); + } + return true; + } + + protected virtual bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { return true; } + public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) { SerializedProperty = property; @@ -123,8 +146,10 @@ namespace Spine.Unity.Editor { position = EditorGUI.PrefixLabel(position, label); Texture2D image = Icon; + GUIStyle usedStyle = IsValueValid(property) ? EditorStyles.popup : ErrorPopupStyle; string propertyStringValue = (property.hasMultipleDifferentValues) ? SpineInspectorUtility.EmDash : property.stringValue; - if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) : SpineInspectorUtility.TempContent(propertyStringValue, image), EditorStyles.popup)) + if (GUI.Button(position, string.IsNullOrEmpty(propertyStringValue) ? NoneLabel(image) : + SpineInspectorUtility.TempContent(propertyStringValue, image), usedStyle)) Selector(property); } @@ -174,6 +199,10 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.slot; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindSlot(property.stringValue) != null; + } + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) { if (TargetAttribute.includeNone) menu.AddItem(new GUIContent(NoneString), !property.hasMultipleDifferentValues && string.IsNullOrEmpty(property.stringValue), HandleSelect, new SpineDrawerValuePair(string.Empty, property)); @@ -223,6 +252,10 @@ namespace Spine.Unity.Editor { internal override string NoneString { get { return TargetAttribute.defaultAsEmptyString ? DefaultSkinName : NoneStringConstant; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindSkin(property.stringValue) != null; + } + public static void GetSkinMenuItems (SkeletonData data, List outputNames, List outputMenuItems, bool includeNone = true) { if (data == null) return; if (outputNames == null) return; @@ -269,6 +302,10 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.animation; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindAnimation(property.stringValue) != null; + } + public static void GetAnimationMenuItems (SkeletonData data, List outputNames, List outputMenuItems, bool includeNone = true) { if (data == null) return; if (outputNames == null) return; @@ -311,6 +348,10 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.userEvent; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindEvent(property.stringValue) != null; + } + public static void GetEventMenuItems (SkeletonData data, List eventNames, List menuItems, bool includeNone = true) { if (data == null) return; @@ -356,6 +397,10 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.constraintIK; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindIkConstraint(property.stringValue) != null; + } + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineIkConstraint targetAttribute, SkeletonData data) { var constraints = skeletonDataAsset.GetSkeletonData(false).IkConstraints; @@ -376,6 +421,10 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.constraintTransform; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindTransformConstraint(property.stringValue) != null; + } + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineTransformConstraint targetAttribute, SkeletonData data) { var constraints = skeletonDataAsset.GetSkeletonData(false).TransformConstraints; @@ -395,6 +444,10 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.constraintPath; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindPathConstraint(property.stringValue) != null; + } + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpinePathConstraint targetAttribute, SkeletonData data) { var constraints = skeletonDataAsset.GetSkeletonData(false).PathConstraints; @@ -516,6 +569,10 @@ namespace Spine.Unity.Editor { protected override Texture2D Icon { get { return SpineEditorUtilities.Icons.bone; } } + protected override bool IsValueValid (SkeletonData skeletonData, SerializedProperty property) { + return skeletonData.FindBone(property.stringValue) != null; + } + protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) { menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name)); menu.AddSeparator(""); diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs index 92c4d41cf..db0a0112e 100644 --- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs +++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs @@ -349,6 +349,8 @@ namespace Spine.Unity.Editor { } } + AddDependentAtlasIfImageChanged(atlasPaths, imagePaths); + // Import atlases first. var newAtlases = new List(); foreach (string ap in atlasPaths) { @@ -453,6 +455,18 @@ namespace Spine.Unity.Editor { } } + static void AddDependentAtlasIfImageChanged (List atlasPaths, List imagePaths) { + foreach (var imagePath in imagePaths) { + string atlasPath = imagePath.Replace(".png", ".atlas.txt"); + if (!System.IO.File.Exists(atlasPath)) + continue; + + if (!atlasPaths.Contains(atlasPath)) { + atlasPaths.Add(atlasPath); + } + } + } + static void AddDependentSkeletonIfAtlasChanged (List skeletonPaths, List atlasPaths) { foreach (var atlasPath in atlasPaths) { string skeletonPathJson = atlasPath.Replace(".atlas.txt", ".json"); diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs index 1cbb76b15..778e50264 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs @@ -217,14 +217,30 @@ namespace Spine.Unity { FillStateData(); } - public void FillStateData () { + public void FillStateData (bool quiet = false) { if (stateData != null) { stateData.DefaultMix = defaultMix; for (int i = 0, n = fromAnimation.Length; i < n; i++) { - if (fromAnimation[i].Length == 0 || toAnimation[i].Length == 0) + string fromAnimationName = fromAnimation[i]; + string toAnimationName = toAnimation[i]; + if (fromAnimationName.Length == 0 || toAnimationName.Length == 0) continue; - stateData.SetMix(fromAnimation[i], toAnimation[i], duration[i]); +#if UNITY_EDITOR + if (skeletonData.FindAnimation(fromAnimationName) == null) { + if (!quiet) Debug.LogError( + string.Format("Custom Mix Durations: Animation '{0}' not found, was it renamed?", + fromAnimationName), this); + continue; + } + if (skeletonData.FindAnimation(toAnimationName) == null) { + if (!quiet) Debug.LogError( + string.Format("Custom Mix Durations: Animation '{0}' not found, was it renamed?", + toAnimationName), this); + continue; + } +#endif + stateData.SetMix(fromAnimationName, toAnimationName, duration[i]); } } } diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs index 601af1faf..bb7fe0dc8 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs @@ -52,14 +52,23 @@ namespace Spine.Unity.AttachmentTools { /// If true and the original Attachment is a RegionAttachment, then /// the original region's scale value is used instead of the Sprite's pixels per unit property. Since uniform scale is used, /// x scale of the original attachment (width scale) is used, scale in y direction (height scale) is ignored. + /// If premultiplyAlpha is true>, the TextureFormat of the + /// newly created PMA attachment Texture. + /// If premultiplyAlpha is , whether the newly created + /// PMA attachment Texture has mipmaps enabled. /// When parameter premultiplyAlpha is set to true, a premultiply alpha clone of the /// original texture will be created. Additionally, this PMA Texture clone is cached for later re-use, /// which might steadily increase the Texture memory footprint when used excessively. /// See on how to clear these cached textures. public static Attachment GetRemappedClone (this Attachment o, Sprite sprite, Material sourceMaterial, bool premultiplyAlpha = true, bool cloneMeshAsLinked = true, bool useOriginalRegionSize = false, - bool pivotShiftsMeshUVCoords = true, bool useOriginalRegionScale = false) { - var atlasRegion = premultiplyAlpha ? sprite.ToAtlasRegionPMAClone(sourceMaterial) : sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture }); + bool pivotShiftsMeshUVCoords = true, bool useOriginalRegionScale = false, + TextureFormat pmaCloneTextureFormat = AtlasUtilities.SpineTextureFormat, + bool pmaCloneMipmaps = AtlasUtilities.UseMipMaps) { + + var atlasRegion = premultiplyAlpha ? + sprite.ToAtlasRegionPMAClone(sourceMaterial, pmaCloneTextureFormat, pmaCloneMipmaps) : + sprite.ToAtlasRegion(new Material(sourceMaterial) { mainTexture = sprite.texture }); if (!pivotShiftsMeshUVCoords && o is MeshAttachment) { // prevent non-central sprite pivot setting offsetX/Y and shifting uv coords out of mesh bounds atlasRegion.offsetX = 0;