Skip to content

Commit be767b2

Browse files
committed
[ts] Port latest physics changes.
1 parent 86a320a commit be767b2

8 files changed

+128
-85
lines changed

spine-ts/spine-core/src/IkConstraintData.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export class IkConstraintData extends ConstraintData {
4747
}
4848

4949
/** Controls the bend direction of the IK bones, either 1 or -1. */
50-
bendDirection = 1;
50+
bendDirection = 0;
5151

5252
/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
5353
compress = false;
@@ -61,7 +61,7 @@ export class IkConstraintData extends ConstraintData {
6161
uniform = false;
6262

6363
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
64-
mix = 1;
64+
mix = 0;
6565

6666
/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
6767
softness = 0;

spine-ts/spine-core/src/PhysicsConstraint.ts

+40-29
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,8 @@ export class PhysicsConstraint implements Updatable {
134134
this.reset();
135135
// Fall through.
136136
case Physics.update:
137-
this.remaining += Math.max(this.skeleton.time - this.lastTime, 0);
137+
const delta = Math.max(this.skeleton.time - this.lastTime, 0);
138+
this.remaining += delta;
138139
this.lastTime = this.skeleton.time;
139140

140141
const bx = bone.worldX, by = bone.worldY;
@@ -143,41 +144,52 @@ export class PhysicsConstraint implements Updatable {
143144
this.ux = bx;
144145
this.uy = by;
145146
} else {
146-
let remaining = this.remaining, i = this.inertia, step = this.data.step;
147+
let a = this.remaining, i = this.inertia, q = this.data.limit * delta, t = this.data.step, f = this.skeleton.data.referenceScale, d = -1;
147148
if (x || y) {
148149
if (x) {
149-
this.xOffset += (this.ux - bx) * i;
150+
const u = (this.ux - bx) * i;
151+
this.xOffset += u > q ? q : u < -q ? -q : u;
150152
this.ux = bx;
151153
}
152154
if (y) {
153-
this.yOffset += (this.uy - by) * i;
155+
const u = (this.uy - by) * i;
156+
this.yOffset += u > q ? q : u < -q ? -q : u;
154157
this.uy = by;
155158
}
156-
if (remaining >= step) {
157-
const m = this.massInverse * step, e = this.strength, w = this.wind * 100, g = this.gravity * -100;
158-
const d = Math.pow(this.damping, 60 * step);
159+
if (a >= t) {
160+
d = Math.pow(this.damping, 60 * t);
161+
const m = this.massInverse * t, e = this.strength, w = this.wind * f, g = this.gravity * f;
159162
do {
160163
if (x) {
161164
this.xVelocity += (w - this.xOffset * e) * m;
162-
this.xOffset += this.xVelocity * step;
165+
this.xOffset += this.xVelocity * t;
163166
this.xVelocity *= d;
164167
}
165168
if (y) {
166-
this.yVelocity += (g - this.yOffset * e) * m;
167-
this.yOffset += this.yVelocity * step;
169+
this.yVelocity -= (g + this.yOffset * e) * m;
170+
this.yOffset += this.yVelocity * t;
168171
this.yVelocity *= d;
169172
}
170-
remaining -= step;
171-
} while (remaining >= step);
173+
a -= t;
174+
} while (a >= t);
172175
}
173176
if (x) bone.worldX += this.xOffset * mix * this.data.x;
174177
if (y) bone.worldY += this.yOffset * mix * this.data.y;
175178
}
176179
if (rotateOrShearX || scaleX) {
177180
let ca = Math.atan2(bone.c, bone.a), c = 0, s = 0, mr = 0;
181+
let dx = this.cx - bone.worldX, dy = this.cy - bone.worldY;
182+
if (dx > q)
183+
dx = q;
184+
else if (dx < -q) //
185+
dx = -q;
186+
if (dy > q)
187+
dy = q;
188+
else if (dy < -q) //
189+
dy = -q;
178190
if (rotateOrShearX) {
179191
mr = (this.data.rotate + this.data.shearX) * mix;
180-
let dx = this.cx - bone.worldX, dy = this.cy - bone.worldY, r = Math.atan2(dy + this.ty, dx + this.tx) - ca - this.rotateOffset * mr;
192+
let r = Math.atan2(dy + this.ty, dx + this.tx) - ca - this.rotateOffset * mr;
181193
this.rotateOffset += (r - Math.ceil(r * MathUtils.invPI2 - 0.5) * MathUtils.PI2) * i;
182194
r = this.rotateOffset * mr + ca;
183195
c = Math.cos(r);
@@ -190,33 +202,33 @@ export class PhysicsConstraint implements Updatable {
190202
c = Math.cos(ca);
191203
s = Math.sin(ca);
192204
const r = l * bone.getWorldScaleX();
193-
if (r > 0) this.scaleOffset += ((this.cx - bone.worldX) * c + (this.cy - bone.worldY) * s) * i / r;
205+
if (r > 0) this.scaleOffset += (dx * c + dy * s) * i / r;
194206
}
195-
remaining = this.remaining;
196-
if (remaining >= step) {
197-
const m = this.massInverse * step, e = this.strength, w = this.wind, g = this.gravity;
198-
const d = Math.pow(this.damping, 60 * step);
207+
a = this.remaining;
208+
if (a >= t) {
209+
if (d == -1) d = Math.pow(this.damping, 60 * t);
210+
const m = this.massInverse * t, e = this.strength, w = this.wind, g = this.gravity, h = l / f;
199211
while (true) {
200-
remaining -= step;
212+
a -= t;
201213
if (scaleX) {
202214
this.scaleVelocity += (w * c - g * s - this.scaleOffset * e) * m;
203-
this.scaleOffset += this.scaleVelocity * step;
215+
this.scaleOffset += this.scaleVelocity * t;
204216
this.scaleVelocity *= d;
205217
}
206218
if (rotateOrShearX) {
207-
this.rotateVelocity += (-0.01 * l * (w * s + g * c) - this.rotateOffset * e) * m;
208-
this.rotateOffset += this.rotateVelocity * step;
219+
this.rotateVelocity -= ((w * s + g * c) * h + this.rotateOffset * e) * m;
220+
this.rotateOffset += this.rotateVelocity * t;
209221
this.rotateVelocity *= d;
210-
if (remaining < step) break;
222+
if (a < t) break;
211223
const r = this.rotateOffset * mr + ca;
212224
c = Math.cos(r);
213225
s = Math.sin(r);
214-
} else if (remaining < step) //
226+
} else if (a < t) //
215227
break;
216228
}
217229
}
218230
}
219-
this.remaining = remaining;
231+
this.remaining = a;
220232
}
221233
this.cx = bone.worldX;
222234
this.cy = bone.worldY;
@@ -268,6 +280,8 @@ export class PhysicsConstraint implements Updatable {
268280
bone.updateAppliedTransform();
269281
}
270282

283+
/** Translates the physics constraint so next {@link #update(Physics)} forces are applied as if the bone moved an additional
284+
* amount in world space. */
271285
translate (x: number, y: number) {
272286
this.ux -= x;
273287
this.uy -= y;
@@ -278,10 +292,7 @@ export class PhysicsConstraint implements Updatable {
278292
/** Rotates the physics constraint so next {@link #update(Physics)} forces are applied as if the bone rotated around the
279293
* specified point in world space. */
280294
rotate (x: number, y: number, degrees: number) {
281-
let r = degrees * MathUtils.degRad, cos = Math.cos(r), sin = Math.sin(r);
282-
r = this.tx * cos - this.ty * sin;
283-
this.ty = this.tx * sin + this.ty * cos;
284-
this.tx = r;
295+
const r = degrees * MathUtils.degRad, cos = Math.cos(r), sin = Math.sin(r);
285296
const dx = this.cx - x, dy = this.cy - y;
286297
this.translate(dx * cos - dy * sin - dx, dx * sin + dy * cos - dy);
287298
}

spine-ts/spine-core/src/PhysicsConstraintData.ts

+1
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export class PhysicsConstraintData extends ConstraintData {
4848
rotate = 0;
4949
scaleX = 0;
5050
shearX = 0;
51+
limit = 0;
5152
step = 0;
5253
inertia = 0;
5354
strength = 0;

spine-ts/spine-core/src/Skeleton.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export class Skeleton {
5454
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
5555
bones: Array<Bone>;
5656

57-
/** The skeleton's slots. */
57+
/** The skeleton's slots in the setup pose draw order. */
5858
slots: Array<Slot>;
5959

6060
/** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */

spine-ts/spine-core/src/SkeletonBinary.ts

+54-39
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,11 @@ export class SkeletonBinary {
8080
skeletonData.y = input.readFloat();
8181
skeletonData.width = input.readFloat();
8282
skeletonData.height = input.readFloat();
83+
skeletonData.referenceScale = input.readFloat() * scale;
8384

8485
let nonessential = input.readBoolean();
8586
if (nonessential) {
8687
skeletonData.fps = input.readFloat();
87-
8888
skeletonData.imagesPath = input.readString();
8989
skeletonData.audioPath = input.readString();
9090
}
@@ -128,6 +128,14 @@ export class SkeletonBinary {
128128
for (let i = 0; i < n; i++) {
129129
let slotName = input.readString();
130130
if (!slotName) throw new Error("Slot name must not be null.");
131+
let path: string | null = null;
132+
if (nonessential) {
133+
const slash = slotName!.lastIndexOf('/');
134+
if (slash != -1) {
135+
path = slotName.substring(0, slash);
136+
slotName = slotName.substring(slash + 1);
137+
}
138+
}
131139
let boneData = skeletonData.bones[input.readInt(true)];
132140
let data = new SlotData(i, slotName, boneData);
133141
Color.rgba8888ToColor(data.color, input.readInt32());
@@ -137,7 +145,10 @@ export class SkeletonBinary {
137145

138146
data.attachmentName = input.readStringRef();
139147
data.blendMode = input.readInt(true);
140-
if (nonessential) data.visible = input.readBoolean();
148+
if (nonessential) {
149+
data.visible = input.readBoolean();
150+
data.path = path;
151+
}
141152
skeletonData.slots.push(data);
142153
}
143154

@@ -152,14 +163,14 @@ export class SkeletonBinary {
152163
for (let ii = 0; ii < nn; ii++)
153164
data.bones.push(skeletonData.bones[input.readInt(true)]);
154165
data.target = skeletonData.bones[input.readInt(true)];
155-
data.mix = input.readFloat();
156-
data.softness = input.readFloat() * scale;
157166
let flags = input.readByte();
158167
data.skinRequired = (flags & 1) != 0;
159168
data.bendDirection = (flags & 2) != 0 ? 1 : -1;
160169
data.compress = (flags & 4) != 0;
161170
data.stretch = (flags & 8) != 0;
162171
data.uniform = (flags & 16) != 0;
172+
if ((flags & 32) != 0) data.mix = (flags & 64) != 0 ? input.readFloat() : 1;
173+
if ((flags & 128) != 0) data.softness = input.readFloat() * scale;
163174
skeletonData.ikConstraints.push(data);
164175
}
165176

@@ -174,22 +185,23 @@ export class SkeletonBinary {
174185
for (let ii = 0; ii < nn; ii++)
175186
data.bones.push(skeletonData.bones[input.readInt(true)]);
176187
data.target = skeletonData.bones[input.readInt(true)];
177-
const flags = input.readByte();
188+
let flags = input.readByte();
178189
data.skinRequired = (flags & 1) != 0;
179190
data.local = (flags & 2) != 0;
180191
data.relative = (flags & 4) != 0;
181-
data.offsetRotation = input.readFloat();
182-
data.offsetX = input.readFloat() * scale;
183-
data.offsetY = input.readFloat() * scale;
184-
data.offsetScaleX = input.readFloat();
185-
data.offsetScaleY = input.readFloat();
186-
data.offsetShearY = input.readFloat();
187-
data.mixRotate = input.readFloat();
188-
data.mixX = input.readFloat();
189-
data.mixY = input.readFloat();
190-
data.mixScaleX = input.readFloat();
191-
data.mixScaleY = input.readFloat();
192-
data.mixShearY = input.readFloat();
192+
if ((flags & 8) != 0) data.offsetRotation = input.readFloat();
193+
if ((flags & 16) != 0) data.offsetX = input.readFloat() * scale;
194+
if ((flags & 32) != 0) data.offsetY = input.readFloat() * scale;
195+
if ((flags & 64) != 0) data.offsetScaleX = input.readFloat();
196+
if ((flags & 128) != 0) data.offsetScaleY = input.readFloat();
197+
flags = input.readByte();
198+
if ((flags & 1) != 0) data.offsetShearY = input.readFloat();
199+
if ((flags & 2) != 0) data.mixRotate = input.readFloat();
200+
if ((flags & 4) != 0) data.mixX = input.readFloat();
201+
if ((flags & 8) != 0) data.mixY = input.readFloat();
202+
if ((flags & 16) != 0) data.mixScaleX = input.readFloat();
203+
if ((flags & 32) != 0) data.mixScaleY = input.readFloat();
204+
if ((flags & 64) != 0) data.mixShearY = input.readFloat();
193205
skeletonData.transformConstraints.push(data);
194206
}
195207

@@ -205,10 +217,11 @@ export class SkeletonBinary {
205217
for (let ii = 0; ii < nn; ii++)
206218
data.bones.push(skeletonData.bones[input.readInt(true)]);
207219
data.target = skeletonData.slots[input.readInt(true)];
208-
data.positionMode = input.readInt(true);
209-
data.spacingMode = input.readInt(true);
210-
data.rotateMode = input.readInt(true);
211-
data.offsetRotation = input.readFloat();
220+
const flags = input.readByte();
221+
data.positionMode = flags & 1;
222+
data.spacingMode = (flags >> 1) & 3;
223+
data.rotateMode = (flags >> 3) & 3;
224+
if ((flags & 128) != 0) data.offsetRotation = input.readFloat();
212225
data.position = input.readFloat();
213226
if (data.positionMode == PositionMode.Fixed) data.position *= scale;
214227
data.spacing = input.readFloat();
@@ -234,14 +247,14 @@ export class SkeletonBinary {
234247
if ((flags & 8) != 0) data.rotate = input.readFloat();
235248
if ((flags & 16) != 0) data.scaleX = input.readFloat();
236249
if ((flags & 32) != 0) data.shearX = input.readFloat();
250+
data.limit = ((flags & 64) != 0 ? input.readFloat() : 5000) * scale;
237251
data.step = 1 / input.readByte();
238252
data.inertia = input.readFloat();
239253
data.strength = input.readFloat();
240254
data.damping = input.readFloat();
241-
data.massInverse = input.readFloat();
242-
data.wind = input.readFloat() * scale;
243-
data.gravity = input.readFloat() * scale;
244-
data.mix = input.readFloat();
255+
data.massInverse = (flags & 128) != 0 ? input.readFloat() : 1;
256+
data.wind = input.readFloat();
257+
data.gravity = input.readFloat();
245258
flags = input.readByte();
246259
if ((flags & 1) != 0) data.inertiaGlobal = true;
247260
if ((flags & 2) != 0) data.strengthGlobal = true;
@@ -250,6 +263,7 @@ export class SkeletonBinary {
250263
if ((flags & 16) != 0) data.windGlobal = true;
251264
if ((flags & 32) != 0) data.gravityGlobal = true;
252265
if ((flags & 64) != 0) data.mixGlobal = true;
266+
data.mix = (flags & 128) != 0 ? input.readFloat() : 1;
253267
skeletonData.physicsConstraints.push(data);
254268
}
255269

@@ -365,7 +379,7 @@ export class SkeletonBinary {
365379
let path = (flags & 16) != 0 ? input.readStringRef() : null;
366380
const color = (flags & 32) != 0 ? input.readInt32() : 0xffffffff;
367381
const sequence = (flags & 64) != 0 ? this.readSequence(input) : null;
368-
let rotation = input.readFloat();
382+
let rotation = (flags & 128) != 0 ? input.readFloat() : 0;
369383
let x = input.readFloat();
370384
let y = input.readFloat();
371385
let scaleX = input.readFloat();
@@ -827,19 +841,20 @@ export class SkeletonBinary {
827841
for (let i = 0, n = input.readInt(true); i < n; i++) {
828842
let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
829843
let timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
830-
let time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale;
844+
let flags = input.readByte();
845+
let time = input.readFloat(), mix = (flags & 1) != 0 ? ((flags & 2) != 0 ? input.readFloat() : 1) : 0;
846+
let softness = (flags & 4) != 0 ? input.readFloat() * scale : 0;
831847
for (let frame = 0, bezier = 0; ; frame++) {
832-
const flags = input.readByte();
833-
timeline.setFrame(frame, time, mix, softness, input.readByte(), (flags & 1) != 0, (flags & 2) != 0);
848+
timeline.setFrame(frame, time, mix, softness, (flags & 8) != 0 ? 1 : -1, (flags & 16) != 0, (flags & 32) != 0);
834849
if (frame == frameLast) break;
835-
let time2 = input.readFloat(), mix2 = input.readFloat(), softness2 = input.readFloat() * scale;
836-
switch (input.readByte()) {
837-
case CURVE_STEPPED:
838-
timeline.setStepped(frame);
839-
break;
840-
case CURVE_BEZIER:
841-
setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
842-
setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
850+
flags = input.readByte();
851+
const time2 = input.readFloat(), mix2 = (flags & 1) != 0 ? ((flags & 2) != 0 ? input.readFloat() : 1) : 0;
852+
const softness2 = (flags & 4) != 0 ? input.readFloat() * scale : 0;
853+
if ((flags & 64) != 0) {
854+
timeline.setStepped(frame);
855+
} else if ((flags & 128) != 0) {
856+
setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
857+
setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
843858
}
844859
time = time2;
845860
mix = mix2;
@@ -953,10 +968,10 @@ export class SkeletonBinary {
953968
timelines.push(readTimeline1(input, new PhysicsConstraintMassTimeline(frameCount, bezierCount, index), 1));
954969
break;
955970
case PHYSICS_WIND:
956-
timelines.push(readTimeline1(input, new PhysicsConstraintWindTimeline(frameCount, bezierCount, index), scale));
971+
timelines.push(readTimeline1(input, new PhysicsConstraintWindTimeline(frameCount, bezierCount, index), 1));
957972
break;
958973
case PHYSICS_GRAVITY:
959-
timelines.push(readTimeline1(input, new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index), scale));
974+
timelines.push(readTimeline1(input, new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index), 1));
960975
break;
961976
case PHYSICS_MIX:
962977
timelines.push(readTimeline1(input, new PhysicsConstraintMixTimeline(frameCount, bezierCount, index), 1));

spine-ts/spine-core/src/SkeletonData.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,9 @@ export class SkeletonData {
4949
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
5050
bones = new Array<BoneData>(); // Ordered parents first.
5151

52-
/** The skeleton's slots. */
52+
/** The skeleton's slots in the setup pose draw order. */
5353
slots = new Array<SlotData>(); // Setup pose draw order.
54+
5455
skins = new Array<Skin>();
5556

5657
/** The skeleton's default skin. By default this skin contains all attachments that were not in a skin in Spine.
@@ -89,6 +90,10 @@ export class SkeletonData {
8990
/** The height of the skeleton's axis aligned bounding box in the setup pose. */
9091
height: number = 0;
9192

93+
/** Baseline scale factor for applying distance-dependent effects on non-scalable properties, such as angle or scale. Default
94+
* is 100. */
95+
referenceScale = 100;
96+
9297
/** The Spine version used to export the skeleton data, or null. */
9398
version: string | null = null;
9499

0 commit comments

Comments
 (0)