Browse Source

[ts][threejs] Fixed bug in core AssetManager when loading multi-page atlases. Rewrote THREE.JS renderer to support multi-page atlases. Fixed clipping in THREE.JS backend. Closes #1066, closes #677.

badlogic 7 years ago
parent
commit
fe9ae97747

+ 20 - 19
CHANGELOG.md

@@ -11,7 +11,7 @@
  * **Additions**
   * Added `Skeleton.getBounds` from reference implementation.
   * Added support for local and relative transform constraint calculation, including additional fields in `TransformConstraintData`
-  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).    
+  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).
   * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.
   * Added `PointAttachment`, additional method `newPointAttachment` in `AttachmentLoader` interface.
   * Added `ClippingAttachment`, additional method `newClippingAttachment` in `AttachmentLoader` interface.
@@ -38,10 +38,10 @@
   * Removed `spVertexIndex`from public API.
   * Listeners on `spAnimationState` or `spTrackEntry` will now be also called in case a track entry is disposed as part of dispoing the `spAnimationState`.
  * **Additions**
-  * Added support for local and relative transform constraint calculation, including additional fields in `spTransformConstraintData`.  
+  * Added support for local and relative transform constraint calculation, including additional fields in `spTransformConstraintData`.
   * Added `spPointAttachment`, additional method `spAtlasAttachmentLoadeR_newPointAttachment`.
   * Added support for local and relative transform constraint calculation, including additional fields in `TransformConstraintData`
-  * Added `spBone_localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).  	
+  * Added `spBone_localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).
    * Added two color tinting support, including `spTwoColorTimeline` and additional fields on `spSlot` and `spSlotData`.
   * Added `userData` field to `spTrackEntry`, so users can expose data in `spAnimationState` callbacks.
   * Modified kvec.h used by SkeletonBinary.c to use Spine's MALLOC/FREE macros. That way there's only one place to inject custom allocators ([extension.h](https://github.com/EsotericSoftware/spine-runtimes/blob/master/spine-c/spine-c/include/spine/extension.h)) [commit](https://github.com/EsotericSoftware/spine-runtimes/commit/c2cfbc6cb8709daa082726222d558188d75a004f)
@@ -95,10 +95,10 @@
   * Added `stride` parameter to `VertexAttachment.ComputeWorldVertices`.
   * Removed `RegionAttachment.Vertices` field. The vertices array is provided to `RegionAttachment.ComputeWorldVertices` by the API user now.
   * Removed `RegionAttachment.UpdateWorldVertices`, added `RegionAttachment.ComputeWorldVertices`. The new method now computes the x/y positions of the 4 vertices of the corner and places them in the provided `worldVertices` array, starting at `offset`, then moving by `stride` array elements when advancing to the next vertex. This allows to directly compose the vertex buffer and avoids a copy. The computation of the full vertices, including vertex colors and texture coordinates, is now done by the backend's respective renderer.
- * **Additions**  
+ * **Additions**
   * Added support for local and relative transform constraint calculation, including additional fields in `TransformConstraintData`
-  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).  
-  * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.  
+  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).
+  * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.
   * Added `PointAttachment`, additional method `NewPointAttachment` in `AttachmentLoader` interface.
   * Added `ClippingAttachment`, additional method `NewClippingAttachment` in `AttachmentLoader` interface.
   * Added `SkeletonClipper` and `Triangulator`, used to implement software clipping of attachments.
@@ -110,10 +110,10 @@
    * **Two color tinting** is currently supported via extra UV2 and UV3 mesh vertex streams. To use Two color tinting, you need to:
      * switch on "Tint Black" under "Advanced...",
      * use the new `Spine/Skeleton Tint Black` shader, or your own shader that treats the UV2 and UV3 streams similarly.
-     * Additionally, for SkeletonGraphic, you can use `Spine/SkeletonGraphic Tint Black` (or the bundled SkeletonGraphicTintBlack material) or your own shader that uses UV2 and UV3 streams similarly. **Additional Shader Channels** TexCoord1 and TexCoord2 will need to be enabled from the Canvas component's inspector. These correspond to UV2 and UV3. 
+     * Additionally, for SkeletonGraphic, you can use `Spine/SkeletonGraphic Tint Black` (or the bundled SkeletonGraphicTintBlack material) or your own shader that uses UV2 and UV3 streams similarly. **Additional Shader Channels** TexCoord1 and TexCoord2 will need to be enabled from the Canvas component's inspector. These correspond to UV2 and UV3.
    * **Clipping** is now supported. Caution: The SkeletonAnimation switches to slightly slower mesh generation code when clipping so limit your use of `ClippingAttachment`s when using on large numbers of skeletons.
- * **SkeletonRenderer.initialFlip** Spine components such as SkeletonRenderer, SkeletonAnimation, SkeletonAnimator now has `initialFlipX` and `initialFlipY` fields which are also visible in the inspector under "Advanced...". It will allow you to set and preview a starting flip value for your skeleton component. This is applied immediately when the internal skeleton object is instantiated. 
- * **[SpineAttribute] Improvements** 
+ * **SkeletonRenderer.initialFlip** Spine components such as SkeletonRenderer, SkeletonAnimation, SkeletonAnimator now has `initialFlipX` and `initialFlipY` fields which are also visible in the inspector under "Advanced...". It will allow you to set and preview a starting flip value for your skeleton component. This is applied immediately when the internal skeleton object is instantiated.
+ * **[SpineAttribute] Improvements**
    * **Icons have been added to SpineAttributeDrawers**. This should make your default inspectors easier to understand at a glance.
    * **Added Constraint Attributes** You can now use `[SpineIkConstraint]` `[SpineTransformConstraint]` `[SpinePathConstraint]`
    * **SpineAttribute dataField** parameter can also now detect sibling fields within arrays and serializable structs/classes.
@@ -127,12 +127,12 @@
  * **SkeletonRenderer.OnPostProcessVertices** is a new callback that gives you a reference to the MeshGenerator after it has generated a mesh from the current skeleton pose. You can access `meshGenerator.VertexBuffer` or `meshGenerator.ColorBuffer` to modify these before they get pushed into the UnityEngine.Mesh for rendering. This can be useful for non-shader vertex effects.
  * **Examples**
    * **Examples now use properties**. The code in the example scripts have been switched over to using properties instead of fields to encourage their use for consistency. This is in anticipation of both users who want to move the Spine folders to the Unity Plugins folder (compiled as a different assembly), and of Unity 2017's ability to manually define different assemblies for shorter compilation times.
