mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
Much better paths.
This commit is contained in:
parent
ee29123dfa
commit
a84e3379d8
@ -154,7 +154,7 @@ public class Animation {
|
||||
/** Base class for frames that use an interpolation bezier curve. */
|
||||
abstract static public class CurveTimeline implements Timeline {
|
||||
static public final float LINEAR = 0, STEPPED = 1, BEZIER = 2;
|
||||
static private final int BEZIER_SEGMENTS = 10, BEZIER_SIZE = BEZIER_SEGMENTS * 2 - 1;
|
||||
static private final int BEZIER_SIZE = 10 * 2 - 1;
|
||||
|
||||
private final float[] curves; // type, x, y, ...
|
||||
|
||||
@ -188,12 +188,10 @@ public class Animation {
|
||||
* cx1 and cx2 are from 0 to 1, representing the percent of time between the two keyframes. cy1 and cy2 are the percent of
|
||||
* the difference between the keyframe's values. */
|
||||
public void setCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2) {
|
||||
float subdiv1 = 1f / BEZIER_SEGMENTS, subdiv2 = subdiv1 * subdiv1, subdiv3 = subdiv2 * subdiv1;
|
||||
float pre1 = 3 * subdiv1, pre2 = 3 * subdiv2, pre4 = 6 * subdiv2, pre5 = 6 * subdiv3;
|
||||
float tmp1x = -cx1 * 2 + cx2, tmp1y = -cy1 * 2 + cy2, tmp2x = (cx1 - cx2) * 3 + 1, tmp2y = (cy1 - cy2) * 3 + 1;
|
||||
float dfx = cx1 * pre1 + tmp1x * pre2 + tmp2x * subdiv3, dfy = cy1 * pre1 + tmp1y * pre2 + tmp2y * subdiv3;
|
||||
float ddfx = tmp1x * pre4 + tmp2x * pre5, ddfy = tmp1y * pre4 + tmp2y * pre5;
|
||||
float dddfx = tmp2x * pre5, dddfy = tmp2y * pre5;
|
||||
float tmpx = (-cx1 * 2 + cx2) * 0.03f, tmpy = (-cy1 * 2 + cy2) * 0.03f;
|
||||
float dddfx = ((cx1 - cx2) * 3 + 1) * 0.006f, dddfy = ((cy1 - cy2) * 3 + 1) * 0.006f;
|
||||
float ddfx = tmpx * 2 + dddfx, ddfy = tmpy * 2 + dddfy;
|
||||
float dfx = cx1 * 0.3f + tmpx + dddfx * 0.16666667f, dfy = cy1 * 0.3f + tmpy + dddfy * 0.16666667f;
|
||||
|
||||
int i = frameIndex * BEZIER_SIZE;
|
||||
float[] curves = this.curves;
|
||||
|
||||
@ -10,6 +10,7 @@ public class PathConstraint implements Updatable {
|
||||
Bone bone;
|
||||
Slot target;
|
||||
float position, rotateMix, translateMix;
|
||||
final Vector2 temp = new Vector2();
|
||||
|
||||
public PathConstraint (PathConstraintData data, Skeleton skeleton) {
|
||||
this.data = data;
|
||||
@ -41,7 +42,7 @@ public class PathConstraint implements Updatable {
|
||||
|
||||
float translateMix = this.translateMix;
|
||||
if (translateMix > 0) {
|
||||
Vector2 temp = path.computeWorldPosition(target, position);
|
||||
path.computeWorldPosition(target, position, temp);
|
||||
bone.worldX += (temp.x - bone.worldX) * translateMix;
|
||||
bone.worldY += (temp.y - bone.worldY) * translateMix;
|
||||
}
|
||||
|
||||
@ -38,9 +38,8 @@ import com.esotericsoftware.spine.Slot;
|
||||
public class PathAttachment extends VertexAttachment {
|
||||
// Nonessential.
|
||||
final Color color = new Color(1, 0.5f, 0, 1);
|
||||
final Vector2 temp = new Vector2();
|
||||
|
||||
float[] worldVertices, lengths;
|
||||
int totalLength;
|
||||
boolean closed;
|
||||
|
||||
public PathAttachment (String name) {
|
||||
@ -51,98 +50,115 @@ public class PathAttachment extends VertexAttachment {
|
||||
super.computeWorldVertices(slot, worldVertices);
|
||||
}
|
||||
|
||||
public Vector2 computeWorldPosition (Slot slot, float position) {
|
||||
public void computeWorldPosition (Slot slot, float position, Vector2 out) {
|
||||
// BOZO - Remove check?
|
||||
if (worldVerticesLength < 12) return;
|
||||
|
||||
float[] worldVertices = this.worldVertices;
|
||||
super.computeWorldVertices(slot, worldVertices);
|
||||
|
||||
// Determine curve containing position.
|
||||
float pathLength = pathLengths(worldVertices);
|
||||
float[] lengths = this.lengths;
|
||||
float target = pathLength * position, distance = 0, t = 0;
|
||||
int curve = 0;
|
||||
float t = 0;
|
||||
if (closed) {
|
||||
// BOZO - closed boolean used to turn off fancy calculations for now.
|
||||
int curves = (worldVerticesLength >> 2) - 1;
|
||||
curve = position < 1 ? (int)(curves * position) : curves - 1;
|
||||
t = (position - curve / (float)curves) * curves;
|
||||
} else {
|
||||
// Compute lengths of all curves.
|
||||
totalLength = 0;
|
||||
float[] lengths = this.lengths;
|
||||
float x1 = worldVertices[0], y1 = worldVertices[1];
|
||||
float cx1 = x1 + (x1 - worldVertices[2]), cy1 = y1 + (y1 - worldVertices[3]);
|
||||
for (int i = 0, w = 4, n = worldVerticesLength; w < n; i += 6, w += 4) {
|
||||
float x2 = worldVertices[w], y2 = worldVertices[w + 1];
|
||||
float cx2 = worldVertices[w + 2], cy2 = worldVertices[w + 3];
|
||||
addLengths(i, x1, y1, cx1, cy1, cx2, cy2, x2, y2);
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
cx1 = x2 + (x2 - cx2);
|
||||
cy1 = y2 + (y2 - cy2);
|
||||
for (;; curve++) {
|
||||
float length = lengths[curve];
|
||||
float nextDistance = distance + length;
|
||||
if (nextDistance >= target) {
|
||||
t = (target - distance) / length;
|
||||
break;
|
||||
}
|
||||
distance = nextDistance;
|
||||
}
|
||||
curve *= 6;
|
||||
|
||||
// Determine curve containing position.
|
||||
float target = totalLength * position, distance = 0;
|
||||
for (int i = 5;; i += 6) {
|
||||
float curveLength = lengths[i];
|
||||
if (distance + curveLength > target) {
|
||||
curve = i / 6;
|
||||
t = (target - distance) / curveLength;
|
||||
break;
|
||||
}
|
||||
distance += curveLength;
|
||||
}
|
||||
|
||||
// Adjust t for constant speed using lengths of curves as weights.
|
||||
for (int i = curve * 6, n = i + 5; i < n; i++) {
|
||||
float bezierPercent = lengths[i];
|
||||
if (t > bezierPercent) {
|
||||
float linearPercent = 0.75f - 0.25f * (i - curve * 6 - 1);
|
||||
float bezierPercentNext = lengths[i - 1];
|
||||
t = linearPercent + 0.25f * ((t - bezierPercent) / (bezierPercentNext - bezierPercent));
|
||||
break;
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate bezier point.
|
||||
int i = curve << 2;
|
||||
float x1 = worldVertices[i], y1 = worldVertices[i + 1];
|
||||
float cx1 = x1 + (x1 - worldVertices[i + 2]), cy1 = y1 + (y1 - worldVertices[i + 3]);
|
||||
float x2 = worldVertices[i + 4], y2 = worldVertices[i + 5];
|
||||
float cx2 = worldVertices[i + 6], cy2 = worldVertices[i + 7];
|
||||
// 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;
|
||||
return temp.set(x, y);
|
||||
out.set(x, y);
|
||||
}
|
||||
|
||||
private void addLengths (int index, float x1, float y1, float cx1, float cy1, float cx2, float cy2, float x2, float y2) {
|
||||
float tmp1x = x1 - cx1 * 2 + cx2, tmp1y = y1 - cy1 * 2 + cy2;
|
||||
float tmp2x = (cx1 - cx2) * 3 - x1 + x2, tmp2y = (cy1 - cy2) * 3 - y1 + y2;
|
||||
float dfx = (cx1 - x1) * 0.75f + tmp1x * 0.1875f + tmp2x * 0.015625f;
|
||||
float dfy = (cy1 - y1) * 0.75f + tmp1y * 0.1875f + tmp2y * 0.015625f;
|
||||
float ddfx = tmp1x * 0.375f + tmp2x * 0.09375f, ddfy = tmp1y * 0.375f + tmp2y * 0.09375f;
|
||||
float dddfx = tmp2x * 0.09375f, dddfy = tmp2y * 0.09375f;
|
||||
float length0 = (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
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;
|
||||
ddfx += dddfx;
|
||||
ddfy += dddfy;
|
||||
float length1 = length0 + (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
dfx += ddfx;
|
||||
dfy += ddfy;
|
||||
float length2 = length1 + (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
total += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
lengths[1] = total;
|
||||
dfx += ddfx + dddfx;
|
||||
dfy += ddfy + dddfy;
|
||||
float total = length2 + (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
totalLength += total;
|
||||
float[] lengths = this.lengths;
|
||||
lengths[index] = 1;
|
||||
lengths[index + 1] = length2 / total;
|
||||
lengths[index + 2] = length1 / total;
|
||||
lengths[index + 3] = length0 / total;
|
||||
lengths[index + 4] = 0;
|
||||
lengths[index + 5] = total;
|
||||
total += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||
lengths[0] = total;
|
||||
return total;
|
||||
}
|
||||
|
||||
public Color getColor () {
|
||||
@ -159,7 +175,8 @@ public class PathAttachment extends VertexAttachment {
|
||||
|
||||
public void setWorldVerticesLength (int worldVerticesLength) {
|
||||
super.setWorldVerticesLength(worldVerticesLength);
|
||||
worldVertices = new float[worldVerticesLength];
|
||||
lengths = new float[(worldVerticesLength >> 2) * 6];
|
||||
// BOZO! - Don't reallocate for editor.
|
||||
worldVertices = new float[Math.max(2, worldVerticesLength)];
|
||||
lengths = new float[Math.max(11, worldVerticesLength >> 1)];
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +99,7 @@ public class VertexAttachment extends Attachment {
|
||||
|
||||
/** Returns true if a deform originally applied to the specified attachment should be applied to this attachment. */
|
||||
public boolean applyDeform (VertexAttachment sourceAttachment) {
|
||||
return true;
|
||||
return this == sourceAttachment;
|
||||
}
|
||||
|
||||
/** @return May be null if this attachment has no weights. */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user