mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
Scale mix for path constraints.
This commit is contained in:
parent
05b3e66dc0
commit
cdbc878f31
@ -874,20 +874,23 @@ public class Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public class PathConstraintTimeline extends CurveTimeline {
|
static public class PathConstraintTimeline extends CurveTimeline {
|
||||||
static private final int PREV_TIME = -4;
|
static private final int PREV_TIME = -5;
|
||||||
static private final int PREV_POSITION = -3;
|
static private final int PREV_POSITION = -4;
|
||||||
static private final int PREV_ROTATE_MIX = -2;
|
static private final int PREV_ROTATE_MIX = -3;
|
||||||
static private final int PREV_TRANSLATE_MIX = -1;
|
static private final int PREV_TRANSLATE_MIX = -2;
|
||||||
|
static private final int PREV_SCALE_MIX = -1;
|
||||||
static private final int POSITION = 1;
|
static private final int POSITION = 1;
|
||||||
static private final int ROTATE_MIX = 2;
|
static private final int ROTATE_MIX = 2;
|
||||||
static private final int TRANSLATE_MIX = 3;
|
static private final int TRANSLATE_MIX = 3;
|
||||||
|
static private final int SCALE_MIX = 4;
|
||||||
|
|
||||||
int pathConstraintIndex;
|
int pathConstraintIndex;
|
||||||
|
|
||||||
private final float[] frames; // time, rotate mix, translate mix, scale mix, shear mix, ...
|
private final float[] frames; // time, rotate mix, translate mix, scale mix, shear mix, ...
|
||||||
|
|
||||||
public PathConstraintTimeline (int frameCount) {
|
public PathConstraintTimeline (int frameCount) {
|
||||||
super(frameCount);
|
super(frameCount);
|
||||||
frames = new float[frameCount * 4];
|
frames = new float[frameCount * 5];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setPathConstraintIndex (int index) {
|
public void setPathConstraintIndex (int index) {
|
||||||
@ -903,12 +906,13 @@ public class Animation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the time, position, and mixes of the specified keyframe. */
|
/** Sets the time, position, and mixes of the specified keyframe. */
|
||||||
public void setFrame (int frameIndex, float time, float position, float rotateMix, float translateMix) {
|
public void setFrame (int frameIndex, float time, float position, float rotateMix, float translateMix, float scaleMix) {
|
||||||
frameIndex *= 4;
|
frameIndex *= 5;
|
||||||
frames[frameIndex] = time;
|
frames[frameIndex] = time;
|
||||||
frames[frameIndex + 1] = position;
|
frames[frameIndex + 1] = position;
|
||||||
frames[frameIndex + 2] = rotateMix;
|
frames[frameIndex + 2] = rotateMix;
|
||||||
frames[frameIndex + 3] = translateMix;
|
frames[frameIndex + 3] = translateMix;
|
||||||
|
frames[frameIndex + 4] = scaleMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha) {
|
||||||
@ -917,27 +921,30 @@ public class Animation {
|
|||||||
|
|
||||||
PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex);
|
PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex);
|
||||||
|
|
||||||
if (time >= frames[frames.length - 4]) { // Time is after last frame.
|
if (time >= frames[frames.length - 5]) { // Time is after last frame.
|
||||||
int i = frames.length - 1;
|
int i = frames.length - 1;
|
||||||
constraint.position += (frames[i - 2] - constraint.position) * alpha;
|
constraint.position += (frames[i - 3] - constraint.position) * alpha;
|
||||||
constraint.rotateMix += (frames[i - 1] - constraint.rotateMix) * alpha;
|
constraint.rotateMix += (frames[i - 2] - constraint.rotateMix) * alpha;
|
||||||
constraint.translateMix += (frames[i] - constraint.translateMix) * alpha;
|
constraint.translateMix += (frames[i - 1] - constraint.translateMix) * alpha;
|
||||||
|
constraint.scaleMix += (frames[i] - constraint.scaleMix) * alpha;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Interpolate between the previous frame and the current frame.
|
// Interpolate between the previous frame and the current frame.
|
||||||
int frame = binarySearch(frames, time, 4);
|
int frame = binarySearch(frames, time, 5);
|
||||||
float frameTime = frames[frame];
|
float frameTime = frames[frame];
|
||||||
float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1);
|
float percent = MathUtils.clamp(1 - (time - frameTime) / (frames[frame + PREV_TIME] - frameTime), 0, 1);
|
||||||
percent = getCurvePercent(frame / 4 - 1, percent);
|
percent = getCurvePercent(frame / 5 - 1, percent);
|
||||||
|
|
||||||
float position = frames[frame + PREV_POSITION];
|
float position = frames[frame + PREV_POSITION];
|
||||||
float rotate = frames[frame + PREV_ROTATE_MIX];
|
float rotate = frames[frame + PREV_ROTATE_MIX];
|
||||||
float translate = frames[frame + PREV_TRANSLATE_MIX];
|
float translate = frames[frame + PREV_TRANSLATE_MIX];
|
||||||
|
float scale = frames[frame + PREV_SCALE_MIX];
|
||||||
constraint.position += (position + (frames[frame + POSITION] - position) * percent - constraint.position) * alpha;
|
constraint.position += (position + (frames[frame + POSITION] - position) * percent - constraint.position) * alpha;
|
||||||
constraint.rotateMix += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha;
|
constraint.rotateMix += (rotate + (frames[frame + ROTATE_MIX] - rotate) * percent - constraint.rotateMix) * alpha;
|
||||||
constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix)
|
constraint.translateMix += (translate + (frames[frame + TRANSLATE_MIX] - translate) * percent - constraint.translateMix)
|
||||||
* alpha;
|
* alpha;
|
||||||
|
constraint.scaleMix += (scale + (frames[frame + SCALE_MIX] - scale) * percent - constraint.scaleMix) * alpha;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,6 @@ package com.esotericsoftware.spine;
|
|||||||
|
|
||||||
import static com.badlogic.gdx.math.MathUtils.*;
|
import static com.badlogic.gdx.math.MathUtils.*;
|
||||||
|
|
||||||
import com.badlogic.gdx.math.Vector2;
|
|
||||||
import com.badlogic.gdx.utils.Array;
|
import com.badlogic.gdx.utils.Array;
|
||||||
import com.badlogic.gdx.utils.FloatArray;
|
import com.badlogic.gdx.utils.FloatArray;
|
||||||
import com.esotericsoftware.spine.attachments.Attachment;
|
import com.esotericsoftware.spine.attachments.Attachment;
|
||||||
@ -13,15 +12,16 @@ public class PathConstraint implements Updatable {
|
|||||||
final PathConstraintData data;
|
final PathConstraintData data;
|
||||||
final Array<Bone> bones;
|
final Array<Bone> bones;
|
||||||
Slot target;
|
Slot target;
|
||||||
float position, rotateMix, translateMix;
|
float position, rotateMix, translateMix, scaleMix;
|
||||||
final FloatArray lengths = new FloatArray(), positions = new FloatArray();
|
final FloatArray lengths = new FloatArray(), positions = new FloatArray();
|
||||||
final Vector2 temp = new Vector2();
|
final FloatArray worldVertices = new FloatArray(), temp = new FloatArray();
|
||||||
|
|
||||||
public PathConstraint (PathConstraintData data, Skeleton skeleton) {
|
public PathConstraint (PathConstraintData data, Skeleton skeleton) {
|
||||||
this.data = data;
|
this.data = data;
|
||||||
position = data.position;
|
position = data.position;
|
||||||
rotateMix = data.rotateMix;
|
rotateMix = data.rotateMix;
|
||||||
translateMix = data.translateMix;
|
translateMix = data.translateMix;
|
||||||
|
scaleMix = data.scaleMix;
|
||||||
|
|
||||||
bones = new Array(data.bones.size);
|
bones = new Array(data.bones.size);
|
||||||
for (BoneData boneData : data.bones)
|
for (BoneData boneData : data.bones)
|
||||||
@ -40,6 +40,7 @@ public class PathConstraint implements Updatable {
|
|||||||
position = constraint.position;
|
position = constraint.position;
|
||||||
rotateMix = constraint.rotateMix;
|
rotateMix = constraint.rotateMix;
|
||||||
translateMix = constraint.translateMix;
|
translateMix = constraint.translateMix;
|
||||||
|
scaleMix = constraint.scaleMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply () {
|
public void apply () {
|
||||||
@ -50,12 +51,12 @@ public class PathConstraint implements Updatable {
|
|||||||
Attachment attachment = target.getAttachment();
|
Attachment attachment = target.getAttachment();
|
||||||
if (!(attachment instanceof PathAttachment)) return;
|
if (!(attachment instanceof PathAttachment)) return;
|
||||||
|
|
||||||
float translateMix = this.translateMix, rotateMix = this.rotateMix;
|
float rotateMix = this.rotateMix, translateMix = this.translateMix, scaleMix = this.scaleMix;
|
||||||
boolean translate = translateMix > 0, rotate = rotateMix > 0;
|
boolean translate = translateMix > 0, rotate = rotateMix > 0, scale = scaleMix > 0;
|
||||||
if (!translate && !rotate) return;
|
if (!translate && !rotate) return;
|
||||||
|
|
||||||
PathAttachment path = (PathAttachment)attachment;
|
PathAttachment path = (PathAttachment)attachment;
|
||||||
FloatArray lengths = this.lengths, positions = this.positions;
|
FloatArray lengths = this.lengths;
|
||||||
lengths.clear();
|
lengths.clear();
|
||||||
lengths.add(0);
|
lengths.add(0);
|
||||||
positions.clear();
|
positions.clear();
|
||||||
@ -63,7 +64,7 @@ public class PathConstraint implements Updatable {
|
|||||||
Array<Bone> bones = this.bones;
|
Array<Bone> bones = this.bones;
|
||||||
int boneCount = bones.size;
|
int boneCount = bones.size;
|
||||||
if (boneCount == 1) {
|
if (boneCount == 1) {
|
||||||
path.computeWorldPositions(target, position, lengths, positions, rotate);
|
computeWorldPositions(path, rotate);
|
||||||
Bone bone = bones.first();
|
Bone bone = bones.first();
|
||||||
bone.worldX += (positions.first() - bone.worldX) * translateMix;
|
bone.worldX += (positions.first() - bone.worldX) * translateMix;
|
||||||
bone.worldY += (positions.get(1) - bone.worldY) * translateMix;
|
bone.worldY += (positions.get(1) - bone.worldY) * translateMix;
|
||||||
@ -85,22 +86,39 @@ public class PathConstraint implements Updatable {
|
|||||||
|
|
||||||
for (int i = 0; i < boneCount; i++)
|
for (int i = 0; i < boneCount; i++)
|
||||||
lengths.add(bones.get(i).data.length);
|
lengths.add(bones.get(i).data.length);
|
||||||
path.computeWorldPositions(target, position, lengths, positions, false);
|
computeWorldPositions(path, false);
|
||||||
|
|
||||||
Vector2 temp = this.temp;
|
float[] positions = this.positions.items;
|
||||||
float boneX = positions.first(), boneY = positions.get(1);
|
float boneX = positions[0], boneY = positions[1];
|
||||||
for (int i = 0, p = 2; i < boneCount; i++, p += 2) {
|
for (int i = 0, p = 2; i < boneCount; i++, p += 2) {
|
||||||
Bone bone = bones.get(i);
|
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.worldX += (boneX - bone.worldX) * translateMix;
|
||||||
bone.worldY += (boneY - bone.worldY) * translateMix;
|
bone.worldY += (boneY - bone.worldY) * translateMix;
|
||||||
|
|
||||||
float x = positions.get(p), y = positions.get(p + 1);
|
float r = atan2(y - boneY, x - boneX) + data.offsetRotation * degRad;
|
||||||
|
if (data.offsetRotation != 0) {
|
||||||
|
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) {
|
if (rotate) {
|
||||||
// Scale.
|
|
||||||
// float dist = (float)Math.sqrt((boneX - x) * (boneX - x) + (boneY - y) * (boneY - y));
|
|
||||||
// bone.scaleX = dist / bone.data.length;
|
|
||||||
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
float a = bone.a, b = bone.b, c = bone.c, d = bone.d;
|
||||||
float r = atan2(y - boneY, x - boneX) - atan2(c, a) + data.offsetRotation * degRad;
|
r -= atan2(c, a);
|
||||||
if (r > PI)
|
if (r > PI)
|
||||||
r -= PI2;
|
r -= PI2;
|
||||||
else if (r < -PI) r += PI2;
|
else if (r < -PI) r += PI2;
|
||||||
@ -110,22 +128,214 @@ public class PathConstraint implements Updatable {
|
|||||||
bone.b = cos * b - sin * d;
|
bone.b = cos * b - sin * d;
|
||||||
bone.c = sin * a + cos * c;
|
bone.c = sin * a + cos * c;
|
||||||
bone.d = sin * b + cos * d;
|
bone.d = sin * b + cos * d;
|
||||||
if (data.offsetRotation == 0 && rotateMix == 1) {
|
}
|
||||||
// Place at tip.
|
}
|
||||||
bone.localToWorld(temp.set(bone.data.length, 0));
|
}
|
||||||
boneX = temp.x;
|
|
||||||
boneY = temp.y;
|
private void computeWorldPositions (PathAttachment path, boolean tangents) {
|
||||||
|
Slot slot = target;
|
||||||
|
float position = this.position;
|
||||||
|
FloatArray out = positions;
|
||||||
|
FloatArray lengths = this.lengths;
|
||||||
|
|
||||||
|
int verticesLength = path.getWorldVerticesLength(), curves = verticesLength / 6;
|
||||||
|
boolean closed = path.getClosed();
|
||||||
|
float[] vertices;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
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);
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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];
|
||||||
} else {
|
} else {
|
||||||
// Place on path.
|
verticesLength -= 4;
|
||||||
boneX = x;
|
vertices = worldVertices.setSize(verticesLength);
|
||||||
boneY = y;
|
path.computeWorldVertices(slot, 2, verticesLength, vertices, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
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];
|
||||||
|
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;
|
||||||
|
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
dfx += ddfx;
|
||||||
|
dfy += ddfy;
|
||||||
|
ddfx += dddfx;
|
||||||
|
ddfy += dddfy;
|
||||||
|
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
dfx += ddfx;
|
||||||
|
dfy += ddfy;
|
||||||
|
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
dfx += ddfx + dddfx;
|
||||||
|
dfy += ddfy + dddfy;
|
||||||
|
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
temp[i] = pathLength;
|
||||||
|
x1 = x2;
|
||||||
|
y1 = y2;
|
||||||
|
}
|
||||||
|
position *= pathLength;
|
||||||
|
|
||||||
|
for (int i = 0, n = lengths.size; i < n; i++) {
|
||||||
|
position += lengths.get(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);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine curve containing position.
|
||||||
|
int curve;
|
||||||
|
float length = temp[10];
|
||||||
|
if (p <= length) {
|
||||||
|
curve = 0;
|
||||||
|
p /= length;
|
||||||
} else {
|
} else {
|
||||||
// Place on path.
|
for (curve = 11;; curve++) {
|
||||||
boneX = x;
|
length = temp[curve];
|
||||||
boneY = y;
|
if (p <= length) {
|
||||||
|
float prev = temp[curve - 1];
|
||||||
|
p = (p - prev) / (length - prev);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
curve = (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++) {
|
||||||
|
dfx += ddfx;
|
||||||
|
dfy += ddfy;
|
||||||
|
ddfx += dddfx;
|
||||||
|
ddfy += dddfy;
|
||||||
|
length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
||||||
|
temp[ii] = length;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
length = temp[0];
|
||||||
|
if (p <= length)
|
||||||
|
p = 0.1f * p / length;
|
||||||
|
else {
|
||||||
|
for (int ii = 1;; ii++) {
|
||||||
|
length = temp[ii];
|
||||||
|
if (p <= length) {
|
||||||
|
float prev = temp[ii - 1];
|
||||||
|
p = 0.1f * (ii + (p - prev) / (length - prev));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addCurvePoint(x1, y1, cx1, cy1, cx2, cy2, x2, y2, p, tangents, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addOutsidePoint (float position, float[] vertices, 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;
|
||||||
|
} else {
|
||||||
|
x1 = vertices[verticesLength - 2];
|
||||||
|
y1 = vertices[verticesLength - 1];
|
||||||
|
x2 = x1 - vertices[verticesLength - 4];
|
||||||
|
y2 = y1 - vertices[verticesLength - 3];
|
||||||
|
position -= pathLength;
|
||||||
|
}
|
||||||
|
float r = atan2(y2, x2);
|
||||||
|
out.add(x1 + position * cos(r));
|
||||||
|
out.add(y1 + position * 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;
|
||||||
|
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);
|
||||||
|
if (tangents) out.add(atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public float getPosition () {
|
public float getPosition () {
|
||||||
@ -152,6 +362,14 @@ public class PathConstraint implements Updatable {
|
|||||||
this.translateMix = translateMix;
|
this.translateMix = translateMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getScaleMix () {
|
||||||
|
return scaleMix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScaleMix (float scaleMix) {
|
||||||
|
this.scaleMix = scaleMix;
|
||||||
|
}
|
||||||
|
|
||||||
public Array<Bone> getBones () {
|
public Array<Bone> getBones () {
|
||||||
return bones;
|
return bones;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,7 @@ public class PathConstraintData {
|
|||||||
final String name;
|
final String name;
|
||||||
final Array<BoneData> bones = new Array();
|
final Array<BoneData> bones = new Array();
|
||||||
SlotData target;
|
SlotData target;
|
||||||
float position, rotateMix, translateMix;
|
float position, rotateMix, translateMix, scaleMix;
|
||||||
float offsetRotation;
|
float offsetRotation;
|
||||||
|
|
||||||
public PathConstraintData (String name) {
|
public PathConstraintData (String name) {
|
||||||
@ -50,6 +50,14 @@ public class PathConstraintData {
|
|||||||
this.translateMix = translateMix;
|
this.translateMix = translateMix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getScaleMix () {
|
||||||
|
return scaleMix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScaleMix (float scaleMix) {
|
||||||
|
this.scaleMix = scaleMix;
|
||||||
|
}
|
||||||
|
|
||||||
public float getOffsetRotation () {
|
public float getOffsetRotation () {
|
||||||
return offsetRotation;
|
return offsetRotation;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -227,6 +227,7 @@ public class Skeleton {
|
|||||||
constraint.position = data.position;
|
constraint.position = data.position;
|
||||||
constraint.rotateMix = data.rotateMix;
|
constraint.rotateMix = data.rotateMix;
|
||||||
constraint.translateMix = data.translateMix;
|
constraint.translateMix = data.translateMix;
|
||||||
|
constraint.scaleMix = data.scaleMix;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -217,6 +217,7 @@ public class SkeletonBinary {
|
|||||||
data.position = input.readFloat();
|
data.position = input.readFloat();
|
||||||
data.rotateMix = input.readFloat();
|
data.rotateMix = input.readFloat();
|
||||||
data.translateMix = input.readFloat();
|
data.translateMix = input.readFloat();
|
||||||
|
data.scaleMix = input.readFloat();
|
||||||
skeletonData.pathConstraints.add(data);
|
skeletonData.pathConstraints.add(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -588,11 +589,12 @@ public class SkeletonBinary {
|
|||||||
PathConstraintTimeline timeline = new PathConstraintTimeline(frameCount);
|
PathConstraintTimeline timeline = new PathConstraintTimeline(frameCount);
|
||||||
timeline.pathConstraintIndex = index;
|
timeline.pathConstraintIndex = index;
|
||||||
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
|
||||||
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat());
|
timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readFloat(), input.readFloat(),
|
||||||
|
input.readFloat());
|
||||||
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
|
||||||
}
|
}
|
||||||
timelines.add(timeline);
|
timelines.add(timeline);
|
||||||
duration = Math.max(duration, timeline.getFrames()[frameCount * 4 - 4]);
|
duration = Math.max(duration, timeline.getFrames()[frameCount * 5 - 5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deform timelines.
|
// Deform timelines.
|
||||||
|
|||||||
@ -199,6 +199,7 @@ public class SkeletonJson {
|
|||||||
data.position = constraintMap.getFloat("position", 0);
|
data.position = constraintMap.getFloat("position", 0);
|
||||||
data.rotateMix = constraintMap.getFloat("rotateMix", 1);
|
data.rotateMix = constraintMap.getFloat("rotateMix", 1);
|
||||||
data.translateMix = constraintMap.getFloat("translateMix", 1);
|
data.translateMix = constraintMap.getFloat("translateMix", 1);
|
||||||
|
data.scaleMix = constraintMap.getFloat("scaleMix", 1);
|
||||||
|
|
||||||
skeletonData.pathConstraints.add(data);
|
skeletonData.pathConstraints.add(data);
|
||||||
}
|
}
|
||||||
@ -506,12 +507,12 @@ public class SkeletonJson {
|
|||||||
int frameIndex = 0;
|
int frameIndex = 0;
|
||||||
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
|
for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
|
||||||
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("scaleMix", 1),
|
timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("scaleMix", 1),
|
||||||
valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1));
|
valueMap.getFloat("rotateMix", 1), valueMap.getFloat("translateMix", 1), valueMap.getFloat("scaleMix", 1));
|
||||||
readCurve(valueMap, timeline, frameIndex);
|
readCurve(valueMap, timeline, frameIndex);
|
||||||
frameIndex++;
|
frameIndex++;
|
||||||
}
|
}
|
||||||
timelines.add(timeline);
|
timelines.add(timeline);
|
||||||
duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 4 - 4]);
|
duration = Math.max(duration, timeline.getFrames()[timeline.getFrameCount() * 5 - 5]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Deform timelines.
|
// Deform timelines.
|
||||||
|
|||||||
@ -31,19 +31,16 @@
|
|||||||
|
|
||||||
package com.esotericsoftware.spine.attachments;
|
package com.esotericsoftware.spine.attachments;
|
||||||
|
|
||||||
import static com.badlogic.gdx.math.MathUtils.*;
|
|
||||||
|
|
||||||
import com.badlogic.gdx.graphics.Color;
|
import com.badlogic.gdx.graphics.Color;
|
||||||
import com.badlogic.gdx.utils.FloatArray;
|
|
||||||
import com.esotericsoftware.spine.Slot;
|
import com.esotericsoftware.spine.Slot;
|
||||||
|
|
||||||
public class PathAttachment extends VertexAttachment {
|
public class PathAttachment extends VertexAttachment {
|
||||||
|
float length;
|
||||||
|
boolean closed, constantSpeed;
|
||||||
|
|
||||||
// Nonessential.
|
// Nonessential.
|
||||||
final Color color = new Color(1, 0.5f, 0, 1);
|
final Color color = new Color(1, 0.5f, 0, 1);
|
||||||
|
|
||||||
float[] worldVertices, temp;
|
|
||||||
boolean closed, constantSpeed;
|
|
||||||
|
|
||||||
public PathAttachment (String name) {
|
public PathAttachment (String name) {
|
||||||
super(name);
|
super(name);
|
||||||
}
|
}
|
||||||
@ -52,201 +49,8 @@ public class PathAttachment extends VertexAttachment {
|
|||||||
super.computeWorldVertices(slot, worldVertices);
|
super.computeWorldVertices(slot, worldVertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void computeWorldPositions (Slot slot, float position, FloatArray lengths, FloatArray out, boolean tangents) {
|
public void computeWorldVertices (Slot slot, int start, int count, float[] worldVertices, int offset) {
|
||||||
float[] vertices = this.worldVertices;
|
super.computeWorldVertices(slot, start, count, worldVertices, offset);
|
||||||
int verticesLength = worldVerticesLength;
|
|
||||||
int curves = verticesLength / 6;
|
|
||||||
|
|
||||||
if (!constantSpeed) {
|
|
||||||
for (int i = 0, n = lengths.size; i < n; i++) {
|
|
||||||
// BOZO - Wrong. Use path length property to give !constantSpeed paths support for oob and multiple bones?
|
|
||||||
position += lengths.get(i);
|
|
||||||
if (closed) {
|
|
||||||
position %= 1;
|
|
||||||
if (position < 0) position += 1;
|
|
||||||
} else {
|
|
||||||
position = clamp(position, 0, 1);
|
|
||||||
curves--;
|
|
||||||
}
|
|
||||||
int curve = position < 1 ? (int)(curves * position) : curves - 1;
|
|
||||||
position = (position - curve / (float)curves) * curves;
|
|
||||||
|
|
||||||
if (closed && curve == curves - 1) {
|
|
||||||
super.computeWorldVertices(slot, curves * 6 - 4, 4, vertices, 0);
|
|
||||||
super.computeWorldVertices(slot, 0, 4, vertices, 4);
|
|
||||||
} else
|
|
||||||
super.computeWorldVertices(slot, curve * 6 + 2, 8, vertices, 0);
|
|
||||||
|
|
||||||
addPoint(vertices[0], vertices[1], vertices[2], vertices[3], vertices[4], vertices[5], vertices[6], vertices[7],
|
|
||||||
position, tangents, out);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (closed) {
|
|
||||||
super.computeWorldVertices(slot, 2, verticesLength - 2, vertices, 0);
|
|
||||||
super.computeWorldVertices(slot, 0, 2, vertices, verticesLength - 2);
|
|
||||||
vertices[verticesLength] = vertices[0];
|
|
||||||
vertices[verticesLength + 1] = vertices[1];
|
|
||||||
verticesLength += 2;
|
|
||||||
} else {
|
|
||||||
verticesLength -= 4;
|
|
||||||
super.computeWorldVertices(slot, 2, verticesLength, vertices, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Curve lengths.
|
|
||||||
float[] temp = this.temp;
|
|
||||||
float pathLength = 0, x1 = vertices[0], y1 = vertices[1], cx1, cy1, cx2, cy2, x2, y2;
|
|
||||||
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];
|
|
||||||
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;
|
|
||||||
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
|
||||||
dfx += ddfx;
|
|
||||||
dfy += ddfy;
|
|
||||||
ddfx += dddfx;
|
|
||||||
ddfy += dddfy;
|
|
||||||
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
|
||||||
dfx += ddfx;
|
|
||||||
dfy += ddfy;
|
|
||||||
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
|
||||||
dfx += ddfx + dddfx;
|
|
||||||
dfy += ddfy + dddfy;
|
|
||||||
pathLength += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
|
||||||
temp[i] = pathLength;
|
|
||||||
x1 = x2;
|
|
||||||
y1 = y2;
|
|
||||||
}
|
|
||||||
position *= pathLength;
|
|
||||||
|
|
||||||
for (int i = 0, n = lengths.size; i < n; i++) {
|
|
||||||
position += lengths.get(i);
|
|
||||||
float p = position;
|
|
||||||
|
|
||||||
if (closed) {
|
|
||||||
p %= pathLength;
|
|
||||||
if (p < 0) p += pathLength;
|
|
||||||
} else if (p < 0 || p > pathLength) {
|
|
||||||
// Outside path.
|
|
||||||
if (p < 0) {
|
|
||||||
x1 = vertices[0];
|
|
||||||
y1 = vertices[1];
|
|
||||||
cx1 = vertices[2] - x1;
|
|
||||||
cy1 = vertices[3] - y1;
|
|
||||||
} else {
|
|
||||||
x1 = vertices[verticesLength - 2];
|
|
||||||
y1 = vertices[verticesLength - 1];
|
|
||||||
cx1 = x1 - vertices[verticesLength - 4];
|
|
||||||
cy1 = y1 - vertices[verticesLength - 3];
|
|
||||||
p -= pathLength;
|
|
||||||
}
|
|
||||||
float r = atan2(cy1, cx1);
|
|
||||||
out.add(x1 + p * cos(r));
|
|
||||||
out.add(y1 + p * sin(r));
|
|
||||||
if (tangents) out.add(r + PI);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine curve containing position.
|
|
||||||
int curve;
|
|
||||||
float length = temp[10];
|
|
||||||
if (p <= length) {
|
|
||||||
curve = 0;
|
|
||||||
p /= length;
|
|
||||||
} else {
|
|
||||||
for (curve = 11;; curve++) {
|
|
||||||
length = temp[curve];
|
|
||||||
if (p <= length) {
|
|
||||||
float prev = temp[curve - 1];
|
|
||||||
p = (p - prev) / (length - prev);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
curve = (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);
|
|
||||||
// BOZO! - Don't overwrite curve lengths with segment lengths.
|
|
||||||
temp[0] = length;
|
|
||||||
for (int ii = 1; ii < 8; ii++) {
|
|
||||||
dfx += ddfx;
|
|
||||||
dfy += ddfy;
|
|
||||||
ddfx += dddfx;
|
|
||||||
ddfy += dddfy;
|
|
||||||
length += (float)Math.sqrt(dfx * dfx + dfy * dfy);
|
|
||||||
temp[ii] = length;
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
length = temp[0];
|
|
||||||
if (p <= length)
|
|
||||||
p = 0.1f * p / length;
|
|
||||||
else {
|
|
||||||
for (int ii = 1;; ii++) {
|
|
||||||
length = temp[ii];
|
|
||||||
if (p <= length) {
|
|
||||||
float prev = temp[ii - 1];
|
|
||||||
p = 0.1f * (ii + (p - prev) / (length - prev));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addPoint(x1, y1, cx1, cy1, cx2, cy2, x2, y2, p, tangents, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addPoint (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;
|
|
||||||
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);
|
|
||||||
if (tangents) out.add(atan2(y - (y1 * uu + cy1 * ut * 2 + cy2 * tt), x - (x1 * uu + cx1 * ut * 2 + cx2 * tt)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Color getColor () {
|
|
||||||
return color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean getClosed () {
|
public boolean getClosed () {
|
||||||
@ -265,10 +69,15 @@ public class PathAttachment extends VertexAttachment {
|
|||||||
this.constantSpeed = constantSpeed;
|
this.constantSpeed = constantSpeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWorldVerticesLength (int worldVerticesLength) {
|
public float getLength () {
|
||||||
super.setWorldVerticesLength(worldVerticesLength);
|
return length;
|
||||||
// BOZO! - Share working memory?
|
}
|
||||||
worldVertices = new float[Math.max(2, worldVerticesLength + 4)];
|
|
||||||
temp = new float[10 + worldVerticesLength / 6];
|
public void setLength (float length) {
|
||||||
|
this.length = length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color getColor () {
|
||||||
|
return color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user