Selaa lähdekoodia

Merge remote-tracking branch 'EsotericSoftware/3.6' into 3.6

Stephen Gowen 8 vuotta sitten
vanhempi
commit
3ee1058628

+ 1 - 1
spine-cocos2d-objc/src/spine/GLUtils.c

@@ -88,7 +88,7 @@ void main() {
    vec4 texColor = texture2D(texture, v_texCoord);
    float alpha = texColor.a * v_light.a;
    gl_FragColor.a = alpha;
-   gl_FragColor.rgb = (1.0 - texColor.rgb) * v_dark.rgb * alpha + texColor.rgb * v_light.rgb;
+   gl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb;
 }
 );
 

+ 4 - 4
spine-cocos2d-objc/src/spine/SkeletonRenderer.m

@@ -192,7 +192,7 @@ static bool handlerQueued = false;
 	unsigned short* triangles = 0;
 	int trianglesCount = 0;
 	float r = 0, g = 0, b = 0, a = 0;
-	float dr = 0, dg = 0, db = 0;
+	float dr = 0, dg = 0, db = 0, da = _premultipliedAlpha ? 1 : 0;
 	for (int i = 0, n = _skeleton->slotsCount; i < n; i++) {
 		spSlot* slot = _skeleton->drawOrder[i];
 		if (!slot->attachment) continue;
@@ -336,7 +336,7 @@ static bool handlerQueued = false;
 							dark.r = dr;
 							dark.g = dg;
 							dark.b = db;
-							dark.a = 1;
+							dark.a = da;
 							for (int i = 0; i * 2 < verticesCount; i++, verts++) {
 								spColor lightCopy = light;
 								spColor darkCopy = dark;
@@ -353,7 +353,7 @@ static bool handlerQueued = false;
 								verts->z = vertex.position.z;
 								verts->w = vertex.position.w;
 								verts->color = ((unsigned short)(lightCopy.r * 255))| ((unsigned short)(lightCopy.g * 255)) << 8 | ((unsigned short)(lightCopy.b * 255)) <<16 | ((unsigned short)(lightCopy.a * 255)) << 24;
-								verts->color2 = ((unsigned short)(darkCopy.r * 255)) | ((unsigned short)(darkCopy.g * 255)) << 8 | ((unsigned short)(darkCopy.b * 255)) << 16 | ((unsigned short)(255)) << 24;
+								verts->color2 = ((unsigned short)(darkCopy.r * 255)) | ((unsigned short)(darkCopy.g * 255)) << 8 | ((unsigned short)(darkCopy.b * 255)) << 16 | ((unsigned short)(darkCopy.a * 255)) << 24;
 								
 							}
 						} else {
@@ -366,7 +366,7 @@ static bool handlerQueued = false;
 								verts->z = vertex.position.z;
 								verts->w = vertex.position.w;
 								verts->color = ((unsigned short)(r * 255))| ((unsigned short)(g * 255)) << 8 | ((unsigned short)(b * 255)) <<16 | ((unsigned short)(a * 255)) << 24;
-								verts->color2 = ((unsigned short)(dr * 255)) | ((unsigned short)(dg * 255)) << 8 | ((unsigned short)(db * 255)) << 16 | ((unsigned short)(255)) << 24;
+								verts->color2 = ((unsigned short)(dr * 255)) | ((unsigned short)(dg * 255)) << 8 | ((unsigned short)(db * 255)) << 16 | ((unsigned short)(da * 255)) << 24;
 								verts->u = uvs[i * 2];
 								verts->v = 1 - uvs[i * 2 + 1];
 							}

+ 6 - 4
spine-cocos2dx/src/spine/SkeletonRenderer.cpp

@@ -227,6 +227,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
 	
     Color4F color;
 	Color4F darkColor;
+	float darkPremultipliedAlpha = _premultipliedAlpha ? 255 : 0;
 	AttachmentVertices* attachmentVertices = nullptr;
 	TwoColorTrianglesCommand* lastTwoColorTrianglesCommand = nullptr;
 	for (int i = 0, n = _skeleton->slotsCount; i < n; ++i) {
@@ -317,6 +318,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
 			darkColor.g = 0;
 			darkColor.b = 0;
 		}
