From 77099330adb2d793dc0f66f9bbd949c70350d698 Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Mon, 28 Nov 2016 00:41:41 +0100 Subject: [PATCH] Refactored skeleton renderer, added two color tint polygon renderer. --- .../esotericsoftware/spine/SimpleTest3.java | 4 +- .../spine/SkeletonAttachmentTest.java | 4 +- .../spine/SkeletonMeshRenderer.java | 128 ---------- .../spine/SkeletonRenderer.java | 200 +++++++++++++++- .../spine/utils/TwoColorPolygonBatch.java | 220 ++++++++++++++++++ .../spine/SkeletonViewer.java | 11 +- 6 files changed, 419 insertions(+), 148 deletions(-) delete mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java create mode 100644 spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java index 9426581fe..d206fc142 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SimpleTest3.java @@ -41,7 +41,7 @@ import com.badlogic.gdx.graphics.g2d.TextureAtlas; public class SimpleTest3 extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; - SkeletonMeshRenderer renderer; + SkeletonRenderer renderer; SkeletonRendererDebug debugRenderer; TextureAtlas atlas; @@ -51,7 +51,7 @@ public class SimpleTest3 extends ApplicationAdapter { public void create () { camera = new OrthographicCamera(); batch = new PolygonSpriteBatch(); // Required to render meshes. SpriteBatch can't render meshes. - renderer = new SkeletonMeshRenderer(); + renderer = new SkeletonRenderer(); renderer.setPremultipliedAlpha(true); debugRenderer = new SkeletonRendererDebug(); debugRenderer.setMeshTriangles(false); diff --git a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java index a5bb35a18..250d75dd4 100644 --- a/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java +++ b/spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/SkeletonAttachmentTest.java @@ -42,7 +42,7 @@ import com.esotericsoftware.spine.attachments.SkeletonAttachment; public class SkeletonAttachmentTest extends ApplicationAdapter { OrthographicCamera camera; PolygonSpriteBatch batch; - SkeletonMeshRenderer renderer; + SkeletonRenderer renderer; Skeleton spineboy, goblin; AnimationState spineboyState, goblinState; @@ -50,7 +50,7 @@ public class SkeletonAttachmentTest extends ApplicationAdapter { public void create () { camera = new OrthographicCamera(); batch = new PolygonSpriteBatch(); - renderer = new SkeletonMeshRenderer(); + renderer = new SkeletonRenderer(); renderer.setPremultipliedAlpha(true); { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java deleted file mode 100644 index 5ef9a13c0..000000000 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonMeshRenderer.java +++ /dev/null @@ -1,128 +0,0 @@ -/****************************************************************************** - * Spine Runtimes Software License v2.5 - * - * Copyright (c) 2013-2016, Esoteric Software - * All rights reserved. - * - * You are granted a perpetual, non-exclusive, non-sublicensable, and - * non-transferable license to use, install, execute, and perform the Spine - * Runtimes software and derivative works solely for personal or internal - * use. Without the written permission of Esoteric Software (see Section 2 of - * the Spine Software License Agreement), you may not (a) modify, translate, - * adapt, or develop new applications using the Spine Runtimes or otherwise - * create derivative works or improvements of the Spine Runtimes or (b) remove, - * delete, alter, or obscure any trademarks or any copyright, trademark, patent, - * or other intellectual property or proprietary rights notices on or in the - * Software, including any copy thereof. Redistributions in binary or source - * form must include this license and terms. - * - * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF - * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER - * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - *****************************************************************************/ - -package com.esotericsoftware.spine; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.FloatArray; -import com.badlogic.gdx.utils.NumberUtils; -import com.esotericsoftware.spine.attachments.Attachment; -import com.esotericsoftware.spine.attachments.RegionAttachment; -import com.esotericsoftware.spine.attachments.SkeletonAttachment; -import com.esotericsoftware.spine.attachments.MeshAttachment; - -public class SkeletonMeshRenderer extends SkeletonRenderer { - static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0}; - - private final FloatArray vertices = new FloatArray(32); - - @SuppressWarnings("null") - public void draw (PolygonSpriteBatch batch, Skeleton skeleton) { - boolean premultipliedAlpha = this.premultipliedAlpha; - BlendMode blendMode = null; - - int verticesLength = 0; - float[] vertices = null, uvs = null; - short[] triangles = null; - Texture texture = null; - Color color = null, skeletonColor = skeleton.color; - Array drawOrder = skeleton.drawOrder; - for (int i = 0, n = drawOrder.size; i < n; i++) { - Slot slot = drawOrder.get(i); - Attachment attachment = slot.attachment; - if (attachment instanceof RegionAttachment) { - RegionAttachment region = (RegionAttachment)attachment; - verticesLength = 20; - vertices = this.vertices.items; - region.computeWorldVertices(slot, vertices, 0, 5); - triangles = quadTriangles; - texture = region.getRegion().getTexture(); - uvs = region.getUVs(); - color = region.getColor(); - - } else if (attachment instanceof MeshAttachment) { - MeshAttachment mesh = (MeshAttachment)attachment; - verticesLength = (mesh.getWorldVerticesLength() >> 1) * 5; - vertices = this.vertices.setSize(verticesLength); - mesh.computeWorldVertices(slot, 0, mesh.getWorldVerticesLength(), vertices, 0, 5); - triangles = mesh.getTriangles(); - texture = mesh.getRegion().getTexture(); - uvs = mesh.getUVs(); - color = mesh.getColor(); - - } else if (attachment instanceof SkeletonAttachment) { - Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); - if (attachmentSkeleton == null) continue; - Bone bone = slot.getBone(); - Bone rootBone = attachmentSkeleton.getRootBone(); - float oldScaleX = rootBone.getScaleX(); - float oldScaleY = rootBone.getScaleY(); - float oldRotation = rootBone.getRotation(); - attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY()); - // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); - // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); - // Also set shear. - rootBone.setRotation(oldRotation + bone.getWorldRotationX()); - attachmentSkeleton.updateWorldTransform(); - - draw(batch, attachmentSkeleton); - - attachmentSkeleton.setPosition(0, 0); - rootBone.setScaleX(oldScaleX); - rootBone.setScaleY(oldScaleY); - rootBone.setRotation(oldRotation); - } - - if (texture != null) { - Color slotColor = slot.getColor(); - float alpha = skeletonColor.a * slotColor.a * color.a * 255; - float c = NumberUtils.intToFloatColor(((int)alpha << 24) // - | ((int)(skeletonColor.b * slotColor.b * color.b * alpha) << 16) // - | ((int)(skeletonColor.g * slotColor.g * color.g * alpha) << 8) // - | (int)(skeletonColor.r * slotColor.r * color.r * alpha)); - for (int v = 2, u = 0; v < verticesLength; v += 5, u += 2) { - vertices[v] = c; - vertices[v + 1] = uvs[u]; - vertices[v + 2] = uvs[u + 1]; - } - - BlendMode slotBlendMode = slot.data.getBlendMode(); - if (slotBlendMode != blendMode) { - blendMode = slotBlendMode; - batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest()); - } - batch.draw(texture, vertices, 0, vertices.length, triangles, 0, triangles.length); - } - } - } -} 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 65610ea9f..cdd8e86d6 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java @@ -30,24 +30,32 @@ package com.esotericsoftware.spine; +import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.FloatArray; import com.badlogic.gdx.utils.NumberUtils; import com.esotericsoftware.spine.attachments.Attachment; import com.esotericsoftware.spine.attachments.MeshAttachment; import com.esotericsoftware.spine.attachments.RegionAttachment; import com.esotericsoftware.spine.attachments.SkeletonAttachment; +import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; -public class SkeletonRenderer { - boolean premultipliedAlpha; - private final float[] vertices = new float[20]; +public class SkeletonRenderer { + static private final short[] quadTriangles = {0, 1, 2, 2, 3, 0}; - public void draw (T batch, Skeleton skeleton) { + private boolean premultipliedAlpha; + private final FloatArray vertices = new FloatArray(32); + + public void draw (Batch batch, Skeleton skeleton) { boolean premultipliedAlpha = this.premultipliedAlpha; - - float[] vertices = this.vertices; + float[] vertices = this.vertices.items; Color skeletonColor = skeleton.color; + float r = skeletonColor.r, g = skeletonColor.g, b = skeletonColor.b, a = skeletonColor.a; Array drawOrder = skeleton.drawOrder; for (int i = 0, n = drawOrder.size; i < n; i++) { Slot slot = drawOrder.get(i); @@ -56,11 +64,11 @@ public class SkeletonRenderer { RegionAttachment region = (RegionAttachment)attachment; region.computeWorldVertices(slot, vertices, 0, 5); Color color = region.getColor(), slotColor = slot.getColor(); - float alpha = skeletonColor.a * slotColor.a * color.a * 255; + float alpha = a * slotColor.a * color.a * 255; float c = NumberUtils.intToFloatColor(((int)alpha << 24) // - | ((int)(skeletonColor.b * slotColor.b * color.b * alpha) << 16) // - | ((int)(skeletonColor.g * slotColor.g * color.g * alpha) << 8) // - | (int)(skeletonColor.r * slotColor.r * color.r * alpha)); + | ((int)(b * slotColor.b * color.b * alpha) << 16) // + | ((int)(g * slotColor.g * color.g * alpha) << 8) // + | (int)(r * slotColor.r * color.r * alpha)); float[] uvs = region.getUVs(); for (int u = 0, v = 2; u < 8; u += 2, v += 5) { vertices[v] = c; @@ -101,6 +109,178 @@ public class SkeletonRenderer { } } + @SuppressWarnings("null") + public void draw (PolygonSpriteBatch batch, Skeleton skeleton) { + boolean premultipliedAlpha = this.premultipliedAlpha; + BlendMode blendMode = null; + int verticesLength = 0; + float[] vertices = null, uvs = null; + short[] triangles = null; + Texture texture = null; + Color color = null, skeletonColor = skeleton.color; + float r = skeletonColor.r, g = skeletonColor.g, b = skeletonColor.b, a = skeletonColor.a; + Array drawOrder = skeleton.drawOrder; + for (int i = 0, n = drawOrder.size; i < n; i++) { + Slot slot = drawOrder.get(i); + Attachment attachment = slot.attachment; + if (attachment instanceof RegionAttachment) { + RegionAttachment region = (RegionAttachment)attachment; + verticesLength = 20; + vertices = this.vertices.items; + region.computeWorldVertices(slot, vertices, 0, 5); + triangles = quadTriangles; + texture = region.getRegion().getTexture(); + uvs = region.getUVs(); + color = region.getColor(); + + } else if (attachment instanceof MeshAttachment) { + MeshAttachment mesh = (MeshAttachment)attachment; + int count = mesh.getWorldVerticesLength(); + verticesLength = (count >> 1) * 5; + vertices = this.vertices.setSize(verticesLength); + mesh.computeWorldVertices(slot, 0, count, vertices, 0, 5); + triangles = mesh.getTriangles(); + texture = mesh.getRegion().getTexture(); + uvs = mesh.getUVs(); + color = mesh.getColor(); + + } else if (attachment instanceof SkeletonAttachment) { + Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); + if (attachmentSkeleton == null) continue; + Bone bone = slot.getBone(); + Bone rootBone = attachmentSkeleton.getRootBone(); + float oldScaleX = rootBone.getScaleX(); + float oldScaleY = rootBone.getScaleY(); + float oldRotation = rootBone.getRotation(); + attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY()); + // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); + // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); + // Also set shear. + rootBone.setRotation(oldRotation + bone.getWorldRotationX()); + attachmentSkeleton.updateWorldTransform(); + + draw(batch, attachmentSkeleton); + + attachmentSkeleton.setPosition(0, 0); + rootBone.setScaleX(oldScaleX); + rootBone.setScaleY(oldScaleY); + rootBone.setRotation(oldRotation); + continue; + } + + if (texture != null) { + Color slotColor = slot.getColor(); + float alpha = a * slotColor.a * color.a * 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)); + for (int v = 2, u = 0; v < verticesLength; v += 5, u += 2) { + vertices[v] = c; + vertices[v + 1] = uvs[u]; + vertices[v + 2] = uvs[u + 1]; + } + + BlendMode slotBlendMode = slot.data.getBlendMode(); + if (slotBlendMode != blendMode) { + blendMode = slotBlendMode; + batch.setBlendFunction(blendMode.getSource(premultipliedAlpha), blendMode.getDest()); + } + batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length); + } + } + } + + @SuppressWarnings("null") + public void draw (TwoColorPolygonBatch batch, Skeleton skeleton) { + boolean premultipliedAlpha = this.premultipliedAlpha; + BlendMode blendMode = null; + int verticesLength = 0; + float[] vertices = null, uvs = null; + short[] triangles = null; + Texture texture = null; + Color color = null, skeletonColor = skeleton.color; + float r = skeletonColor.r, g = skeletonColor.g, b = skeletonColor.b, a = skeletonColor.a; + Array drawOrder = skeleton.drawOrder; + for (int i = 0, n = drawOrder.size; i < n; i++) { + Slot slot = drawOrder.get(i); + Attachment attachment = slot.attachment; + if (attachment instanceof RegionAttachment) { + RegionAttachment region = (RegionAttachment)attachment; + verticesLength = 24; + vertices = this.vertices.items; + region.computeWorldVertices(slot, vertices, 0, 6); + triangles = quadTriangles; + texture = region.getRegion().getTexture(); + uvs = region.getUVs(); + color = region.getColor(); + + } else if (attachment instanceof MeshAttachment) { + MeshAttachment mesh = (MeshAttachment)attachment; + int count = mesh.getWorldVerticesLength(); + verticesLength = count * 3; + vertices = this.vertices.setSize(verticesLength); + mesh.computeWorldVertices(slot, 0, count, vertices, 0, 6); + triangles = mesh.getTriangles(); + texture = mesh.getRegion().getTexture(); + uvs = mesh.getUVs(); + color = mesh.getColor(); + + } else if (attachment instanceof SkeletonAttachment) { + Skeleton attachmentSkeleton = ((SkeletonAttachment)attachment).getSkeleton(); + if (attachmentSkeleton == null) continue; + Bone bone = slot.getBone(); + Bone rootBone = attachmentSkeleton.getRootBone(); + float oldScaleX = rootBone.getScaleX(); + float oldScaleY = rootBone.getScaleY(); + float oldRotation = rootBone.getRotation(); + attachmentSkeleton.setPosition(bone.getWorldX(), bone.getWorldY()); + // rootBone.setScaleX(1 + bone.getWorldScaleX() - oldScaleX); + // rootBone.setScaleY(1 + bone.getWorldScaleY() - oldScaleY); + // Also set shear. + rootBone.setRotation(oldRotation + bone.getWorldRotationX()); + attachmentSkeleton.updateWorldTransform(); + + draw(batch, attachmentSkeleton); + + attachmentSkeleton.setPosition(0, 0); + rootBone.setScaleX(oldScaleX); + rootBone.setScaleY(oldScaleY); + rootBone.setRotation(oldRotation); + continue; + } + + if (texture != null) { + Color lightColor = slot.getColor(); + float alpha = a * lightColor.a * color.a * 255; + 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)); + 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)); + for (int v = 2, u = 0; v < verticesLength; v += 6, u += 2) { + vertices[v] = light; + vertices[v + 1] = dark; + vertices[v + 2] = uvs[u]; + vertices[v + 3] = uvs[u + 1]; + } + + BlendMode slotBlendMode = slot.data.getBlendMode(); + if (slotBlendMode != blendMode) { + blendMode = slotBlendMode; + Gdx.gl.glEnable(GL20.GL_BLEND); + Gdx.gl.glBlendFunc(blendMode.getSource(premultipliedAlpha), blendMode.getDest()); + } + batch.draw(texture, vertices, 0, verticesLength, triangles, 0, triangles.length); + } + } + } + public void setPremultipliedAlpha (boolean premultipliedAlpha) { this.premultipliedAlpha = premultipliedAlpha; } 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 new file mode 100644 index 000000000..dcf322f93 --- /dev/null +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/TwoColorPolygonBatch.java @@ -0,0 +1,220 @@ +/****************************************************************************** + * Spine Runtimes Software License v2.5 + * + * Copyright (c) 2013-2016, Esoteric Software + * All rights reserved. + * + * You are granted a perpetual, non-exclusive, non-sublicensable, and + * non-transferable license to use, install, execute, and perform the Spine + * Runtimes software and derivative works solely for personal or internal + * use. Without the written permission of Esoteric Software (see Section 2 of + * the Spine Software License Agreement), you may not (a) modify, translate, + * adapt, or develop new applications using the Spine Runtimes or otherwise + * create derivative works or improvements of the Spine Runtimes or (b) remove, + * delete, alter, or obscure any trademarks or any copyright, trademark, patent, + * or other intellectual property or proprietary rights notices on or in the + * Software, including any copy thereof. Redistributions in binary or source + * form must include this license and terms. + * + * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, BUSINESS INTERRUPTION, OR LOSS OF + * USE, DATA, OR PROFITS) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + *****************************************************************************/ + +package com.esotericsoftware.spine.utils; + +import com.badlogic.gdx.Gdx; +import com.badlogic.gdx.graphics.GL20; +import com.badlogic.gdx.graphics.Mesh; +import com.badlogic.gdx.graphics.Mesh.VertexDataType; +import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.graphics.VertexAttribute; +import com.badlogic.gdx.graphics.VertexAttributes.Usage; +import com.badlogic.gdx.graphics.glutils.ShaderProgram; +import com.badlogic.gdx.math.Matrix4; + +public class TwoColorPolygonBatch { + private final Mesh mesh; + private final float[] vertices; + private final short[] triangles; + private final Matrix4 transformMatrix = new Matrix4(); + private final Matrix4 projectionMatrix = new Matrix4(); + private final Matrix4 combinedMatrix = new Matrix4(); + private final ShaderProgram defaultShader; + private ShaderProgram shader; + private int vertexIndex, triangleIndex; + private Texture lastTexture; + private boolean drawing; + + public TwoColorPolygonBatch (int size) { + this(size, size * 2); + } + + public TwoColorPolygonBatch (int maxVertices, int maxTriangles) { + // 32767 is max vertex index. + if (maxVertices > 32767) + throw new IllegalArgumentException("Can't have more than 32767 vertices per batch: " + maxTriangles); + + Mesh.VertexDataType vertexDataType = Mesh.VertexDataType.VertexArray; + if (Gdx.gl30 != null) vertexDataType = VertexDataType.VertexBufferObjectWithVAO; + 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, 3, "a_dark"), // + new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0")); + + vertices = new float[maxVertices * 6]; + triangles = new short[maxTriangles * 3]; + defaultShader = createDefaultShader(); + shader = defaultShader; + projectionMatrix.setToOrtho2D(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); + } + + public void begin () { + if (drawing) throw new IllegalStateException("end must be called before begin."); + Gdx.gl.glDepthMask(false); + shader.begin(); + setupMatrices(); + drawing = true; + } + + public void end () { + if (!drawing) throw new IllegalStateException("begin must be called before end."); + if (vertexIndex > 0) flush(); + shader.end(); + Gdx.gl.glDepthMask(true); + Gdx.gl.glDisable(GL20.GL_BLEND); + lastTexture = null; + drawing = false; + } + + public void draw (Texture texture, float[] polygonVertices, int verticesOffset, int verticesCount, short[] polygonTriangles, + int trianglesOffset, int trianglesCount) { + if (!drawing) throw new IllegalStateException("begin must be called before draw."); + + final short[] triangles = this.triangles; + final float[] vertices = this.vertices; + + if (texture != lastTexture) { + flush(); + lastTexture = texture; + } else if (triangleIndex + trianglesCount > triangles.length || vertexIndex + verticesCount > vertices.length) // + flush(); + + int triangleIndex = this.triangleIndex; + final int vertexIndex = this.vertexIndex; + final int startVertex = vertexIndex / 6; + + for (int i = trianglesOffset, n = i + trianglesCount; i < n; i++) + triangles[triangleIndex++] = (short)(polygonTriangles[i] + startVertex); + this.triangleIndex = triangleIndex; + + System.arraycopy(polygonVertices, verticesOffset, vertices, vertexIndex, verticesCount); + this.vertexIndex += verticesCount; + } + + public void flush () { + if (vertexIndex == 0) return; + + lastTexture.bind(); + Mesh mesh = this.mesh; + mesh.setVertices(vertices, 0, vertexIndex); + mesh.setIndices(triangles, 0, triangleIndex); + Gdx.gl.glEnable(GL20.GL_BLEND); + + mesh.render(shader, GL20.GL_TRIANGLES, 0, triangleIndex); + + vertexIndex = 0; + triangleIndex = 0; + } + + public void dispose () { + mesh.dispose(); + shader.dispose(); + } + + public Matrix4 getProjectionMatrix () { + return projectionMatrix; + } + + public Matrix4 getTransformMatrix () { + return transformMatrix; + } + + public void setProjectionMatrix (Matrix4 projection) { + if (drawing) flush(); + projectionMatrix.set(projection); + if (drawing) setupMatrices(); + } + + public void setTransformMatrix (Matrix4 transform) { + if (drawing) flush(); + transformMatrix.set(transform); + if (drawing) setupMatrices(); + } + + private void setupMatrices () { + combinedMatrix.set(projectionMatrix).mul(transformMatrix); + shader.setUniformMatrix("u_projTrans", combinedMatrix); + shader.setUniformi("u_texture", 0); + } + + public void setShader (ShaderProgram newShader) { + if (drawing) { + flush(); + shader.end(); + } + shader = newShader == null ? defaultShader : newShader; + if (drawing) { + shader.begin(); + setupMatrices(); + } + } + + private ShaderProgram createDefaultShader () { + String vertexShader = "attribute vec4 a_position;\n" // + + "attribute vec4 a_light;\n" // + + "attribute vec4 a_dark;\n" // + + "attribute vec2 a_texCoord0;\n" // + + "uniform mat4 u_projTrans;\n" // + + "varying vec4 v_light;\n" // + + "varying vec4 v_dark;\n" // + + "varying vec2 v_texCoords;\n" // + + "\n" // + + "void main()\n" // + + "{\n" // + + " v_light = a_light;\n" // + + " v_light.a = v_light.a * (255.0/254.0);\n" // + + " v_dark = a_dark;\n" // + + " v_texCoords = a_texCoord0;\n" // + + " gl_Position = u_projTrans * a_position;\n" // + + "}\n"; + String fragmentShader = "#ifdef GL_ES\n" // + + "#define LOWP lowp\n" // + + "precision mediump float;\n" // + + "#else\n" // + + "#define LOWP \n" // + + "#endif\n" // + + "varying LOWP vec4 v_light;\n" // + + "varying LOWP vec4 v_dark;\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 - texColor.rgb) * v_dark * gl_FragColor.a + texColor.rgb * v_light.rgb;\n" // + + "}"; + + ShaderProgram shader = new ShaderProgram(vertexShader, fragmentShader); + if (shader.isCompiled() == false) throw new IllegalArgumentException("Error compiling shader: " + shader.getLog()); + return shader; + } +} 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 23d16c81e..cccc01c0a 100644 --- a/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java +++ b/spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java @@ -51,7 +51,6 @@ import com.badlogic.gdx.graphics.Pixmap; import com.badlogic.gdx.graphics.Pixmap.Format; import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.Texture.TextureFilter; -import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.badlogic.gdx.graphics.g2d.TextureAtlas; import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData; @@ -81,6 +80,7 @@ import com.badlogic.gdx.utils.StringBuilder; import com.badlogic.gdx.utils.viewport.ScreenViewport; import com.esotericsoftware.spine.AnimationState.AnimationStateAdapter; import com.esotericsoftware.spine.AnimationState.TrackEntry; +import com.esotericsoftware.spine.utils.TwoColorPolygonBatch; public class SkeletonViewer extends ApplicationAdapter { static final float checkModifiedInterval = 0.250f; @@ -88,8 +88,8 @@ public class SkeletonViewer extends ApplicationAdapter { UI ui; - PolygonSpriteBatch batch; - SkeletonMeshRenderer renderer; + TwoColorPolygonBatch batch; + SkeletonRenderer renderer; SkeletonRendererDebug debugRenderer; SkeletonData skeletonData; Skeleton skeleton; @@ -111,8 +111,8 @@ public class SkeletonViewer extends ApplicationAdapter { prefs = Gdx.app.getPreferences("spine-skeletonviewer"); ui = new UI(); - batch = new PolygonSpriteBatch(); - renderer = new SkeletonMeshRenderer(); + batch = new TwoColorPolygonBatch(3100); + renderer = new SkeletonRenderer(); debugRenderer = new SkeletonRendererDebug(); skeletonX = (int)(ui.window.getWidth() + (Gdx.graphics.getWidth() - ui.window.getWidth()) / 2); skeletonY = Gdx.graphics.getHeight() / 4; @@ -278,7 +278,6 @@ public class SkeletonViewer extends ApplicationAdapter { state.apply(skeleton); skeleton.updateWorldTransform(); - batch.setColor(Color.WHITE); batch.begin(); renderer.draw(batch, skeleton); batch.end();