diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java index b22a47c2e..2aac860a1 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java @@ -46,7 +46,7 @@ public class Skeleton { final Array ikConstraints; final Array transformConstraints; final Array pathConstraints; - private final Array updateCache = new Array(); + final Array updateCache = new Array(); Skin skin; final Color color; float time; diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java index eb7397a48..867b2906d 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/BoundingBoxAttachment.java @@ -43,7 +43,7 @@ public class BoundingBoxAttachment extends VertexAttachment { } public void computeWorldVertices (Slot slot, float[] worldVertices) { - super.computeWorldVertices(slot, worldVertices); + computeWorldVertices(slot, 0, worldVerticesLength, worldVertices, 0); } public Color getColor () { diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java index 8754896a2..787e85f75 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/PathAttachment.java @@ -40,7 +40,7 @@ public class PathAttachment extends VertexAttachment { final Color color = new Color(1, 0.5f, 0, 1); float[] worldVertices, lengths; - boolean closed; + boolean closed, constantSpeed; public PathAttachment (String name) { super(name); @@ -54,111 +54,147 @@ public class PathAttachment extends VertexAttachment { // BOZO - Remove check? if (worldVerticesLength < 12) return; - float[] worldVertices = this.worldVertices; - super.computeWorldVertices(slot, worldVertices); + // BOZO - Path constraint rotation. + // BOZO - Closed paths. + // BOZO - Before/after open paths. - // Determine curve containing position. - float pathLength = pathLengths(worldVertices); - float[] lengths = this.lengths; - float target = pathLength * position, distance = 0, t = 0; - int curve = 0; - for (;; curve++) { - float length = lengths[curve]; - float nextDistance = distance + length; - if (nextDistance >= target) { - t = (target - distance) / length; - break; + float x1, y1, cx1, cy1, cx2, cy2, x2, y2; + if (!constantSpeed) { + int curves = worldVerticesLength / 6 - 1; + int curve = position < 1 ? (int)(curves * position) : curves - 1; + position = (position - curve / (float)curves) * curves; + + float[] worldVertices = this.worldVertices; + super.computeWorldVertices(slot, curve * 6 + 2, 8, worldVertices, 0); + + x1 = worldVertices[0]; + y1 = worldVertices[1]; + cx1 = worldVertices[2]; + cy1 = worldVertices[3]; + cx2 = worldVertices[4]; + cy2 = worldVertices[5]; + x2 = worldVertices[6]; + y2 = worldVertices[7]; + } else { + // BOZO - Use offset and count for all attachment compute methods. + float[] worldVertices = this.worldVertices; + int verticesLength = worldVerticesLength - 4; + super.computeWorldVertices(slot, 2, verticesLength, worldVertices, 0); + + // Curve lengths. + float[] lengths = this.lengths; + float length = 0; + x1 = worldVertices[0]; + y1 = worldVertices[1]; + float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; + for (int i = 0, w = 2; w < verticesLength; i++, w += 6) { + cx1 = worldVertices[w]; + cy1 = worldVertices[w + 1]; + cx2 = worldVertices[w + 2]; + cy2 = worldVertices[w + 3]; + x2 = worldVertices[w + 4]; + y2 = worldVertices[w + 5]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx; + dfy += ddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + lengths[i] = length; + x1 = x2; + y1 = y2; } - distance = nextDistance; - } - curve *= 6; - // Adjust t for constant speed using lengths of curves as weights. - t *= curveLengths(curve, worldVertices); - for (int i = 1;; i++) { - float length = lengths[i]; - if (t >= length) { - t = 1 - 0.1f * i + 0.1f * (t - length) / (lengths[i - 1] - length); - break; + // Determine curve containing position. + int curve; + position *= length; + length = lengths[0]; + if (position <= length) { + curve = 0; + position /= length; + } else { + for (curve = 1;; curve++) { + length = lengths[curve]; + if (position <= length) { + float prev = lengths[curve - 1]; + position = (position - prev) / (length - prev); + break; + } + } + curve *= 6; + } + + // Curve segment lengths. + x1 = worldVertices[curve]; + y1 = worldVertices[curve + 1]; + cx1 = worldVertices[curve + 2]; + cy1 = worldVertices[curve + 3]; + cx2 = worldVertices[curve + 4]; + cy2 = worldVertices[curve + 5]; + x2 = worldVertices[curve + 6]; + y2 = worldVertices[curve + 7]; + tmpx = (x1 - cx1 * 2 + cx2) * 0.03f; + tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; + dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f; + dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; + ddfx = tmpx * 2 + dddfx; + ddfy = tmpy * 2 + dddfy; + dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f; + dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; + length = (float)Math.sqrt(dfx * dfx + dfy * dfy); + lengths[0] = length; + for (int i = 1; i < 8; i++) { + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + lengths[i] = length; + } + dfx += ddfx; + dfy += ddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + lengths[8] = length; + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + length += (float)Math.sqrt(dfx * dfx + dfy * dfy); + lengths[9] = length; + + // Weight by segment length. + position *= length; + length = lengths[0]; + if (position <= length) + position = 0.1f * position / length; + else { + for (int i = 1;; i++) { + length = lengths[i]; + if (position <= length) { + float prev = lengths[i - 1]; + position = 0.1f * (i + (position - prev) / (length - prev)); + break; + } + } } } // Calculate point. - float x1 = worldVertices[curve], y1 = worldVertices[curve + 1]; - float cx1 = worldVertices[curve + 4], cy1 = worldVertices[curve + 5]; - float x2 = worldVertices[curve + 6], y2 = worldVertices[curve + 7]; - float cx2 = worldVertices[curve + 8], cy2 = worldVertices[curve + 9]; - float tt = t * t, ttt = tt * t, t3 = t * 3; - float x = (x1 + t * (-x1 * 3 + t * (3 * x1 - x1 * t))) + t * (3 * cx1 + t * (-6 * cx1 + cx1 * t3)) - + tt * (cx2 * 3 - cx2 * t3) + x2 * ttt; - float y = (y1 + t * (-y1 * 3 + t * (3 * y1 - y1 * t))) + t * (3 * cy1 + t * (-6 * cy1 + cy1 * t3)) - + tt * (cy2 * 3 - cy2 * t3) + y2 * ttt; - out.set(x, y); - } - - private float pathLengths (float[] worldVertices) { - float[] lengths = this.lengths; - float total = 0; - float x1 = worldVertices[0], y1 = worldVertices[1]; - for (int i = 0, w = 4, n = 4 + worldVerticesLength - 6; w < n; i++, w += 6) { - float cx1 = worldVertices[w], cy1 = worldVertices[w + 1]; - float x2 = worldVertices[w + 2], y2 = worldVertices[w + 3]; - float cx2 = worldVertices[w + 4], cy2 = worldVertices[w + 5]; - float tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f, tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; - float dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f, dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.09375f; - float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; - float dfx = (cx1 - x1) * 0.75f + tmpx + dddfx * 0.16666667f, dfy = (cy1 - y1) * 0.75f + tmpy + dddfy * 0.16666667f; - float length = (float)Math.sqrt(dfx * dfx + dfy * dfy); - dfx += ddfx; - dfy += ddfy; - ddfx += dddfx; - ddfy += dddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - dfx += ddfx; - dfy += ddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - dfx += ddfx + dddfx; - dfy += ddfy + dddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - total += length; - lengths[i] = length; - x1 = x2; - y1 = y2; - } - return total; - } - - private float curveLengths (int curve, float[] worldVertices) { - float x1 = worldVertices[curve], y1 = worldVertices[curve + 1]; - float cx1 = worldVertices[curve + 4], cy1 = worldVertices[curve + 5]; - float x2 = worldVertices[curve + 6], y2 = worldVertices[curve + 7]; - float cx2 = worldVertices[curve + 8], cy2 = worldVertices[curve + 9]; - float tmpx = (x1 - cx1 * 2 + cx2) * 0.03f, tmpy = (y1 - cy1 * 2 + cy2) * 0.03f; - float dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f, dddfy = ((cy1 - cy2) * 3 - y1 + y2) * 0.006f; - float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy; - float dfx = (cx1 - x1) * 0.3f + tmpx + dddfx * 0.16666667f, dfy = (cy1 - y1) * 0.3f + tmpy + dddfy * 0.16666667f; - float[] lengths = this.lengths; - lengths[10] = 0; - float total = 0; - for (int i = 9; i > 2; i--) { - total += (float)Math.sqrt(dfx * dfx + dfy * dfy); - lengths[i] = total; - dfx += ddfx; - dfy += ddfy; - ddfx += dddfx; - ddfy += dddfy; - } - total += (float)Math.sqrt(dfx * dfx + dfy * dfy); - lengths[2] = total; - dfx += ddfx; - dfy += ddfy; - total += (float)Math.sqrt(dfx * dfx + dfy * dfy); - lengths[1] = total; - dfx += ddfx + dddfx; - dfy += ddfy + dddfy; - total += (float)Math.sqrt(dfx * dfx + dfy * dfy); - lengths[0] = total; - return total; + float ttt = position * position * position, u = 1 - position; + float uuu = u * u * u, ut3 = u * position * 3, uut3 = u * ut3, utt3 = position * ut3; + out.set(x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt); } public Color getColor () { @@ -173,10 +209,18 @@ public class PathAttachment extends VertexAttachment { this.closed = closed; } + public boolean getConstantSpeed () { + return constantSpeed; + } + + public void setConstantSpeed (boolean constantSpeed) { + this.constantSpeed = constantSpeed; + } + public void setWorldVerticesLength (int worldVerticesLength) { super.setWorldVerticesLength(worldVerticesLength); // BOZO! - Don't reallocate for editor. worldVertices = new float[Math.max(2, worldVerticesLength)]; - lengths = new float[Math.max(11, worldVerticesLength >> 1)]; + lengths = new float[Math.max(10, worldVerticesLength / 6 - 1)]; } } diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java index 529d8fa84..2d2fc478f 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/attachments/VertexAttachment.java @@ -47,31 +47,45 @@ public class VertexAttachment extends Attachment { } protected void computeWorldVertices (Slot slot, float[] worldVertices) { + computeWorldVertices(slot, 0, worldVerticesLength, worldVertices, 0); + } + + /** Transforms local vertices to world coordinates. + * @param start The index of the first local vertex value to transform. Each vertex has 2 values, x and y. + * @param count The number of world vertex values to output. Must be <= {@link #getWorldVerticesLength()} - start. + * @param worldVertices The output world vertices. Must have a length >= offset + count. + * @param offset The worldVertices index to begin writing values. */ + protected void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) { + count += offset; Skeleton skeleton = slot.getSkeleton(); float x = skeleton.getX(), y = skeleton.getY(); FloatArray deformArray = slot.getAttachmentVertices(); float[] vertices = this.vertices; int[] bones = this.bones; if (bones == null) { - int verticesLength = vertices.length; if (deformArray.size > 0) vertices = deformArray.items; Bone bone = slot.getBone(); x += bone.getWorldX(); y += bone.getWorldY(); float a = bone.getA(), b = bone.getB(), c = bone.getC(), d = bone.getD(); - for (int v = 0; v < verticesLength; v += 2) { + for (int v = start, w = offset; w < count; v += 2, w += 2) { float vx = vertices[v], vy = vertices[v + 1]; - worldVertices[v] = vx * a + vy * b + x; - worldVertices[v + 1] = vx * c + vy * d + y; + worldVertices[w] = vx * a + vy * b + x; + worldVertices[w + 1] = vx * c + vy * d + y; } return; } + int v = 0, skip = 0; + for (int i = 0; i < start; i += 2) { + int n = bones[v]; + v += n + 1; + skip += n; + } Object[] skeletonBones = skeleton.getBones().items; if (deformArray.size == 0) { - for (int w = 0, v = 0, b = 0, n = bones.length; v < n; w += 2) { + for (int w = offset, b = skip * 3; w < count; w += 2) { float wx = x, wy = y; - int nn = bones[v++] + v; - for (; v < nn; v++, b += 3) { + for (int n = bones[v++] + v; v < n; v++, b += 3) { Bone bone = (Bone)skeletonBones[bones[v]]; float vx = vertices[b], vy = vertices[b + 1], weight = vertices[b + 2]; wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight; @@ -82,10 +96,9 @@ public class VertexAttachment extends Attachment { } } else { float[] deform = deformArray.items; - for (int w = 0, v = 0, b = 0, f = 0, n = bones.length; v < n; w += 2) { + for (int w = offset, b = skip * 3, f = skip << 1; w < count; w += 2) { float wx = x, wy = y; - int nn = bones[v++] + v; - for (; v < nn; v++, b += 3, f += 2) { + for (int n = bones[v++] + v; v < n; v++, b += 3, f += 2) { Bone bone = (Bone)skeletonBones[bones[v]]; float vx = vertices[b] + deform[f], vy = vertices[b + 1] + deform[f + 1], weight = vertices[b + 2]; wx += (vx * bone.getA() + vy * bone.getB() + bone.getWorldX()) * weight;