+		darkColor.a = darkPremultipliedAlpha;
 		
 		color.a *= nodeColor.a * _skeleton->color.a * slot->color.a * 255;
 		// skip rendering if the color of this attachment is 0
@@ -482,7 +484,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
 						vertex->color2.r = (GLubyte)(darkCopy.r * 255);
 						vertex->color2.g = (GLubyte)(darkCopy.g * 255);
 						vertex->color2.b = (GLubyte)(darkCopy.b * 255);
-						vertex->color2.a = 1;
+						vertex->color2.a = (GLubyte)darkColor.a;
 					}
 				} else {
 					for (int v = 0, vn = batchedTriangles->getTriangles().vertCount, vv = 0; v < vn; ++v, vv += 2) {
@@ -498,7 +500,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
 						vertex->color2.r = (GLubyte)darkColor.r;
 						vertex->color2.g = (GLubyte)darkColor.g;
 						vertex->color2.b = (GLubyte)darkColor.b;
-						vertex->color2.a = 1;
+						vertex->color2.a = (GLubyte)darkColor.a;
 					}
 				}
 			} else {
@@ -528,7 +530,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
 						vertex->color2.r = (GLubyte)(darkCopy.r * 255);
 						vertex->color2.g = (GLubyte)(darkCopy.g * 255);
 						vertex->color2.b = (GLubyte)(darkCopy.b * 255);
-						vertex->color2.a = 1;
+						vertex->color2.a = (GLubyte)darkColor.a;
 					}
 				} else {
 					for (int v = 0, vn = batchedTriangles->getTriangles().vertCount; v < vn; ++v) {
@@ -540,7 +542,7 @@ void SkeletonRenderer::draw (Renderer* renderer, const Mat4& transform, uint32_t
 						vertex->color2.r = (GLubyte)darkColor.r;
 						vertex->color2.g = (GLubyte)darkColor.g;
 						vertex->color2.b = (GLubyte)darkColor.b;
-						vertex->color2.a = 1;
+						vertex->color2.a = (GLubyte)darkColor.a;
 					}
 				}
 			}

+ 2 - 2
spine-cocos2dx/src/spine/SkeletonTwoColorBatch.cpp

@@ -143,8 +143,8 @@ varying vec2 v_texCoord;
 void main() {
 	vec4 texColor = texture2D(CC_Texture0, v_texCoord);
 	float alpha = texColor.a * v_light.a;
-	gl_FragColor.a = alpha;
-	gl_FragColor.rgb = (1.0 - texColor.rgb) * v_dark.rgb * alpha + texColor.rgb * v_light.rgb;	
+	gl_FragColor.a = alpha;	
+	gl_FragColor.rgb = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb;
 }
 );
 

+ 20 - 14
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java

