/****************************************************************************** * 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. *****************************************************************************/ #include #include spSkeletonClipping* spSkeletonClipping_create() { spSkeletonClipping* clipping = CALLOC(spSkeletonClipping, 1); clipping->triangulator = spTriangulator_create(); clipping->clippingPolygon = spFloatArray_create(128); clipping->clipOutput = spFloatArray_create(128); clipping->clippedVertices = spFloatArray_create(128); clipping->clippedUVs = spFloatArray_create(128); clipping->clippedTriangles = spUnsignedShortArray_create(128); clipping->scratch = spFloatArray_create(128); return clipping; } void spSkeletonClipping_dispose(spSkeletonClipping* self) { spTriangulator_dispose(self->triangulator); spFloatArray_dispose(self->clippingPolygon); spFloatArray_dispose(self->clipOutput); spFloatArray_dispose(self->clippedVertices); spFloatArray_dispose(self->clippedUVs); spUnsignedShortArray_dispose(self->clippedTriangles); spFloatArray_dispose(self->scratch); FREE(self); } static void _makeClockwise (spFloatArray* polygon) { int i, n, lastX; 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 (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 (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; } } int spSkeletonClipping_clipStart(spSkeletonClipping* self, spSlot* slot, spClippingAttachment* clip) { int i, n; float* vertices; if (self->clipAttachment) return 0; self->clipAttachment = clip; n = clip->super.worldVerticesLength; vertices = spFloatArray_setSize(self->clippingPolygon, n)->items; spVertexAttachment_computeWorldVertices(SUPER(clip), slot, 0, n, vertices, 0, 2); _makeClockwise(self->clippingPolygon); self->clippingPolygons = spTriangulator_decompose(self->triangulator, self->clippingPolygon, spTriangulator_triangulate(self->triangulator, self->clippingPolygon)); for (i = 0, n = self->clippingPolygons->size; i < n; i++) { spFloatArray* polygon = self->clippingPolygons->items[i]; _makeClockwise(polygon); spFloatArray_add(polygon, polygon->items[0]); spFloatArray_add(polygon, polygon->items[1]); } return self->clippingPolygons->size; } void spSkeletonClipping_clipEnd(spSkeletonClipping* self, spSlot* slot) { if (self->clipAttachment != 0 && self->clipAttachment->endSlot == slot->data) spSkeletonClipping_clipEnd2(self); } void spSkeletonClipping_clipEnd2(spSkeletonClipping* self) { if (!self->clipAttachment) return; self->clipAttachment = 0; self->clippingPolygons = 0; spFloatArray_clear(self->clippedVertices); spFloatArray_clear(self->clippedUVs); spUnsignedShortArray_clear(self->clippedTriangles); spFloatArray_clear(self->clippingPolygon); } int /*boolean*/ spSkeletonClipping_isClipping(spSkeletonClipping* self) { return self->clipAttachment != 0; } int /*boolean*/ _clip(spSkeletonClipping* self, float x1, float y1, float x2, float y2, float x3, float y3, spFloatArray* clippingArea, spFloatArray* output) { int i; spFloatArray* originalOutput = output; int clipped = 0; float* clippingVertices; int clippingVerticesLast; spFloatArray* input = 0; if (clippingArea->size % 4 >= 2) { input = output; output = self->scratch; } else input = self->scratch; spFloatArray_clear(input); spFloatArray_add(input, x1); spFloatArray_add(input, y1); spFloatArray_add(input, x2); spFloatArray_add(input, y2); spFloatArray_add(input, x3); spFloatArray_add(input, y3); spFloatArray_add(input, x1); spFloatArray_add(input, y1); spFloatArray_clear(output); clippingVertices = clippingArea->items; clippingVerticesLast = clippingArea->size - 4; for (i = 0;; i += 2) { int ii; spFloatArray* temp; 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 (ii = 0; ii < inputVerticesLength; ii += 2) { float inputX = inputVertices[ii], inputY = inputVertices[ii + 1]; float inputX2 = inputVertices[ii + 2], inputY2 = inputVertices[ii + 3]; int side2 = deltaX * (inputY2 - edgeY2) - deltaY * (inputX2 - edgeX2) > 0; if (deltaX * (inputY - edgeY2) - deltaY * (inputX - edgeX2) > 0) { float c0, c2; float ua; if (side2) { spFloatArray_add(output, inputX2); spFloatArray_add(output, inputY2); continue; } c0 = inputY2 - inputY, c2 = inputX2 - inputX; ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); spFloatArray_add(output, edgeX + (edgeX2 - edgeX) * ua); spFloatArray_add(output, edgeY + (edgeY2 - edgeY) * ua); } else if (side2) { float c0 = inputY2 - inputY, c2 = inputX2 - inputX; float ua = (c2 * (edgeY - inputY) - c0 * (edgeX - inputX)) / (c0 * (edgeX2 - edgeX) - c2 * (edgeY2 - edgeY)); spFloatArray_add(output, edgeX + (edgeX2 - edgeX) * ua); spFloatArray_add(output, edgeY + (edgeY2 - edgeY) * ua); spFloatArray_add(output, inputX2); spFloatArray_add(output, inputY2); } clipped = 1; } if (outputStart == output->size) { spFloatArray_clear(originalOutput); return 1; } spFloatArray_add(output, output->items[0]); spFloatArray_add(output, output->items[1]); if (i == clippingVerticesLast) break; temp = output; output = input; spFloatArray_clear(output); input = temp; } if (originalOutput != output) { spFloatArray_clear(originalOutput); spFloatArray_addAllValues(originalOutput, output->items, 0, output->size - 2); } else spFloatArray_setSize(originalOutput, originalOutput->size - 2); return clipped; } void spSkeletonClipping_clipTriangles(spSkeletonClipping* self, float* vertices, int verticesLength, unsigned short* triangles, int trianglesLength, float* uvs, int stride) { int i; spFloatArray* clipOutput = self->clipOutput; spFloatArray* clippedVertices = self->clippedVertices; spFloatArray* clippedUVs = self->clippedUVs; spUnsignedShortArray* clippedTriangles = self->clippedTriangles; spFloatArray** polygons = self->clippingPolygons->items; int polygonsCount = self->clippingPolygons->size; short index = 0; spFloatArray_clear(clippedVertices); spFloatArray_clear(clippedUVs); spUnsignedShortArray_clear(clippedTriangles); i = 0; outer: for (; i < trianglesLength; i += 3) { int p; int vertexOffset = triangles[i] * stride; float x2, y2, u2, v2, x3, y3, u3, v3; float x1 = vertices[vertexOffset], y1 = vertices[vertexOffset + 1]; float u1 = uvs[vertexOffset], v1 = uvs[vertexOffset + 1]; vertexOffset = triangles[i + 1] * stride; x2 = vertices[vertexOffset]; y2 = vertices[vertexOffset + 1]; u2 = uvs[vertexOffset]; v2 = uvs[vertexOffset + 1]; vertexOffset = triangles[i + 2] * stride; x3 = vertices[vertexOffset]; y3 = vertices[vertexOffset + 1]; u3 = uvs[vertexOffset]; v3 = uvs[vertexOffset + 1]; for (p = 0; p < polygonsCount; p++) { int s = clippedVertices->size; if (_clip(self, x1, y1, x2, y2, x3, y3, polygons[p], clipOutput)) { int ii; float d0, d1, d2, d4, d; unsigned short* clippedTrianglesItems; int clipOutputCount; float* clipOutputItems; float* clippedVerticesItems; float* clippedUVsItems; int clipOutputLength = clipOutput->size; if (clipOutputLength == 0) continue; d0 = y2 - y3; d1 = x3 - x2; d2 = x1 - x3; d4 = y3 - y1; d = 1 / (d0 * d2 + d1 * (y1 - y3)); clipOutputCount = clipOutputLength >> 1; clipOutputItems = clipOutput->items; clippedVerticesItems = spFloatArray_setSize(clippedVertices, s + (clipOutputCount << 1))->items; clippedUVsItems = spFloatArray_setSize(clippedUVs, s + (clipOutputCount << 1))->items; for (ii = 0; ii < clipOutputLength; ii += 2) { float c0, c1, a, b, c; float x = clipOutputItems[ii], y = clipOutputItems[ii + 1]; clippedVerticesItems[s] = x; clippedVerticesItems[s + 1] = y; c0 = x - x3; c1 = y - y3; a = (d0 * c0 + d1 * c1) * d; b = (d4 * c0 + d2 * c1) * d; c = 1 - a - b; clippedUVsItems[s] = u1 * a + u2 * b + u3 * c; clippedUVsItems[s + 1] = v1 * a + v2 * b + v3 * c; s += 2; } s = clippedTriangles->size; clippedTrianglesItems = spUnsignedShortArray_setSize(clippedTriangles, s + 3 * (clipOutputCount - 2))->items; clipOutputCount--; for (ii = 1; ii < clipOutputCount; ii++) { clippedTrianglesItems[s] = index; clippedTrianglesItems[s + 1] = (unsigned short)(index + ii); clippedTrianglesItems[s + 2] = (unsigned short)(index + ii + 1); s += 3; } index += clipOutputCount + 1; } else { unsigned short* clippedTrianglesItems; float* clippedVerticesItems = spFloatArray_setSize(clippedVertices, s + (3 << 1))->items; float* clippedUVsItems = spFloatArray_setSize(clippedUVs, s + (3 << 1))->items; clippedVerticesItems[s] = x1; clippedVerticesItems[s + 1] = y1; clippedVerticesItems[s + 2] = x2; clippedVerticesItems[s + 3] = y2; clippedVerticesItems[s + 4] = x3; clippedVerticesItems[s + 5] = y3; clippedUVsItems[s] = u1; clippedUVsItems[s + 1] = v1; clippedUVsItems[s + 2] = u2; clippedUVsItems[s + 3] = v2; clippedUVsItems[s + 4] = u3; clippedUVsItems[s + 5] = v3; s = clippedTriangles->size; clippedTrianglesItems = spUnsignedShortArray_setSize(clippedTriangles, s + 3)->items; clippedTrianglesItems[s] = index; clippedTrianglesItems[s + 1] = (unsigned short)(index + 1); clippedTrianglesItems[s + 2] = (unsigned short)(index + 2); index += 3; i += 3; goto outer; } } } }