diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java index f0c55c3df..a60dc87f4 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -240,7 +240,7 @@ public class SkeletonRenderer { if (vertexEffect != null) vertexEffect.begin(skeleton); boolean premultipliedAlpha = this.premultipliedAlpha; - int darkPremultipliedAlpha = (premultipliedAlpha ? 255 : 0) << 24; + batch.setPremultipliedAlpha(premultipliedAlpha); BlendMode blendMode = null; int verticesLength = 0; float[] vertices = null, uvs = null; @@ -288,16 +288,18 @@ public class SkeletonRenderer { 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 * multiplier) << 16) // - | ((int)(g * lightColor.g * color.g * multiplier) << 8) // - | (int)(r * lightColor.r * color.r * multiplier)); + | ((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 = darkColor == null ? 0 : NumberUtils.intToFloatColor(darkPremultipliedAlpha // - | (int)(b * darkColor.b * color.b * multiplier) << 16 // - | (int)(g * darkColor.g * color.g * multiplier) << 8 // - | (int)(r * darkColor.r * color.r * multiplier)); + 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) { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java index 9c82e7282..62af40b87 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java +++ b/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 = ((texColor.a - 1.0) * v_dark.a + 1.0 - texColor.rgb) * v_dark.rgb + 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); diff --git a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java index a85aa6f55..7687005a2 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/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)); + 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); + 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)); + 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(); + } } } diff --git a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs b/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs index 699c7aef5..b9e04b5a0 100644 --- a/spine-unity/Assets/spine-unity/Editor/SkeletonBaker.cs +++ b/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 boneList, Mesh mesh = null) { + internal static Mesh ExtractWeightedMeshAttachment (string name, MeshAttachment attachment, int slotIndex, SkeletonData skeletonData, List 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) { diff --git a/spine-unity/Assets/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader b/spine-unity/Assets/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader index 3ef83b319..117ae495e 100644 --- a/spine-unity/Assets/spine-unity/Modules/Shaders/Spine-Skeleton-Tint.shader +++ b/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 } diff --git a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader b/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader index b45e14749..696d66006 100644 --- a/spine-unity/Assets/spine-unity/Modules/SkeletonGraphic/Shaders/Spine-SkeletonGraphic-TintBlack.shader +++ b/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 } diff --git a/spine-unity/Assets/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader b/spine-unity/Assets/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader index 5e3420499..7eeca1e75 100644 --- a/spine-unity/Assets/spine-unity/Shaders/Spine-Skeleton-TintBlack.shader +++ b/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 } diff --git a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/spine-unity/SkeletonExtensions.cs index e178907a0..e81435733 100644 --- a/spine-unity/Assets/spine-unity/SkeletonExtensions.cs +++ b/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)); } + /// Gets a bone-local space UnityEngine.Quaternion representation of bone.rotation. + 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)); + } + /// Gets the PointAttachment's Unity World position using its Spine GameObject Transform. 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; diff --git a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs b/spine-unity/Assets/spine-unity/SkeletonRenderer.cs index 1d2f004aa..e12b0b0d0 100644 --- a/spine-unity/Assets/spine-unity/SkeletonRenderer.cs +++ b/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);