@@ -78,10 +78,11 @@ public class SkeletonRenderer {
 				region.computeWorldVertices(slot.getBone(), vertices, 0, 5);
 				Color color = region.getColor(), slotColor = slot.getColor();
 				float alpha = a * slotColor.a * color.a * 255;
+				float multiplier = premultipliedAlpha ? alpha : 255;
 				float c = NumberUtils.intToFloatColor(((int)alpha << 24) //
-					| ((int)(b * slotColor.b * color.b * alpha) << 16) //
-					| ((int)(g * slotColor.g * color.g * alpha) << 8) //
-					| (int)(r * slotColor.r * color.r * alpha));
+					| ((int)(b * slotColor.b * color.b * multiplier) << 16) //
+					| ((int)(g * slotColor.g * color.g * multiplier) << 8) //
+					| (int)(r * slotColor.r * color.r * multiplier));
 				float[] uvs = region.getUVs();
 				for (int u = 0, v = 2; u < 8; u += 2, v += 5) {
 					vertices[v] = c;
@@ -173,10 +174,11 @@ public class SkeletonRenderer {
 			if (texture != null) {
 				Color slotColor = slot.getColor();
 				float alpha = a * slotColor.a * color.a * 255;
+				float multiplier = premultipliedAlpha ? alpha : 255;
 				float c = NumberUtils.intToFloatColor(((int)alpha << 24) //
-					| ((int)(b * slotColor.b * color.b * alpha) << 16) //
-					| ((int)(g * slotColor.g * color.g * alpha) << 8) //
-					| (int)(r * slotColor.r * color.r * alpha));
+					| ((int)(b * slotColor.b * color.b * multiplier) << 16) //
+					| ((int)(g * slotColor.g * color.g * multiplier) << 8) //
+					| (int)(r * slotColor.r * color.r * multiplier));
 
 				BlendMode slotBlendMode = slot.data.getBlendMode();
 				if (slotBlendMode != blendMode) {
@@ -238,6 +240,7 @@ public class SkeletonRenderer {
 		if (vertexEffect != null) vertexEffect.begin(skeleton);
 
 		boolean premultipliedAlpha = this.premultipliedAlpha;
+		batch.setPremultipliedAlpha(premultipliedAlpha);
 		BlendMode blendMode = null;
 		int verticesLength = 0;
 		float[] vertices = null, uvs = null;
@@ -284,16 +287,19 @@ public class SkeletonRenderer {
 			if (texture != null) {
 				Color lightColor = slot.getColor();
 				float alpha = a * lightColor.a * color.a * 255;
+				float multiplier = premultipliedAlpha ? alpha : 255;
+				float red = r * color.r * multiplier;
+				float green = g * color.g * multiplier;
+				float blue = b * color.b * multiplier;
 				float light = NumberUtils.intToFloatColor(((int)alpha << 24) //
-					| ((int)(b * lightColor.b * color.b * alpha) << 16) //
-					| ((int)(g * lightColor.g * color.g * alpha) << 8) //
-					| (int)(r * lightColor.r * color.r * alpha));
+					| ((int)(blue * lightColor.b) << 16) //
+					| ((int)(green * lightColor.g) << 8) //
+					| (int)(red * lightColor.r));
 				Color darkColor = slot.getDarkColor();
-				if (darkColor == null) darkColor = Color.BLACK;
-				float dark = NumberUtils.intToFloatColor( //
-					((int)(b * darkColor.b * color.b * 255) << 16) //
-						| ((int)(g * darkColor.g * color.g * 255) << 8) //
-						| (int)(r * darkColor.r * color.r * 255));
+				float dark = darkColor == null ? 0
+					: NumberUtils.intToFloatColor((int)(blue * darkColor.b) << 16 //
+						| (int)(green * darkColor.g) << 8 //
+						| (int)(red * darkColor.r));
 
 				BlendMode slotBlendMode = slot.data.getBlendMode();
 				if (slotBlendMode != blendMode) {

+ 36 - 9
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java

@@ -54,6 +54,9 @@ public class TwoColorPolygonBatch {
 	private boolean drawing;
 	private int blendSrcFunc = GL20.GL_SRC_ALPHA;
 	private int blendDstFunc = GL20.GL_ONE_MINUS_SRC_ALPHA;
+	private int blendSrcFuncAlpha = GL20.GL_SRC_ALPHA;
+	private int blendDstFuncAlpha = GL20.GL_ONE_MINUS_SRC_ALPHA;
+	private boolean premultipliedAlpha;
 
 	public TwoColorPolygonBatch (int size) {
 		this(size, size * 2);
@@ -69,7 +72,7 @@ public class TwoColorPolygonBatch {
 		mesh = new Mesh(vertexDataType, false, maxVertices, maxTriangles * 3, //
 			new VertexAttribute(Usage.Position, 2, "a_position"), //
 			new VertexAttribute(Usage.ColorPacked, 4, "a_light"), //
-			new VertexAttribute(Usage.ColorPacked, 4, "a_dark"), //
+			new VertexAttribute(Usage.ColorPacked, 4, "a_dark"), // Dark alpha is unused, but colors are packed as 4 byte floats.
 			new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0"));
 
 		vertices = new float[maxVertices * 6];
@@ -130,7 +133,7 @@ public class TwoColorPolygonBatch {
 		mesh.setVertices(vertices, 0, vertexIndex);
 		mesh.setIndices(triangles, 0, triangleIndex);
 		Gdx.gl.glEnable(GL20.GL_BLEND);
-		if (blendSrcFunc != -1) Gdx.gl.glBlendFunc(blendSrcFunc, blendDstFunc);
+		if (blendSrcFunc != -1) Gdx.gl.glBlendFuncSeparate(blendSrcFunc, blendDstFunc, blendSrcFuncAlpha, blendDstFuncAlpha);
 		mesh.render(shader, GL20.GL_TRIANGLES, 0, triangleIndex);
 
 		vertexIndex = 0;
@@ -150,25 +153,39 @@ public class TwoColorPolygonBatch {
 		return transformMatrix;
 	}
 
+	/** Flushes the batch. */
 	public void setProjectionMatrix (Matrix4 projection) {
 		if (drawing) flush();
 		projectionMatrix.set(projection);
 		if (drawing) setupMatrices();
 	}
 
+	/** Flushes the batch. */
 	public void setTransformMatrix (Matrix4 transform) {
 		if (drawing) flush();
 		transformMatrix.set(transform);
 		if (drawing) setupMatrices();
 	}
 
+	/** Specifies whether the texture colors have premultiplied alpha. Required for correct dark color tinting. Does not change the
+	 * blending function. Flushes the batch if the setting was changed. */
+	public void setPremultipliedAlpha (boolean premultipliedAlpha) {
+		if (this.premultipliedAlpha == premultipliedAlpha) return;
+		if (drawing) flush();
+		this.premultipliedAlpha = premultipliedAlpha;
+		if (drawing) setupMatrices();
+	}
+
 	private void setupMatrices () {
 		combinedMatrix.set(projectionMatrix).mul(transformMatrix);
+		shader.setUniformf("u_pma", premultipliedAlpha ? 1 : 0);
 		shader.setUniformMatrix("u_projTrans", combinedMatrix);
 		shader.setUniformi("u_texture", 0);
 	}
 
+	/** Flushes the batch if the shader was changed. */
 	public void setShader (ShaderProgram newShader) {
+		if (shader == newShader) return;
 		if (drawing) {
 			flush();
 			shader.end();
@@ -180,21 +197,30 @@ public class TwoColorPolygonBatch {
 		}
 	}
 
+	/** Flushes the batch if the blend function was changed. */
 	public void setBlendFunction (int srcFunc, int dstFunc) {
-		if (blendSrcFunc == srcFunc && blendDstFunc == dstFunc) return;
+		setBlendFunctionSeparate(srcFunc, dstFunc, srcFunc, dstFunc);
+	}
+
+	/** Flushes the batch if the blend function was changed. */
+	public void setBlendFunctionSeparate (int srcFuncColor, int dstFuncColor, int srcFuncAlpha, int dstFuncAlpha) {
+		if (blendSrcFunc == srcFuncColor && blendDstFunc == dstFuncColor && blendSrcFuncAlpha == srcFuncAlpha
+			&& blendDstFuncAlpha == dstFuncAlpha) return;
 		flush();
-		blendSrcFunc = srcFunc;
-		blendDstFunc = dstFunc;
+		blendSrcFunc = srcFuncColor;
+		blendDstFunc = dstFuncColor;
+		blendSrcFuncAlpha = srcFuncAlpha;
+		blendDstFuncAlpha = dstFuncAlpha;
 	}
 
 	private ShaderProgram createDefaultShader () {
 		String vertexShader = "attribute vec4 a_position;\n" //
 			+ "attribute vec4 a_light;\n" //
-			+ "attribute vec3 a_dark;\n" //
+			+ "attribute vec4 a_dark;\n" //
 			+ "attribute vec2 a_texCoord0;\n" //
 			+ "uniform mat4 u_projTrans;\n" //
 			+ "varying vec4 v_light;\n" //
-			+ "varying vec3 v_dark;\n" //
+			+ "varying vec4 v_dark;\n" //
 			+ "varying vec2 v_texCoords;\n" //
 			+ "\n" //
 			+ "void main()\n" //
@@ -212,14 +238,15 @@ public class TwoColorPolygonBatch {
 			+ "#define LOWP \n" //
 			+ "#endif\n" //
 			+ "varying LOWP vec4 v_light;\n" //
-			+ "varying LOWP vec3 v_dark;\n" //
+			+ "varying LOWP vec4 v_dark;\n" //
+			+ "uniform float u_pma;\n" //
 			+ "varying vec2 v_texCoords;\n" //
 			+ "uniform sampler2D u_texture;\n" //
 			+ "void main()\n"//
 			+ "{\n" //
 			+ "  vec4 texColor = texture2D(u_texture, v_texCoords);\n" //
 			+ "  gl_FragColor.a = texColor.a * v_light.a;\n" //
-			+ "  gl_FragColor.rgb = (1.0 - texColor.rgb) * v_dark * gl_FragColor.a + texColor.rgb * v_light.rgb;\n" //
+			+ "  gl_FragColor.rgb = ((texColor.a - 1.0) * u_pma + 1.0 - texColor.rgb) * v_dark.rgb + texColor.rgb * v_light.rgb;\n" //
 			+ "}";
 
 		ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader);

+ 25 - 20
spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java

@@ -904,26 +904,31 @@ public class SkeletonViewer extends ApplicationAdapter {
 		}
 
 		void loadPrefs () {
-			debugBonesCheckbox.setChecked(prefs.getBoolean("debugBones", true));
-			debugRegionsCheckbox.setChecked(prefs.getBoolean("debugRegions", false));
-			debugMeshHullCheckbox.setChecked(prefs.getBoolean("debugMeshHull", false));
-			debugMeshTrianglesCheckbox.setChecked(prefs.getBoolean("debugMeshTriangles", false));
-			debugPathsCheckbox.setChecked(prefs.getBoolean("debugPaths", true));
-			debugPointsCheckbox.setChecked(prefs.getBoolean("debugPoints", true));
-			debugClippingCheckbox.setChecked(prefs.getBoolean("debugClipping", true));
-			premultipliedCheckbox.setChecked(prefs.getBoolean("premultiplied", true));
-			loopCheckbox.setChecked(prefs.getBoolean("loop", false));
-			speedSlider.setValue(prefs.getFloat("speed", 0.3f));
-			mixSlider.setValue(prefs.getFloat("mix", 0.3f));
-
-			zoomSlider.setValue(prefs.getFloat("zoom", 1));
-			camera.zoom = 1 / prefs.getFloat("zoom", 1);
-			camera.position.x = prefs.getFloat("x", 0);
-			camera.position.y = prefs.getFloat("y", 0);
-
-			scaleSlider.setValue(prefs.getFloat("scale", 1));
-			animationList.setSelected(prefs.getString("animationName", null));
-			skinList.setSelected(prefs.getString("skinName", null));
+			try {
+				debugBonesCheckbox.setChecked(prefs.getBoolean("debugBones", true));
+				debugRegionsCheckbox.setChecked(prefs.getBoolean("debugRegions", false));
+				debugMeshHullCheckbox.setChecked(prefs.getBoolean("debugMeshHull", false));
+				debugMeshTrianglesCheckbox.setChecked(prefs.getBoolean("debugMeshTriangles", false));
+				debugPathsCheckbox.setChecked(prefs.getBoolean("debugPaths", true));
+				debugPointsCheckbox.setChecked(prefs.getBoolean("debugPoints", true));
+				debugClippingCheckbox.setChecked(prefs.getBoolean("debugClipping", true));
+				premultipliedCheckbox.setChecked(prefs.getBoolean("premultiplied", true));
+				loopCheckbox.setChecked(prefs.getBoolean("loop", false));
+				speedSlider.setValue(prefs.getFloat("speed", 0.3f));
+				mixSlider.setValue(prefs.getFloat("mix", 0.3f));
+
+				zoomSlider.setValue(prefs.getFloat("zoom", 1));
+				camera.zoom = 1 / prefs.getFloat("zoom", 1);
+				camera.position.x = prefs.getFloat("x", 0);
+				camera.position.y = prefs.getFloat("y", 0);
+
+				scaleSlider.setValue(prefs.getFloat("scale", 1));
+				animationList.setSelected(prefs.getString("animationName", null));
+				skinList.setSelected(prefs.getString("skinName", null));
+			} catch (Exception ex) {
+				System.out.println("Unable to read preferences:");
+				ex.printStackTrace();
+			}
 		}
 	}
 

+ 18 - 14
spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs

@@ -457,7 +457,7 @@ namespace Spine.Unity.Editor {
 		static Bone extractionBone;
 		static Slot extractionSlot;
 
-		static Bone GetExtractionBone () {
+		internal static Bone GetExtractionBone () {
 			if (extractionBone != null)
 				return extractionBone;
 
@@ -479,7 +479,7 @@ namespace Spine.Unity.Editor {
 			return extractionBone;
 		}
 
-		static Slot GetExtractionSlot () {
+		internal static Slot GetExtractionSlot () {
 			if (extractionSlot != null)
 				return extractionSlot;
 
@@ -491,11 +491,14 @@ namespace Spine.Unity.Editor {
 			return extractionSlot;
 		}
 
-		static Mesh ExtractRegionAttachment (string name, RegionAttachment attachment, Mesh mesh = null) {
+		internal static Mesh ExtractRegionAttachment (string name, RegionAttachment attachment, Mesh mesh = null, bool centered = true) {
 			var bone = GetExtractionBone();
 
-			bone.X = -attachment.X;
-			bone.Y = -attachment.Y;
+			if (centered) {
+				bone.X = -attachment.X;
+				bone.Y = -attachment.Y;
+			}	
+
 			bone.UpdateWorldTransform();
 
 			Vector2[] uvs = ExtractUV(attachment.UVs);
@@ -504,12 +507,13 @@ namespace Spine.Unity.Editor {
 			Vector3[] verts = ExtractVerts(floatVerts);
 
 			//unrotate verts now that they're centered
-			for (int i = 0; i < verts.Length; i++) {
-				verts[i] = Quaternion.Euler(0, 0, -attachment.Rotation) * verts[i];
+			if (centered) {
+				for (int i = 0; i < verts.Length; i++)
+					verts[i] = Quaternion.Euler(0, 0, -attachment.Rotation) * verts[i];
 			}
 
-			int[] triangles = new int[6] { 1, 3, 0, 2, 3, 1 };
-			Color color = new Color(attachment.R, attachment.G, attachment.B, attachment.A);
+			int[] triangles = { 1, 3, 0, 2, 3, 1 };
+			Color color = attachment.GetColor();
 
 			if (mesh == null)
 				mesh = new Mesh();
@@ -519,7 +523,7 @@ namespace Spine.Unity.Editor {
 			mesh.vertices = verts;
 			mesh.uv = uvs;
 			mesh.triangles = triangles;
-			mesh.colors = new Color[] { color, color, color, color };
+			mesh.colors = new [] { color, color, color, color };
 			mesh.RecalculateBounds();
 			mesh.RecalculateNormals();
 			mesh.name = name;
@@ -527,7 +531,7 @@ namespace Spine.Unity.Editor {
 			return mesh;
 		}
 
-		static Mesh ExtractMeshAttachment (string name, MeshAttachment attachment, Mesh mesh = null) {
+		internal static Mesh ExtractMeshAttachment (string name, MeshAttachment attachment, Mesh mesh = null) {
 			var slot = GetExtractionSlot();
 
 			slot.Bone.X = 0;
@@ -592,7 +596,7 @@ namespace Spine.Unity.Editor {
 			}
 		}
 
-		static Mesh ExtractWeightedMeshAttachment (string name, MeshAttachment attachment, int slotIndex, SkeletonData skeletonData, List<Transform> boneList, Mesh mesh = null) {
+		internal static Mesh ExtractWeightedMeshAttachment (string name, MeshAttachment attachment, int slotIndex, SkeletonData skeletonData, List<Transform> boneList, Mesh mesh = null) {
 			if (attachment.Bones == null)
 				throw new System.ArgumentException("Mesh is not weighted.", "attachment");
 
@@ -708,7 +712,7 @@ namespace Spine.Unity.Editor {
 			return mesh;
 		}
 
-		static Vector2[] ExtractUV (float[] floats) {
+		internal static Vector2[] ExtractUV (float[] floats) {
 			Vector2[] arr = new Vector2[floats.Length / 2];
 
 			for (int i = 0; i < floats.Length; i += 2) {
@@ -718,7 +722,7 @@ namespace Spine.Unity.Editor {
 			return arr;
 		}
 
-		static Vector3[] ExtractVerts (float[] floats) {
+		internal static Vector3[] ExtractVerts (float[] floats) {
 			Vector3[] arr = new Vector3[floats.Length / 2];
 
 			for (int i = 0; i < floats.Length; i += 2) {

+ 1 - 1
spine-unity/Assets/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader

@@ -52,7 +52,7 @@ Shader "Spine/Skeleton Tint" {
 
 			float4 frag (VertexOutput i) : COLOR {
 				float4 texColor = tex2D(_MainTex, i.uv);
-				return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * texColor.a * _Black.rgb), 0);
+				return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * _Black.rgb * texColor.a*_Color.a*i.vertexColor.a), 0);
 			}
 			ENDCG
 		}

+ 1 - 1
spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader

@@ -110,7 +110,7 @@ Shader "Spine/SkeletonGraphic Tint Black (Premultiply Alpha)"
 				clip (texColor.a - 0.001);
 				#endif
 
-				return (texColor * IN.color) + float4(((1-texColor.rgb) * texColor.a * (_Black.rgb + float3(IN.uv1.r, IN.uv1.g, IN.uv2.r))), 0);
+				return (texColor * IN.color) + float4(((1-texColor.rgb) * (_Black.rgb + float3(IN.uv1.r, IN.uv1.g, IN.uv2.r)) * texColor.a*_Color.a*i.vertexColor.a), 0);
 			}
 		ENDCG
 		}

+ 1 - 1
spine-unity/Assets/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader

@@ -61,7 +61,7 @@ Shader "Spine/Skeleton Tint Black" {
 
 			float4 frag (VertexOutput i) : COLOR {
 				float4 texColor = tex2D(_MainTex, i.uv);
-				return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * texColor.a * (_Black.rgb + float3(i.uv1.r, i.uv1.g, i.uv2.r))), 0);
+				return (texColor * i.vertexColor) + float4(((1-texColor.rgb) * (_Black.rgb + float3(i.uv1.r, i.uv1.g, i.uv2.r)) * texColor.a*_Color.a*i.vertexColor.a), 0);
 			}
 			ENDCG
 		}

+ 10 - 0
spine-unity/Assets/spine-unity/SkeletonExtensions.cs

@@ -144,6 +144,12 @@ namespace Spine.Unity {
 			return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation));
 		}
 
+		/// <summary>Gets a bone-local space UnityEngine.Quaternion representation of bone.rotation.</summary>
+		public static Quaternion GetLocalQuaternion (this Bone bone) {
+			var halfRotation = bone.rotation * Mathf.Deg2Rad * 0.5f;
+			return new Quaternion(0, 0, Mathf.Sin(halfRotation), Mathf.Cos(halfRotation));
+		}
+
 		/// <summary>Gets the PointAttachment's Unity World position using its Spine GameObject Transform.</summary>
 		public static Vector3 GetWorldPosition (this PointAttachment attachment, Slot slot, Transform spineGameObjectTransform) {
 			Vector3 skeletonSpacePosition;
@@ -288,6 +294,10 @@ namespace Spine {
 			return va.bones != null && va.bones.Length > 0;
 		}
 
+		public static bool IsRenderable (this Attachment a) {
+			return a is RegionAttachment || a is MeshAttachment;
+		}
+
 		#region Transform Modes
 		public static bool InheritsRotation (this TransformMode mode) {
 			const int RotationBit = 0;

+ 1 - 1
spine-unity/Assets/spine-unity/SkeletonRenderer.cs

@@ -213,7 +213,7 @@ namespace Spine.Unity {
 			for (int i = 0; i < separatorSlotNames.Length; i++)
 				separatorSlots.Add(skeleton.FindSlot(separatorSlotNames[i]));
 
-			LateUpdate();
+			LateUpdate(); // Generate mesh for the first frame it exists.
 
 			if (OnRebuild != null)
 				OnRebuild(this);