From 63c3a4801493741a03e91eaf680c792c267c6d35 Mon Sep 17 00:00:00 2001 From: badlogic Date: Wed, 19 Apr 2017 14:14:09 +0200 Subject: [PATCH] [csharp] Added clipping attachment, loading and subs for clipping code --- spine-csharp/spine-csharp.csproj | 1 + .../src/Attachments/AtlasAttachmentLoader.cs | 4 + .../src/Attachments/AttachmentLoader.cs | 2 + .../src/Attachments/AttachmentType.cs | 2 +- .../src/Attachments/ClippingAttachment.cs | 42 +++ spine-csharp/src/ConvexDecomposer.cs | 268 +++++++++++++++ spine-csharp/src/SkeletonBinary.cs | 24 +- spine-csharp/src/SkeletonClipping.cs | 319 ++++++++++++++++++ spine-csharp/src/SkeletonJson.cs | 14 +- 9 files changed, 668 insertions(+), 8 deletions(-) create mode 100644 spine-csharp/src/Attachments/ClippingAttachment.cs create mode 100644 spine-csharp/src/ConvexDecomposer.cs create mode 100644 spine-csharp/src/SkeletonClipping.cs diff --git a/spine-csharp/spine-csharp.csproj b/spine-csharp/spine-csharp.csproj index 8d91148f5..f1326425f 100644 --- a/spine-csharp/spine-csharp.csproj +++ b/spine-csharp/spine-csharp.csproj @@ -62,6 +62,7 @@ + diff --git a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs index c96d05f5c..43e6627ea 100644 --- a/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs +++ b/spine-csharp/src/Attachments/AtlasAttachmentLoader.cs @@ -90,6 +90,10 @@ namespace Spine { return new PointAttachment(name); } + public ClippingAttachment NewClippingAttachment(Skin skin, string name) { + return new ClippingAttachment(name); + } + public AtlasRegion FindRegion (string name) { AtlasRegion region; diff --git a/spine-csharp/src/Attachments/AttachmentLoader.cs b/spine-csharp/src/Attachments/AttachmentLoader.cs index 8917dc626..23fd6d865 100644 --- a/spine-csharp/src/Attachments/AttachmentLoader.cs +++ b/spine-csharp/src/Attachments/AttachmentLoader.cs @@ -43,5 +43,7 @@ namespace Spine { PathAttachment NewPathAttachment (Skin skin, string name); PointAttachment NewPointAttachment (Skin skin, string name); + + ClippingAttachment NewClippingAttachment (Skin skin, string name); } } diff --git a/spine-csharp/src/Attachments/AttachmentType.cs b/spine-csharp/src/Attachments/AttachmentType.cs index f0ee07054..afb0bf5e9 100644 --- a/spine-csharp/src/Attachments/AttachmentType.cs +++ b/spine-csharp/src/Attachments/AttachmentType.cs @@ -30,6 +30,6 @@ namespace Spine { public enum AttachmentType { - Region, Boundingbox, Mesh, Linkedmesh, Path, Point + Region, Boundingbox, Mesh, Linkedmesh, Path, Point, Clipping } } diff --git a/spine-csharp/src/Attachments/ClippingAttachment.cs b/spine-csharp/src/Attachments/ClippingAttachment.cs new file mode 100644 index 000000000..2f845bf66 --- /dev/null +++ b/spine-csharp/src/Attachments/ClippingAttachment.cs @@ -0,0 +1,42 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +using System; + +namespace Spine { + public class ClippingAttachment : VertexAttachment { + internal SlotData endSlot; + + public SlotData EndSlot { get { return endSlot; } set { endSlot = value; } } + + public ClippingAttachment(string name) : base(name) { + } + } +} diff --git a/spine-csharp/src/ConvexDecomposer.cs b/spine-csharp/src/ConvexDecomposer.cs new file mode 100644 index 000000000..23614d3a6 --- /dev/null +++ b/spine-csharp/src/ConvexDecomposer.cs @@ -0,0 +1,268 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +namespace Spine { + internal class ConvexDecomposer { + // private final Array convexPolygons = new Array(); + // private final Array convexPolygonsIndices = new Array(); + + // private final ShortArray indicesArray = new ShortArray(); + // private final BooleanArray isConcaveArray = new BooleanArray(); + // private final ShortArray triangles = new ShortArray(); + + // private final Pool polygonPool = new Pool() { + // protected FloatArray newObject() { + // return new FloatArray(16); + // } + //}; + + //private final Pool polygonIndicesPool = new Pool() { + // protected ShortArray newObject() { + // return new ShortArray(16); + //} + // }; + + // public Array decompose(FloatArray input) { + // float[] vertices = input.items; + // int vertexCount = input.size >> 1; + + // ShortArray indicesArray = this.indicesArray; + // indicesArray.clear(); + // short[] indices = indicesArray.setSize(vertexCount); + // for (short i = 0; i < vertexCount; i++) + // indices[i] = i; + + // BooleanArray isConcaveArray = this.isConcaveArray; + // boolean[] isConcave = isConcaveArray.setSize(vertexCount); + // for (int i = 0, n = vertexCount; i < n; ++i) + // isConcave[i] = isConcave(i, vertexCount, vertices, indices); + + // ShortArray triangles = this.triangles; + // triangles.clear(); + // triangles.ensureCapacity(Math.max(0, vertexCount - 2) << 2); + + // while (vertexCount > 3) { + // // Find ear tip. + // int previous = vertexCount - 1, i = 0, next = 1; + // while (true) { + // outer: + // if (!isConcave[i]) { + // int p1 = indices[previous] << 1, p2 = indices[i] << 1, p3 = indices[next] << 1; + // float p1x = vertices[p1], p1y = vertices[p1 + 1]; + // float p2x = vertices[p2], p2y = vertices[p2 + 1]; + // float p3x = vertices[p3], p3y = vertices[p3 + 1]; + // for (int ii = (next + 1) % vertexCount; ii != previous; ii = (ii + 1) % vertexCount) { + // if (!isConcave[ii]) continue; + // int v = indices[ii] << 1; + // float vx = vertices[v], vy = vertices[v + 1]; + // if (positiveArea(p3x, p3y, p1x, p1y, vx, vy)) { + // if (positiveArea(p1x, p1y, p2x, p2y, vx, vy)) { + // if (positiveArea(p2x, p2y, p3x, p3y, vx, vy)) break outer; + // } + // } + // } + // break; + // } + + // if (next == 0) { + // do { + // if (!isConcave[i]) break; + // i--; + // } while (i > 0); + // break; + // } + + // previous = i; + // i = next; + // next = (next + 1) % vertexCount; + // } + + // // Cut ear tip. + // triangles.add(indices[(vertexCount + i - 1) % vertexCount]); + // triangles.add(indices[i]); + // triangles.add(indices[(i + 1) % vertexCount]); + // indicesArray.removeIndex(i); + // isConcaveArray.removeIndex(i); + // vertexCount--; + + // int previousIndex = (vertexCount + i - 1) % vertexCount; + // int nextIndex = i == vertexCount ? 0 : i; + // isConcave[previousIndex] = isConcave(previousIndex, vertexCount, vertices, indices); + // isConcave[nextIndex] = isConcave(nextIndex, vertexCount, vertices, indices); + // } + + // if (vertexCount == 3) { + // triangles.add(indices[2]); + // triangles.add(indices[0]); + // triangles.add(indices[1]); + // } + + // Array convexPolygons = this.convexPolygons; + // polygonPool.freeAll(convexPolygons); + // convexPolygons.clear(); + + // Array convexPolygonsIndices = this.convexPolygonsIndices; + // polygonIndicesPool.freeAll(convexPolygonsIndices); + // convexPolygonsIndices.clear(); + + // ShortArray polygonIndices = polygonIndicesPool.obtain(); + // polygonIndices.clear(); + + // FloatArray polygon = polygonPool.obtain(); + // polygon.clear(); + + // // Merge subsequent triangles if they form a triangle fan. + // int fanBaseIndex = -1, lastWinding = 0; + // short[] trianglesItems = triangles.items; + // for (int i = 0, n = triangles.size; i < n; i += 3) { + // int t1 = trianglesItems[i] << 1, t2 = trianglesItems[i + 1] << 1, t3 = trianglesItems[i + 2] << 1; + // float x1 = vertices[t1], y1 = vertices[t1 + 1]; + // float x2 = vertices[t2], y2 = vertices[t2 + 1]; + // float x3 = vertices[t3], y3 = vertices[t3 + 1]; + + // // If the base of the last triangle is the same as this triangle, check if they form a convex polygon (triangle fan). + // boolean merged = false; + // if (fanBaseIndex == t1) { + // int o = polygon.size - 4; + // float[] p = polygon.items; + // int winding1 = winding(p[o], p[o + 1], p[o + 2], p[o + 3], x3, y3); + // int winding2 = winding(x3, y3, p[0], p[1], p[2], p[3]); + // if (winding1 == lastWinding && winding2 == lastWinding) { + // polygon.add(x3); + // polygon.add(y3); + // polygonIndices.add(t3); + // merged = true; + // } + // } + + // // Otherwise make this triangle the new base. + // if (!merged) { + // if (polygon.size > 0) { + // convexPolygons.add(polygon); + // convexPolygonsIndices.add(polygonIndices); + // } + // polygon = polygonPool.obtain(); + // polygon.clear(); + // polygon.add(x1); + // polygon.add(y1); + // polygon.add(x2); + // polygon.add(y2); + // polygon.add(x3); + // polygon.add(y3); + // polygonIndices = polygonIndicesPool.obtain(); + // polygonIndices.clear(); + // polygonIndices.add(t1); + // polygonIndices.add(t2); + // polygonIndices.add(t3); + // lastWinding = winding(x1, y1, x2, y2, x3, y3); + // fanBaseIndex = t1; + // } + // } + + // if (polygon.size > 0) { + // convexPolygons.add(polygon); + // convexPolygonsIndices.add(polygonIndices); + // } + + // // Go through the list of polygons and try to merge the remaining triangles with the found triangle fans. + // for (int i = 0, n = convexPolygons.size; i < n; i++) { + // polygonIndices = convexPolygonsIndices.get(i); + // if (polygonIndices.size == 0) continue; + // int firstIndex = polygonIndices.get(0); + // int lastIndex = polygonIndices.get(polygonIndices.size - 1); + + // polygon = convexPolygons.get(i); + // int o = polygon.size - 4; + // float[] p = polygon.items; + // float prevPrevX = p[o], prevPrevY = p[o + 1]; + // float prevX = p[o + 2], prevY = p[o + 3]; + // float firstX = p[0], firstY = p[1]; + // float secondX = p[2], secondY = p[3]; + // int winding = winding(prevPrevX, prevPrevY, prevX, prevY, firstX, firstY); + + // for (int ii = 0; ii < n; ii++) { + // if (ii == i) continue; + // ShortArray otherIndices = convexPolygonsIndices.get(ii); + // if (otherIndices.size != 3) continue; + // int otherFirstIndex = otherIndices.get(0); + // int otherSecondIndex = otherIndices.get(1); + // int otherLastIndex = otherIndices.get(2); + + // FloatArray otherPoly = convexPolygons.get(ii); + // float x3 = otherPoly.get(otherPoly.size - 2), y3 = otherPoly.get(otherPoly.size - 1); + + // if (otherFirstIndex != firstIndex || otherSecondIndex != lastIndex) continue; + // int winding1 = winding(prevPrevX, prevPrevY, prevX, prevY, x3, y3); + // int winding2 = winding(x3, y3, firstX, firstY, secondX, secondY); + // if (winding1 == winding && winding2 == winding) { + // otherPoly.clear(); + // otherIndices.clear(); + // polygon.add(x3); + // polygon.add(y3); + // polygonIndices.add(otherLastIndex); + // prevPrevX = prevX; + // prevPrevY = prevY; + // prevX = x3; + // prevY = y3; + // ii = 0; + // } + // } + // } + + // // Remove empty polygons that resulted from the merge step above. + // for (int i = convexPolygons.size - 1; i >= 0; i--) { + // polygon = convexPolygons.get(i); + // if (polygon.size == 0) { + // convexPolygons.removeIndex(i); + // polygonPool.free(polygon); + // } + // } + + // return convexPolygons; + //} + + //static private boolean isConcave(int index, int vertexCount, float[] vertices, short[] indices) { + // int previous = indices[(vertexCount + index - 1) % vertexCount] << 1; + // int current = indices[index] << 1; + // int next = indices[(index + 1) % vertexCount] << 1; + // return !positiveArea(vertices[previous], vertices[previous + 1], vertices[current], vertices[current + 1], vertices[next], + // vertices[next + 1]); + //} + + //static private boolean positiveArea(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + // return p1x * (p3y - p2y) + p2x * (p1y - p3y) + p3x * (p2y - p1y) >= 0; + //} + + //static private int winding(float p1x, float p1y, float p2x, float p2y, float p3x, float p3y) { + // float px = p2x - p1x, py = p2y - p1y; + // return p3x * py - p3y * px + px * p1y - p1x * py >= 0 ? 1 : -1; + //} + } +} diff --git a/spine-csharp/src/SkeletonBinary.cs b/spine-csharp/src/SkeletonBinary.cs index a53670efc..cb3629c2e 100644 --- a/spine-csharp/src/SkeletonBinary.cs +++ b/spine-csharp/src/SkeletonBinary.cs @@ -252,7 +252,7 @@ namespace Spine { } // Default skin. - Skin defaultSkin = ReadSkin(input, "default", nonessential); + Skin defaultSkin = ReadSkin(input, skeletonData, "default", nonessential); if (defaultSkin != null) { skeletonData.defaultSkin = defaultSkin; skeletonData.skins.Add(defaultSkin); @@ -260,7 +260,7 @@ namespace Spine { // Skins. for (int i = 0, n = ReadVarint(input, true); i < n; i++) - skeletonData.skins.Add(ReadSkin(input, ReadString(input), nonessential)); + skeletonData.skins.Add(ReadSkin(input, skeletonData, ReadString(input), nonessential)); // Linked meshes. for (int i = 0, n = linkedMeshes.Count; i < n; i++) { @@ -299,7 +299,7 @@ namespace Spine { /// May be null. - private Skin ReadSkin (Stream input, String skinName, bool nonessential) { + private Skin ReadSkin (Stream input, SkeletonData skeletonData, String skinName, bool nonessential) { int slotCount = ReadVarint(input, true); if (slotCount == 0) return null; Skin skin = new Skin(skinName); @@ -307,14 +307,14 @@ namespace Spine { int slotIndex = ReadVarint(input, true); for (int ii = 0, nn = ReadVarint(input, true); ii < nn; ii++) { String name = ReadString(input); - Attachment attachment = ReadAttachment(input, skin, slotIndex, name, nonessential); + Attachment attachment = ReadAttachment(input, skeletonData, skin, slotIndex, name, nonessential); if (attachment != null) skin.AddAttachment(slotIndex, name, attachment); } } return skin; } - private Attachment ReadAttachment (Stream input, Skin skin, int slotIndex, String attachmentName, bool nonessential) { + private Attachment ReadAttachment (Stream input, SkeletonData skeletonData, Skin skin, int slotIndex, String attachmentName, bool nonessential) { float scale = Scale; String name = ReadString(input); @@ -463,6 +463,20 @@ namespace Spine { //if (nonessential) point.color = color; return point; } + case AttachmentType.Clipping: { + int endSlotIndex = ReadVarint(input, true); + int vertexCount = ReadVarint(input, true); + Vertices vertices = ReadVertices(input, vertexCount); + if (nonessential) ReadInt(input); + + ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name); + if (clip == null) return null; + clip.EndSlot = skeletonData.slots.Items[endSlotIndex]; + clip.worldVerticesLength = vertexCount << 1; + clip.vertices = vertices.vertices; + clip.bones = vertices.bones; + return clip; + } } return null; } diff --git a/spine-csharp/src/SkeletonClipping.cs b/spine-csharp/src/SkeletonClipping.cs new file mode 100644 index 000000000..ebbf74f1b --- /dev/null +++ b/spine-csharp/src/SkeletonClipping.cs @@ -0,0 +1,319 @@ +/****************************************************************************** + * 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. + *****************************************************************************/ + +using System; + +namespace Spine { + public class SkeletonClipping { + // private final ConvexDecomposer decomposer = new ConvexDecomposer(); + // private final FloatArray clippingPolygon = new FloatArray(); + // private final FloatArray clipOutput = new FloatArray(128); + // private final FloatArray clippedVertices = new FloatArray(128); + // private final ShortArray clippedTriangles = new ShortArray(128); + // private final FloatArray scratch = new FloatArray(); + + // private ClippingAttachment clipAttachment; + // private Array clippingPolygons; + + // public void clipStart(Slot slot, ClippingAttachment clip) { + // if (clipAttachment != null) return; + // clipAttachment = clip; + + // int n = clip.getWorldVerticesLength(); + // float[] vertices = clippingPolygon.setSize(n); + // clip.computeWorldVertices(slot, 0, n, vertices, 0, 2); + // makeClockwise(clippingPolygon); + // clippingPolygons = decomposer.decompose(clippingPolygon); + // for (FloatArray polygon : clippingPolygons) { + // makeClockwise(polygon); + // polygon.add(polygon.items[0]); + // polygon.add(polygon.items[1]); + // } + // } + + // public void clipEnd(Slot slot) { + // if (clipAttachment != null && clipAttachment.getEndSlot() == slot.getData()) clipEnd(); + // } + + // public void clipEnd() { + // if (clipAttachment == null) return; + // clipAttachment = null; + // clippingPolygons = null; + // clippedVertices.clear(); + // clippedTriangles.clear(); + // clippingPolygon.clear(); + // } + + // public boolean isClipping() { + // return clipAttachment != null; + // } + + // public void clipTriangles(float[] vertices, int verticesLength, short[] triangles, int trianglesLength, float[] uvs, + // float light, float dark, boolean twoColor) { + + // FloatArray clipOutput = this.clipOutput, clippedVertices = this.clippedVertices; + // ShortArray clippedTriangles = this.clippedTriangles; + // Object[] polygons = clippingPolygons.items; + // int polygonsCount = clippingPolygons.size; + // int vertexSize = twoColor ? 6 : 5; + + // short index = 0; + // clippedVertices.clear(); + // clippedTriangles.clear(); + // outer: + // for (int i = 0; i < trianglesLength; i += 3) { + // int vertexOffset = triangles[i] << 1; + // float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; + // float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; + + // vertexOffset = triangles[i + 1] << 1; + // float x2 = vertices[vertexOffset], y2 = vertices[vertexOffset + 1]; + // float u2 = uvs[vertexOffset], v2 = uvs[vertexOffset + 1]; + + // vertexOffset = triangles[i + 2] << 1; + // float x3 = vertices[vertexOffset], y3 = vertices[vertexOffset + 1]; + // float u3 = uvs[vertexOffset], v3 = uvs[vertexOffset + 1]; + + // for (int p = 0; p < polygonsCount; p++) { + // int s = clippedVertices.size; + // if (clip(x1, y1, x2, y2, x3, y3, (FloatArray)polygons[p], clipOutput)) { + // int clipOutputLength = clipOutput.size; + // if (clipOutputLength == 0) continue; + // float d0 = y2 - y3, d1 = x3 - x2, d2 = x1 - x3, d4 = y3 - y1; + // float d = 1 / (d0 * d2 + d1 * (y1 - y3)); + + // int clipOutputCount = clipOutputLength >> 1; + // float[] clipOutputItems = clipOutput.items; + // float[] clippedVerticesItems = clippedVertices.setSize(s + clipOutputCount * vertexSize); + // for (int ii = 0; ii < clipOutputLength; ii += 2) { + // float x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; + // clippedVerticesItems[s] = x; + // clippedVerticesItems[s + 1] = y; + // clippedVerticesItems[s + 2] = light; + // if (twoColor) { + // clippedVerticesItems[s + 3] = dark; + // s += 4; + // } + // else + // s += 3; + // float c0 = x - x3, c1 = y - y3; + // float a = (d0 * c0 + d1 * c1) * d; + // float b = (d4 * c0 + d2 * c1) * d; + // float c = 1 - a - b; + // clippedVerticesItems[s] = u1 * a + u2 * b + u3 * c; + // clippedVerticesItems[s + 1] = v1 * a + v2 * b + v3 * c; + // s += 2; + // } + + // s = clippedTriangles.size; + // short[] clippedTrianglesItems = clippedTriangles.setSize(s + 3 * (clipOutputCount - 2)); + // clipOutputCount--; + // for (int ii = 1; ii < clipOutputCount; ii++) { + // clippedTrianglesItems[s] = index; + // clippedTrianglesItems[s + 1] = (short)(index + ii); + // clippedTrianglesItems[s + 2] = (short)(index + ii + 1); + // s += 3; + // } + // index += clipOutputCount + 1; + + // } + // else { + // float[] clippedVerticesItems = clippedVertices.setSize(s + 3 * vertexSize); + // clippedVerticesItems[s] = x1; + // clippedVerticesItems[s + 1] = y1; + // clippedVerticesItems[s + 2] = light; + // if (!twoColor) { + // clippedVerticesItems[s + 3] = u1; + // clippedVerticesItems[s + 4] = v1; + + // clippedVerticesItems[s + 5] = x2; + // clippedVerticesItems[s + 6] = y2; + // clippedVerticesItems[s + 7] = light; + // clippedVerticesItems[s + 8] = u2; + // clippedVerticesItems[s + 9] = v2; + + // clippedVerticesItems[s + 10] = x3; + // clippedVerticesItems[s + 11] = y3; + // clippedVerticesItems[s + 12] = light; + // clippedVerticesItems[s + 13] = u3; + // clippedVerticesItems[s + 14] = v3; + // } + // else { + // clippedVerticesItems[s + 3] = dark; + // clippedVerticesItems[s + 4] = u1; + // clippedVerticesItems[s + 5] = v1; + + // clippedVerticesItems[s + 6] = x2; + // clippedVerticesItems[s + 7] = y2; + // clippedVerticesItems[s + 8] = light; + // clippedVerticesItems[s + 9] = dark; + // clippedVerticesItems[s + 10] = u2; + // clippedVerticesItems[s + 11] = v2; + + // clippedVerticesItems[s + 12] = x3; + // clippedVerticesItems[s + 13] = y3; + // clippedVerticesItems[s + 14] = light; + // clippedVerticesItems[s + 15] = dark; + // clippedVerticesItems[s + 16] = u3; + // clippedVerticesItems[s + 17] = v3; + // } + + // s = clippedTriangles.size; + // short[] clippedTrianglesItems = clippedTriangles.setSize(s + 3); + // clippedTrianglesItems[s] = index; + // clippedTrianglesItems[s + 1] = (short)(index + 1); + // clippedTrianglesItems[s + 2] = (short)(index + 2); + // index += 3; + // continue outer; + // } + // } + // } + // } + + // /** Clips the input triangle against the convex, clockwise clipping area. If the triangle lies entirely within the clipping + // * area, false is returned. The clipping area must duplicate the first vertex at the end of the vertices list. */ + // boolean clip(float x1, float y1, float x2, float y2, float x3, float y3, FloatArray clippingArea, FloatArray output) { + // FloatArray originalOutput = output; + // boolean clipped = false; + + // // Avoid copy at the end. + // FloatArray input = null; + // if (clippingArea.size % 4 >= 2) { + // input = output; + // output = scratch; + // } + // else + // input = scratch; + + // input.clear(); + // input.add(x1); + // input.add(y1); + // input.add(x2); + // input.add(y2); + // input.add(x3); + // input.add(y3); + // input.add(x1); + // input.add(y1); + // output.clear(); + + // float[] clippingVertices = clippingArea.items; + // int clippingVerticesLast = clippingArea.size - 4; + // for (int i = 0; ; i += 2) { + // float edgeX = clippingVertices[i], edgeY = clippingVertices[i + 1]; + // float edgeX2 = clippingVertices[i + 2], edgeY2 = clippingVertices[i + 3]; + // float deltaX = edgeX - edgeX2, deltaY = edgeY - edgeY2; + + // float[] inputVertices = input.items; + // int inputVerticesLength = input.size - 2, outputStart = output.size; + // for (int ii = 0; ii < inputVerticesLength; ii += 2) { + // float inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; + // float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; + // boolean side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; + // if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { + // if (side2) { // v1 inside, v2 inside + // output.add(inputX2); + // output.add(inputY2); + // continue; + // } + // // v1 inside, v2 outside + // float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + // float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + // output.add(edgeX + (edgeX2 - edgeX) * ua); + // output.add(edgeY + (edgeY2 - edgeY) * ua); + // } + // else if (side2) { // v1 outside, v2 inside + // float c0 = inputY2 - inputY, c2 = inputX2 - inputX; + // float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); + // output.add(edgeX + (edgeX2 - edgeX) * ua); + // output.add(edgeY + (edgeY2 - edgeY) * ua); + // output.add(inputX2); + // output.add(inputY2); + // } + // clipped = true; + // } + + // if (outputStart == output.size) { // All edges outside. + // originalOutput.clear(); + // return true; + // } + + // output.add(output.items[0]); + // output.add(output.items[1]); + + // if (i == clippingVerticesLast) break; + // FloatArray temp = output; + // output = input; + // output.clear(); + // input = temp; + // } + + // if (originalOutput != output) { + // originalOutput.clear(); + // originalOutput.addAll(output.items, 0, output.size - 2); + // } + // else + // originalOutput.setSize(originalOutput.size - 2); + + // return clipped; + // } + + // public FloatArray getClippedVertices() { + // return clippedVertices; + // } + + // public ShortArray getClippedTriangles() { + // return clippedTriangles; + // } + + // static void makeClockwise(FloatArray polygon) { + // float[] vertices = polygon.items; + // int verticeslength = polygon.size; + + // float area = vertices[verticeslength - 2] * vertices[1] - vertices[0] * vertices[verticeslength - 1], p1x, p1y, p2x, p2y; + // for (int i = 0, n = verticeslength - 3; i < n; i += 2) { + // p1x = vertices[i]; + // p1y = vertices[i + 1]; + // p2x = vertices[i + 2]; + // p2y = vertices[i + 3]; + // area += p1x * p2y - p2x * p1y; + // } + // if (area < 0) return; + + // for (int i = 0, lastX = verticeslength - 2, n = verticeslength >> 1; i < n; i += 2) { + // float x = vertices[i], y = vertices[i + 1]; + // int other = lastX - i; + // vertices[i] = vertices[other]; + // vertices[i + 1] = vertices[other + 1]; + // vertices[other] = x; + // vertices[other + 1] = y; + // } + // } + } +} diff --git a/spine-csharp/src/SkeletonJson.cs b/spine-csharp/src/SkeletonJson.cs index 437823197..948834850 100644 --- a/spine-csharp/src/SkeletonJson.cs +++ b/spine-csharp/src/SkeletonJson.cs @@ -261,7 +261,7 @@ namespace Spine { int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key); foreach (KeyValuePair entry in ((Dictionary)slotEntry.Value)) { try { - Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key); + Attachment attachment = ReadAttachment((Dictionary)entry.Value, skin, slotIndex, entry.Key, skeletonData); if (attachment != null) skin.AddAttachment(slotIndex, entry.Key, attachment); } catch (Exception e) { throw new Exception("Error reading attachment: " + entry.Key + ", skin: " + skin, e); @@ -317,7 +317,7 @@ namespace Spine { return skeletonData; } - private Attachment ReadAttachment (Dictionary map, Skin skin, int slotIndex, String name) { + private Attachment ReadAttachment (Dictionary map, Skin skin, int slotIndex, String name, SkeletonData skeletonData) { var scale = this.Scale; name = GetString(map, "name", name); @@ -416,6 +416,16 @@ namespace Spine { //if (color != null) point.color = color; return point; } + case AttachmentType.Clipping: { + ClippingAttachment clip = attachmentLoader.NewClippingAttachment(skin, name); + if (clip == null) return null; + + SlotData slot = skeletonData.FindSlot(GetString(map, "end", null)); + if (slot == null) throw new Exception("Clipping end slot not found: " + GetString(map, "end", null)); + clip.endSlot = slot; + ReadVertices(map, clip, GetInt(map, "vertexCount", 0) << 1); + return clip; + } } return null; }