Improved vertex attachment, paths.

This commit is contained in:
NathanSweet 2016-05-25 01:16:37 +02:00
parent c976f8038b
commit c406e44785
4 changed files with 169 additions and 112 deletions

View File

@ -46,7 +46,7 @@ public class Skeleton {
final Array<IkConstraint> ikConstraints;
final Array<TransformConstraint> transformConstraints;
final Array<PathConstraint> pathConstraints;
private final Array<Updatable> updateCache = new Array();
final Array<Updatable> updateCache = new Array();
Skin skin;
final Color color;
float time;

View File

@ -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 () {

View File

@ -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)];
}
}

View File

@ -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;