From c4810e23d68177a3e2eb736d70e01c2f6131ae3f Mon Sep 17 00:00:00 2001 From: NathanSweet Date: Sun, 29 May 2016 05:58:23 +0200 Subject: [PATCH] Path constraint optimization. --- .../spine/PathConstraint.java | 273 +++++++++--------- 1 file changed, 142 insertions(+), 131 deletions(-) diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java index 459148127..83a3c2601 100644 --- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java +++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/PathConstraint.java @@ -13,8 +13,7 @@ public class PathConstraint implements Updatable { final Array bones; Slot target; float position, rotateMix, translateMix, scaleMix; - final FloatArray lengths = new FloatArray(), positions = new FloatArray(); - final FloatArray worldVertices = new FloatArray(), temp = new FloatArray(); + final FloatArray lengths = new FloatArray(), positions = new FloatArray(), temp = new FloatArray(); public PathConstraint (PathConstraintData data, Skeleton skeleton) { this.data = data; @@ -53,24 +52,23 @@ public class PathConstraint implements Updatable { float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix; boolean translate = translateMix > 0, rotate = rotateMix > 0, scale = scaleMix > 0; - if (!translate && !rotate) return; + if (!translate && !rotate && !scale) return; PathAttachment path = (PathAttachment)attachment; FloatArray lengths = this.lengths; lengths.clear(); lengths.add(0); - positions.clear(); Array bones = this.bones; int boneCount = bones.size; if (boneCount == 1) { - computeWorldPositions(path, rotate); + float[] positions = computeWorldPositions(path, rotate); Bone bone = bones.first(); - bone.worldX += (positions.first() - bone.worldX) * translateMix; - bone.worldY += (positions.get(1) - bone.worldY) * translateMix; + bone.worldX += (positions[0] - bone.worldX) * translateMix; + bone.worldY += (positions[1] - bone.worldY) * translateMix; if (rotate) { float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - float r = positions.get(2) - atan2(c, a) + data.offsetRotation * degRad; + float r = positions[2] - atan2(c, a) + data.offsetRotation * degRad; if (r > PI) r -= PI2; else if (r < -PI) r += PI2; @@ -84,46 +82,48 @@ public class PathConstraint implements Updatable { return; } - for (int i = 0; i < boneCount; i++) - lengths.add(bones.get(i).data.length); - computeWorldPositions(path, false); + for (int i = 0; i < boneCount; i++) { + Bone bone = bones.get(i); + float length = bone.data.length; + float x = length * bone.a, y = length * bone.c; + lengths.add((float)Math.sqrt(x * x + y * y)); + } + float[] positions = computeWorldPositions(path, false); - float[] positions = this.positions.items; - float boneX = positions[0], boneY = positions[1]; + float boneX = positions[0], boneY = positions[1], offsetRotation = data.offsetRotation; for (int i = 0, p = 2; i < boneCount; i++, p += 2) { Bone bone = bones.get(i); - float x = positions[p], y = positions[p + 1]; - - if (scale) { - float dx = boneX - x, dy = boneY - y, d = (float)Math.sqrt(dx * dx + dy * dy); - // BOZO - Length not transformed by bone matrix. - float sx = bone.scaleX + (d / bone.data.length - bone.scaleX) * scaleMix; - bone.a *= sx; - bone.c *= sx; - } - bone.worldX += (boneX - bone.worldX) * translateMix; bone.worldY += (boneY - bone.worldY) * translateMix; - - float r = atan2(y - boneY, x - boneX) + data.offsetRotation * degRad; - if (data.offsetRotation != 0) { + float x = positions[p], y = positions[p + 1], dx = x - boneX, dy = y - boneY; + if (scale) { + float s = ((float)Math.sqrt(dx * dx + dy * dy) / lengths.get(i + 1) - 1) * scaleMix + 1; + bone.a *= s; + bone.c *= s; + } + if (!rotate) { boneX = x; boneY = y; } else { - // BOZO - Doesn't transform by bone matrix. - float cos = cos(r), sin = sin(r); - float length = bone.data.length, mix = rotateMix * (1 - scaleMix); - boneX = x + (length * cos + boneX - x) * mix; - boneY = y + (length * sin + boneY - y) * mix; - } - if (rotate) { float a = bone.a, b = bone.b, c = bone.c, d = bone.d; - r -= atan2(c, a); + float r = atan2(dy, dx) - atan2(c, a) + offsetRotation * degRad, cos, sin; + if (offsetRotation != 0) { + boneX = x; + boneY = y; + } else { // Mix between on path and at tip. + cos = cos(r); + sin = sin(r); + float length = bone.data.length; + boneX = x + (length * (cos * a - sin * c) - dx) * rotateMix; + boneY = y + (length * (sin * a + cos * c) - dy) * rotateMix; + } if (r > PI) r -= PI2; - else if (r < -PI) r += PI2; + else if (r < -PI) // + r += PI2; r *= rotateMix; - float cos = cos(r), sin = sin(r); + cos = cos(r); + sin = sin(r); bone.a = cos * a - sin * c; bone.b = cos * b - sin * d; bone.c = sin * a + cos * c; @@ -132,68 +132,71 @@ public class PathConstraint implements Updatable { } } - private void computeWorldPositions (PathAttachment path, boolean tangents) { - Slot slot = target; + private float[] computeWorldPositions (PathAttachment path, boolean tangents) { + Slot target = this.target; float position = this.position; - FloatArray out = positions; - FloatArray lengths = this.lengths; - - int verticesLength = path.getWorldVerticesLength(), curves = verticesLength / 6; + int lengthCount = lengths.size; + float[] lengths = this.lengths.items; + FloatArray positions = this.positions; + positions.clear(); boolean closed = path.getClosed(); - float[] vertices; + int verticesLength = path.getWorldVerticesLength(), curves = verticesLength / 6; + float[] temp; if (!path.getConstantSpeed()) { if (!closed) curves--; float pathLength = path.getLength(); - vertices = worldVertices.setSize(8); - for (int i = 0, n = lengths.size; i < n; i++) { - position += lengths.get(i) / pathLength; + temp = this.temp.setSize(8); + for (int i = 0; i < lengthCount; i++) { + position += lengths[i] / pathLength; if (closed) { position %= 1; if (position < 0) position += 1; } else if (position < 0 || position > 1) { - path.computeWorldVertices(slot, 0, 4, vertices, 0); - path.computeWorldVertices(slot, verticesLength - 4, 4, vertices, 4); - addOutsidePoint(position * pathLength, vertices, 8, pathLength, out, tangents); + path.computeWorldVertices(target, 0, 4, temp, 0); + path.computeWorldVertices(target, verticesLength - 4, 4, temp, 4); + addOutsidePosition(position * pathLength, temp, 0, 8, pathLength, positions, tangents); continue; } int curve = position < 1 ? (int)(curves * position) : curves - 1; if (closed && curve == curves - 1) { - path.computeWorldVertices(slot, verticesLength - 4, 4, vertices, 0); - path.computeWorldVertices(slot, 0, 4, vertices, 4); + path.computeWorldVertices(target, verticesLength - 4, 4, temp, 0); + path.computeWorldVertices(target, 0, 4, temp, 4); } else - path.computeWorldVertices(slot, curve * 6 + 2, 8, vertices, 0); - addCurvePoint(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], vertices[6], vertices[7], - (position - curve / (float)curves) * curves, tangents, out); + path.computeWorldVertices(target, curve * 6 + 2, 8, temp, 0); + addCurvePosition((position - curve / (float)curves) * curves, temp[0], temp[1], temp[2], temp[3], temp[4], temp[5], + temp[6], temp[7], positions, tangents); } - return; + return positions.items; } + // World vertices, verticesStart to verticesStart + verticesLength. + int verticesStart = 10 + curves; + temp = this.temp.setSize(verticesStart + verticesLength + 2); if (closed) { verticesLength += 2; - vertices = worldVertices.setSize(verticesLength); - path.computeWorldVertices(slot, 2, verticesLength - 4, vertices, 0); - path.computeWorldVertices(slot, 0, 2, vertices, verticesLength - 4); - vertices[verticesLength - 2] = vertices[0]; - vertices[verticesLength - 1] = vertices[1]; + int verticesEnd = verticesStart + verticesLength; + path.computeWorldVertices(target, 2, verticesLength - 4, temp, verticesStart); + path.computeWorldVertices(target, 0, 2, temp, verticesEnd - 4); + temp[verticesEnd - 2] = temp[verticesStart]; + temp[verticesEnd - 1] = temp[verticesStart + 1]; } else { + verticesStart--; verticesLength -= 4; - vertices = worldVertices.setSize(verticesLength); - path.computeWorldVertices(slot, 2, verticesLength, vertices, 0); + path.computeWorldVertices(target, 2, verticesLength, temp, verticesStart); } - // Curve lengths. - temp.setSize(10 + curves); // BOZO - Combine with worldVertices? - float[] temp = this.temp.items; - float pathLength = 0, x1 = vertices[0], y1 = vertices[1], cx1, cy1, cx2, cy2, x2, y2; + // Curve lengths, 10 to verticesStart. + float pathLength = 0; + float x1 = temp[verticesStart], y1 = temp[verticesStart + 1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0; float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy; - for (int i = 10, w = 2; w < verticesLength; i++, w += 6) { - cx1 = vertices[w]; - cy1 = vertices[w + 1]; - cx2 = vertices[w + 2]; - cy2 = vertices[w + 3]; - x2 = vertices[w + 4]; - y2 = vertices[w + 5]; + for (int i = 10, v = verticesStart + 2; i < verticesStart; i++, v += 6) { + cx1 = temp[v]; + cy1 = temp[v + 1]; + cx2 = temp[v + 2]; + cy2 = temp[v + 3]; + x2 = temp[v + 4]; + y2 = temp[v + 5]; tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f; tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f; dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f; @@ -220,15 +223,17 @@ public class PathConstraint implements Updatable { } position *= pathLength; - for (int i = 0, n = lengths.size; i < n; i++) { - position += lengths.get(i); + int lastCurve = 0; + float curveLength = 0; + for (int i = 0; i < lengthCount; i++) { + position += lengths[i]; float p = position; if (closed) { p %= pathLength; if (p < 0) p += pathLength; } else if (p < 0 || p > pathLength) { - addOutsidePoint(p, vertices, verticesLength, pathLength, out, tangents); + addOutsidePosition(p, temp, verticesStart, verticesLength, pathLength, positions, tangents); continue; } @@ -236,7 +241,7 @@ public class PathConstraint implements Updatable { int curve; float length = temp[10]; if (p <= length) { - curve = 0; + curve = verticesStart; p /= length; } else { for (curve = 11;; curve++) { @@ -247,47 +252,50 @@ public class PathConstraint implements Updatable { break; } } - curve = (curve - 10) * 6; + curve = verticesStart + (curve - 10) * 6; } - // Curve segment lengths. - x1 = vertices[curve]; - y1 = vertices[curve + 1]; - cx1 = vertices[curve + 2]; - cy1 = vertices[curve + 3]; - cx2 = vertices[curve + 4]; - cy2 = vertices[curve + 5]; - x2 = vertices[curve + 6]; - y2 = vertices[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); - temp[0] = length; - for (int ii = 1; ii < 8; ii++) { + // Curve segment lengths, 0 to 10. + if (curve != lastCurve) { + lastCurve = curve; + x1 = temp[curve]; + y1 = temp[curve + 1]; + cx1 = temp[curve + 2]; + cy1 = temp[curve + 3]; + cx2 = temp[curve + 4]; + cy2 = temp[curve + 5]; + x2 = temp[curve + 6]; + y2 = temp[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; + curveLength = (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[0] = curveLength; + for (int ii = 1; ii < 8; ii++) { + dfx += ddfx; + dfy += ddfy; + ddfx += dddfx; + ddfy += dddfy; + curveLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[ii] = curveLength; + } dfx += ddfx; dfy += ddfy; - ddfx += dddfx; - ddfy += dddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - temp[ii] = length; + curveLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[8] = curveLength; + dfx += ddfx + dddfx; + dfy += ddfy + dddfy; + curveLength += (float)Math.sqrt(dfx * dfx + dfy * dfy); + temp[9] = curveLength; } - dfx += ddfx; - dfy += ddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - temp[8] = length; - dfx += ddfx + dddfx; - dfy += ddfy + dddfy; - length += (float)Math.sqrt(dfx * dfx + dfy * dfy); - temp[9] = length; // Weight by segment length. - p *= length; + p *= curveLength; length = temp[0]; if (p <= length) p = 0.1f * p / length; @@ -302,36 +310,39 @@ public class PathConstraint implements Updatable { } } - addCurvePoint(x1, y1, cx1, cy1, cx2, cy2, x2, y2, p, tangents, out); + addCurvePosition(p, x1, y1, cx1, cy1, cx2, cy2, x2, y2, positions, tangents); } + + return positions.items; } - private void addOutsidePoint (float position, float[] vertices, int verticesLength, float pathLength, FloatArray out, - boolean tangents) { + private void addOutsidePosition (float p, float[] temp, int verticesStart, int verticesLength, float pathLength, + FloatArray out, boolean tangents) { float x1, y1, x2, y2; - if (position < 0) { - x1 = vertices[0]; - y1 = vertices[1]; - x2 = vertices[2] - x1; - y2 = vertices[3] - y1; + if (p < 0) { + x1 = temp[verticesStart]; + y1 = temp[verticesStart + 1]; + x2 = temp[verticesStart + 2] - x1; + y2 = temp[verticesStart + 3] - y1; } else { - x1 = vertices[verticesLength - 2]; - y1 = vertices[verticesLength - 1]; - x2 = x1 - vertices[verticesLength - 4]; - y2 = y1 - vertices[verticesLength - 3]; - position -= pathLength; + verticesStart += verticesLength; + x1 = temp[verticesStart - 2]; + y1 = temp[verticesStart - 1]; + x2 = x1 - temp[verticesStart - 4]; + y2 = y1 - temp[verticesStart - 3]; + p -= pathLength; } float r = atan2(y2, x2); - out.add(x1 + position * cos(r)); - out.add(y1 + position * sin(r)); + out.add(x1 + p * cos(r)); + out.add(y1 + p * sin(r)); if (tangents) out.add(r + PI); } - private void addCurvePoint (float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, float position, - boolean tangents, FloatArray out) { - if (position == 0) position = 0.0001f; - float tt = position * position, ttt = tt * position, u = 1 - position, uu = u * u, uuu = uu * u; - float ut = u * position, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * position; + private void addCurvePosition (float p, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2, + FloatArray out, boolean tangents) { + if (p == 0) p = 0.0001f; + float tt = p * p, ttt = tt * p, u = 1 - p, uu = u * u, uuu = uu * u; + float ut = u * p, ut3 = ut * 3, uut3 = u * ut3, utt3 = ut3 * p; float x = x1 * uuu + cx1 * uut3 + cx2 * utt3 + x2 * ttt, y = y1 * uuu + cy1 * uut3 + cy2 * utt3 + y2 * ttt; out.add(x); out.add(y);