Kaynağa Gözat

Merge branch '4.1' into 4.2-beta

Harald Csaszar 2 yıl önce
ebeveyn
işleme
0d1948a821

+ 1 - 0
CHANGELOG.md

@@ -178,6 +178,7 @@
     3) Copy the original material, add *_Outline* to its name and set the shader to your outline-only shader like `Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly` or `Spine/Outline/OutlineOnly-ZWrite`.
     4) Assign this *_Outline* material at the new child GameObject's `MeshRenderer` component.
     If you are using `SkeletonRenderSeparator` and need to enable and disable the `SkeletonRenderSeparator` component at runtime, you can increase the `RenderCombinedMesh` `Reference Renderers` array by one and assign the `SkeletonRenderer` itself at the last entry after the parts renderers. Disabled `MeshRenderer` components will be skipped when combining the final mesh, so the combined mesh is automatically filled from the desired active renderers.
+  * Timeline extension package: Added static `EditorEvent` callback to allow editor scripts to react to animation events outside of play-mode. Register to the events via `Spine.Unity.Playables.SpineAnimationStateMixerBehaviour.EditorEvent += YourCallback;`.
 
 * **Breaking changes**
   * Made `SkeletonGraphic.unscaledTime` parameter protected, use the new property `UnscaledTime` instead.

+ 0 - 7
spine-haxe/.vscode/launch.json

