mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-04 14:24:53 +08:00
[libgdx] Binary, timelines, physics updates.
* Refactored timelines for code reuse. * Added physics timelines. * Improved physics behavior. * Optimized binary format for ~7% size reduction. * Added physics to binary format. * Changed rotation normalization to ceil. * Added bone icon as nonessential data.
This commit is contained in:
parent
55bb8fc65a
commit
95ce8a5547
@ -179,6 +179,8 @@ public class Animation {
|
||||
event, drawOrder, //
|
||||
ikConstraint, transformConstraint, //
|
||||
pathConstraintPosition, pathConstraintSpacing, pathConstraintMix, //
|
||||
physicsConstraintInertia, physicsConstraintStrength, physicsConstraintDamping, physicsConstraintFriction, //
|
||||
physicsConstraintMass, physicsConstraintWind, physicsConstraintGravity, physicsConstraintMix, //
|
||||
sequence
|
||||
}
|
||||
|
||||
@ -421,6 +423,82 @@ public class Animation {
|
||||
}
|
||||
return getBezierValue(time, i, VALUE, curveType - BEZIER);
|
||||
}
|
||||
|
||||
public float getRelativeValue (float time, float alpha, MixBlend blend, float current, float setup) {
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
return setup;
|
||||
case first:
|
||||
return current + (setup - current) * alpha;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
float value = getCurveValue(time);
|
||||
switch (blend) {
|
||||
case setup:
|
||||
return setup + value * alpha;
|
||||
case first:
|
||||
case replace:
|
||||
value += setup - current;
|
||||
}
|
||||
return current + value * alpha;
|
||||
}
|
||||
|
||||
public float getAbsoluteValue (float time, float alpha, MixBlend blend, float current, float setup) {
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
return setup;
|
||||
case first:
|
||||
return current + (setup - current) * alpha;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
float value = getCurveValue(time);
|
||||
if (blend == MixBlend.setup) return setup + (value - setup) * alpha;
|
||||
return current + (value - current) * alpha;
|
||||
}
|
||||
|
||||
public float getScaleValue (float time, float alpha, MixBlend blend, MixDirection direction, float current, float setup) {
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
return setup;
|
||||
case first:
|
||||
return current + (setup - current) * alpha;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
float value = getCurveValue(time) * setup;
|
||||
if (alpha == 1) {
|
||||
if (blend == add) return current + value - setup;
|
||||
return value;
|
||||
}
|
||||
// Mixing out uses sign of setup or current pose, else use sign of key.
|
||||
if (direction == out) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
return setup + (Math.abs(value) * Math.signum(setup) - setup) * alpha;
|
||||
case first:
|
||||
case replace:
|
||||
return current + (Math.abs(value) * Math.signum(current) - current) * alpha;
|
||||
}
|
||||
} else {
|
||||
float s;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
s = Math.abs(setup) * Math.signum(value);
|
||||
return s + (value - s) * alpha;
|
||||
case first:
|
||||
case replace:
|
||||
s = Math.abs(current) * Math.signum(value);
|
||||
return s + (value - s) * alpha;
|
||||
}
|
||||
}
|
||||
return current + (value - setup) * alpha;
|
||||
}
|
||||
}
|
||||
|
||||
/** The base class for a {@link CurveTimeline} which sets two properties. */
|
||||
@ -467,31 +545,7 @@ public class Animation {
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.rotation = bone.data.rotation;
|
||||
return;
|
||||
case first:
|
||||
bone.rotation += (bone.data.rotation - bone.rotation) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float r = getCurveValue(time);
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.rotation = bone.data.rotation + r * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
r += bone.data.rotation - bone.rotation;
|
||||
// Fall through.
|
||||
case add:
|
||||
bone.rotation += r * alpha;
|
||||
}
|
||||
if (bone.active) bone.rotation = getRelativeValue(time, alpha, blend, bone.rotation, bone.data.rotation);
|
||||
}
|
||||
}
|
||||
|
||||
@ -517,7 +571,7 @@ public class Animation {
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.x = bone.data.x;
|
||||
@ -584,32 +638,7 @@ public class Animation {
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.x = bone.data.x;
|
||||
return;
|
||||
case first:
|
||||
bone.x += (bone.data.x - bone.x) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float x = getCurveValue(time);
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.x = bone.data.x + x * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
bone.x += (bone.data.x + x - bone.x) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.x += x * alpha;
|
||||
}
|
||||
if (bone.active) bone.x = getRelativeValue(time, alpha, blend, bone.x, bone.data.x);
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,32 +659,7 @@ public class Animation {
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.y = bone.data.y;
|
||||
return;
|
||||
case first:
|
||||
bone.y += (bone.data.y - bone.y) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float y = getCurveValue(time);
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.y = bone.data.y + y * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
bone.y += (bone.data.y + y - bone.y) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.y += y * alpha;
|
||||
}
|
||||
if (bone.active) bone.y = getRelativeValue(time, alpha, blend, bone.y, bone.data.y);
|
||||
}
|
||||
}
|
||||
|
||||
@ -681,7 +685,7 @@ public class Animation {
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.scaleX = bone.data.scaleX;
|
||||
@ -787,59 +791,7 @@ public class Animation {
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.scaleX = bone.data.scaleX;
|
||||
return;
|
||||
case first:
|
||||
bone.scaleX += (bone.data.scaleX - bone.scaleX) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float x = getCurveValue(time) * bone.data.scaleX;
|
||||
if (alpha == 1) {
|
||||
if (blend == add)
|
||||
bone.scaleX += x - bone.data.scaleX;
|
||||
else
|
||||
bone.scaleX = x;
|
||||
} else {
|
||||
// Mixing out uses sign of setup or current pose, else use sign of key.
|
||||
float bx;
|
||||
if (direction == out) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bx = bone.data.scaleX;
|
||||
bone.scaleX = bx + (Math.abs(x) * Math.signum(bx) - bx) * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
bx = bone.scaleX;
|
||||
bone.scaleX = bx + (Math.abs(x) * Math.signum(bx) - bx) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.scaleX += (x - bone.data.scaleX) * alpha;
|
||||
}
|
||||
} else {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bx = Math.abs(bone.data.scaleX) * Math.signum(x);
|
||||
bone.scaleX = bx + (x - bx) * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
bx = Math.abs(bone.scaleX) * Math.signum(x);
|
||||
bone.scaleX = bx + (x - bx) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.scaleX += (x - bone.data.scaleX) * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bone.active) bone.scaleX = getScaleValue(time, alpha, blend, direction, bone.scaleX, bone.data.scaleX);
|
||||
}
|
||||
}
|
||||
|
||||
@ -860,59 +812,7 @@ public class Animation {
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.scaleY = bone.data.scaleY;
|
||||
return;
|
||||
case first:
|
||||
bone.scaleY += (bone.data.scaleY - bone.scaleY) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float y = getCurveValue(time) * bone.data.scaleY;
|
||||
if (alpha == 1) {
|
||||
if (blend == add)
|
||||
bone.scaleY += y - bone.data.scaleY;
|
||||
else
|
||||
bone.scaleY = y;
|
||||
} else {
|
||||
// Mixing out uses sign of setup or current pose, else use sign of key.
|
||||
float by;
|
||||
if (direction == out) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
by = bone.data.scaleY;
|
||||
bone.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
by = bone.scaleY;
|
||||
bone.scaleY = by + (Math.abs(y) * Math.signum(by) - by) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.scaleY += (y - bone.data.scaleY) * alpha;
|
||||
}
|
||||
} else {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
by = Math.abs(bone.data.scaleY) * Math.signum(y);
|
||||
bone.scaleY = by + (y - by) * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
by = Math.abs(bone.scaleY) * Math.signum(y);
|
||||
bone.scaleY = by + (y - by) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.scaleY += (y - bone.data.scaleY) * alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bone.active) bone.scaleY = getScaleValue(time, alpha, blend, direction, bone.scaleX, bone.data.scaleY);
|
||||
}
|
||||
}
|
||||
|
||||
@ -938,7 +838,7 @@ public class Animation {
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.shearX = bone.data.shearX;
|
||||
@ -1005,32 +905,7 @@ public class Animation {
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.shearX = bone.data.shearX;
|
||||
return;
|
||||
case first:
|
||||
bone.shearX += (bone.data.shearX - bone.shearX) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float x = getCurveValue(time);
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.shearX = bone.data.shearX + x * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
bone.shearX += (bone.data.shearX + x - bone.shearX) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.shearX += x * alpha;
|
||||
}
|
||||
if (bone.active) bone.shearX = getRelativeValue(time, alpha, blend, bone.shearX, bone.data.shearX);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1051,32 +926,7 @@ public class Animation {
|
||||
MixDirection direction) {
|
||||
|
||||
Bone bone = skeleton.bones.get(boneIndex);
|
||||
if (!bone.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.shearY = bone.data.shearY;
|
||||
return;
|
||||
case first:
|
||||
bone.shearY += (bone.data.shearY - bone.shearY) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float y = getCurveValue(time);
|
||||
switch (blend) {
|
||||
case setup:
|
||||
bone.shearY = bone.data.shearY + y * alpha;
|
||||
break;
|
||||
case first:
|
||||
case replace:
|
||||
bone.shearY += (bone.data.shearY + y - bone.shearY) * alpha;
|
||||
break;
|
||||
case add:
|
||||
bone.shearY += y * alpha;
|
||||
}
|
||||
if (bone.active) bone.shearY = getRelativeValue(time, alpha, blend, bone.shearX, bone.data.shearY);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1122,7 +972,7 @@ public class Animation {
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color color = slot.color;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
Color setup = slot.data.color;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
@ -1211,7 +1061,7 @@ public class Animation {
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color color = slot.color;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
Color setup = slot.data.color;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
@ -1290,7 +1140,7 @@ public class Animation {
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color color = slot.color;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
Color setup = slot.data.color;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
@ -1360,7 +1210,7 @@ public class Animation {
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color light = slot.color, dark = slot.darkColor;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
Color setupLight = slot.data.color, setupDark = slot.data.darkColor;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
@ -1486,7 +1336,7 @@ public class Animation {
|
||||
|
||||
float[] frames = this.frames;
|
||||
Color light = slot.color, dark = slot.darkColor;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
Color setupLight = slot.data.color, setupDark = slot.data.darkColor;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
@ -1614,7 +1464,7 @@ public class Animation {
|
||||
return;
|
||||
}
|
||||
|
||||
if (time < this.frames[0]) { // Time is before first frame.
|
||||
if (time < this.frames[0]) {
|
||||
if (blend == setup || blend == first) setAttachment(skeleton, slot, slot.data.attachmentName);
|
||||
return;
|
||||
}
|
||||
@ -1737,7 +1587,7 @@ public class Animation {
|
||||
int vertexCount = vertices[0].length;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
deformArray.clear();
|
||||
@ -1945,7 +1795,7 @@ public class Animation {
|
||||
lastTime = -1f;
|
||||
} else if (lastTime >= frames[frameCount - 1]) // Last time is after last frame.
|
||||
return;
|
||||
if (time < frames[0]) return; // Time is before first frame.
|
||||
if (time < frames[0]) return;
|
||||
|
||||
int i;
|
||||
if (lastTime < frames[0])
|
||||
@ -2001,7 +1851,7 @@ public class Animation {
|
||||
return;
|
||||
}
|
||||
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
if (blend == setup || blend == first)
|
||||
arraycopy(skeleton.slots.items, 0, skeleton.drawOrder.items, 0, skeleton.slots.size);
|
||||
return;
|
||||
@ -2025,21 +1875,21 @@ public class Animation {
|
||||
static public final int ENTRIES = 6;
|
||||
static private final int MIX = 1, SOFTNESS = 2, BEND_DIRECTION = 3, COMPRESS = 4, STRETCH = 5;
|
||||
|
||||
final int ikConstraintIndex;
|
||||
final int constraintIndex;
|
||||
|
||||
public IkConstraintTimeline (int frameCount, int bezierCount, int ikConstraintIndex) {
|
||||
super(frameCount, bezierCount, Property.ikConstraint.ordinal() + "|" + ikConstraintIndex);
|
||||
this.ikConstraintIndex = ikConstraintIndex;
|
||||
constraintIndex = ikConstraintIndex;
|
||||
}
|
||||
|
||||
public int getFrameEntries () {
|
||||
return ENTRIES;
|
||||
}
|
||||
|
||||
/** The index of the IK constraint slot in {@link Skeleton#getIkConstraints()} that will be changed when this timeline is
|
||||
/** The index of the IK constraint in {@link Skeleton#getIkConstraints()} that will be changed when this timeline is
|
||||
* applied. */
|
||||
public int getIkConstraintIndex () {
|
||||
return ikConstraintIndex;
|
||||
return constraintIndex;
|
||||
}
|
||||
|
||||
/** Sets the time, mix, softness, bend direction, compress, and stretch for the specified frame.
|
||||
@ -2060,11 +1910,11 @@ public class Animation {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
IkConstraint constraint = skeleton.ikConstraints.get(ikConstraintIndex);
|
||||
IkConstraint constraint = skeleton.ikConstraints.get(constraintIndex);
|
||||
if (!constraint.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
switch (blend) {
|
||||
case setup:
|
||||
constraint.mix = constraint.data.mix;
|
||||
@ -2134,21 +1984,21 @@ public class Animation {
|
||||
static public final int ENTRIES = 7;
|
||||
static private final int ROTATE = 1, X = 2, Y = 3, SCALEX = 4, SCALEY = 5, SHEARY = 6;
|
||||
|
||||
final int transformConstraintIndex;
|
||||
final int constraintIndex;
|
||||
|
||||
public TransformConstraintTimeline (int frameCount, int bezierCount, int transformConstraintIndex) {
|
||||
super(frameCount, bezierCount, Property.transformConstraint.ordinal() + "|" + transformConstraintIndex);
|
||||
this.transformConstraintIndex = transformConstraintIndex;
|
||||
constraintIndex = transformConstraintIndex;
|
||||
}
|
||||
|
||||
public int getFrameEntries () {
|
||||
return ENTRIES;
|
||||
}
|
||||
|
||||
/** The index of the transform constraint slot in {@link Skeleton#getTransformConstraints()} that will be changed when this
|
||||
/** The index of the transform constraint in {@link Skeleton#getTransformConstraints()} that will be changed when this
|
||||
* timeline is applied. */
|
||||
public int getTransformConstraintIndex () {
|
||||
return transformConstraintIndex;
|
||||
return constraintIndex;
|
||||
}
|
||||
|
||||
/** Sets the time, rotate mix, translate mix, scale mix, and shear mix for the specified frame.
|
||||
@ -2169,11 +2019,11 @@ public class Animation {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
TransformConstraint constraint = skeleton.transformConstraints.get(transformConstraintIndex);
|
||||
TransformConstraint constraint = skeleton.transformConstraints.get(constraintIndex);
|
||||
if (!constraint.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
TransformConstraintData data = constraint.data;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
@ -2252,105 +2102,73 @@ public class Animation {
|
||||
|
||||
/** Changes a path constraint's {@link PathConstraint#getPosition()}. */
|
||||
static public class PathConstraintPositionTimeline extends CurveTimeline1 {
|
||||
final int pathConstraintIndex;
|
||||
final int constraintIndex;
|
||||
|
||||
public PathConstraintPositionTimeline (int frameCount, int bezierCount, int pathConstraintIndex) {
|
||||
super(frameCount, bezierCount, Property.pathConstraintPosition.ordinal() + "|" + pathConstraintIndex);
|
||||
this.pathConstraintIndex = pathConstraintIndex;
|
||||
constraintIndex = pathConstraintIndex;
|
||||
}
|
||||
|
||||
/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed when this timeline
|
||||
* is applied. */
|
||||
/** The index of the path constraint in {@link Skeleton#getPathConstraints()} that will be changed when this timeline is
|
||||
* applied. */
|
||||
public int getPathConstraintIndex () {
|
||||
return pathConstraintIndex;
|
||||
return constraintIndex;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex);
|
||||
if (!constraint.active) return;
|
||||
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
constraint.position = constraint.data.position;
|
||||
return;
|
||||
case first:
|
||||
constraint.position += (constraint.data.position - constraint.position) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float position = getCurveValue(time);
|
||||
if (blend == setup)
|
||||
constraint.position = constraint.data.position + (position - constraint.data.position) * alpha;
|
||||
else
|
||||
constraint.position += (position - constraint.position) * alpha;
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
constraint.position = getAbsoluteValue(time, alpha, blend, constraint.position, constraint.data.position);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a path constraint's {@link PathConstraint#getSpacing()}. */
|
||||
static public class PathConstraintSpacingTimeline extends CurveTimeline1 {
|
||||
final int pathConstraintIndex;
|
||||
final int constraintIndex;
|
||||
|
||||
public PathConstraintSpacingTimeline (int frameCount, int bezierCount, int pathConstraintIndex) {
|
||||
super(frameCount, bezierCount, Property.pathConstraintSpacing.ordinal() + "|" + pathConstraintIndex);
|
||||
this.pathConstraintIndex = pathConstraintIndex;
|
||||
constraintIndex = pathConstraintIndex;
|
||||
}
|
||||
|
||||
/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed when this timeline
|
||||
* is applied. */
|
||||
/** The index of the path constraint in {@link Skeleton#getPathConstraints()} that will be changed when this timeline is
|
||||
* applied. */
|
||||
public int getPathConstraintIndex () {
|
||||
return pathConstraintIndex;
|
||||
return constraintIndex;
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex);
|
||||
if (!constraint.active) return;
|
||||
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
switch (blend) {
|
||||
case setup:
|
||||
constraint.spacing = constraint.data.spacing;
|
||||
return;
|
||||
case first:
|
||||
constraint.spacing += (constraint.data.spacing - constraint.spacing) * alpha;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float spacing = getCurveValue(time);
|
||||
if (blend == setup)
|
||||
constraint.spacing = constraint.data.spacing + (spacing - constraint.data.spacing) * alpha;
|
||||
else
|
||||
constraint.spacing += (spacing - constraint.spacing) * alpha;
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
constraint.spacing = getAbsoluteValue(time, alpha, blend, constraint.spacing, constraint.data.spacing);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a transform constraint's {@link PathConstraint#getMixRotate()}, {@link PathConstraint#getMixX()}, and
|
||||
/** Changes a path constraint's {@link PathConstraint#getMixRotate()}, {@link PathConstraint#getMixX()}, and
|
||||
* {@link PathConstraint#getMixY()}. */
|
||||
static public class PathConstraintMixTimeline extends CurveTimeline {
|
||||
static public final int ENTRIES = 4;
|
||||
static private final int ROTATE = 1, X = 2, Y = 3;
|
||||
|
||||
final int pathConstraintIndex;
|
||||
final int constraintIndex;
|
||||
|
||||
public PathConstraintMixTimeline (int frameCount, int bezierCount, int pathConstraintIndex) {
|
||||
super(frameCount, bezierCount, Property.pathConstraintMix.ordinal() + "|" + pathConstraintIndex);
|
||||
this.pathConstraintIndex = pathConstraintIndex;
|
||||
constraintIndex = pathConstraintIndex;
|
||||
}
|
||||
|
||||
public int getFrameEntries () {
|
||||
return ENTRIES;
|
||||
}
|
||||
|
||||
/** The index of the path constraint slot in {@link Skeleton#getPathConstraints()} that will be changed when this timeline
|
||||
* is applied. */
|
||||
/** The index of the path constraint in {@link Skeleton#getPathConstraints()} that will be changed when this timeline is
|
||||
* applied. */
|
||||
public int getPathConstraintIndex () {
|
||||
return pathConstraintIndex;
|
||||
return constraintIndex;
|
||||
}
|
||||
|
||||
/** Sets the time and color for the specified frame.
|
||||
@ -2367,11 +2185,11 @@ public class Animation {
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(pathConstraintIndex);
|
||||
PathConstraint constraint = skeleton.pathConstraints.get(constraintIndex);
|
||||
if (!constraint.active) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
PathConstraintData data = constraint.data;
|
||||
switch (blend) {
|
||||
case setup:
|
||||
@ -2424,6 +2242,124 @@ public class Animation {
|
||||
}
|
||||
}
|
||||
|
||||
/** The base class for {@link PhysicsConstraint} timelines. */
|
||||
static public abstract class PhysicsConstraintTimeline extends CurveTimeline1 {
|
||||
final int constraintIndex;
|
||||
|
||||
public PhysicsConstraintTimeline (int frameCount, int bezierCount, int physicsConstraintIndex, Property property) {
|
||||
super(frameCount, bezierCount, property.ordinal() + "|" + physicsConstraintIndex);
|
||||
constraintIndex = physicsConstraintIndex;
|
||||
}
|
||||
|
||||
/** The index of the physics constraint in {@link Skeleton#getPhysicsConstraints()} that will be changed when this timeline
|
||||
* is applied. */
|
||||
public int getPhysicsConstraintIndex () {
|
||||
return constraintIndex;
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a physics constraint's {@link PhysicsConstraint#getInertia()}. */
|
||||
static public class PhysicsConstraintInertiaTimeline extends PhysicsConstraintTimeline {
|
||||
public PhysicsConstraintInertiaTimeline (int frameCount, int bezierCount, int physicsConstraintIndex) {
|
||||
super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintInertia);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PhysicsConstraint constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
constraint.inertia = getAbsoluteValue(time, alpha, blend, constraint.inertia, constraint.data.inertia);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a physics constraint's {@link PhysicsConstraint#getStrength()}. */
|
||||
static public class PhysicsConstraintStrengthTimeline extends PhysicsConstraintTimeline {
|
||||
public PhysicsConstraintStrengthTimeline (int frameCount, int bezierCount, int physicsConstraintIndex) {
|
||||
super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintStrength);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PhysicsConstraint constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
constraint.strength = getAbsoluteValue(time, alpha, blend, constraint.strength, constraint.data.strength);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a physics constraint's {@link PhysicsConstraint#getDamping()}. */
|
||||
static public class PhysicsConstraintDampingTimeline extends PhysicsConstraintTimeline {
|
||||
public PhysicsConstraintDampingTimeline (int frameCount, int bezierCount, int physicsConstraintIndex) {
|
||||
super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintDamping);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PhysicsConstraint constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
constraint.damping = getAbsoluteValue(time, alpha, blend, constraint.damping, constraint.data.damping);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a physics constraint's {@link PhysicsConstraint#getMass()}. */
|
||||
static public class PhysicsConstraintMassTimeline extends PhysicsConstraintTimeline {
|
||||
public PhysicsConstraintMassTimeline (int frameCount, int bezierCount, int physicsConstraintIndex) {
|
||||
super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintMass);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PhysicsConstraint constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active) constraint.mass = getAbsoluteValue(time, alpha, blend, constraint.mass, constraint.data.mass);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a physics constraint's {@link PhysicsConstraint#getWind()}. */
|
||||
static public class PhysicsConstraintWindTimeline extends PhysicsConstraintTimeline {
|
||||
public PhysicsConstraintWindTimeline (int frameCount, int bezierCount, int physicsConstraintIndex) {
|
||||
super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintWind);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PhysicsConstraint constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active) constraint.wind = getAbsoluteValue(time, alpha, blend, constraint.wind, constraint.data.wind);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a physics constraint's {@link PhysicsConstraint#getGravity()}. */
|
||||
static public class PhysicsConstraintGravityTimeline extends PhysicsConstraintTimeline {
|
||||
public PhysicsConstraintGravityTimeline (int frameCount, int bezierCount, int physicsConstraintIndex) {
|
||||
super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintGravity);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PhysicsConstraint constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active)
|
||||
constraint.gravity = getAbsoluteValue(time, alpha, blend, constraint.gravity, constraint.data.gravity);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a physics constraint's {@link PhysicsConstraint#getMix()}. */
|
||||
static public class PhysicsConstraintMixTimeline extends PhysicsConstraintTimeline {
|
||||
public PhysicsConstraintMixTimeline (int frameCount, int bezierCount, int physicsConstraintIndex) {
|
||||
super(frameCount, bezierCount, physicsConstraintIndex, Property.physicsConstraintMix);
|
||||
}
|
||||
|
||||
public void apply (Skeleton skeleton, float lastTime, float time, @Null Array<Event> events, float alpha, MixBlend blend,
|
||||
MixDirection direction) {
|
||||
|
||||
PhysicsConstraint constraint = skeleton.physicsConstraints.get(constraintIndex);
|
||||
if (constraint.active) constraint.mix = getAbsoluteValue(time, alpha, blend, constraint.mix, constraint.data.mix);
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a slot's {@link Slot#getSequenceIndex()} for an attachment's {@link Sequence}. */
|
||||
static public class SequenceTimeline extends Timeline implements SlotTimeline {
|
||||
static public final int ENTRIES = 3;
|
||||
@ -2475,7 +2411,7 @@ public class Animation {
|
||||
if (sequence == null) return;
|
||||
|
||||
float[] frames = this.frames;
|
||||
if (time < frames[0]) { // Time is before first frame.
|
||||
if (time < frames[0]) {
|
||||
if (blend == setup || blend == first) slot.setSequenceIndex(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -434,7 +434,7 @@ public class AnimationState {
|
||||
|
||||
// Mix between rotations using the direction of the shortest route on the first frame.
|
||||
float total, diff = r2 - r1;
|
||||
diff -= (16384 - (int)(16384.499999999996 - diff / 360)) * 360;
|
||||
diff -= (float)Math.ceil(diff / 360 - 0.5f) * 360;
|
||||
if (diff == 0)
|
||||
total = timelinesRotation[i];
|
||||
else {
|
||||
|
||||
@ -46,6 +46,7 @@ public class BoneData {
|
||||
|
||||
// Nonessential.
|
||||
final Color color = new Color(0.61f, 0.61f, 0.61f, 1); // 9b9b9bff
|
||||
@Null String icon;
|
||||
|
||||
public BoneData (int index, String name, @Null BoneData parent) {
|
||||
if (index < 0) throw new IllegalArgumentException("index must be >= 0.");
|
||||
@ -195,6 +196,15 @@ public class BoneData {
|
||||
return color;
|
||||
}
|
||||
|
||||
/** The bone icon as it was in Spine, or null if nonessential data was not exported. */
|
||||
public @Null String getIcon () {
|
||||
return icon;
|
||||
}
|
||||
|
||||
public void setIcon (@Null String icon) {
|
||||
this.icon = icon;
|
||||
}
|
||||
|
||||
public String toString () {
|
||||
return name;
|
||||
}
|
||||
|
||||
@ -39,11 +39,10 @@ import com.esotericsoftware.spine.Skeleton.Physics;
|
||||
public class PhysicsConstraint implements Updatable {
|
||||
final PhysicsConstraintData data;
|
||||
public Bone bone;
|
||||
|
||||
float mix;
|
||||
float inertia, strength, damping, mass, wind, gravity, mix;
|
||||
|
||||
boolean reset = true;
|
||||
float beforeX, beforeY, afterX, afterY, tipX, tipY;
|
||||
float ux, uy, cx, cy, tx, ty;
|
||||
float xOffset, xVelocity;
|
||||
float yOffset, yVelocity;
|
||||
float rotateOffset, rotateVelocity;
|
||||
@ -59,8 +58,13 @@ public class PhysicsConstraint implements Updatable {
|
||||
if (skeleton == null) throw new IllegalArgumentException("skeleton cannot be null.");
|
||||
this.data = data;
|
||||
this.skeleton = skeleton;
|
||||
|
||||
bone = skeleton.bones.get(data.bone.index);
|
||||
inertia = data.inertia;
|
||||
strength = data.strength;
|
||||
damping = data.damping;
|
||||
mass = data.mass;
|
||||
wind = data.wind;
|
||||
gravity = data.gravity;
|
||||
mix = data.mix;
|
||||
}
|
||||
|
||||
@ -70,13 +74,18 @@ public class PhysicsConstraint implements Updatable {
|
||||
data = constraint.data;
|
||||
skeleton = constraint.skeleton;
|
||||
bone = constraint.bone;
|
||||
inertia = constraint.inertia;
|
||||
strength = constraint.strength;
|
||||
damping = constraint.damping;
|
||||
mass = constraint.mass;
|
||||
wind = constraint.wind;
|
||||
gravity = constraint.gravity;
|
||||
mix = constraint.mix;
|
||||
}
|
||||
|
||||
public void reset () {
|
||||
remaining = 0;
|
||||
lastTime = skeleton.time;
|
||||
|
||||
reset = true;
|
||||
xOffset = 0;
|
||||
xVelocity = 0;
|
||||
@ -90,8 +99,13 @@ public class PhysicsConstraint implements Updatable {
|
||||
|
||||
public void setToSetupPose () {
|
||||
reset();
|
||||
|
||||
PhysicsConstraintData data = this.data;
|
||||
inertia = data.inertia;
|
||||
strength = data.strength;
|
||||
damping = data.damping;
|
||||
mass = data.mass;
|
||||
wind = data.wind;
|
||||
gravity = data.gravity;
|
||||
mix = data.mix;
|
||||
}
|
||||
|
||||
@ -100,110 +114,126 @@ public class PhysicsConstraint implements Updatable {
|
||||
float mix = this.mix;
|
||||
if (mix == 0) return;
|
||||
|
||||
boolean x = data.x, y = data.y, rotateOrShear = data.rotate || data.shearX, scaleX = data.scaleX;
|
||||
boolean x = data.x, y = data.y, rotateOrShearX = data.rotate || data.shearX, scaleX = data.scaleX;
|
||||
Bone bone = this.bone;
|
||||
float l = bone.data.length;
|
||||
|
||||
switch (physics) {
|
||||
case none:
|
||||
return;
|
||||
case reset:
|
||||
reset();
|
||||
break;
|
||||
// Fall through.
|
||||
case update:
|
||||
remaining += Math.max(skeleton.time - lastTime, 0);
|
||||
lastTime = skeleton.time;
|
||||
|
||||
float length = bone.data.length, br = atan2(bone.c, bone.a);
|
||||
float wind = data.wind, gravity = data.gravity, strength = data.strength, friction = data.friction, mass = data.mass;
|
||||
float damping = data.damping, step = data.step;
|
||||
boolean angle = rotateOrShear || scaleX;
|
||||
float cos = 0, sin = 0;
|
||||
while (remaining >= step) {
|
||||
remaining -= step;
|
||||
if (x) {
|
||||
xVelocity += (wind * 500 - xOffset * strength - xVelocity * friction) * mass;
|
||||
xOffset += xVelocity * step;
|
||||
xVelocity *= damping;
|
||||
float step = data.step;
|
||||
if (remaining >= step) {
|
||||
float bx = bone.worldX, by = bone.worldY, ca = 0, c = 0, s = 0;
|
||||
if (reset) {
|
||||
reset = false;
|
||||
ux = bx;
|
||||
uy = by;
|
||||
} else {
|
||||
float i = this.inertia;
|
||||
if (x) {
|
||||
xOffset += (ux - bx) * i;
|
||||
ux = bx;
|
||||
bone.worldX += xOffset * mix;
|
||||
}
|
||||
if (y) {
|
||||
yOffset += (uy - by) * i;
|
||||
uy = by;
|
||||
bone.worldY += yOffset * mix;
|
||||
}
|
||||
if (rotateOrShearX) {
|
||||
ca = atan2(bone.c, bone.a);
|
||||
float dx = cx - bone.worldX, dy = cy - bone.worldY, r = atan2(dy + ty, dx + tx) - ca - rotateOffset * mix;
|
||||
rotateOffset += (r - (float)Math.ceil(r * invPI2 - 0.5f) * PI2) * i;
|
||||
r = rotateOffset * mix + ca;
|
||||
c = cos(r);
|
||||
s = sin(r);
|
||||
if (scaleX) scaleOffset += (dx * c + dy * s) * i / l;
|
||||
} else if (scaleX) {
|
||||
float r = rotateOffset * mix + atan2(bone.c, bone.a);
|
||||
c = cos(r);
|
||||
s = sin(r);
|
||||
scaleOffset += ((cx - bone.worldX) * c + (cy - bone.worldY) * s) * i / l;
|
||||
}
|
||||
}
|
||||
if (y) {
|
||||
yVelocity += (-gravity * 500 - yOffset * strength - yVelocity * friction) * mass;
|
||||
yOffset += yVelocity * step;
|
||||
yVelocity *= damping;
|
||||
}
|
||||
if (angle) {
|
||||
float r = br + rotateOffset * degRad;
|
||||
cos = cos(r);
|
||||
sin = sin(r);
|
||||
if (rotateOrShear) {
|
||||
rotateVelocity += (length * (-wind * sin - gravity * cos) - rotateOffset * strength - rotateVelocity * friction)
|
||||
* mass;
|
||||
rotateOffset += rotateVelocity * step;
|
||||
rotateVelocity *= damping;
|
||||
cx = bone.worldX;
|
||||
cy = bone.worldY;
|
||||
|
||||
float strength = this.strength, mass = this.mass * step, wind = this.wind, gravity = this.gravity;
|
||||
float damping = (float)Math.pow(this.damping, 60 * step);
|
||||
while (true) {
|
||||
remaining -= step;
|
||||
if (x) {
|
||||
xVelocity += (100 * wind - xOffset * strength) * mass;
|
||||
xOffset += xVelocity * step;
|
||||
xVelocity *= damping;
|
||||
}
|
||||
if (y) {
|
||||
yVelocity += (-100 * gravity - yOffset * strength) * mass;
|
||||
yOffset += yVelocity * step;
|
||||
yVelocity *= damping;
|
||||
}
|
||||
if (scaleX) {
|
||||
scaleVelocity += (wind * cos - gravity * sin - scaleOffset * strength - scaleVelocity * friction) * mass;
|
||||
scaleVelocity += (wind * c - gravity * s - scaleOffset * strength) * mass;
|
||||
scaleOffset += scaleVelocity * step;
|
||||
scaleVelocity *= damping;
|
||||
}
|
||||
if (rotateOrShearX) {
|
||||
rotateVelocity += (-0.01f * l * (wind * s + gravity * c) - rotateOffset * strength) * mass;
|
||||
rotateOffset += rotateVelocity * step;
|
||||
rotateVelocity *= damping;
|
||||
if (remaining < step) break;
|
||||
float r = rotateOffset * mix + ca;
|
||||
c = cos(r);
|
||||
s = sin(r);
|
||||
} else if (remaining < step) //
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float bx = bone.worldX, by = bone.worldY;
|
||||
if (reset)
|
||||
reset = false;
|
||||
else {
|
||||
float i = data.inertia;
|
||||
if (x) {
|
||||
xOffset += (beforeX - bx) * i;
|
||||
bone.worldX += xOffset * mix;
|
||||
}
|
||||
if (y) {
|
||||
yOffset += (beforeY - by) * i;
|
||||
bone.worldY += yOffset * mix;
|
||||
}
|
||||
if (rotateOrShear) {
|
||||
if (length == 0)
|
||||
rotateOffset = 0;
|
||||
else {
|
||||
float r = (atan2(afterY - bone.worldY + tipY, afterX - bone.worldX + tipX) - br) * radDeg;
|
||||
rotateOffset += (r - (16384 - (int)(16384.499999999996 - r / 360)) * 360) * i;
|
||||
}
|
||||
}
|
||||
if (scaleX) {
|
||||
if (length == 0)
|
||||
scaleOffset = 0;
|
||||
else {
|
||||
float r = br + rotateOffset * degRad;
|
||||
scaleOffset += ((afterX - bone.worldX) * cos(r) + (afterY - bone.worldY) * sin(r)) * i / length;
|
||||
}
|
||||
}
|
||||
}
|
||||
beforeX = bx;
|
||||
beforeY = by;
|
||||
afterX = bone.worldX;
|
||||
afterY = bone.worldY;
|
||||
tipX = length * bone.a;
|
||||
tipY = length * bone.c;
|
||||
break;
|
||||
case pose:
|
||||
if (x) bone.worldX += xOffset * mix;
|
||||
if (y) bone.worldY += yOffset * mix;
|
||||
}
|
||||
|
||||
if (rotateOrShear) {
|
||||
float r = rotateOffset * mix;
|
||||
if (data.rotate) bone.rotateWorld(r);
|
||||
if (data.shearX) {
|
||||
float t = (float)Math.tan(r * 0.5f * degRad);
|
||||
bone.b += bone.a * t;
|
||||
bone.d += bone.c * t;
|
||||
if (rotateOrShearX) {
|
||||
float r = rotateOffset * mix, ra = bone.a, sin, cos;
|
||||
if (data.rotate) {
|
||||
if (data.shearX) {
|
||||
r *= 0.5f;
|
||||
sin = sin(r);
|
||||
cos = cos(r);
|
||||
bone.a = ra = cos * ra - sin * bone.c;
|
||||
bone.c = sin * ra + cos * bone.c;
|
||||
} else {
|
||||
sin = sin(r);
|
||||
cos = cos(r);
|
||||
}
|
||||
float rb = bone.b;
|
||||
bone.b = cos * rb - sin * bone.d;
|
||||
bone.d = sin * rb + cos * bone.d;
|
||||
} else {
|
||||
sin = sin(r);
|
||||
cos = cos(r);
|
||||
}
|
||||
bone.a = cos * ra - sin * bone.c;
|
||||
bone.c = sin * ra + cos * bone.c;
|
||||
}
|
||||
if (scaleX) {
|
||||
float s = 1 + scaleOffset * mix;
|
||||
bone.a *= s;
|
||||
bone.c *= s;
|
||||
}
|
||||
if (physics == Physics.update) {
|
||||
tx = l * bone.a;
|
||||
ty = l * bone.c;
|
||||
}
|
||||
bone.updateAppliedTransform();
|
||||
}
|
||||
|
||||
@ -216,6 +246,55 @@ public class PhysicsConstraint implements Updatable {
|
||||
this.bone = bone;
|
||||
}
|
||||
|
||||
public float getInertia () {
|
||||
return inertia;
|
||||
}
|
||||
|
||||
public void setInertia (float inertia) {
|
||||
this.inertia = inertia;
|
||||
}
|
||||
|
||||
public float getStrength () {
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void setStrength (float strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public float getDamping () {
|
||||
return damping;
|
||||
}
|
||||
|
||||
public void setDamping (float damping) {
|
||||
this.damping = damping;
|
||||
}
|
||||
|
||||
/** The inverse of the mass. */
|
||||
public float getMass () {
|
||||
return mass;
|
||||
}
|
||||
|
||||
public void setMass (float mass) {
|
||||
this.mass = mass;
|
||||
}
|
||||
|
||||
public float getWind () {
|
||||
return wind;
|
||||
}
|
||||
|
||||
public void setWind (float wind) {
|
||||
this.wind = wind;
|
||||
}
|
||||
|
||||
public float getGravity () {
|
||||
return gravity;
|
||||
}
|
||||
|
||||
public void setGravity (float gravity) {
|
||||
this.gravity = gravity;
|
||||
}
|
||||
|
||||
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
|
||||
public float getMix () {
|
||||
return mix;
|
||||
|
||||
@ -34,9 +34,8 @@ package com.esotericsoftware.spine;
|
||||
* See <a href="http://esotericsoftware.com/spine-physics-constraints">Physics constraints</a> in the Spine User Guide. */
|
||||
public class PhysicsConstraintData extends ConstraintData {
|
||||
BoneData bone;
|
||||
float step = 1 / 60f, mass = 1;
|
||||
float strength, friction, damping, inertia, wind, gravity, mix;
|
||||
boolean x, y, rotate, scaleX, shearX;
|
||||
float step, inertia, strength, damping, friction, mass, wind, gravity, mix;
|
||||
|
||||
public PhysicsConstraintData (String name) {
|
||||
super(name);
|
||||
@ -59,62 +58,6 @@ public class PhysicsConstraintData extends ConstraintData {
|
||||
this.step = step;
|
||||
}
|
||||
|
||||
public float getMass () {
|
||||
return mass;
|
||||
}
|
||||
|
||||
public void setMass (float mass) {
|
||||
this.mass = mass;
|
||||
}
|
||||
|
||||
public float getStrength () {
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void setStrength (float strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public float getFriction () {
|
||||
return friction;
|
||||
}
|
||||
|
||||
public void setFriction (float friction) {
|
||||
this.friction = friction;
|
||||
}
|
||||
|
||||
public float getDamping () {
|
||||
return damping;
|
||||
}
|
||||
|
||||
public void setDamping (float damping) {
|
||||
this.damping = damping;
|
||||
}
|
||||
|
||||
public float getInertia () {
|
||||
return inertia;
|
||||
}
|
||||
|
||||
public void setInertia (float inertia) {
|
||||
this.inertia = inertia;
|
||||
}
|
||||
|
||||
public float getWind () {
|
||||
return wind;
|
||||
}
|
||||
|
||||
public void setWind (float wind) {
|
||||
this.wind = wind;
|
||||
}
|
||||
|
||||
public float getGravity () {
|
||||
return gravity;
|
||||
}
|
||||
|
||||
public void setGravity (float gravity) {
|
||||
this.gravity = gravity;
|
||||
}
|
||||
|
||||
public boolean getX () {
|
||||
return x;
|
||||
}
|
||||
@ -155,6 +98,63 @@ public class PhysicsConstraintData extends ConstraintData {
|
||||
this.shearX = shearX;
|
||||
}
|
||||
|
||||
public float getInertia () {
|
||||
return inertia;
|
||||
}
|
||||
|
||||
public void setInertia (float inertia) {
|
||||
this.inertia = inertia;
|
||||
}
|
||||
|
||||
public float getStrength () {
|
||||
return strength;
|
||||
}
|
||||
|
||||
public void setStrength (float strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public float getDamping () {
|
||||
return damping;
|
||||
}
|
||||
|
||||
public void setDamping (float damping) {
|
||||
this.damping = damping;
|
||||
}
|
||||
|
||||
public float getFriction () {
|
||||
return friction;
|
||||
}
|
||||
|
||||
public void setFriction (float friction) {
|
||||
this.friction = friction;
|
||||
}
|
||||
|
||||
/** The inverse of the mass. */
|
||||
public float getMass () {
|
||||
return mass;
|
||||
}
|
||||
|
||||
public void setMass (float mass) {
|
||||
this.mass = mass;
|
||||
}
|
||||
|
||||
public float getWind () {
|
||||
return wind;
|
||||
}
|
||||
|
||||
public void setWind (float wind) {
|
||||
this.wind = wind;
|
||||
}
|
||||
|
||||
public float getGravity () {
|
||||
return gravity;
|
||||
}
|
||||
|
||||
public void setGravity (float gravity) {
|
||||
this.gravity = gravity;
|
||||
}
|
||||
|
||||
/** A percentage (0-1) that controls the mix between the constrained and unconstrained poses. */
|
||||
public float getMix () {
|
||||
return mix;
|
||||
|
||||
@ -55,6 +55,13 @@ import com.esotericsoftware.spine.Animation.IkConstraintTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintMixTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintPositionTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PathConstraintSpacingTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintDampingTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintGravityTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintInertiaTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintMassTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintMixTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintStrengthTimeline;
|
||||
import com.esotericsoftware.spine.Animation.PhysicsConstraintWindTimeline;
|
||||
import com.esotericsoftware.spine.Animation.RGB2Timeline;
|
||||
import com.esotericsoftware.spine.Animation.RGBA2Timeline;
|
||||
import com.esotericsoftware.spine.Animation.RGBATimeline;
|
||||
@ -76,7 +83,6 @@ import com.esotericsoftware.spine.BoneData.TransformMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.PositionMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.RotateMode;
|
||||
import com.esotericsoftware.spine.PathConstraintData.SpacingMode;
|
||||
import com.esotericsoftware.spine.SkeletonJson.LinkedMesh;
|
||||
import com.esotericsoftware.spine.attachments.Attachment;
|
||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||
import com.esotericsoftware.spine.attachments.AttachmentType;
|
||||
@ -121,10 +127,20 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
static public final int PATH_SPACING = 1;
|
||||
static public final int PATH_MIX = 2;
|
||||
|
||||
static public final int PHYSICS_INERTIA = 0;
|
||||
static public final int PHYSICS_STRENGTH = 1;
|
||||
static public final int PHYSICS_DAMPING = 2;
|
||||
static public final int PHYSICS_MASS = 4;
|
||||
static public final int PHYSICS_WIND = 5;
|
||||
static public final int PHYSICS_GRAVITY = 6;
|
||||
static public final int PHYSICS_MIX = 7;
|
||||
|
||||
static public final int CURVE_LINEAR = 0;
|
||||
static public final int CURVE_STEPPED = 1;
|
||||
static public final int CURVE_BEZIER = 2;
|
||||
|
||||
private final Array<LinkedMesh> linkedMeshes = new Array();
|
||||
|
||||
public SkeletonBinary (AttachmentLoader attachmentLoader) {
|
||||
super(attachmentLoader);
|
||||
}
|
||||
@ -192,7 +208,10 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
data.length = input.readFloat() * scale;
|
||||
data.transformMode = TransformMode.values[input.readInt(true)];
|
||||
data.skinRequired = input.readBoolean();
|
||||
if (nonessential) Color.rgba8888ToColor(data.color, input.readInt());
|
||||
if (nonessential) {
|
||||
Color.rgba8888ToColor(data.color, input.readInt());
|
||||
data.icon = input.readString();
|
||||
}
|
||||
bones[i] = data;
|
||||
}
|
||||
|
||||
@ -217,17 +236,18 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
for (int i = 0, nn; i < n; i++) {
|
||||
IkConstraintData data = new IkConstraintData(input.readString());
|
||||
data.order = input.readInt(true);
|
||||
data.skinRequired = input.readBoolean();
|
||||
Object[] constraintBones = data.bones.setSize(nn = input.readInt(true));
|
||||
for (int ii = 0; ii < nn; ii++)
|
||||
constraintBones[ii] = bones[input.readInt(true)];
|
||||
data.target = (BoneData)bones[input.readInt(true)];
|
||||
data.mix = input.readFloat();
|
||||
data.softness = input.readFloat() * scale;
|
||||
data.bendDirection = input.readByte();
|
||||
data.compress = input.readBoolean();
|
||||
data.stretch = input.readBoolean();
|
||||
data.uniform = input.readBoolean();
|
||||
int flags = input.read();
|
||||
data.skinRequired = (flags & 1) != 0;
|
||||
data.bendDirection = (flags & 2) != 0 ? 1 : -1;
|
||||
data.compress = (flags & 4) != 0;
|
||||
data.stretch = (flags & 8) != 0;
|
||||
data.uniform = (flags & 16) != 0;
|
||||
o[i] = data;
|
||||
}
|
||||
|
||||
@ -236,13 +256,14 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
for (int i = 0, nn; i < n; i++) {
|
||||
TransformConstraintData data = new TransformConstraintData(input.readString());
|
||||
data.order = input.readInt(true);
|
||||
data.skinRequired = input.readBoolean();
|
||||
Object[] constraintBones = data.bones.setSize(nn = input.readInt(true));
|
||||
for (int ii = 0; ii < nn; ii++)
|
||||
constraintBones[ii] = bones[input.readInt(true)];
|
||||
data.target = (BoneData)bones[input.readInt(true)];
|
||||
data.local = input.readBoolean();
|
||||
data.relative = input.readBoolean();
|
||||
int flags = input.read();
|
||||
data.skinRequired = (flags & 1) != 0;
|
||||
data.local = (flags & 2) != 0;
|
||||
data.relative = (flags & 4) != 0;
|
||||
data.offsetRotation = input.readFloat();
|
||||
data.offsetX = input.readFloat() * scale;
|
||||
data.offsetY = input.readFloat() * scale;
|
||||
@ -282,6 +303,31 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
o[i] = data;
|
||||
}
|
||||
|
||||
// Physics constraints.
|
||||
o = skeletonData.physicsConstraints.setSize(n = input.readInt(true));
|
||||
for (int i = 0; i < n; i++) {
|
||||
PhysicsConstraintData data = new PhysicsConstraintData(input.readString());
|
||||
data.order = input.readInt(true);
|
||||
data.bone = (BoneData)bones[input.readInt(true)];
|
||||
int flags = input.read();
|
||||
data.skinRequired = (flags & 1) != 0;
|
||||
data.x = (flags & 2) != 0;
|
||||
data.y = (flags & 4) != 0;
|
||||
data.rotate = (flags & 8) != 0;
|
||||
data.scaleX = (flags & 16) != 0;
|
||||
data.shearX = (flags & 32) != 0;
|
||||
data.step = input.readFloat();
|
||||
data.inertia = input.readFloat();
|
||||
data.strength = input.readFloat();
|
||||
data.damping = input.readFloat();
|
||||
data.friction = input.readFloat();
|
||||
data.mass = input.readFloat();
|
||||
data.wind = input.readFloat();
|
||||
data.gravity = input.readFloat();
|
||||
data.mix = input.readFloat();
|
||||
o[i] = data;
|
||||
}
|
||||
|
||||
// Default skin.
|
||||
Skin defaultSkin = readSkin(input, skeletonData, true, nonessential);
|
||||
if (defaultSkin != null) {
|
||||
@ -302,8 +348,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
Object[] items = linkedMeshes.items;
|
||||
for (int i = 0; i < n; i++) {
|
||||
LinkedMesh linkedMesh = (LinkedMesh)items[i];
|
||||
Skin skin = linkedMesh.skin == null ? skeletonData.getDefaultSkin() : skeletonData.findSkin(linkedMesh.skin);
|
||||
if (skin == null) throw new SerializationException("Skin not found: " + linkedMesh.skin);
|
||||
Skin skin = skeletonData.skins.get(linkedMesh.skinIndex);
|
||||
Attachment parent = skin.getAttachment(linkedMesh.slotIndex, linkedMesh.parent);
|
||||
if (parent == null) throw new SerializationException("Parent mesh not found: " + linkedMesh.parent);
|
||||
linkedMesh.mesh.setTimelineAttachment(linkedMesh.inheritTimelines ? (VertexAttachment)parent : linkedMesh.mesh);
|
||||
@ -315,7 +360,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
// Events.
|
||||
o = skeletonData.events.setSize(n = input.readInt(true));
|
||||
for (int i = 0; i < n; i++) {
|
||||
EventData data = new EventData(input.readStringRef());
|
||||
EventData data = new EventData(input.readString());
|
||||
data.intValue = input.readInt(false);
|
||||
data.floatValue = input.readFloat();
|
||||
data.stringValue = input.readString();
|
||||
@ -353,7 +398,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
if (slotCount == 0) return null;
|
||||
skin = new Skin("default");
|
||||
} else {
|
||||
skin = new Skin(input.readStringRef());
|
||||
skin = new Skin(input.readString());
|
||||
Object[] bones = skin.bones.setSize(input.readInt(true)), items = skeletonData.bones.items;
|
||||
for (int i = 0, n = skin.bones.size; i < n; i++)
|
||||
bones[i] = items[input.readInt(true)];
|
||||
@ -365,6 +410,9 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++)
|
||||
skin.constraints.add((ConstraintData)items[input.readInt(true)]);
|
||||
items = skeletonData.pathConstraints.items;
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++)
|
||||
skin.constraints.add((ConstraintData)items[input.readInt(true)]);
|
||||
items = skeletonData.pathConstraints.items;
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++)
|
||||
skin.constraints.add((ConstraintData)items[input.readInt(true)]);
|
||||
skin.constraints.shrink();
|
||||
@ -387,12 +435,13 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
String attachmentName, boolean nonessential) throws IOException {
|
||||
float scale = this.scale;
|
||||
|
||||
String name = input.readStringRef();
|
||||
if (name == null) name = attachmentName;
|
||||
|
||||
switch (AttachmentType.values[input.readByte()]) {
|
||||
int flags = input.readByte();
|
||||
String name = (flags & 8) != 0 ? input.readStringRef() : attachmentName;
|
||||
switch (AttachmentType.values[flags & 0b111]) {
|
||||
case region: {
|
||||
String path = input.readStringRef();
|
||||
String path = (flags & 16) != 0 ? input.readStringRef() : null;
|
||||
int color = (flags & 32) != 0 ? input.readInt() : 0xffffffff;
|
||||
Sequence sequence = (flags & 64) != 0 ? readSequence(input) : null;
|
||||
float rotation = input.readFloat();
|
||||
float x = input.readFloat();
|
||||
float y = input.readFloat();
|
||||
@ -400,8 +449,6 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
float scaleY = input.readFloat();
|
||||
float width = input.readFloat();
|
||||
float height = input.readFloat();
|
||||
int color = input.readInt();
|
||||
Sequence sequence = readSequence(input);
|
||||
|
||||
if (path == null) path = name;
|
||||
RegionAttachment region = attachmentLoader.newRegionAttachment(skin, name, path, sequence);
|
||||
@ -420,43 +467,41 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
return region;
|
||||
}
|
||||
case boundingbox: {
|
||||
int vertexCount = input.readInt(true);
|
||||
Vertices vertices = readVertices(input, vertexCount);
|
||||
Vertices vertices = readVertices(input, (flags & 16) != 0);
|
||||
int color = nonessential ? input.readInt() : 0;
|
||||
|
||||
BoundingBoxAttachment box = attachmentLoader.newBoundingBoxAttachment(skin, name);
|
||||
if (box == null) return null;
|
||||
box.setWorldVerticesLength(vertexCount << 1);
|
||||
box.setWorldVerticesLength(vertices.length);
|
||||
box.setVertices(vertices.vertices);
|
||||
box.setBones(vertices.bones);
|
||||
if (nonessential) Color.rgba8888ToColor(box.getColor(), color);
|
||||
return box;
|
||||
}
|
||||
case mesh: {
|
||||
String path = input.readStringRef();
|
||||
int color = input.readInt();
|
||||
int vertexCount = input.readInt(true);
|
||||
float[] uvs = readFloatArray(input, vertexCount << 1, 1);
|
||||
short[] triangles = readShortArray(input);
|
||||
Vertices vertices = readVertices(input, vertexCount);
|
||||
String path = (flags & 16) != 0 ? input.readStringRef() : name;
|
||||
int color = (flags & 32) != 0 ? input.readInt() : 0xffffffff;
|
||||
Sequence sequence = (flags & 64) != 0 ? readSequence(input) : null;
|
||||
int hullLength = input.readInt(true);
|
||||
Sequence sequence = readSequence(input);
|
||||
Vertices vertices = readVertices(input, (flags & 128) != 0);
|
||||
float[] uvs = readFloatArray(input, vertices.length, 1);
|
||||
short[] triangles = readShortArray(input, (vertices.length - hullLength - 2) * 3);
|
||||
|
||||
short[] edges = null;
|
||||
float width = 0, height = 0;
|
||||
if (nonessential) {
|
||||
edges = readShortArray(input);
|
||||
edges = readShortArray(input, input.readInt(true));
|
||||
width = input.readFloat();
|
||||
height = input.readFloat();
|
||||
}
|
||||
|
||||
if (path == null) path = name;
|
||||
MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence);
|
||||
if (mesh == null) return null;
|
||||
mesh.setPath(path);
|
||||
Color.rgba8888ToColor(mesh.getColor(), color);
|
||||
mesh.setBones(vertices.bones);
|
||||
mesh.setVertices(vertices.vertices);
|
||||
mesh.setWorldVerticesLength(vertexCount << 1);
|
||||
mesh.setWorldVerticesLength(vertices.length);
|
||||
mesh.setTriangles(triangles);
|
||||
mesh.setRegionUVs(uvs);
|
||||
if (sequence == null) mesh.updateRegion();
|
||||
@ -470,19 +515,18 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
return mesh;
|
||||
}
|
||||
case linkedmesh: {
|
||||
String path = input.readStringRef();
|
||||
int color = input.readInt();
|
||||
String skinName = input.readStringRef();
|
||||
String path = (flags & 16) != 0 ? input.readStringRef() : name;
|
||||
int color = (flags & 32) != 0 ? input.readInt() : 0xffffffff;
|
||||
Sequence sequence = (flags & 64) != 0 ? readSequence(input) : null;
|
||||
boolean inheritTimelines = (flags & 128) != 0;
|
||||
int skinIndex = input.readInt(true);
|
||||
String parent = input.readStringRef();
|
||||
boolean inheritTimelines = input.readBoolean();
|
||||
Sequence sequence = readSequence(input);
|
||||
float width = 0, height = 0;
|
||||
if (nonessential) {
|
||||
width = input.readFloat();
|
||||
height = input.readFloat();
|
||||
}
|
||||
|
||||
if (path == null) path = name;
|
||||
MeshAttachment mesh = attachmentLoader.newMeshAttachment(skin, name, path, sequence);
|
||||
if (mesh == null) return null;
|
||||
mesh.setPath(path);
|
||||
@ -492,15 +536,14 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
mesh.setWidth(width * scale);
|
||||
mesh.setHeight(height * scale);
|
||||
}
|
||||
linkedMeshes.add(new LinkedMesh(mesh, skinName, slotIndex, parent, inheritTimelines));
|
||||
linkedMeshes.add(new LinkedMesh(mesh, skinIndex, slotIndex, parent, inheritTimelines));
|
||||
return mesh;
|
||||
}
|
||||
case path: {
|
||||
boolean closed = input.readBoolean();
|
||||
boolean constantSpeed = input.readBoolean();
|
||||
int vertexCount = input.readInt(true);
|
||||
Vertices vertices = readVertices(input, vertexCount);
|
||||
float[] lengths = new float[vertexCount / 3];
|
||||
boolean closed = (flags & 16) != 0;
|
||||
boolean constantSpeed = (flags & 32) != 0;
|
||||
Vertices vertices = readVertices(input, (flags & 64) != 0);
|
||||
float[] lengths = new float[vertices.length / 6];
|
||||
for (int i = 0, n = lengths.length; i < n; i++)
|
||||
lengths[i] = input.readFloat() * scale;
|
||||
int color = nonessential ? input.readInt() : 0;
|
||||
@ -509,7 +552,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
if (path == null) return null;
|
||||
path.setClosed(closed);
|
||||
path.setConstantSpeed(constantSpeed);
|
||||
path.setWorldVerticesLength(vertexCount << 1);
|
||||
path.setWorldVerticesLength(vertices.length);
|
||||
path.setVertices(vertices.vertices);
|
||||
path.setBones(vertices.bones);
|
||||
path.setLengths(lengths);
|
||||
@ -532,14 +575,13 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
}
|
||||
case clipping:
|
||||
int endSlotIndex = input.readInt(true);
|
||||
int vertexCount = input.readInt(true);
|
||||
Vertices vertices = readVertices(input, vertexCount);
|
||||
Vertices vertices = readVertices(input, (flags & 16) != 0);
|
||||
int color = nonessential ? input.readInt() : 0;
|
||||
|
||||
ClippingAttachment clip = attachmentLoader.newClippingAttachment(skin, name);
|
||||
if (clip == null) return null;
|
||||
clip.setEndSlot(skeletonData.slots.get(endSlotIndex));
|
||||
clip.setWorldVerticesLength(vertexCount << 1);
|
||||
clip.setWorldVerticesLength(vertices.length);
|
||||
clip.setVertices(vertices.vertices);
|
||||
clip.setBones(vertices.bones);
|
||||
if (nonessential) Color.rgba8888ToColor(clip.getColor(), color);
|
||||
@ -549,7 +591,6 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
}
|
||||
|
||||
private Sequence readSequence (SkeletonInput input) throws IOException {
|
||||
if (!input.readBoolean()) return null;
|
||||
Sequence sequence = new Sequence(input.readInt(true));
|
||||
sequence.setStart(input.readInt(true));
|
||||
sequence.setDigits(input.readInt(true));
|
||||
@ -557,16 +598,17 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
return sequence;
|
||||
}
|
||||
|
||||
private Vertices readVertices (SkeletonInput input, int vertexCount) throws IOException {
|
||||
private Vertices readVertices (SkeletonInput input, boolean weighted) throws IOException {
|
||||
float scale = this.scale;
|
||||
int verticesLength = vertexCount << 1;
|
||||
int vertexCount = input.readInt(true);
|
||||
Vertices vertices = new Vertices();
|
||||
if (!input.readBoolean()) {
|
||||
vertices.vertices = readFloatArray(input, verticesLength, scale);
|
||||
vertices.length = vertexCount << 1;
|
||||
if (!weighted) {
|
||||
vertices.vertices = readFloatArray(input, vertices.length, scale);
|
||||
return vertices;
|
||||
}
|
||||
FloatArray weights = new FloatArray(verticesLength * 3 * 3);
|
||||
IntArray bonesArray = new IntArray(verticesLength * 3);
|
||||
FloatArray weights = new FloatArray(vertices.length * 3 * 3);
|
||||
IntArray bonesArray = new IntArray(vertices.length * 3);
|
||||
for (int i = 0; i < vertexCount; i++) {
|
||||
int boneCount = input.readInt(true);
|
||||
bonesArray.add(boneCount);
|
||||
@ -594,11 +636,10 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
return array;
|
||||
}
|
||||
|
||||
private short[] readShortArray (SkeletonInput input) throws IOException {
|
||||
int n = input.readInt(true);
|
||||
private short[] readShortArray (SkeletonInput input, int n) throws IOException {
|
||||
short[] array = new short[n];
|
||||
for (int i = 0; i < n; i++)
|
||||
array[i] = input.readShort();
|
||||
array[i] = (short)input.readInt(true);
|
||||
return array;
|
||||
}
|
||||
|
||||
@ -777,34 +818,34 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
int type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true);
|
||||
switch (type) {
|
||||
case BONE_ROTATE:
|
||||
timelines.add(readTimeline(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1));
|
||||
readTimeline(input, timelines, new RotateTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
break;
|
||||
case BONE_TRANSLATE:
|
||||
timelines.add(readTimeline(input, new TranslateTimeline(frameCount, bezierCount, boneIndex), scale));
|
||||
readTimeline(input, timelines, new TranslateTimeline(frameCount, bezierCount, boneIndex), scale);
|
||||
break;
|
||||
case BONE_TRANSLATEX:
|
||||
timelines.add(readTimeline(input, new TranslateXTimeline(frameCount, bezierCount, boneIndex), scale));
|
||||
readTimeline(input, timelines, new TranslateXTimeline(frameCount, bezierCount, boneIndex), scale);
|
||||
break;
|
||||
case BONE_TRANSLATEY:
|
||||
timelines.add(readTimeline(input, new TranslateYTimeline(frameCount, bezierCount, boneIndex), scale));
|
||||
readTimeline(input, timelines, new TranslateYTimeline(frameCount, bezierCount, boneIndex), scale);
|
||||
break;
|
||||
case BONE_SCALE:
|
||||
timelines.add(readTimeline(input, new ScaleTimeline(frameCount, bezierCount, boneIndex), 1));
|
||||
readTimeline(input, timelines, new ScaleTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
break;
|
||||
case BONE_SCALEX:
|
||||
timelines.add(readTimeline(input, new ScaleXTimeline(frameCount, bezierCount, boneIndex), 1));
|
||||
readTimeline(input, timelines, new ScaleXTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
break;
|
||||
case BONE_SCALEY:
|
||||
timelines.add(readTimeline(input, new ScaleYTimeline(frameCount, bezierCount, boneIndex), 1));
|
||||
readTimeline(input, timelines, new ScaleYTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
break;
|
||||
case BONE_SHEAR:
|
||||
timelines.add(readTimeline(input, new ShearTimeline(frameCount, bezierCount, boneIndex), 1));
|
||||
readTimeline(input, timelines, new ShearTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
break;
|
||||
case BONE_SHEARX:
|
||||
timelines.add(readTimeline(input, new ShearXTimeline(frameCount, bezierCount, boneIndex), 1));
|
||||
readTimeline(input, timelines, new ShearXTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
break;
|
||||
case BONE_SHEARY:
|
||||
timelines.add(readTimeline(input, new ShearYTimeline(frameCount, bezierCount, boneIndex), 1));
|
||||
readTimeline(input, timelines, new ShearYTimeline(frameCount, bezierCount, boneIndex), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -815,7 +856,8 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
|
||||
float time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale;
|
||||
for (int frame = 0, bezier = 0;; frame++) {
|
||||
timeline.setFrame(frame, time, mix, softness, input.readByte(), input.readBoolean(), input.readBoolean());
|
||||
int flags = input.read();
|
||||
timeline.setFrame(frame, time, mix, softness, input.readByte(), (flags & 1) != 0, (flags & 2) != 0);
|
||||
if (frame == frameLast) break;
|
||||
float time2 = input.readFloat(), mix2 = input.readFloat(), softness2 = input.readFloat() * scale;
|
||||
switch (input.readByte()) {
|
||||
@ -872,20 +914,18 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
int index = input.readInt(true);
|
||||
PathConstraintData data = skeletonData.pathConstraints.get(index);
|
||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
|
||||
switch (input.readByte()) {
|
||||
int type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true);
|
||||
switch (type) {
|
||||
case PATH_POSITION:
|
||||
timelines
|
||||
.add(readTimeline(input, new PathConstraintPositionTimeline(input.readInt(true), input.readInt(true), index),
|
||||
data.positionMode == PositionMode.fixed ? scale : 1));
|
||||
readTimeline(input, timelines, new PathConstraintPositionTimeline(frameCount, bezierCount, index),
|
||||
data.positionMode == PositionMode.fixed ? scale : 1);
|
||||
break;
|
||||
case PATH_SPACING:
|
||||
timelines
|
||||
.add(readTimeline(input, new PathConstraintSpacingTimeline(input.readInt(true), input.readInt(true), index),
|
||||
data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1));
|
||||
readTimeline(input, timelines, new PathConstraintSpacingTimeline(frameCount, bezierCount, index),
|
||||
data.spacingMode == SpacingMode.length || data.spacingMode == SpacingMode.fixed ? scale : 1);
|
||||
break;
|
||||
case PATH_MIX:
|
||||
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(input.readInt(true), input.readInt(true),
|
||||
index);
|
||||
PathConstraintMixTimeline timeline = new PathConstraintMixTimeline(frameCount, bezierCount, index);
|
||||
float time = input.readFloat(), mixRotate = input.readFloat(), mixX = input.readFloat(), mixY = input.readFloat();
|
||||
for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) {
|
||||
timeline.setFrame(frame, time, mixRotate, mixX, mixY);
|
||||
@ -911,6 +951,36 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
}
|
||||
}
|
||||
|
||||
// Physics timelines.
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||
int index = input.readInt(true);
|
||||
for (int ii = 0, nn = input.readInt(true); ii < nn; ii++) {
|
||||
int type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true);
|
||||
switch (type) {
|
||||
case PHYSICS_INERTIA:
|
||||
readTimeline(input, timelines, new PhysicsConstraintInertiaTimeline(frameCount, bezierCount, index), 1);
|
||||
break;
|
||||
case PHYSICS_STRENGTH:
|
||||
readTimeline(input, timelines, new PhysicsConstraintStrengthTimeline(frameCount, bezierCount, index), 1);
|
||||
break;
|
||||
case PHYSICS_DAMPING:
|
||||
readTimeline(input, timelines, new PhysicsConstraintDampingTimeline(frameCount, bezierCount, index), 1);
|
||||
break;
|
||||
case PHYSICS_MASS:
|
||||
readTimeline(input, timelines, new PhysicsConstraintMassTimeline(frameCount, bezierCount, index), 1);
|
||||
break;
|
||||
case PHYSICS_WIND:
|
||||
readTimeline(input, timelines, new PhysicsConstraintWindTimeline(frameCount, bezierCount, index), 1);
|
||||
break;
|
||||
case PHYSICS_GRAVITY:
|
||||
readTimeline(input, timelines, new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index), 1);
|
||||
break;
|
||||
case PHYSICS_MIX:
|
||||
readTimeline(input, timelines, new PhysicsConstraintMixTimeline(frameCount, bezierCount, index), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attachment timelines.
|
||||
for (int i = 0, n = input.readInt(true); i < n; i++) {
|
||||
Skin skin = skeletonData.skins.get(input.readInt(true));
|
||||
@ -1024,7 +1094,8 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
Event event = new Event(time, eventData);
|
||||
event.intValue = input.readInt(false);
|
||||
event.floatValue = input.readFloat();
|
||||
event.stringValue = input.readBoolean() ? input.readString() : eventData.stringValue;
|
||||
event.stringValue = input.readString();
|
||||
if (event.stringValue == null) event.stringValue = eventData.stringValue;
|
||||
if (event.getData().audioPath != null) {
|
||||
event.volume = input.readFloat();
|
||||
event.balance = input.readFloat();
|
||||
@ -1041,7 +1112,8 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
return new Animation(name, timelines, duration);
|
||||
}
|
||||
|
||||
private Timeline readTimeline (SkeletonInput input, CurveTimeline1 timeline, float scale) throws IOException {
|
||||
private void readTimeline (SkeletonInput input, Array<Timeline> timelines, CurveTimeline1 timeline, float scale)
|
||||
throws IOException {
|
||||
float time = input.readFloat(), value = input.readFloat() * scale;
|
||||
for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) {
|
||||
timeline.setFrame(frame, time, value);
|
||||
@ -1057,10 +1129,11 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
time = time2;
|
||||
value = value2;
|
||||
}
|
||||
return timeline;
|
||||
timelines.add(timeline);
|
||||
}
|
||||
|
||||
private Timeline readTimeline (SkeletonInput input, CurveTimeline2 timeline, float scale) throws IOException {
|
||||
private void readTimeline (SkeletonInput input, Array<Timeline> timelines, CurveTimeline2 timeline, float scale)
|
||||
throws IOException {
|
||||
float time = input.readFloat(), value1 = input.readFloat() * scale, value2 = input.readFloat() * scale;
|
||||
for (int frame = 0, bezier = 0, frameLast = timeline.getFrameCount() - 1;; frame++) {
|
||||
timeline.setFrame(frame, time, value1, value2);
|
||||
@ -1078,7 +1151,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
value1 = nvalue1;
|
||||
value2 = nvalue2;
|
||||
}
|
||||
return timeline;
|
||||
timelines.add(timeline);
|
||||
}
|
||||
|
||||
void setBezier (SkeletonInput input, CurveTimeline timeline, int bezier, int frame, int value, float time1, float time2,
|
||||
@ -1088,6 +1161,7 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
}
|
||||
|
||||
static class Vertices {
|
||||
int length;
|
||||
int[] bones;
|
||||
float[] vertices;
|
||||
}
|
||||
@ -1143,4 +1217,19 @@ public class SkeletonBinary extends SkeletonLoader {
|
||||
return new String(chars, 0, charCount);
|
||||
}
|
||||
}
|
||||
|
||||
static class LinkedMesh {
|
||||
String parent;
|
||||
int skinIndex, slotIndex;
|
||||
MeshAttachment mesh;
|
||||
boolean inheritTimelines;
|
||||
|
||||
public LinkedMesh (MeshAttachment mesh, int skinIndex, int slotIndex, String parent, boolean inheritTimelines) {
|
||||
this.mesh = mesh;
|
||||
this.skinIndex = skinIndex;
|
||||
this.slotIndex = slotIndex;
|
||||
this.parent = parent;
|
||||
this.inheritTimelines = inheritTimelines;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,6 +98,8 @@ import com.esotericsoftware.spine.attachments.VertexAttachment;
|
||||
* <a href="http://esotericsoftware.com/spine-loading-skeleton-data#JSON-and-binary-data">JSON and binary data</a> in the Spine
|
||||
* Runtimes Guide. */
|
||||
public class SkeletonJson extends SkeletonLoader {
|
||||
private final Array<LinkedMesh> linkedMeshes = new Array();
|
||||
|
||||
public SkeletonJson (AttachmentLoader attachmentLoader) {
|
||||
super(attachmentLoader);
|
||||
}
|
||||
@ -161,6 +163,8 @@ public class SkeletonJson extends SkeletonLoader {
|
||||
String color = boneMap.getString("color", null);
|
||||
if (color != null) Color.valueOf(color, data.getColor());
|
||||
|
||||
data.icon = boneMap.getString("icon", null);
|
||||
|
||||
skeletonData.bones.add(data);
|
||||
}
|
||||
|
||||
@ -877,6 +881,8 @@ public class SkeletonJson extends SkeletonLoader {
|
||||
}
|
||||
}
|
||||
|
||||
// BOZO - Physics timelines.
|
||||
|
||||
// Attachment timelines.
|
||||
for (JsonValue attachmentsMap = map.getChild("attachments"); attachmentsMap != null; attachmentsMap = attachmentsMap.next) {
|
||||
Skin skin = skeletonData.findSkin(attachmentsMap.name);
|
||||
|
||||
@ -33,9 +33,7 @@ import java.io.InputStream;
|
||||
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureAtlas;
|
||||
import com.badlogic.gdx.utils.Array;
|
||||
|
||||
import com.esotericsoftware.spine.SkeletonJson.LinkedMesh;
|
||||
import com.esotericsoftware.spine.attachments.AtlasAttachmentLoader;
|
||||
import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||
|
||||
@ -46,7 +44,6 @@ import com.esotericsoftware.spine.attachments.AttachmentLoader;
|
||||
abstract public class SkeletonLoader {
|
||||
final AttachmentLoader attachmentLoader;
|
||||
float scale = 1;
|
||||
final Array<LinkedMesh> linkedMeshes = new Array();
|
||||
|
||||
/** Creates a skeleton loader that loads attachments using an {@link AtlasAttachmentLoader} with the specified atlas. */
|
||||
public SkeletonLoader (TextureAtlas atlas) {
|
||||
|
||||
@ -251,7 +251,7 @@ public class TransformConstraint implements Updatable {
|
||||
float rotation = bone.arotation;
|
||||
if (mixRotate != 0) {
|
||||
float r = target.arotation - rotation + data.offsetRotation;
|
||||
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
|
||||
r -= (float)Math.ceil(r / 360 - 0.5f) * 360;
|
||||
rotation += r * mixRotate;
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ public class TransformConstraint implements Updatable {
|
||||
float shearY = bone.ashearY;
|
||||
if (mixShearY != 0) {
|
||||
float r = target.ashearY - shearY + data.offsetShearY;
|
||||
r -= (16384 - (int)(16384.499999999996 - r / 360)) * 360;
|
||||
r -= (float)Math.ceil(r / 360 - 0.5f) * 360;
|
||||
shearY += r * mixShearY;
|
||||
}
|
||||
|
||||
|
||||
@ -32,6 +32,7 @@ package com.esotericsoftware.spine.utils;
|
||||
public class SpineUtils {
|
||||
static public final float PI = 3.1415927f;
|
||||
static public final float PI2 = PI * 2;
|
||||
static public final float invPI2 = 1 / PI2;
|
||||
static public final float radiansToDegrees = 180f / PI;
|
||||
static public final float radDeg = radiansToDegrees;
|
||||
static public final float degreesToRadians = PI / 180;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user