-   * **Mix And Match**. The mix-and-match example scene, code and data have been updated to reflect the current recommended setup for animation-compatible custom equip systems The underlying API has changed since 3.5 and the new API calls in MixAndMatch.cs is recommended. Documentation is in progress.  
+   * **Mix And Match**. The mix-and-match example scene, code and data have been updated to reflect the current recommended setup for animation-compatible custom equip systems The underlying API has changed since 3.5 and the new API calls in MixAndMatch.cs is recommended. Documentation is in progress.
    * **Sample Components**. `AtasRegionAttacher` and `SpriteAttacher` are now part of `Sample Components`, to reflect that they are meant to be used as sample code rather than production. A few other sample components have also been added. New imports of the unitypackage Examples folder will see a "Legacy" folder comprised of old sample components that no longer contain the most up-to-date and recommended workflows, but are kept in case old setups used them for production.
  * **Spine folder**. In the unitypackage, the "spine-csharp" and "spine-unity" folders are now inside a "Spine" folder. This change will only affect fresh imports. Importing the unitypackage to update Spine-Unity in your existing project will update the appropriate files however you chose to arrange them, as long as the meta files are intact.
  * **Breaking changes**
    * The Sprite shaders module was updated to the latest version from the [source](https://github.com/traggett/UnitySpriteShaders/commits/master). Some changes were made to the underlying keyword structure. You may need to review the settings of your lit materials. Particularly, your Fixed Normals settings.
-   * The `Spine/Skeleton Lit` shader was switched over to non-fixed-function code. It now no longer requires mesh normals and has fixed normals at the shader level. 
+   * The `Spine/Skeleton Lit` shader was switched over to non-fixed-function code. It now no longer requires mesh normals and has fixed normals at the shader level.
    * The old MeshGenerator classes, interfaces and code in `Spine.Unity.MeshGeneration` are now deprecated. All mesh-generating components now share the class `Spine.Unity.MeshGenerator` defined in `SpineMesh.cs`. MeshGenerator is a serializable class.
      * The `SkeletonRenderer.renderMeshes` optimization is currently non-functional.
      * Old triangle-winding code has been removed from `SkeletonRenderer`. Please use shaders that have backface culling off.
@@ -161,15 +161,15 @@
   * Removed `RegionAttachment.updateWorldVertices`, added `RegionAttachment.computeWorldVertices`. The new method now computes the x/y positions of the 4 vertices of the corner and places them in the provided `worldVertices` array, starting at `offset`, then moving by `stride` array elements when advancing to the next vertex. This allows to directly compose the vertex buffer and avoids a copy. The computation of the full vertices, including vertex colors and texture coordinates, is now done by the backend's respective renderer.
   * Skeleton attachments: Moved update of attached skeleton out of libGDX `SkeletonRenderer`, added overloaded method `Skeleton#updateWorldTransform(Bone), used for `SkeletonAttachment`. You now MUST call this new method
   with the bone of the parent skeleton to which the child skeleton is attached. See `SkeletonAttachmentTest` for and example.
- * **Additions**  
+ * **Additions**
   * Added support for local and relative transform constraint calculation, including additional fields in `TransformConstraintData`
-  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).  
-  * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.  
+  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).
+  * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.
   * Added `PointAttachment`, additional method `newPointAttachment` in `AttachmentLoader` interface.
   * Added `ClippingAttachment`, additional method `newClippingAttachment` in `AttachmentLoader` interface.
   * Added `SkeletonClipper` and `Triangulator`, used to implement software clipping of attachments.
   * `AnimationState#apply` returns boolean indicating if any timeline was applied or not.
-  * `Animation#apply` and `Timeline#apply`` now take enums `MixPose` and `MixDirection` instead of booleans  
+  * `Animation#apply` and `Timeline#apply`` now take enums `MixPose` and `MixDirection` instead of booleans
 
 ### libGDX
  * Fixed renderer to work with 3.6 changes
@@ -187,7 +187,7 @@
  * **Additions**
   * Added `Bone:localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).
   * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.
-  * Added `PointAttachment`, additional method `newPointAttachment` in `AttachmentLoader` interface.  
+  * Added `PointAttachment`, additional method `newPointAttachment` in `AttachmentLoader` interface.
   * Added support for local and relative transform constraint calculation, including additional fields in `TransformConstraintData`
   * Added `ClippingAttachment`, additional method `newClippingAttachment` in `AttachmentLoader` interface.
   * Added `SkeletonClipper` and `Triangulator`, used to implement software clipping of attachments.
@@ -213,10 +213,10 @@
   * Removed `VertexAttachment.computeWorldVertices` overload, changed `VertexAttachment.computeWorldVerticesWith` to `VertexAttachment.computeWorldVertices`, added `stride` parameter.
   * Removed `RegionAttachment.vertices` field. The vertices array is provided to `RegionAttachment.computeWorldVertices` by the API user now.
   * Removed `RegionAttachment.updateWorldVertices`, added `RegionAttachment.computeWorldVertices`. The new method now computes the x/y positions of the 4 vertices of the corner and places them in the provided `worldVertices` array, starting at `offset`, then moving by `stride` array elements when advancing to the next vertex. This allows to directly compose the vertex buffer and avoids a copy. The computation of the full vertices, including vertex colors and texture coordinates, is now done by the backend's respective renderer.
- * **Additions**  
+ * **Additions**
   * Added support for local and relative transform constraint calculation, including additional fields in `TransformConstraintData`
-  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).  
-  * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.  
+  * Added `Bone.localToWorldRotation`(rotation given relative to x-axis, counter-clockwise, in degrees).
+  * Added two color tinting support, including `TwoColorTimeline` and additional fields on `Slot` and `SlotData`.
   * Added `PointAttachment`, additional method `newPointAttachment` in `AttachmentLoader` interface.
   * Added `ClippingAttachment`, additional method `newClippingAttachment` in `AttachmentLoader` interface.
   * Added `SkeletonClipper` and `Triangulator`, used to implement software clipping of attachments.
@@ -250,6 +250,7 @@
  * Fixed renderer to work with 3.6 changes. Two color tinting is not supported.
  * Added clipping support
  * Added `VertexEffect` interface, instances of which can be set on `SkeletonMesh`. Allows to modify vertices before submitting them to GPU. See `SwirlEffect`, `JitterEffect`.
+ * Added support for multi-page atlases
 
 ### Widget backend
  * Fixed WebGL context loss (see WebGL backend changes). Enabled automatically.

+ 4 - 4
spine-ts/README.md

@@ -20,11 +20,11 @@ The Spine Runtimes are developed with the intent to be used with data exported f
 
 spine-ts works with data exported from Spine 3.6.xx.
 
-spine-ts WebGL & Widget backends supports all Spine features. 
+spine-ts WebGL & Widget backends supports all Spine features.
 
-spine-ts Canvas does not support color tinting, mesh attachments and clipping. Only the alpha channel from tint colors is applied. Experimental support for mesh attachments can be enabled by setting `spine.canvas.SkeletonRenderer.useTriangleRendering` to true. Note that this method is slow and may lead to artifacts on some browsers. 
+spine-ts Canvas does not support color tinting, mesh attachments and clipping. Only the alpha channel from tint colors is applied. Experimental support for mesh attachments can be enabled by setting `spine.canvas.SkeletonRenderer.useTriangleRendering` to true. Note that this method is slow and may lead to artifacts on some browsers.
 
-spine-ts THREE.JS does not support color tinting, blend modes and clipping. The THREE.JS backend provides `SkeletonMesh.zOffset` to avoid z-fighting. Adjust to your near/far plane settings.
+spine-ts THREE.JS does not support two color tinting & blend modes. The THREE.JS backend provides `SkeletonMesh.zOffset` to avoid z-fighting. Adjust to your near/far plane settings.
 
 spine-ts does not yet support loading the binary format.
 
@@ -115,7 +115,7 @@ You can disable two-color tinting like this:
 // If you use SceneRenderer, disable two-color tinting via the last constructor argument
 var sceneRenderer = new spine.SceneRenderer(canvas, gl, false);
 
-// If you use SkeletonRenderer and PolygonBatcher directly, 
+// If you use SkeletonRenderer and PolygonBatcher directly,
 // disable two-color tinting in the respective constructor
 // and use the shader returned by Shader.newColoredTextured()
 // instead of Shader.newTwoColoredTextured()

+ 12 - 5
spine-ts/build/spine-all.d.ts

@@ -1701,22 +1701,26 @@ declare module spine.threejs {
 	}
 }
 declare module spine.threejs {
-	class MeshBatcher {
-		mesh: THREE.Mesh;
+	class MeshBatcher extends THREE.Mesh {
 		private static VERTEX_SIZE;
 		private vertexBuffer;
 		private vertices;
 		private verticesLength;
 		private indices;
 		private indicesLength;
-		constructor(mesh: THREE.Mesh, maxVertices?: number);
+		constructor(maxVertices?: number);
+		clear(): void;
 		begin(): void;
+		canBatch(verticesLength: number, indicesLength: number): boolean;
 		batch(vertices: ArrayLike<number>, verticesLength: number, indices: ArrayLike<number>, indicesLength: number, z?: number): void;
 		end(): void;
 	}
 }
 declare module spine.threejs {
-	class SkeletonMesh extends THREE.Mesh {
+	class SkeletonMeshMaterial extends THREE.ShaderMaterial {
+		constructor();
+	}
+	class SkeletonMesh extends THREE.Object3D {
 		tempPos: Vector2;
 		tempUv: Vector2;
 		tempLight: Color;
@@ -1725,7 +1729,8 @@ declare module spine.threejs {
 		state: AnimationState;
 		zOffset: number;
 		vertexEffect: VertexEffect;
-		private batcher;
+		private batches;
+		private nextBatchIndex;
 		private clipper;
 		static QUAD_TRIANGLES: number[];
 		static VERTEX_SIZE: number;
@@ -1733,6 +1738,8 @@ declare module spine.threejs {
 		private tempColor;
 		constructor(skeletonData: SkeletonData);
 		update(deltaTime: number): void;
+		private clearBatches();
+		private nextBatch();
 		private updateGeometry();
 	}
 }

+ 113 - 33
spine-ts/build/spine-all.js

@@ -2001,6 +2001,7 @@ var spine;
 			path = this.pathPrefix + path;
 			this.toLoad++;
 			AssetManager.downloadText(path, function (atlasData) {
+				var pagesLoaded = { count: 0 };
 				var atlasPages = new Array();
 				try {
 					var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2021,11 +2022,10 @@ var spine;
 					return;
 				}
 				var _loop_1 = function (atlasPage) {
-					var pagesLoaded = 0;
 					var pageLoadError = false;
 					_this.loadTexture(atlasPage, function (imagePath, image) {
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							if (!pageLoadError) {
 								try {
 									var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2056,8 +2056,8 @@ var spine;
 						}
 					}, function (imagePath, errorMessage) {
 						pageLoadError = true;
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path;
 							if (error)
 								error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path);
@@ -9433,18 +9433,19 @@ var spine;
 (function (spine) {
 	var threejs;
 	(function (threejs) {
-		var MeshBatcher = (function () {
-			function MeshBatcher(mesh, maxVertices) {
+		var MeshBatcher = (function (_super) {
+			__extends(MeshBatcher, _super);
+			function MeshBatcher(maxVertices) {
 				if (maxVertices === void 0) { maxVertices = 10920; }
-				this.verticesLength = 0;
-				this.indicesLength = 0;
+				var _this = _super.call(this) || this;
+				_this.verticesLength = 0;
+				_this.indicesLength = 0;
 				if (maxVertices > 10920)
 					throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
-				var vertices = this.vertices = new Float32Array(maxVertices * MeshBatcher.VERTEX_SIZE);
-				var indices = this.indices = new Uint16Array(maxVertices * 3);
-				this.mesh = mesh;
+				var vertices = _this.vertices = new Float32Array(maxVertices * MeshBatcher.VERTEX_SIZE);
+				var indices = _this.indices = new Uint16Array(maxVertices * 3);
 				var geo = new THREE.BufferGeometry();
-				var vertexBuffer = this.vertexBuffer = new THREE.InterleavedBuffer(vertices, MeshBatcher.VERTEX_SIZE);
+				var vertexBuffer = _this.vertexBuffer = new THREE.InterleavedBuffer(vertices, MeshBatcher.VERTEX_SIZE);
 				vertexBuffer.dynamic = true;
 				geo.addAttribute("position", new THREE.InterleavedBufferAttribute(vertexBuffer, 3, 0, false));
 				geo.addAttribute("color", new THREE.InterleavedBufferAttribute(vertexBuffer, 4, 3, false));
@@ -9453,12 +9454,27 @@ var spine;
 				geo.getIndex().dynamic = true;
 				geo.drawRange.start = 0;
 				geo.drawRange.count = 0;
-				mesh.geometry = geo;
+				_this.geometry = geo;
+				_this.material = new threejs.SkeletonMeshMaterial();
+				return _this;
 			}
+			MeshBatcher.prototype.clear = function () {
+				var geo = this.geometry;
+				geo.drawRange.start = 0;
+				geo.drawRange.count = 0;
+				this.material.uniforms.map.value = null;
+			};
 			MeshBatcher.prototype.begin = function () {
 				this.verticesLength = 0;
 				this.indicesLength = 0;
 			};
+			MeshBatcher.prototype.canBatch = function (verticesLength, indicesLength) {
+				if (this.indicesLength + indicesLength >= this.indices.byteLength / 2)
+					return false;
+				if (this.verticesLength + verticesLength >= this.vertices.byteLength / 2)
+					return false;
+				return true;
+			};
 			MeshBatcher.prototype.batch = function (vertices, verticesLength, indices, indicesLength, z) {
 				if (z === void 0) { z = 0; }
 				var indexStart = this.verticesLength / MeshBatcher.VERTEX_SIZE;
@@ -9486,7 +9502,7 @@ var spine;
 				this.vertexBuffer.needsUpdate = true;
 				this.vertexBuffer.updateRange.offset = 0;
 				this.vertexBuffer.updateRange.count = this.verticesLength;
-				var geo = this.mesh.geometry;
+				var geo = this.geometry;
 				geo.getIndex().needsUpdate = true;
 				geo.getIndex().updateRange.offset = 0;
 				geo.getIndex().updateRange.count = this.indicesLength;
@@ -9494,7 +9510,7 @@ var spine;
 				geo.drawRange.count = this.indicesLength;
 			};
 			return MeshBatcher;
-		}());
+		}(THREE.Mesh));
 		MeshBatcher.VERTEX_SIZE = 9;
 		threejs.MeshBatcher = MeshBatcher;
 	})(threejs = spine.threejs || (spine.threejs = {}));
@@ -9503,6 +9519,29 @@ var spine;
 (function (spine) {
 	var threejs;
 	(function (threejs) {
+		var SkeletonMeshMaterial = (function (_super) {
+			__extends(SkeletonMeshMaterial, _super);
+			function SkeletonMeshMaterial() {
+				var _this = this;
+				var vertexShader = "\n\t\t\t\tattribute vec4 color;\n\t\t\t\tvarying vec2 vUv;\n\t\t\t\tvarying vec4 vColor;\n\t\t\t\tvoid main() {\n\t\t\t\t\tvUv = uv;\n\t\t\t\t\tvColor = color;\n\t\t\t\t\tgl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);\n\t\t\t\t}\n\t\t\t";
+				var fragmentShader = "\n\t\t\t\tuniform sampler2D map;\n\t\t\t\tvarying vec2 vUv;\n\t\t\t\tvarying vec4 vColor;\n\t\t\t\tvoid main(void) {\n\t\t\t\t\tgl_FragColor = texture2D(map, vUv)*vColor;\n\t\t\t\t}\n\t\t\t";
+				var parameters = {
+					uniforms: {
+						map: { type: "t", value: null }
+					},
+					vertexShader: vertexShader,
+					fragmentShader: fragmentShader,
+					side: THREE.DoubleSide,
+					transparent: true,
+					alphaTest: 0.5
+				};
+				_this = _super.call(this, parameters) || this;
+				return _this;
+			}
+			;
+			return SkeletonMeshMaterial;
+		}(THREE.ShaderMaterial));
+		threejs.SkeletonMeshMaterial = SkeletonMeshMaterial;
 		var SkeletonMesh = (function (_super) {
 			__extends(SkeletonMesh, _super);
 			function SkeletonMesh(skeletonData) {
@@ -9512,17 +9551,14 @@ var spine;
 				_this.tempLight = new spine.Color();
 				_this.tempDark = new spine.Color();
 				_this.zOffset = 0.1;
+				_this.batches = new Array();
+				_this.nextBatchIndex = 0;
 				_this.clipper = new spine.SkeletonClipping();
 				_this.vertices = spine.Utils.newFloatArray(1024);
 				_this.tempColor = new spine.Color();
 				_this.skeleton = new spine.Skeleton(skeletonData);
 				var animData = new spine.AnimationStateData(skeletonData);
 				_this.state = new spine.AnimationState(animData);
-				var material = _this.material = new THREE.MeshBasicMaterial();
-				material.side = THREE.DoubleSide;
-				material.transparent = true;
-				material.alphaTest = 0.5;
-				_this.batcher = new threejs.MeshBatcher(_this);
 				return _this;
 			}
 			SkeletonMesh.prototype.update = function (deltaTime) {
@@ -9533,12 +9569,29 @@ var spine;
 				skeleton.updateWorldTransform();
 				this.updateGeometry();
 			};
+			SkeletonMesh.prototype.clearBatches = function () {
+				for (var i = 0; i < this.batches.length; i++) {
+					this.batches[i].clear();
+					this.batches[i].visible = false;
+				}
+				this.nextBatchIndex = 0;
+			};
+			SkeletonMesh.prototype.nextBatch = function () {
+				if (this.batches.length == this.nextBatchIndex) {
+					var batch_1 = new threejs.MeshBatcher();
+					this.add(batch_1);
+					this.batches.push(batch_1);
+				}
+				var batch = this.batches[this.nextBatchIndex++];
+				batch.visible = true;
+				return batch;
+			};
 			SkeletonMesh.prototype.updateGeometry = function () {
+				this.clearBatches();
 				var tempPos = this.tempPos;
 				var tempUv = this.tempUv;
 				var tempLight = this.tempLight;
 				var tempDark = this.tempDark;
-				var geometry = this.geometry;
 				var numVertices = 0;
 				var verticesLength = 0;
 				var indicesLength = 0;
@@ -9548,8 +9601,8 @@ var spine;
 				var triangles = null;
 				var uvs = null;
 				var drawOrder = this.skeleton.drawOrder;
-				var batcher = this.batcher;
-				batcher.begin();
+				var batch = this.nextBatch();
+				batch.begin();
 				var z = 0;
 				var zOffset = this.zOffset;
 				for (var i = 0, n = drawOrder.length; i < n; i++) {
@@ -9590,17 +9643,16 @@ var spine;
 					else
 						continue;
 					if (texture != null) {
-						if (!this.material.map) {
-							var mat = this.material;
-							mat.map = texture.texture;
-							mat.needsUpdate = true;
-						}
 						var skeleton = slot.bone.skeleton;
 						var skeletonColor = skeleton.color;
 						var slotColor = slot.color;
 						var alpha = skeletonColor.a * slotColor.a * attachmentColor.a;
 						var color = this.tempColor;
 						color.set(skeletonColor.r * slotColor.r * attachmentColor.r, skeletonColor.g * slotColor.g * attachmentColor.g, skeletonColor.b * slotColor.b * attachmentColor.b, alpha);
+						var finalVertices = void 0;
+						var finalVerticesLength = void 0;
+						var finalIndices = void 0;
+						var finalIndicesLength = void 0;
 						if (clipper.isClipping()) {
 							clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, null, false);
 							var clippedVertices = clipper.clippedVertices;
@@ -9626,7 +9678,10 @@ var spine;
 									verts[v + 7] = tempUv.y;
 								}
 							}
-							batcher.batch(clippedVertices, clippedVertices.length, clippedTriangles, clippedTriangles.length, z);
+							finalVertices = clippedVertices;
+							finalVerticesLength = clippedVertices.length;
+							finalIndices = clippedTriangles;
+							finalIndicesLength = clippedTriangles.length;
 						}
 						else {
 							var verts = vertices;
@@ -9660,15 +9715,40 @@ var spine;
 									verts[v + 5] = uvs[u + 1];
 								}
 							}
-							batcher.batch(vertices, numFloats, triangles, triangles.length, z);
+							finalVertices = vertices;
+							finalVerticesLength = numFloats;
+							finalIndices = triangles;
+							finalIndicesLength = triangles.length;
 						}
+						if (finalVerticesLength == 0 || finalIndicesLength == 0)
+							continue;
+						if (!batch.canBatch(finalVerticesLength, finalIndicesLength)) {
+							batch.end();
+							batch = this.nextBatch();
+							batch.begin();
+						}
+						var batchMaterial = batch.material;
+						if (batchMaterial.uniforms.map.value == null) {
+							batchMaterial.uniforms.map.value = texture.texture;
+						}
+						if (batchMaterial.uniforms.map.value != texture.texture) {
+							batch.end();
+							batch = this.nextBatch();
+							batch.begin();
+							batchMaterial = batch.material;
+							batchMaterial.uniforms.map.value = texture.texture;
+						}
+						batchMaterial.needsUpdate = true;
+						batch.batch(finalVertices, finalVerticesLength, finalIndices, finalIndicesLength, z);
 						z += zOffset;
 					}
+					clipper.clipEndWithSlot(slot);
 				}
-				batcher.end();
+				clipper.clipEnd();
+				batch.end();
 			};
 			return SkeletonMesh;
-		}(THREE.Mesh));
+		}(THREE.Object3D));
 		SkeletonMesh.QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
 		SkeletonMesh.VERTEX_SIZE = 2 + 2 + 4;
 		threejs.SkeletonMesh = SkeletonMesh;

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-all.js.map


+ 5 - 5
spine-ts/build/spine-canvas.js

@@ -2001,6 +2001,7 @@ var spine;
 			path = this.pathPrefix + path;
 			this.toLoad++;
 			AssetManager.downloadText(path, function (atlasData) {
+				var pagesLoaded = { count: 0 };
 				var atlasPages = new Array();
 				try {
 					var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2021,11 +2022,10 @@ var spine;
 					return;
 				}
 				var _loop_1 = function (atlasPage) {
-					var pagesLoaded = 0;
 					var pageLoadError = false;
 					_this.loadTexture(atlasPage, function (imagePath, image) {
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							if (!pageLoadError) {
 								try {
 									var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2056,8 +2056,8 @@ var spine;
 						}
 					}, function (imagePath, errorMessage) {
 						pageLoadError = true;
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path;
 							if (error)
 								error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path);

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-canvas.js.map


+ 5 - 5
spine-ts/build/spine-core.js

@@ -2001,6 +2001,7 @@ var spine;
 			path = this.pathPrefix + path;
 			this.toLoad++;
 			AssetManager.downloadText(path, function (atlasData) {
+				var pagesLoaded = { count: 0 };
 				var atlasPages = new Array();
 				try {
 					var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2021,11 +2022,10 @@ var spine;
 					return;
 				}
 				var _loop_1 = function (atlasPage) {
-					var pagesLoaded = 0;
 					var pageLoadError = false;
 					_this.loadTexture(atlasPage, function (imagePath, image) {
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							if (!pageLoadError) {
 								try {
 									var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2056,8 +2056,8 @@ var spine;
 						}
 					}, function (imagePath, errorMessage) {
 						pageLoadError = true;
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path;
 							if (error)
 								error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path);

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-core.js.map


+ 12 - 5
spine-ts/build/spine-threejs.d.ts

@@ -1235,22 +1235,26 @@ declare module spine.threejs {
 	}
 }
 declare module spine.threejs {
-	class MeshBatcher {
-		mesh: THREE.Mesh;
+	class MeshBatcher extends THREE.Mesh {
 		private static VERTEX_SIZE;
 		private vertexBuffer;
 		private vertices;
 		private verticesLength;
 		private indices;
 		private indicesLength;
-		constructor(mesh: THREE.Mesh, maxVertices?: number);
+		constructor(maxVertices?: number);
+		clear(): void;
 		begin(): void;
+		canBatch(verticesLength: number, indicesLength: number): boolean;
 		batch(vertices: ArrayLike<number>, verticesLength: number, indices: ArrayLike<number>, indicesLength: number, z?: number): void;
 		end(): void;
 	}
 }
 declare module spine.threejs {
-	class SkeletonMesh extends THREE.Mesh {
+	class SkeletonMeshMaterial extends THREE.ShaderMaterial {
+		constructor();
+	}
+	class SkeletonMesh extends THREE.Object3D {
 		tempPos: Vector2;
 		tempUv: Vector2;
 		tempLight: Color;
@@ -1259,7 +1263,8 @@ declare module spine.threejs {
 		state: AnimationState;
 		zOffset: number;
 		vertexEffect: VertexEffect;
-		private batcher;
+		private batches;
+		private nextBatchIndex;
 		private clipper;
 		static QUAD_TRIANGLES: number[];
 		static VERTEX_SIZE: number;
@@ -1267,6 +1272,8 @@ declare module spine.threejs {
 		private tempColor;
 		constructor(skeletonData: SkeletonData);
 		update(deltaTime: number): void;
+		private clearBatches();
+		private nextBatch();
 		private updateGeometry();
 	}
 }

+ 113 - 33
spine-ts/build/spine-threejs.js

@@ -2001,6 +2001,7 @@ var spine;
 			path = this.pathPrefix + path;
 			this.toLoad++;
 			AssetManager.downloadText(path, function (atlasData) {
+				var pagesLoaded = { count: 0 };
 				var atlasPages = new Array();
 				try {
 					var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2021,11 +2022,10 @@ var spine;
 					return;
 				}
 				var _loop_1 = function (atlasPage) {
-					var pagesLoaded = 0;
 					var pageLoadError = false;
 					_this.loadTexture(atlasPage, function (imagePath, image) {
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							if (!pageLoadError) {
 								try {
 									var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2056,8 +2056,8 @@ var spine;
 						}
 					}, function (imagePath, errorMessage) {
 						pageLoadError = true;
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path;
 							if (error)
 								error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path);
@@ -6604,18 +6604,19 @@ var spine;
 (function (spine) {
 	var threejs;
 	(function (threejs) {
-		var MeshBatcher = (function () {
-			function MeshBatcher(mesh, maxVertices) {
+		var MeshBatcher = (function (_super) {
+			__extends(MeshBatcher, _super);
+			function MeshBatcher(maxVertices) {
 				if (maxVertices === void 0) { maxVertices = 10920; }
-				this.verticesLength = 0;
-				this.indicesLength = 0;
+				var _this = _super.call(this) || this;
+				_this.verticesLength = 0;
+				_this.indicesLength = 0;
 				if (maxVertices > 10920)
 					throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
-				var vertices = this.vertices = new Float32Array(maxVertices * MeshBatcher.VERTEX_SIZE);
-				var indices = this.indices = new Uint16Array(maxVertices * 3);
-				this.mesh = mesh;
+				var vertices = _this.vertices = new Float32Array(maxVertices * MeshBatcher.VERTEX_SIZE);
+				var indices = _this.indices = new Uint16Array(maxVertices * 3);
 				var geo = new THREE.BufferGeometry();
-				var vertexBuffer = this.vertexBuffer = new THREE.InterleavedBuffer(vertices, MeshBatcher.VERTEX_SIZE);
+				var vertexBuffer = _this.vertexBuffer = new THREE.InterleavedBuffer(vertices, MeshBatcher.VERTEX_SIZE);
 				vertexBuffer.dynamic = true;
 				geo.addAttribute("position", new THREE.InterleavedBufferAttribute(vertexBuffer, 3, 0, false));
 				geo.addAttribute("color", new THREE.InterleavedBufferAttribute(vertexBuffer, 4, 3, false));
@@ -6624,12 +6625,27 @@ var spine;
 				geo.getIndex().dynamic = true;
 				geo.drawRange.start = 0;
 				geo.drawRange.count = 0;
-				mesh.geometry = geo;
+				_this.geometry = geo;
+				_this.material = new threejs.SkeletonMeshMaterial();
+				return _this;
 			}
+			MeshBatcher.prototype.clear = function () {
+				var geo = this.geometry;
+				geo.drawRange.start = 0;
+				geo.drawRange.count = 0;
+				this.material.uniforms.map.value = null;
+			};
 			MeshBatcher.prototype.begin = function () {
 				this.verticesLength = 0;
 				this.indicesLength = 0;
 			};
+			MeshBatcher.prototype.canBatch = function (verticesLength, indicesLength) {
+				if (this.indicesLength + indicesLength >= this.indices.byteLength / 2)
+					return false;
+				if (this.verticesLength + verticesLength >= this.vertices.byteLength / 2)
+					return false;
+				return true;
+			};
 			MeshBatcher.prototype.batch = function (vertices, verticesLength, indices, indicesLength, z) {
 				if (z === void 0) { z = 0; }
 				var indexStart = this.verticesLength / MeshBatcher.VERTEX_SIZE;
@@ -6657,7 +6673,7 @@ var spine;
 				this.vertexBuffer.needsUpdate = true;
 				this.vertexBuffer.updateRange.offset = 0;
 				this.vertexBuffer.updateRange.count = this.verticesLength;
-				var geo = this.mesh.geometry;
+				var geo = this.geometry;
 				geo.getIndex().needsUpdate = true;
 				geo.getIndex().updateRange.offset = 0;
 				geo.getIndex().updateRange.count = this.indicesLength;
@@ -6665,7 +6681,7 @@ var spine;
 				geo.drawRange.count = this.indicesLength;
 			};
 			return MeshBatcher;
-		}());
+		}(THREE.Mesh));
 		MeshBatcher.VERTEX_SIZE = 9;
 		threejs.MeshBatcher = MeshBatcher;
 	})(threejs = spine.threejs || (spine.threejs = {}));
@@ -6674,6 +6690,29 @@ var spine;
 (function (spine) {
 	var threejs;
 	(function (threejs) {
+		var SkeletonMeshMaterial = (function (_super) {
+			__extends(SkeletonMeshMaterial, _super);
+			function SkeletonMeshMaterial() {
+				var _this = this;
+				var vertexShader = "\n\t\t\t\tattribute vec4 color;\n\t\t\t\tvarying vec2 vUv;\n\t\t\t\tvarying vec4 vColor;\n\t\t\t\tvoid main() {\n\t\t\t\t\tvUv = uv;\n\t\t\t\t\tvColor = color;\n\t\t\t\t\tgl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);\n\t\t\t\t}\n\t\t\t";
+				var fragmentShader = "\n\t\t\t\tuniform sampler2D map;\n\t\t\t\tvarying vec2 vUv;\n\t\t\t\tvarying vec4 vColor;\n\t\t\t\tvoid main(void) {\n\t\t\t\t\tgl_FragColor = texture2D(map, vUv)*vColor;\n\t\t\t\t}\n\t\t\t";
+				var parameters = {
+					uniforms: {
+						map: { type: "t", value: null }
+					},
+					vertexShader: vertexShader,
+					fragmentShader: fragmentShader,
+					side: THREE.DoubleSide,
+					transparent: true,
+					alphaTest: 0.5
+				};
+				_this = _super.call(this, parameters) || this;
+				return _this;
+			}
+			;
+			return SkeletonMeshMaterial;
+		}(THREE.ShaderMaterial));
+		threejs.SkeletonMeshMaterial = SkeletonMeshMaterial;
 		var SkeletonMesh = (function (_super) {
 			__extends(SkeletonMesh, _super);
 			function SkeletonMesh(skeletonData) {
@@ -6683,17 +6722,14 @@ var spine;
 				_this.tempLight = new spine.Color();
 				_this.tempDark = new spine.Color();
 				_this.zOffset = 0.1;
+				_this.batches = new Array();
+				_this.nextBatchIndex = 0;
 				_this.clipper = new spine.SkeletonClipping();
 				_this.vertices = spine.Utils.newFloatArray(1024);
 				_this.tempColor = new spine.Color();
 				_this.skeleton = new spine.Skeleton(skeletonData);
 				var animData = new spine.AnimationStateData(skeletonData);
 				_this.state = new spine.AnimationState(animData);
-				var material = _this.material = new THREE.MeshBasicMaterial();
-				material.side = THREE.DoubleSide;
-				material.transparent = true;
-				material.alphaTest = 0.5;
-				_this.batcher = new threejs.MeshBatcher(_this);
 				return _this;
 			}
 			SkeletonMesh.prototype.update = function (deltaTime) {
@@ -6704,12 +6740,29 @@ var spine;
 				skeleton.updateWorldTransform();
 				this.updateGeometry();
 			};
+			SkeletonMesh.prototype.clearBatches = function () {
+				for (var i = 0; i < this.batches.length; i++) {
+					this.batches[i].clear();
+					this.batches[i].visible = false;
+				}
+				this.nextBatchIndex = 0;
+			};
+			SkeletonMesh.prototype.nextBatch = function () {
+				if (this.batches.length == this.nextBatchIndex) {
+					var batch_1 = new threejs.MeshBatcher();
+					this.add(batch_1);
+					this.batches.push(batch_1);
+				}
+				var batch = this.batches[this.nextBatchIndex++];
+				batch.visible = true;
+				return batch;
+			};
 			SkeletonMesh.prototype.updateGeometry = function () {
+				this.clearBatches();
 				var tempPos = this.tempPos;
 				var tempUv = this.tempUv;
 				var tempLight = this.tempLight;
 				var tempDark = this.tempDark;
-				var geometry = this.geometry;
 				var numVertices = 0;
 				var verticesLength = 0;
 				var indicesLength = 0;
@@ -6719,8 +6772,8 @@ var spine;
 				var triangles = null;
 				var uvs = null;
 				var drawOrder = this.skeleton.drawOrder;
-				var batcher = this.batcher;
-				batcher.begin();
+				var batch = this.nextBatch();
+				batch.begin();
 				var z = 0;
 				var zOffset = this.zOffset;
 				for (var i = 0, n = drawOrder.length; i < n; i++) {
@@ -6761,17 +6814,16 @@ var spine;
 					else
 						continue;
 					if (texture != null) {
-						if (!this.material.map) {
-							var mat = this.material;
-							mat.map = texture.texture;
-							mat.needsUpdate = true;
-						}
 						var skeleton = slot.bone.skeleton;
 						var skeletonColor = skeleton.color;
 						var slotColor = slot.color;
 						var alpha = skeletonColor.a * slotColor.a * attachmentColor.a;
 						var color = this.tempColor;
 						color.set(skeletonColor.r * slotColor.r * attachmentColor.r, skeletonColor.g * slotColor.g * attachmentColor.g, skeletonColor.b * slotColor.b * attachmentColor.b, alpha);
+						var finalVertices = void 0;
+						var finalVerticesLength = void 0;
+						var finalIndices = void 0;
+						var finalIndicesLength = void 0;
 						if (clipper.isClipping()) {
 							clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, null, false);
 							var clippedVertices = clipper.clippedVertices;
@@ -6797,7 +6849,10 @@ var spine;
 									verts[v + 7] = tempUv.y;
 								}
 							}
-							batcher.batch(clippedVertices, clippedVertices.length, clippedTriangles, clippedTriangles.length, z);
+							finalVertices = clippedVertices;
+							finalVerticesLength = clippedVertices.length;
+							finalIndices = clippedTriangles;
+							finalIndicesLength = clippedTriangles.length;
 						}
 						else {
 							var verts = vertices;
@@ -6831,15 +6886,40 @@ var spine;
 									verts[v + 5] = uvs[u + 1];
 								}
 							}
-							batcher.batch(vertices, numFloats, triangles, triangles.length, z);
+							finalVertices = vertices;
+							finalVerticesLength = numFloats;
+							finalIndices = triangles;
+							finalIndicesLength = triangles.length;
 						}
+						if (finalVerticesLength == 0 || finalIndicesLength == 0)
+							continue;
+						if (!batch.canBatch(finalVerticesLength, finalIndicesLength)) {
+							batch.end();
+							batch = this.nextBatch();
+							batch.begin();
+						}
+						var batchMaterial = batch.material;
+						if (batchMaterial.uniforms.map.value == null) {
+							batchMaterial.uniforms.map.value = texture.texture;
+						}
+						if (batchMaterial.uniforms.map.value != texture.texture) {
+							batch.end();
+							batch = this.nextBatch();
+							batch.begin();
+							batchMaterial = batch.material;
+							batchMaterial.uniforms.map.value = texture.texture;
+						}
+						batchMaterial.needsUpdate = true;
+						batch.batch(finalVertices, finalVerticesLength, finalIndices, finalIndicesLength, z);
 						z += zOffset;
 					}
+					clipper.clipEndWithSlot(slot);
 				}
-				batcher.end();
+				clipper.clipEnd();
+				batch.end();
 			};
 			return SkeletonMesh;
-		}(THREE.Mesh));
+		}(THREE.Object3D));
 		SkeletonMesh.QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
 		SkeletonMesh.VERTEX_SIZE = 2 + 2 + 4;
 		threejs.SkeletonMesh = SkeletonMesh;

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-threejs.js.map


+ 5 - 5
spine-ts/build/spine-webgl.js

@@ -2001,6 +2001,7 @@ var spine;
 			path = this.pathPrefix + path;
 			this.toLoad++;
 			AssetManager.downloadText(path, function (atlasData) {
+				var pagesLoaded = { count: 0 };
 				var atlasPages = new Array();
 				try {
 					var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2021,11 +2022,10 @@ var spine;
 					return;
 				}
 				var _loop_1 = function (atlasPage) {
-					var pagesLoaded = 0;
 					var pageLoadError = false;
 					_this.loadTexture(atlasPage, function (imagePath, image) {
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							if (!pageLoadError) {
 								try {
 									var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2056,8 +2056,8 @@ var spine;
 						}
 					}, function (imagePath, errorMessage) {
 						pageLoadError = true;
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path;
 							if (error)
 								error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path);

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-webgl.js.map


+ 5 - 5
spine-ts/build/spine-widget.js

@@ -2001,6 +2001,7 @@ var spine;
 			path = this.pathPrefix + path;
 			this.toLoad++;
 			AssetManager.downloadText(path, function (atlasData) {
+				var pagesLoaded = { count: 0 };
 				var atlasPages = new Array();
 				try {
 					var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2021,11 +2022,10 @@ var spine;
 					return;
 				}
 				var _loop_1 = function (atlasPage) {
-					var pagesLoaded = 0;
 					var pageLoadError = false;
 					_this.loadTexture(atlasPage, function (imagePath, image) {
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							if (!pageLoadError) {
 								try {
 									var atlas = new spine.TextureAtlas(atlasData, function (path) {
@@ -2056,8 +2056,8 @@ var spine;
 						}
 					}, function (imagePath, errorMessage) {
 						pageLoadError = true;
-						pagesLoaded++;
-						if (pagesLoaded == atlasPages.length) {
+						pagesLoaded.count++;
+						if (pagesLoaded.count == atlasPages.length) {
 							_this.errors[path] = "Couldn't load texture atlas page " + imagePath + "} of atlas " + path;
 							if (error)
 								error(path, "Couldn't load texture atlas page " + imagePath + " of atlas " + path);

File diff suppressed because it is too large
+ 0 - 0
spine-ts/build/spine-widget.js.map


+ 5 - 5
spine-ts/core/src/AssetManager.ts

@@ -147,6 +147,7 @@ module spine {
 			this.toLoad++;
 
 			AssetManager.downloadText(path, (atlasData: string): void => {
+				var pagesLoaded: any = { count: 0 };
 				var atlasPages = new Array<string>();
 				try {
 					let atlas = new spine.TextureAtlas(atlasData, (path: string) => {
@@ -166,12 +167,11 @@ module spine {
 				}
 
 				for (let atlasPage of atlasPages) {
-					let pagesLoaded = 0;
 					let pageLoadError = false;
 					this.loadTexture(atlasPage, (imagePath: string, image: HTMLImageElement) => {
-						pagesLoaded++;
+						pagesLoaded.count++;
 
-						if (pagesLoaded == atlasPages.length) {
+						if (pagesLoaded.count == atlasPages.length) {
 							if (!pageLoadError) {
 								try {
 									let atlas = new spine.TextureAtlas(atlasData, (path: string) => {
@@ -197,9 +197,9 @@ module spine {
 						}
 					}, (imagePath: string, errorMessage: string) => {
 						pageLoadError = true;
-						pagesLoaded++;
+						pagesLoaded.count++;
 
-						if (pagesLoaded == atlasPages.length) {
+						if (pagesLoaded.count == atlasPages.length) {
 							this.errors[path] = `Couldn't load texture atlas page ${imagePath}} of atlas ${path}`;
 							if (error) error(path, `Couldn't load texture atlas page ${imagePath} of atlas ${path}`);
 							this.toLoad--;

+ 20 - 28
spine-ts/threejs/example/index.html

@@ -19,6 +19,11 @@ var assetManager;
 var canvas;
 var lastFrameTime = Date.now() / 1000;
 
+var baseUrl = "assets/";
+var skeletonFile = "raptor-pro.json";
+var atlasFile = skeletonFile.replace("-pro", "").replace("-ess", "").replace(".json", ".atlas");
+var animation = "walk";
+
 function init () {
 	// create the THREE.JS camera, scene and renderer (WebGL)
 	var width = window.innerWidth, height = window.innerHeight;
@@ -32,10 +37,9 @@ function init () {
 	canvas = renderer.domElement;
 
 	// load the assets required to display the Raptor model
-	assetManager = new spine.threejs.AssetManager();
-	assetManager.loadText("assets/raptor-pro.json");
-	assetManager.loadText("assets/raptor.atlas");
-	assetManager.loadTexture("assets/raptor.png");
+	assetManager = new spine.threejs.AssetManager(baseUrl);
+	assetManager.loadText(skeletonFile);
+	assetManager.loadTextureAtlas(atlasFile);
 
 	requestAnimationFrame(load);
 }
@@ -50,39 +54,27 @@ function load (name, scale) {
 
 		// Load the texture atlas using name.atlas and name.png from the AssetManager.
 		// The function passed to TextureAtlas is used to resolve relative paths.
-		atlas = new spine.TextureAtlas(assetManager.get("assets/raptor.atlas"), function(path) {
-			return assetManager.get("assets/" + path);
-		});
-		var skeletonData = loadSkeleton("raptor-pro", 0.4);
+		atlas = assetManager.get(atlasFile);
+
+		// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
+		atlasLoader = new spine.AtlasAttachmentLoader(atlas);
+
+		// Create a SkeletonJson instance for parsing the .json file.
+		var skeletonJson = new spine.SkeletonJson(atlasLoader);
+
+		// Set the scale to apply during parsing, parse the file, and create a new skeleton.
+		skeletonJson.scale = 0.4;
+		var skeletonData = skeletonJson.readSkeletonData(assetManager.get(skeletonFile));
 
 		// Create a SkeletonMesh from the data and attach it to the scene
 		skeletonMesh = new spine.threejs.SkeletonMesh(skeletonData);
-		skeletonMesh.state.setAnimation(0, "walk", true);
+		skeletonMesh.state.setAnimation(0, animation, true);
 		mesh.add(skeletonMesh);
 
 		requestAnimationFrame(render);
 	} else requestAnimationFrame(load);
 }
 
-function loadSkeleton (name, scale) {
-	// Load the texture atlas using name.atlas and name.png from the AssetManager.
-	// The function passed to TextureAtlas is used to resolve relative paths.
-	atlas = new spine.TextureAtlas(assetManager.get("assets/" + name.replace("-pro", "") + ".atlas"), function(path) {
-		return assetManager.get("assets/" + path);
-	});
-
-	// Create a AtlasAttachmentLoader that resolves region, mesh, boundingbox and path attachments
-	atlasLoader = new spine.AtlasAttachmentLoader(atlas);
-
-	// Create a SkeletonJson instance for parsing the .json file.
-	var skeletonJson = new spine.SkeletonJson(atlasLoader);
-
-	// Set the scale to apply during parsing, parse the file, and create a new skeleton.
-	skeletonJson.scale = scale;
-	var skeletonData = skeletonJson.readSkeletonData(assetManager.get("assets/" + name + ".json"));
-	return skeletonData;
-}
-
 var lastTime = Date.now();
 function render() {
 	// calculate delta time for animation purposes

+ 19 - 8
spine-ts/threejs/src/MeshBatcher.ts

@@ -29,9 +29,7 @@
  *****************************************************************************/
 
 module spine.threejs {
-	export class MeshBatcher {
-		mesh: THREE.Mesh;
-
+	export class MeshBatcher extends THREE.Mesh {
 		private static VERTEX_SIZE = 9;
 		private vertexBuffer: THREE.InterleavedBuffer;
 		private vertices: Float32Array;
@@ -39,12 +37,11 @@ module spine.threejs {
 		private indices: Uint16Array;
 		private indicesLength = 0;
 
-		constructor (mesh: THREE.Mesh, maxVertices: number = 10920) {
+		constructor (maxVertices: number = 10920) {
+			super();
 			if (maxVertices > 10920) throw new Error("Can't have more than 10920 triangles per batch: " + maxVertices);
-
 			let vertices = this.vertices = new Float32Array(maxVertices * MeshBatcher.VERTEX_SIZE);
 			let indices = this.indices = new Uint16Array(maxVertices * 3);
-			this.mesh = mesh;
 			let geo = new THREE.BufferGeometry();
 			let vertexBuffer = this.vertexBuffer = new THREE.InterleavedBuffer(vertices, MeshBatcher.VERTEX_SIZE);
 			vertexBuffer.dynamic = true;
@@ -55,7 +52,15 @@ module spine.threejs {
 			geo.getIndex().dynamic = true;
 			geo.drawRange.start = 0;
 			geo.drawRange.count = 0;
-			mesh.geometry = geo;
+			this.geometry = geo;
+			this.material = new SkeletonMeshMaterial();
+		}
+
+		clear () {
+			let geo = (<THREE.BufferGeometry>this.geometry);
+			geo.drawRange.start = 0;
+			geo.drawRange.count = 0;
+			(<SkeletonMeshMaterial>this.material).uniforms.map.value = null;
 		}
 
 		begin () {
@@ -63,6 +68,12 @@ module spine.threejs {
 			this.indicesLength = 0;
 		}
 
+		canBatch(verticesLength: number, indicesLength: number) {
+			if (this.indicesLength + indicesLength >= this.indices.byteLength / 2) return false;
+			if (this.verticesLength + verticesLength >= this.vertices.byteLength / 2) return false;
+			return true;
+		}
+
 		batch (vertices: ArrayLike<number>, verticesLength: number, indices: ArrayLike<number>, indicesLength: number, z: number = 0) {
 			let indexStart = this.verticesLength / MeshBatcher.VERTEX_SIZE;
 			let vertexBuffer = this.vertices;
@@ -91,7 +102,7 @@ module spine.threejs {
 			this.vertexBuffer.needsUpdate = true;
 			this.vertexBuffer.updateRange.offset = 0;
 			this.vertexBuffer.updateRange.count = this.verticesLength;
-			let geo = (<THREE.BufferGeometry>this.mesh.geometry);
+			let geo = (<THREE.BufferGeometry>this.geometry);
 			geo.getIndex().needsUpdate = true;
 			geo.getIndex().updateRange.offset = 0;
 			geo.getIndex().updateRange.count = this.indicesLength;

+ 110 - 27
spine-ts/threejs/src/SkeletonMesh.ts

@@ -29,7 +29,42 @@
  *****************************************************************************/
 
 module spine.threejs {
-	export class SkeletonMesh extends THREE.Mesh {
+	export class SkeletonMeshMaterial extends THREE.ShaderMaterial {
+		constructor () {
+			let vertexShader = `
+				attribute vec4 color;
+				varying vec2 vUv;
+				varying vec4 vColor;
+				void main() {
+					vUv = uv;
+					vColor = color;
+					gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
+				}
+			`;
+			let fragmentShader = `
+				uniform sampler2D map;
+				varying vec2 vUv;
+				varying vec4 vColor;
+				void main(void) {
+					gl_FragColor = texture2D(map, vUv)*vColor;
+				}
+			`;
+
+			let parameters: THREE.ShaderMaterialParameters = {
+				uniforms: {
+					map: { type: "t", value: null }
+				},
+				vertexShader: vertexShader,
+				fragmentShader: fragmentShader,
+				side: THREE.DoubleSide,
+				transparent: true,
+				alphaTest: 0.5
+			};
+			super(parameters);
+		};
+	}
+
+	export class SkeletonMesh extends THREE.Object3D {
 		tempPos: Vector2 = new Vector2();
 		tempUv: Vector2 = new Vector2();
 		tempLight = new Color();
@@ -39,7 +74,8 @@ module spine.threejs {
 		zOffset: number = 0.1;
 		vertexEffect: VertexEffect;
 
-		private batcher: MeshBatcher;
+		private batches = new Array<MeshBatcher>();
+		private nextBatchIndex = 0;
 		private clipper: SkeletonClipping = new SkeletonClipping();
 
 		static QUAD_TRIANGLES = [0, 1, 2, 2, 3, 0];
@@ -54,12 +90,6 @@ module spine.threejs {
 			this.skeleton = new Skeleton(skeletonData);
 			let animData = new AnimationStateData(skeletonData);
 			this.state = new AnimationState(animData);
-
-			let material = this.material = new THREE.MeshBasicMaterial();
-			material.side = THREE.DoubleSide;
-			material.transparent = true;
-			material.alphaTest = 0.5;
-			this.batcher = new MeshBatcher(this);
 		}
 
 		update(deltaTime: number) {
@@ -73,13 +103,33 @@ module spine.threejs {
 			this.updateGeometry();
 		}
 
+		private clearBatches () {
+			for (var i = 0; i < this.batches.length; i++) {
+				this.batches[i].clear();
+				this.batches[i].visible = false;
+			}
+			this.nextBatchIndex = 0;
+		}
+
+		private nextBatch () {
+			if (this.batches.length == this.nextBatchIndex) {
+				let batch = new MeshBatcher();
+				this.add(batch);
+				this.batches.push(batch);
+			}
+			let batch = this.batches[this.nextBatchIndex++];
+			batch.visible = true;
+			return batch;
+		}
+
 		private updateGeometry() {
+			this.clearBatches();
+
 			let tempPos = this.tempPos;
 			let tempUv = this.tempUv;
 			let tempLight = this.tempLight;
 			let tempDark = this.tempDark;
 
-			let geometry = <THREE.BufferGeometry>this.geometry;
 			var numVertices = 0;
 			var verticesLength = 0;
 			var indicesLength = 0;
@@ -91,8 +141,8 @@ module spine.threejs {
 			let triangles: Array<number> = null;
 			let uvs: ArrayLike<number> = null;
 			let drawOrder = this.skeleton.drawOrder;
-			let batcher = this.batcher;
-			batcher.begin();
+			let batch = this.nextBatch();
+			batch.begin();
 			let z = 0;
 			let zOffset = this.zOffset;
 			for (let i = 0, n = drawOrder.length; i < n; i++) {
@@ -130,12 +180,6 @@ module spine.threejs {
 				} else continue;
 
 				if (texture != null) {
-					if (!(<THREE.MeshBasicMaterial>this.material).map) {
-						let mat = <THREE.MeshBasicMaterial>this.material;
-						mat.map = texture.texture;
-						mat.needsUpdate = true;
-					}
-
 					let skeleton = slot.bone.skeleton;
 					let skeletonColor = skeleton.color;
 					let slotColor = slot.color;
@@ -145,12 +189,11 @@ module spine.threejs {
 							skeletonColor.g * slotColor.g * attachmentColor.g,
 							skeletonColor.b * slotColor.b * attachmentColor.b,
 							alpha);
-					// FIXME per slot blending would require multiple material support
-					//let slotBlendMode = slot.data.blendMode;
-					//if (slotBlendMode != blendMode) {
-					//	blendMode = slotBlendMode;
-					//	batcher.setBlendMode(getSourceGLBlendMode(this._gl, blendMode, premultipliedAlpha), getDestGLBlendMode(this._gl, blendMode));
-					//}
+
+					let finalVertices: ArrayLike<number>;
+					let finalVerticesLength: number;
+					let finalIndices: ArrayLike<number>;
+					let finalIndicesLength: number;
 
 					if (clipper.isClipping()) {
 						clipper.clipTriangles(vertices, numFloats, triangles, triangles.length, uvs, color, null, false);
@@ -177,7 +220,10 @@ module spine.threejs {
 								verts[v + 7] = tempUv.y;
 							}
 						}
-						batcher.batch(clippedVertices, clippedVertices.length, clippedTriangles, clippedTriangles.length, z);
+						finalVertices = clippedVertices;
+						finalVerticesLength = clippedVertices.length;
+						finalIndices = clippedTriangles;
+						finalIndicesLength = clippedTriangles.length;
 					} else {
 						let verts = vertices;
 						if (this.vertexEffect != null) {
@@ -209,13 +255,50 @@ module spine.threejs {
 								verts[v + 5] = uvs[u + 1];
 							}
 						}
-						batcher.batch(vertices, numFloats, triangles, triangles.length, z);
+						finalVertices = vertices;
+						finalVerticesLength = numFloats;
+						finalIndices = triangles;
+						finalIndicesLength = triangles.length;
+					}
+
+					if (finalVerticesLength == 0 || finalIndicesLength == 0)
+						continue;
+
+					// Start new batch if this one can't hold vertices/indices
+					if (!batch.canBatch(finalVerticesLength, finalIndicesLength)) {
+						batch.end();
+						batch = this.nextBatch();
+						batch.begin();
 					}
+
+					// FIXME per slot blending would require multiple material support
+					//let slotBlendMode = slot.data.blendMode;
+					//if (slotBlendMode != blendMode) {
+					//	blendMode = slotBlendMode;
+					//	batcher.setBlendMode(getSourceGLBlendMode(this._gl, blendMode, premultipliedAlpha), getDestGLBlendMode(this._gl, blendMode));
+					//}
+
+					let batchMaterial = <SkeletonMeshMaterial>batch.material;
+					if (batchMaterial.uniforms.map.value == null) {
+						batchMaterial.uniforms.map.value = texture.texture;
+					}
+					if (batchMaterial.uniforms.map.value != texture.texture) {
+						batch.end();
+						batch = this.nextBatch();
+						batch.begin();
+						batchMaterial = <SkeletonMeshMaterial>batch.material;
+						batchMaterial.uniforms.map.value = texture.texture;
+					}
+					batchMaterial.needsUpdate = true;
+
+					batch.batch(finalVertices, finalVerticesLength, finalIndices, finalIndicesLength, z);
 					z += zOffset;
 				}
-			}
 
-			batcher.end();
+				clipper.clipEndWithSlot(slot);
+			}
+			clipper.clipEnd();
+			batch.end();
 		}
 	}
 }

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