@@ -4,13 +4,6 @@
   // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
   "version": "0.2.0",
   "configurations": [
-    {
-      "name": "web",
-      "request": "launch",
-      "type": "chrome",
-      "url": "http://localhost:3000",
-      "webRoot": "${workspaceFolder}"
-    },
     {
       "name": "lime",
       "type": "lime",

+ 28 - 0
spine-haxe/example/src/BasicExample.hx

@@ -0,0 +1,28 @@
+import openfl.geom.Rectangle;
+import spine.SkeletonData;
+import spine.animation.AnimationStateData;
+import spine.atlas.TextureAtlas;
+import spine.starling.SkeletonSprite;
+import starling.core.Starling;
+
+class BasicExample extends Scene {
+	var loadBinary = true;
+
+	public function load():Void {
+		var atlas = TextureAtlas.fromAssets("assets/raptor.atlas");
+		var skeletondata = SkeletonData.fromAssets("assets/raptor-pro" + (loadBinary ? ".skel" : ".json"), atlas);
+		var animationStateData = new AnimationStateData(skeletondata);
+		animationStateData.defaultMix = 0.25;
+
+		var skeletonSprite = new SkeletonSprite(skeletondata, animationStateData);
+		var bounds = skeletonSprite.skeleton.getBounds();
+		skeletonSprite.scale = Starling.current.stage.stageWidth / bounds.width * 0.5;
+		skeletonSprite.x = Starling.current.stage.stageWidth / 2;
+		skeletonSprite.y = Starling.current.stage.stageHeight * 0.9;
+
+		skeletonSprite.state.setAnimationByName(0, "walk", true);
+
+		addChild(skeletonSprite);
+		juggler.add(skeletonSprite);
+	}
+}

+ 2 - 52
spine-haxe/example/src/Main.hx

@@ -1,29 +1,12 @@
 package;
 
-import starling.display.Image;
-import haxe.io.Bytes;
-import openfl.display.Bitmap;
-import openfl.display.BitmapData;
+import Scene.SceneManager;
 import openfl.display.Sprite;
-import openfl.Assets;
 import openfl.geom.Rectangle;
-import openfl.utils.ByteArray;
-import openfl.utils.Endian;
-import spine.animation.AnimationStateData;
-import spine.atlas.TextureAtlas;
-import spine.attachments.AtlasAttachmentLoader;
-import spine.SkeletonBinary;
-import spine.SkeletonData;
-import spine.SkeletonJson;
-import spine.starling.SkeletonAnimation;
-import spine.starling.StarlingTextureLoader;
 import starling.core.Starling;
 import starling.events.Event;
-import starling.textures.Texture;
 
 class Main extends Sprite {
-	private static inline var loadBinary:Bool = false;
-
 	private var starlingSingleton:Starling;
 
 	public function new() {
@@ -39,39 +22,6 @@ class Main extends Sprite {
 		starlingSingleton.start();
 		Starling.current.stage.color = 0x000000;
 
-		loadSpineAnimation();
-	}
-
-	private function loadSpineAnimation():Void {
-		var textureAtlasBitmapData:BitmapData = Assets.getBitmapData("assets/raptor.png");
-		var stAtlas = Assets.getText("assets/raptor.atlas");
-		var binaryData = Assets.getBytes("assets/raptor-pro.skel");
-		var jsonData = Assets.getText("assets/raptor-pro.json");
-
-		var textureAtlas = Texture.fromBitmapData(textureAtlasBitmapData);
-		var textureloader = new StarlingTextureLoader(textureAtlas);
-		var atlas = new TextureAtlas(stAtlas, textureloader);
-
-		var skeletondata:SkeletonData;
-		if (loadBinary) {
-			var skeletonBinary:SkeletonBinary = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
-			var bytearray:ByteArray = ByteArray.fromBytes(binaryData);
-			bytearray.endian = Endian.BIG_ENDIAN;
-			skeletondata = skeletonBinary.readSkeletonData(bytearray);
-		} else {
-			var skeletonJson:SkeletonJson = new SkeletonJson(new AtlasAttachmentLoader(atlas));
-			skeletondata = skeletonJson.readSkeletonData(jsonData);
-		}
-
-		var stateData:AnimationStateData = new AnimationStateData(skeletondata);
-		stateData.defaultMix = 0.25;
-
-		var skeletonanimation:SkeletonAnimation = new SkeletonAnimation(skeletondata, stateData);
-		skeletonanimation.x = Starling.current.stage.stageWidth / 2;
-		skeletonanimation.y = Starling.current.stage.stageHeight * 0.5;
-
-		Starling.current.stage.addChild(skeletonanimation);
-		Starling.current.juggler.add(skeletonanimation);
-		skeletonanimation.state.setAnimationByName(0, "walk", true);
+		SceneManager.getInstance().switchScene(new BasicExample());
 	}
 }

+ 46 - 0
spine-haxe/example/src/Scene.hx

@@ -0,0 +1,46 @@
+import starling.core.Starling;
+import starling.display.Sprite;
+
+class SceneManager {
+	private static var instance:SceneManager;
+
+	private var currentScene:Sprite;
+
+	private function new() {
+		// Singleton pattern to ensure only one instance of SceneManager
+	}
+
+	public static function getInstance():SceneManager {
+		if (instance == null) {
+			instance = new SceneManager();
+		}
+		return instance;
+	}
+
+	public function switchScene(newScene:Scene):Void {
+		if (currentScene != null) {
+			currentScene.dispose();
+			currentScene.removeFromParent(true);
+		}
+		currentScene = newScene;
+		starling.core.Starling.current.stage.addChild(currentScene);
+		newScene.load();
+	}
+}
+
+abstract class Scene extends Sprite {
+	var juggler = new starling.animation.Juggler();
+
+	public function new() {
+		super();
+		Starling.current.juggler.add(juggler);
+	}
+
+	abstract public function load():Void;
+
+	public override function dispose():Void {
+		juggler.purge();
+		Starling.current.juggler.remove(juggler);
+		super.dispose();
+	}
+}

+ 1 - 0
spine-haxe/project.xml

@@ -11,5 +11,6 @@
 
 	<source path="example/src" />
 	<assets path="example/assets" rename="assets" />
+	<assets path="example/assets" include="*.skel" rename="assets" type="binary" />
 
 </project>

+ 1 - 1
spine-haxe/spine-haxe/spine/BinaryInput.hx

@@ -76,7 +76,7 @@ class BinaryInput {
 					chars += String.fromCharCode(((b & 0x0F) << 12 | (readByte() & 0x3F) << 6 | readByte() & 0x3F));
 					i += 3;
 				default:
-					chars += String.fromCharCode(b);
+					chars += String.fromCharCode(b & 0xff);
 					i++;
 			}
 		}

+ 14 - 13
spine-haxe/spine-haxe/spine/Skeleton.hx

@@ -1,5 +1,6 @@
 package spine;
 
+import openfl.geom.Rectangle;
 import openfl.errors.ArgumentError;
 import openfl.utils.Dictionary;
 import openfl.Vector;
@@ -561,11 +562,10 @@ class Skeleton {
 		return _data.name != null ? _data.name : "Skeleton?";
 	}
 
-	public function getBounds(offset:Vector<Float>, size:Vector<Float>, temp:Vector<Float>):Void {
-		if (offset == null)
-			throw new ArgumentError("offset cannot be null.");
-		if (size == null)
-			throw new ArgumentError("size cannot be null.");
+	private var _tempVertices = new Vector<Float>();
+	private var _bounds = new Rectangle();
+
+	public function getBounds():Rectangle {
 		var minX:Float = Math.POSITIVE_INFINITY;
 		var minY:Float = Math.POSITIVE_INFINITY;
 		var maxX:Float = Math.NEGATIVE_INFINITY;
@@ -576,14 +576,14 @@ class Skeleton {
 			var attachment:Attachment = slot.attachment;
 			if (Std.isOfType(attachment, RegionAttachment)) {
 				verticesLength = 8;
-				temp.length = verticesLength;
-				vertices = temp;
+				_tempVertices.length = verticesLength;
+				vertices = _tempVertices;
 				cast(attachment, RegionAttachment).computeWorldVertices(slot, vertices, 0, 2);
 			} else if (Std.isOfType(attachment, MeshAttachment)) {
 				var mesh:MeshAttachment = cast(attachment, MeshAttachment);
 				verticesLength = mesh.worldVerticesLength;
-				temp.length = verticesLength;
-				vertices = temp;
+				_tempVertices.length = verticesLength;
+				vertices = _tempVertices;
 				mesh.computeWorldVertices(slot, 0, verticesLength, vertices, 0, 2);
 			}
 			if (vertices != null) {
@@ -599,9 +599,10 @@ class Skeleton {
 				}
 			}
 		}
-		offset[0] = minX;
-		offset[1] = minY;
-		size[0] = maxX - minX;
-		size[1] = maxY - minY;
+		_bounds.x = minX;
+		_bounds.y = minY;
+		_bounds.width = maxX - minX;
+		_bounds.height = maxY - minY;
+		return _bounds;
 	}
 }

+ 6 - 7
spine-haxe/spine-haxe/spine/SkeletonBinary.hx

@@ -1,5 +1,7 @@
 package spine;
 
+import spine.attachments.AtlasAttachmentLoader;
+import openfl.utils.Endian;
 import spine.animation.SequenceTimeline;
 import openfl.errors.ArgumentError;
 import openfl.errors.Error;
@@ -81,20 +83,17 @@ class SkeletonBinary {
 	private static inline var CURVE_STEPPED:Int = 1;
 	private static inline var CURVE_BEZIER:Int = 2;
 
-	public function new(attachmentLoader:AttachmentLoader = null) {
+	public function new(attachmentLoader:AttachmentLoader) {
 		this.attachmentLoader = attachmentLoader;
 	}
 
-	public function readSkeletonData(object:ByteArray):SkeletonData {
-		if (object == null)
-			throw new ArgumentError("Object cannot be null");
-		if (!Std.isOfType(object, ByteArrayData))
-			throw new ArgumentError("Object must be ByteArrayData");
+	public function readSkeletonData(bytes:ByteArray):SkeletonData {
+		bytes.endian = Endian.BIG_ENDIAN;
 
 		var skeletonData:SkeletonData = new SkeletonData();
 		skeletonData.name = null;
 
-		var input:BinaryInput = new BinaryInput(object);
+		var input:BinaryInput = new BinaryInput(bytes);
 
 		var lowHash:Int = input.readInt32();
 		var highHash:Int = input.readInt32();

+ 19 - 0
spine-haxe/spine-haxe/spine/SkeletonData.hx

@@ -1,5 +1,8 @@
 package spine;
 
+import spine.attachments.AtlasAttachmentLoader;
+import openfl.utils.Assets;
+import spine.atlas.TextureAtlas;
 import openfl.errors.ArgumentError;
 import openfl.Vector;
 import spine.animation.Animation;
@@ -27,6 +30,22 @@ class SkeletonData {
 	public var imagesPath:String;
 	public var audioPath:String;
 
+	public static function fromAssets(path:String, atlas:TextureAtlas, scale:Float = 1.0):SkeletonData {
+		if (StringTools.endsWith(path, ".skel")) {
+			var byteData = Assets.getBytes(path);
+			var loader = new SkeletonBinary(new AtlasAttachmentLoader(atlas));
+			loader.scale = scale;
+			return loader.readSkeletonData(byteData);
+		} else if (StringTools.endsWith(path, ".json")) {
+			var jsonData = Assets.getText(path);
+			var loader = new SkeletonJson(new AtlasAttachmentLoader(atlas));
+			loader.scale = scale;
+			return loader.readSkeletonData(jsonData);
+		} else {
+			throw new SpineException("Path of skeleton data file must end with .json or .skel");
+		}
+	}
+
 	public function new() {}
 
 	// --- Bones.

+ 8 - 19
spine-haxe/spine-haxe/spine/SkeletonJson.hx

@@ -54,28 +54,17 @@ class SkeletonJson {
 
 	private var linkedMeshes:Vector<LinkedMesh> = new Vector<LinkedMesh>();
 
-	public function new(attachmentLoader:AttachmentLoader = null) {
+	public function new(attachmentLoader:AttachmentLoader) {
 		this.attachmentLoader = attachmentLoader;
 	}
 
-	/** @param object A String or ByteArray. */
-	public function readSkeletonData(object:Object, name:String = null):SkeletonData {
-		if (object == null)
+	public function readSkeletonData(json:String):SkeletonData {
+		if (json == null)
 			throw new ArgumentError("object cannot be null.");
 
-		var root:Object;
-		if (Std.isOfType(object, String)) {
-			root = Json.parse(cast(object, String));
-		} else if (Std.isOfType(object, ByteArrayData)) {
-			root = Json.parse(cast(object, ByteArray).readUTFBytes(cast(object, ByteArray).length));
-		} else if (Std.isOfType(object, Dynamic)) {
-			root = object;
-		} else {
-			throw new ArgumentError("object must be a String, ByteArray or Object.");
-		}
+		var root = Json.parse(json);
 
 		var skeletonData:SkeletonData = new SkeletonData();
-		skeletonData.name = name;
 
 		// Skeleton.
 		var skeletonMap:Object = getString(root, "skeleton", "");
@@ -1172,10 +1161,10 @@ class SkeletonJson {
 		}
 
 		var i:Int = value << 2;
-		var cx1:Float = curve[Std.string(i)];
-		var cy1:Float = curve[Std.string(i + 1)] * scale;
-		var cx2:Float = curve[Std.string(i + 2)];
-		var cy2:Float = curve[Std.string(i + 3)] * scale;
+		var cx1:Float = curve[i];
+		var cy1:Float = curve[i + 1] * scale;
+		var cx2:Float = curve[i + 2];
+		var cy2:Float = curve[i + 3] * scale;
 		timeline.setBezier(bezier, frame, value, time1, value1, cx1, cy1, cx2, cy2, time2, value2);
 		return bezier + 1;
 	}

+ 30 - 0
spine-haxe/spine-haxe/spine/atlas/AssetsTextureLoader.hx

@@ -0,0 +1,30 @@
+package spine.atlas;
+
+import starling.textures.Texture;
+import spine.atlas.TextureAtlasRegion;
+import spine.atlas.TextureAtlasPage;
+import spine.atlas.TextureLoader;
+
+class AssetsTextureLoader implements TextureLoader {
+	private var basePath:String;
+
+	public function new(basePath:String) {
+		this.basePath = basePath;
+	}
+
+	public function loadPage(page:TextureAtlasPage, path:String) {
+		var bitmapData = openfl.utils.Assets.getBitmapData(basePath + "/" + path);
+		if (bitmapData == null) {
+			throw new SpineException("Could not load atlas page texture " + basePath + "/" + path);
+		}
+		page.texture = Texture.fromBitmapData(bitmapData);
+	}
+
+	public function loadRegion(region:TextureAtlasRegion):Void {
+		region.texture = region.page.texture;
+	}
+
+	public function unloadPage(page:TextureAtlasPage):Void {
+		cast(page.texture, Texture).dispose();
+	}
+}

+ 13 - 1
spine-haxe/spine-haxe/spine/atlas/TextureAtlas.hx

@@ -1,5 +1,6 @@
 package spine.atlas;
 
+import openfl.utils.Assets;
 import openfl.errors.ArgumentError;
 import openfl.utils.ByteArray;
 import openfl.utils.Dictionary;
@@ -10,6 +11,17 @@ class TextureAtlas {
 	private var regions = new Vector<TextureAtlasRegion>();
 	private var textureLoader:TextureLoader;
 
+	public static function fromAssets(path:String) {
+		var basePath = "";
+		var slashIndex = path.lastIndexOf("/");
+		if (slashIndex != -1) {
+			basePath = path.substring(0, slashIndex);
+		}
+
+		var textureLoader = new AssetsTextureLoader(basePath);
+		return new TextureAtlas(Assets.getText("assets/raptor.atlas"), textureLoader);
+	}
+
 	/** @param object A String or ByteArray. */
 	public function new(object:Dynamic, textureLoader:TextureLoader) {
 		if (object == null) {
@@ -132,7 +144,7 @@ class TextureAtlas {
 						field();
 					}
 				}
-				textureLoader.loadPage(page, line);
+				textureLoader.loadPage(page, page.name);
 				pages.push(page);
 			} else {
 				region = new TextureAtlasRegion(page, line);

+ 0 - 32
spine-haxe/spine-haxe/spine/starling/SkeletonAnimation.hx

@@ -1,32 +0,0 @@
-package spine.starling;
-
-import starling.core.Starling;
-import spine.animation.AnimationState;
-import spine.animation.AnimationStateData;
-import spine.SkeletonData;
-import starling.animation.IAnimatable;
-
-class SkeletonAnimation extends SkeletonSprite implements IAnimatable {
-	public var state:AnimationState;
-
-	private var functionUpdate:Void->Void;
-
-	public function new(skeletonData:SkeletonData, stateData:AnimationStateData = null) {
-		super(skeletonData);
-		state = new AnimationState(stateData != null ? stateData : new AnimationStateData(skeletonData));
-	}
-
-	public function advanceTime(time:Float):Void {
-		var stage = Starling.current.stage;
-		state.update(time);
-		state.apply(skeleton);
-		skeleton.updateWorldTransform();
-		this.setRequiresRedraw();
-		if (this.functionUpdate != null)
-			this.functionUpdate();
-	}
-
-	public function setFunctionAnimationUpdate(functionUpdate:Void->Void):Void {
-		this.functionUpdate = functionUpdate;
-	}
-}

+ 29 - 11
spine-haxe/spine-haxe/spine/starling/SkeletonSprite.hx

@@ -1,37 +1,41 @@
 package spine.starling;
 
-import starling.textures.Texture;
-import starling.utils.Max;
+import starling.animation.IAnimatable;
+import openfl.Vector;
 import openfl.geom.Matrix;
 import openfl.geom.Point;
 import openfl.geom.Rectangle;
-import openfl.Vector;
-import spine.atlas.TextureAtlasRegion;
-import spine.attachments.Attachment;
-import spine.attachments.ClippingAttachment;
-import spine.attachments.MeshAttachment;
-import spine.attachments.RegionAttachment;
 import spine.Bone;
 import spine.Skeleton;
 import spine.SkeletonClipping;
 import spine.SkeletonData;
 import spine.Slot;
+import spine.animation.AnimationState;
+import spine.animation.AnimationStateData;
+import spine.attachments.Attachment;
+import spine.attachments.ClippingAttachment;
+import spine.attachments.MeshAttachment;
+import spine.attachments.RegionAttachment;
 import starling.display.BlendMode;
 import starling.display.DisplayObject;
-import starling.display.Image;
 import starling.rendering.IndexData;
 import starling.rendering.Painter;
 import starling.rendering.VertexData;
+import starling.textures.Texture;
 import starling.utils.Color;
 import starling.utils.MatrixUtil;
+import starling.utils.Max;
 
-class SkeletonSprite extends DisplayObject {
+class SkeletonSprite extends DisplayObject implements IAnimatable {
 	static private var _tempPoint:Point = new Point();
 	static private var _tempMatrix:Matrix = new Matrix();
 	static private var _tempVertices:Vector<Float> = new Vector<Float>();
 	static private var blendModes:Vector<String> = Vector.ofArray([BlendMode.NORMAL, BlendMode.ADD, BlendMode.MULTIPLY, BlendMode.SCREEN]);
 
 	private var _skeleton:Skeleton;
+
+	public var _state:AnimationState;
+
 	private var _smoothing:String = "bilinear";
 
 	private static var clipper:SkeletonClipping = new SkeletonClipping();
@@ -40,11 +44,12 @@ class SkeletonSprite extends DisplayObject {
 	private var tempLight:spine.Color = new spine.Color(0, 0, 0);
 	private var tempDark:spine.Color = new spine.Color(0, 0, 0);
 
-	public function new(skeletonData:SkeletonData) {
+	public function new(skeletonData:SkeletonData, animationStateData:AnimationStateData = null) {
 		super();
 		Bone.yDown = true;
 		_skeleton = new Skeleton(skeletonData);
 		_skeleton.updateWorldTransform();
+		_state = new AnimationState(animationStateData != null ? animationStateData : new AnimationStateData(skeletonData));
 	}
 
 	override public function render(painter:Painter):Void {
@@ -285,6 +290,12 @@ class SkeletonSprite extends DisplayObject {
 		return _skeleton;
 	}
 
+	public var state(get, never):AnimationState;
+
+	private function get_state():AnimationState {
+		return _state;
+	}
+
 	public var smoothing(get, set):String;
 
 	private function get_smoothing():String {
@@ -295,4 +306,11 @@ class SkeletonSprite extends DisplayObject {
 		_smoothing = smoothing;
 		return _smoothing;
 	}
+
+	public function advanceTime(time:Float):Void {
+		_state.update(time);
+		_state.apply(skeleton);
+		skeleton.updateWorldTransform();
+		this.setRequiresRedraw();
+	}
 }

+ 0 - 70
spine-haxe/spine-haxe/spine/starling/StarlingTextureLoader.hx

@@ -1,70 +0,0 @@
-package spine.starling;
-
-import openfl.display.Bitmap;
-import openfl.display.BitmapData;
-import openfl.errors.ArgumentError;
-import openfl.utils.Object;
-import spine.atlas.TextureAtlasPage;
-import spine.atlas.TextureAtlasRegion;
-import spine.atlas.TextureLoader;
-import starling.display.Image;
-import starling.textures.Texture;
-
-class StarlingTextureLoader implements TextureLoader {
-	public var bitmapDatasOrTextures:Object = {};
-	public var singleBitmapDataOrTexture:Dynamic;
-
-	/** @param bitmaps A Bitmap or BitmapData or Texture for an atlas that has only one page, or for a multi page atlas an object where the
-	 * key is the image path and the value is the Bitmap or BitmapData or Texture. */
-	public function new(bitmapsOrTextures:Dynamic) {
-		if (Std.isOfType(bitmapsOrTextures, BitmapData)) {
-			singleBitmapDataOrTexture = cast(bitmapsOrTextures, BitmapData);
-			return;
-		}
-		if (Std.isOfType(bitmapsOrTextures, Bitmap)) {
-			singleBitmapDataOrTexture = cast(bitmapsOrTextures, Bitmap).bitmapData;
-			return;
-		}
-		if (Std.isOfType(bitmapsOrTextures, Texture)) {
-			singleBitmapDataOrTexture = cast(bitmapsOrTextures, Texture);
-			return;
-		}
-
-		for (path in Reflect.fields(bitmapsOrTextures)) {
-			var object:Dynamic = Reflect.getProperty(bitmapsOrTextures, path);
-			var bitmapDataOrTexture:Dynamic;
-			if (Std.isOfType(object, BitmapData)) {
-				bitmapDataOrTexture = cast(object, BitmapData);
-			} else if (Std.isOfType(object, Bitmap)) {
-				bitmapDataOrTexture = cast(object, Bitmap).bitmapData;
-			} else if (Std.isOfType(object, Texture)) {
-				bitmapDataOrTexture = cast(object, Texture);
-			} else {
-				throw new ArgumentError("Object for path \"" + path + "\" must be a Bitmap, BitmapData or Texture: " + object);
-			}
-			bitmapDatasOrTextures[path] = bitmapDataOrTexture;
-		}
-	}
-
-	public function loadPage(page:TextureAtlasPage, path:String):Void {
-		var bitmapDataOrTexture:Dynamic = singleBitmapDataOrTexture != null ? singleBitmapDataOrTexture : bitmapDatasOrTextures[path];
-		if (bitmapDataOrTexture == null) {
-			throw new ArgumentError("BitmapData/Texture not found with name: " + path);
-		}
-		if (Std.isOfType(bitmapDataOrTexture, BitmapData)) {
-			var bitmapData:BitmapData = cast(bitmapDataOrTexture, BitmapData);
-			page.texture = Texture.fromBitmapData(bitmapData);
-		} else {
-			var texture:Texture = cast(bitmapDataOrTexture, Texture);
-			page.texture = texture;
-		}
-	}
-
-	public function loadRegion(region:TextureAtlasRegion):Void {
-		region.texture = region.page.texture;
-	}
-
-	public function unloadPage(page:TextureAtlasPage):Void {
-		cast(page.texture, Texture).dispose();
-	}
-}

+ 8 - 13
spine-unity/Assets/Spine Examples/Scripts/Sample Components/RenderCombinedMesh.cs

@@ -178,12 +178,12 @@ namespace Spine.Unity.Examples {
 				indexBuffer = new ExposedList<int>(combinedIndexCount);
 			}
 
-			if (positionBuffer.Count < combinedVertexCount) {
+			if (positionBuffer.Count != combinedVertexCount) {
 				positionBuffer.Resize(combinedVertexCount);
 				uvBuffer.Resize(combinedVertexCount);
 				colorBuffer.Resize(combinedVertexCount);
 			}
-			if (indexBuffer.Count < combinedIndexCount) {
+			if (indexBuffer.Count != combinedIndexCount) {
 				indexBuffer.Resize(combinedIndexCount);
 			}
 		}
@@ -223,32 +223,27 @@ namespace Spine.Unity.Examples {
 				System.Array.Copy(positions, 0, this.positionBuffer.Items, combinedV, vertexCount);
 				System.Array.Copy(uvs, 0, this.uvBuffer.Items, combinedV, vertexCount);
 				System.Array.Copy(colors, 0, this.colorBuffer.Items, combinedV, vertexCount);
-				combinedV += vertexCount;
 
 				for (int s = 0, submeshCount = mesh.subMeshCount; s < submeshCount; ++s) {
 					int submeshIndexCount = (int)mesh.GetIndexCount(s);
 					int[] submeshIndices = mesh.GetIndices(s);
-					System.Array.Copy(submeshIndices, 0, this.indexBuffer.Items, combinedI, submeshIndexCount);
+					int[] dstIndices = this.indexBuffer.Items;
+					for (int i = 0; i < submeshIndexCount; ++i)
+						dstIndices[i + combinedI] = submeshIndices[i] + combinedV;
 					combinedI += submeshIndexCount;
 				}
+				combinedV += vertexCount;
 			}
 
 			Mesh combinedMesh = doubleBufferedMesh.GetNext();
+			combinedMesh.Clear();
 #if SET_VERTICES_HAS_LENGTH_PARAMETER
 			combinedMesh.SetVertices(this.positionBuffer.Items, 0, this.positionBuffer.Count);
 			combinedMesh.SetUVs(0, this.uvBuffer.Items, 0, this.uvBuffer.Count);
 			combinedMesh.SetColors(this.colorBuffer.Items, 0, this.colorBuffer.Count);
 			combinedMesh.SetTriangles(this.indexBuffer.Items, 0, this.indexBuffer.Count, 0);
 #else
-			// fill excess with zero positions
-			{
-				int listCount = this.positionBuffer.Count;
-				Vector3[] positionArray = this.positionBuffer.Items;
-				int arrayLength = positionArray.Length;
-				Vector3 vector3zero = Vector3.zero;
-				for (int i = listCount; i < arrayLength; i++)
-					positionArray[i] = vector3zero;
-			}
+			// Note: excess already contains zero positions and indices after ExposedList.Resize().
 			combinedMesh.vertices = this.positionBuffer.Items;
 			combinedMesh.uv = this.uvBuffer.Items;
 			combinedMesh.colors32 = this.colorBuffer.Items;

+ 1 - 1
spine-unity/Assets/Spine Examples/package.json

@@ -2,7 +2,7 @@
   "name": "com.esotericsoftware.spine.spine-unity-examples",
   "displayName": "spine-unity Runtime Examples",
   "description": "This plugin provides example scenes and scripts for the spine-unity runtime.",
-  "version": "4.2.21",
+  "version": "4.2.22",
   "unity": "2018.3",
   "author": {
     "name": "Esoteric Software",

+ 16 - 2
spine-unity/Modules/com.esotericsoftware.spine.timeline/Runtime/SpineAnimationState/SpineAnimationStateMixerBehaviour.cs

@@ -31,7 +31,9 @@
 #define SPEED_INCLUDED_IN_CLIP_TIME
 #endif
 
+#if UNITY_EDITOR
 #define SPINE_EDITMODEPOSE
+#endif
 
 using System;
 using UnityEngine;
@@ -250,8 +252,11 @@ namespace Spine.Unity.Playables {
 		}
 
 #if SPINE_EDITMODEPOSE
+		/// <summary>Animation event callback for editor scripts when outside of play-mode.</summary>
+		public static event AnimationState.TrackEntryEventDelegate EditorEvent;
 
 		AnimationState dummyAnimationState;
+		ExposedList<Spine.Event> editorAnimationEvents = new ExposedList<Event>();
 
 		public void PreviewEditModePose (Playable playable,
 			ISkeletonComponent skeletonComponent, IAnimationStateComponent animationStateComponent,
@@ -259,6 +264,7 @@ namespace Spine.Unity.Playables {
 
 			if (Application.isPlaying) return;
 			if (animationStateComponent.IsNullOrDestroyed() || skeletonComponent == null) return;
+			editorAnimationEvents.Clear(false);
 
 			int inputCount = playable.GetInputCount();
 			float rootSpeed = GetRootPlayableSpeed(playable);
@@ -341,11 +347,19 @@ namespace Spine.Unity.Playables {
 					}
 
 					// Apply Pose
+					dummyAnimationState.Event += EditorEvent;
 					dummyAnimationState.Update(0);
 					dummyAnimationState.Apply(skeleton);
+					dummyAnimationState.Event -= EditorEvent;
 				} else {
-					if (toAnimation != null)
-						toAnimation.Apply(skeleton, 0, toClipTime, clipData.loop, null, clipData.alpha, MixBlend.Setup, MixDirection.In);
+					if (toAnimation != null) {
+						toAnimation.Apply(skeleton, 0, toClipTime, clipData.loop, editorAnimationEvents, clipData.alpha, MixBlend.Setup, MixDirection.In);
+						if (EditorEvent != null) {
+							foreach (Spine.Event e in editorAnimationEvents) {
+								EditorEvent(null, e);
+							}
+						}
+					}
 				}
 
 				skeleton.UpdateWorldTransform();

+ 1 - 1
spine-unity/Modules/com.esotericsoftware.spine.timeline/package.json

@@ -2,7 +2,7 @@
 	"name": "com.esotericsoftware.spine.timeline",
 	"displayName": "Spine Timeline Extensions",
 	"description": "This plugin provides integration of spine-unity for the Unity Timeline.\n\nPrerequisites:\nIt requires a working installation of the spine-unity and spine-csharp runtimes as UPM packages (not as spine-unity unitypackage), version 4.2.\n(See http://esotericsoftware.com/git/spine-runtimes/spine-unity)",
-	"version": "4.2.12",
+	"version": "4.2.13",
 	"unity": "2018.3",
 	"author": {
 		"name": "Esoteric Software",