Эх сурвалжийг харах

Merge branch '4.1-beta' of https://github.com/esotericsoftware/spine-runtimes into 4.1-beta

badlogic 3 жил өмнө
parent
commit
ef5e059ed9
38 өөрчлөгдсөн 921 нэмэгдсэн , 493 устгасан
  1. 2 0
      CHANGELOG.md
  2. 9 7
      examples/export/export.sh
  3. 87 0
      examples/export/png-0.5-frame-by-frame.json
  4. BIN
      examples/owl/owl-pro.spine
  5. 60 0
      examples/spineboy/export/spineboy-run.atlas
  6. BIN
      examples/spineboy/export/spineboy-run.png
  7. 2 0
      spine-libgdx/spine-libgdx-tests/.settings/org.eclipse.core.resources.prefs
  8. 1 1
      spine-libgdx/spine-libgdx-tests/assets/skin/skin.json
  9. 385 310
      spine-libgdx/spine-libgdx-tests/assets/spineboy-old/spineboy-old.json
  10. 60 0
      spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.atlas
  11. BIN
      spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.png
  12. 1 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AnimationStateTests.java
  13. 1 1
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/AttachmentTimelineTests.java
  14. 9 3
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/BonePlotting.java
  15. 14 21
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/Box2DExample.java
  16. 1 1
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/EventTimelineTests.java
  17. 1 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FboTest.java
  18. 3 13
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/FrameByFrameTest.java
  19. 6 7
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixAndMatchTest.java
  20. 3 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/NormalMapTest.java
  21. 138 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/PngExportTest.java
  22. 3 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest1.java
  23. 2 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest2.java
  24. 2 1
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java
  25. 0 116
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java
  26. 3 3
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAssetManagerTest.java
  27. 1 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java
  28. 1 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java
  29. 5 2
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TimelineApiTest.java
  30. 2 0
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/VertexEffectTest.java
  31. 2 0
      spine-libgdx/spine-libgdx/.settings/org.eclipse.core.resources.prefs
  32. 12 0
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
  33. 2 0
      spine-libgdx/spine-skeletonviewer/.settings/org.eclipse.core.resources.prefs
  34. 1 1
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Asset Types/SkeletonDataAssetInspector.cs
  35. 58 1
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAttributeDrawers.cs
  36. 14 0
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/AssetUtility.cs
  37. 19 3
      spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/SkeletonDataAsset.cs
  38. 11 2
      spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs

+ 2 - 0
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**
 

+ 9 - 7
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

+ 87 - 0
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
+}

BIN
examples/owl/owl-pro.spine


+ 60 - 0
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

BIN
examples/spineboy/export/spineboy-run.png


+ 2 - 0
spine-libgdx/spine-libgdx-tests/.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=Cp1252

+ 1 - 1
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 },

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 385 - 310
spine-libgdx/spine-libgdx-tests/assets/spineboy-old/spineboy-old.json


+ 60 - 0
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

BIN
spine-libgdx/spine-libgdx-tests/assets/spineboy/spineboy-run.png


+ 1 - 0
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) {

+ 1 - 1
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;

+ 9 - 3
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;
 			}
 		}

+ 14 - 21
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();
 	}
 

+ 1 - 1
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;

+ 1 - 0
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;

+ 3 - 13
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<AtlasSprite> walkAnimation, deathAnimation, current;
+	Animation<AtlasSprite> 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;

+ 6 - 7
spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkinBonesMixAndMatchTest.java → 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());
 	}
 }

+ 3 - 0
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.
+ * <p>
+ * Note the normals are not rotated when bones are rotated, making lighting incorrect. */
 public class NormalMapTest extends ApplicationAdapter {
 	String skeletonPath, animationName;
 	SpriteBatch batch;

+ 138 - 0
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);
+	}
+}

+ 3 - 0
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;

+ 2 - 0
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;

+ 2 - 1
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).

+ 0 - 116
spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest4.java

@@ -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());
-	}
-}

+ 3 - 3
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);
 

+ 1 - 0
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;

+ 1 - 0
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";

+ 5 - 2
spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/MixTest.java → 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}.
+ * <p>
+ * See: http://esotericsoftware.com/spine-applying-animations */
+public class TimelineApiTest extends ApplicationAdapter {
 	SpriteBatch batch;
 	float time;
 	Array<Event> 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());
 	}
 }

+ 2 - 0
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;

+ 2 - 0
spine-libgdx/spine-libgdx/.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=Cp1252

+ 12 - 0
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;

+ 2 - 0
spine-libgdx/spine-skeletonviewer/.settings/org.eclipse.core.resources.prefs

@@ -0,0 +1,2 @@
+eclipse.preferences.version=1
+encoding/<project>=Cp1252

+ 1 - 1
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();
 				}

+ 58 - 1
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<string> outputNames, List<GUIContent> 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<string> outputNames, List<GUIContent> 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<string> eventNames, List<GUIContent> 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("");

+ 14 - 0
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<AtlasAssetBase>();
 			foreach (string ap in atlasPaths) {
@@ -453,6 +455,18 @@ namespace Spine.Unity.Editor {
 			}
 		}
 
+		static void AddDependentAtlasIfImageChanged (List<string> atlasPaths, List<string> 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<PathAndProblemInfo> skeletonPaths, List<string> atlasPaths) {
 			foreach (var atlasPath in atlasPaths) {
 				string skeletonPathJson = atlasPath.Replace(".atlas.txt", ".json");

+ 19 - 3
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]);
 				}
 			}
 		}

+ 11 - 2
spine-unity/Assets/Spine/Runtime/spine-unity/Utility/AttachmentCloneExtensions.cs

@@ -52,14 +52,23 @@ namespace Spine.Unity.AttachmentTools {
 		///	<param name="useOriginalRegionScale">If <c>true</c> 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.</param>
+		///	<param name="pmaCloneTextureFormat">If <c>premultiplyAlpha</c> is <c>true></c>, the TextureFormat of the
+		///	newly created PMA attachment Texture.</param>
+		///	<param name="pmaCloneMipmaps">If <c>premultiplyAlpha</c> is <ctrue></c>, whether the newly created
+		///	PMA attachment Texture has mipmaps enabled.</param>
 		///	<remarks>When parameter <c>premultiplyAlpha</c> is set to <c>true</c>, 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 <see cref="AtlasUtilities.ClearCache()"/> on how to clear these cached textures.</remarks>
 		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;

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно