[ts] Port latest physics changes.

This commit is contained in:
Mario Zechner 2024-03-19 09:57:13 +01:00
parent 86a320afc2
commit be767b21ea
8 changed files with 128 additions and 85 deletions

View File

@ -47,7 +47,7 @@ export class IkConstraintData extends ConstraintData {
} }
/** Controls the bend direction of the IK bones, either 1 or -1. */ /** Controls the bend direction of the IK bones, either 1 or -1. */
bendDirection = 1; bendDirection = 0;
/** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */ /** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
compress = false; compress = false;
@ -61,7 +61,7 @@ export class IkConstraintData extends ConstraintData {
uniform = false; uniform = false;
/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */ /** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
mix = 1; mix = 0;
/** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */ /** For two bone IK, the distance from the maximum reach of the bones that rotation will slow. */
softness = 0; softness = 0;

View File

@ -134,7 +134,8 @@ export class PhysicsConstraint implements Updatable {
this.reset(); this.reset();
// Fall through. // Fall through.
case Physics.update: case Physics.update:
this.remaining += Math.max(this.skeleton.time - this.lastTime, 0); const delta = Math.max(this.skeleton.time - this.lastTime, 0);
this.remaining += delta;
this.lastTime = this.skeleton.time; this.lastTime = this.skeleton.time;
const bx = bone.worldX, by = bone.worldY; const bx = bone.worldX, by = bone.worldY;
@ -143,41 +144,52 @@ export class PhysicsConstraint implements Updatable {
this.ux = bx; this.ux = bx;
this.uy = by; this.uy = by;
} else { } else {
let remaining = this.remaining, i = this.inertia, step = this.data.step; let a = this.remaining, i = this.inertia, q = this.data.limit * delta, t = this.data.step, f = this.skeleton.data.referenceScale, d = -1;
if (x || y) { if (x || y) {
if (x) { if (x) {
this.xOffset += (this.ux - bx) * i; const u = (this.ux - bx) * i;
this.xOffset += u > q ? q : u < -q ? -q : u;
this.ux = bx; this.ux = bx;
} }
if (y) { if (y) {
this.yOffset += (this.uy - by) * i; const u = (this.uy - by) * i;
this.yOffset += u > q ? q : u < -q ? -q : u;
this.uy = by; this.uy = by;
} }
if (remaining >= step) { if (a >= t) {
const m = this.massInverse * step, e = this.strength, w = this.wind * 100, g = this.gravity * -100; d = Math.pow(this.damping, 60 * t);
const d = Math.pow(this.damping, 60 * step); const m = this.massInverse * t, e = this.strength, w = this.wind * f, g = this.gravity * f;
do { do {
if (x) { if (x) {
this.xVelocity += (w - this.xOffset * e) * m; this.xVelocity += (w - this.xOffset * e) * m;
this.xOffset += this.xVelocity * step; this.xOffset += this.xVelocity * t;
this.xVelocity *= d; this.xVelocity *= d;
} }
if (y) { if (y) {
this.yVelocity += (g - this.yOffset * e) * m; this.yVelocity -= (g + this.yOffset * e) * m;
this.yOffset += this.yVelocity * step; this.yOffset += this.yVelocity * t;
this.yVelocity *= d; this.yVelocity *= d;
} }
remaining -= step; a -= t;
} while (remaining >= step); } while (a >= t);
} }
if (x) bone.worldX += this.xOffset * mix * this.data.x; if (x) bone.worldX += this.xOffset * mix * this.data.x;
if (y) bone.worldY += this.yOffset * mix * this.data.y; if (y) bone.worldY += this.yOffset * mix * this.data.y;
} }
if (rotateOrShearX || scaleX) { if (rotateOrShearX || scaleX) {
let ca = Math.atan2(bone.c, bone.a), c = 0, s = 0, mr = 0; let ca = Math.atan2(bone.c, bone.a), c = 0, s = 0, mr = 0;
let dx = this.cx - bone.worldX, dy = this.cy - bone.worldY;
if (dx > q)
dx = q;
else if (dx < -q) //
dx = -q;
if (dy > q)
dy = q;
else if (dy < -q) //
dy = -q;
if (rotateOrShearX) { if (rotateOrShearX) {
mr = (this.data.rotate + this.data.shearX) * mix; mr = (this.data.rotate + this.data.shearX) * mix;
let dx = this.cx - bone.worldX, dy = this.cy - bone.worldY, r = Math.atan2(dy + this.ty, dx + this.tx) - ca - this.rotateOffset * mr; let r = Math.atan2(dy + this.ty, dx + this.tx) - ca - this.rotateOffset * mr;
this.rotateOffset += (r - Math.ceil(r * MathUtils.invPI2 - 0.5) * MathUtils.PI2) * i; this.rotateOffset += (r - Math.ceil(r * MathUtils.invPI2 - 0.5) * MathUtils.PI2) * i;
r = this.rotateOffset * mr + ca; r = this.rotateOffset * mr + ca;
c = Math.cos(r); c = Math.cos(r);
@ -190,33 +202,33 @@ export class PhysicsConstraint implements Updatable {
c = Math.cos(ca); c = Math.cos(ca);
s = Math.sin(ca); s = Math.sin(ca);
const r = l * bone.getWorldScaleX(); const r = l * bone.getWorldScaleX();
if (r > 0) this.scaleOffset += ((this.cx - bone.worldX) * c + (this.cy - bone.worldY) * s) * i / r; if (r > 0) this.scaleOffset += (dx * c + dy * s) * i / r;
} }
remaining = this.remaining; a = this.remaining;
if (remaining >= step) { if (a >= t) {
const m = this.massInverse * step, e = this.strength, w = this.wind, g = this.gravity; if (d == -1) d = Math.pow(this.damping, 60 * t);
const d = Math.pow(this.damping, 60 * step); const m = this.massInverse * t, e = this.strength, w = this.wind, g = this.gravity, h = l / f;
while (true) { while (true) {
remaining -= step; a -= t;
if (scaleX) { if (scaleX) {
this.scaleVelocity += (w * c - g * s - this.scaleOffset * e) * m; this.scaleVelocity += (w * c - g * s - this.scaleOffset * e) * m;
this.scaleOffset += this.scaleVelocity * step; this.scaleOffset += this.scaleVelocity * t;
this.scaleVelocity *= d; this.scaleVelocity *= d;
} }
if (rotateOrShearX) { if (rotateOrShearX) {
this.rotateVelocity += (-0.01 * l * (w * s + g * c) - this.rotateOffset * e) * m; this.rotateVelocity -= ((w * s + g * c) * h + this.rotateOffset * e) * m;
this.rotateOffset += this.rotateVelocity * step; this.rotateOffset += this.rotateVelocity * t;
this.rotateVelocity *= d; this.rotateVelocity *= d;
if (remaining < step) break; if (a < t) break;
const r = this.rotateOffset * mr + ca; const r = this.rotateOffset * mr + ca;
c = Math.cos(r); c = Math.cos(r);
s = Math.sin(r); s = Math.sin(r);
} else if (remaining < step) // } else if (a < t) //
break; break;
} }
} }
} }
this.remaining = remaining; this.remaining = a;
} }
this.cx = bone.worldX; this.cx = bone.worldX;
this.cy = bone.worldY; this.cy = bone.worldY;
@ -268,6 +280,8 @@ export class PhysicsConstraint implements Updatable {
bone.updateAppliedTransform(); bone.updateAppliedTransform();
} }
/** Translates the physics constraint so next {@link #update(Physics)} forces are applied as if the bone moved an additional
* amount in world space. */
translate (x: number, y: number) { translate (x: number, y: number) {
this.ux -= x; this.ux -= x;
this.uy -= y; this.uy -= y;
@ -278,10 +292,7 @@ export class PhysicsConstraint implements Updatable {
/** Rotates the physics constraint so next {@link #update(Physics)} forces are applied as if the bone rotated around the /** Rotates the physics constraint so next {@link #update(Physics)} forces are applied as if the bone rotated around the
* specified point in world space. */ * specified point in world space. */
rotate (x: number, y: number, degrees: number) { rotate (x: number, y: number, degrees: number) {
let r = degrees * MathUtils.degRad, cos = Math.cos(r), sin = Math.sin(r); const r = degrees * MathUtils.degRad, cos = Math.cos(r), sin = Math.sin(r);
r = this.tx * cos - this.ty * sin;
this.ty = this.tx * sin + this.ty * cos;
this.tx = r;
const dx = this.cx - x, dy = this.cy - y; const dx = this.cx - x, dy = this.cy - y;
this.translate(dx * cos - dy * sin - dx, dx * sin + dy * cos - dy); this.translate(dx * cos - dy * sin - dx, dx * sin + dy * cos - dy);
} }

View File

@ -48,6 +48,7 @@ export class PhysicsConstraintData extends ConstraintData {
rotate = 0; rotate = 0;
scaleX = 0; scaleX = 0;
shearX = 0; shearX = 0;
limit = 0;
step = 0; step = 0;
inertia = 0; inertia = 0;
strength = 0; strength = 0;

View File

@ -54,7 +54,7 @@ export class Skeleton {
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */ /** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
bones: Array<Bone>; bones: Array<Bone>;
/** The skeleton's slots. */ /** The skeleton's slots in the setup pose draw order. */
slots: Array<Slot>; slots: Array<Slot>;
/** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */ /** The skeleton's slots in the order they should be drawn. The returned array may be modified to change the draw order. */

View File

@ -80,11 +80,11 @@ export class SkeletonBinary {
skeletonData.y = input.readFloat(); skeletonData.y = input.readFloat();
skeletonData.width = input.readFloat(); skeletonData.width = input.readFloat();
skeletonData.height = input.readFloat(); skeletonData.height = input.readFloat();
skeletonData.referenceScale = input.readFloat() * scale;
let nonessential = input.readBoolean(); let nonessential = input.readBoolean();
if (nonessential) { if (nonessential) {
skeletonData.fps = input.readFloat(); skeletonData.fps = input.readFloat();
skeletonData.imagesPath = input.readString(); skeletonData.imagesPath = input.readString();
skeletonData.audioPath = input.readString(); skeletonData.audioPath = input.readString();
} }
@ -128,6 +128,14 @@ export class SkeletonBinary {
for (let i = 0; i < n; i++) { for (let i = 0; i < n; i++) {
let slotName = input.readString(); let slotName = input.readString();
if (!slotName) throw new Error("Slot name must not be null."); if (!slotName) throw new Error("Slot name must not be null.");
let path: string | null = null;
if (nonessential) {
const slash = slotName!.lastIndexOf('/');
if (slash != -1) {
path = slotName.substring(0, slash);
slotName = slotName.substring(slash + 1);
}
}
let boneData = skeletonData.bones[input.readInt(true)]; let boneData = skeletonData.bones[input.readInt(true)];
let data = new SlotData(i, slotName, boneData); let data = new SlotData(i, slotName, boneData);
Color.rgba8888ToColor(data.color, input.readInt32()); Color.rgba8888ToColor(data.color, input.readInt32());
@ -137,7 +145,10 @@ export class SkeletonBinary {
data.attachmentName = input.readStringRef(); data.attachmentName = input.readStringRef();
data.blendMode = input.readInt(true); data.blendMode = input.readInt(true);
if (nonessential) data.visible = input.readBoolean(); if (nonessential) {
data.visible = input.readBoolean();
data.path = path;
}
skeletonData.slots.push(data); skeletonData.slots.push(data);
} }
@ -152,14 +163,14 @@ export class SkeletonBinary {
for (let ii = 0; ii < nn; ii++) for (let ii = 0; ii < nn; ii++)
data.bones.push(skeletonData.bones[input.readInt(true)]); data.bones.push(skeletonData.bones[input.readInt(true)]);
data.target = skeletonData.bones[input.readInt(true)]; data.target = skeletonData.bones[input.readInt(true)];
data.mix = input.readFloat();
data.softness = input.readFloat() * scale;
let flags = input.readByte(); let flags = input.readByte();
data.skinRequired = (flags & 1) != 0; data.skinRequired = (flags & 1) != 0;
data.bendDirection = (flags & 2) != 0 ? 1 : -1; data.bendDirection = (flags & 2) != 0 ? 1 : -1;
data.compress = (flags & 4) != 0; data.compress = (flags & 4) != 0;
data.stretch = (flags & 8) != 0; data.stretch = (flags & 8) != 0;
data.uniform = (flags & 16) != 0; data.uniform = (flags & 16) != 0;
if ((flags & 32) != 0) data.mix = (flags & 64) != 0 ? input.readFloat() : 1;
if ((flags & 128) != 0) data.softness = input.readFloat() * scale;
skeletonData.ikConstraints.push(data); skeletonData.ikConstraints.push(data);
} }
@ -174,22 +185,23 @@ export class SkeletonBinary {
for (let ii = 0; ii < nn; ii++) for (let ii = 0; ii < nn; ii++)
data.bones.push(skeletonData.bones[input.readInt(true)]); data.bones.push(skeletonData.bones[input.readInt(true)]);
data.target = skeletonData.bones[input.readInt(true)]; data.target = skeletonData.bones[input.readInt(true)];
const flags = input.readByte(); let flags = input.readByte();
data.skinRequired = (flags & 1) != 0; data.skinRequired = (flags & 1) != 0;
data.local = (flags & 2) != 0; data.local = (flags & 2) != 0;
data.relative = (flags & 4) != 0; data.relative = (flags & 4) != 0;
data.offsetRotation = input.readFloat(); if ((flags & 8) != 0) data.offsetRotation = input.readFloat();
data.offsetX = input.readFloat() * scale; if ((flags & 16) != 0) data.offsetX = input.readFloat() * scale;
data.offsetY = input.readFloat() * scale; if ((flags & 32) != 0) data.offsetY = input.readFloat() * scale;
data.offsetScaleX = input.readFloat(); if ((flags & 64) != 0) data.offsetScaleX = input.readFloat();
data.offsetScaleY = input.readFloat(); if ((flags & 128) != 0) data.offsetScaleY = input.readFloat();
data.offsetShearY = input.readFloat(); flags = input.readByte();
data.mixRotate = input.readFloat(); if ((flags & 1) != 0) data.offsetShearY = input.readFloat();
data.mixX = input.readFloat(); if ((flags & 2) != 0) data.mixRotate = input.readFloat();
data.mixY = input.readFloat(); if ((flags & 4) != 0) data.mixX = input.readFloat();
data.mixScaleX = input.readFloat(); if ((flags & 8) != 0) data.mixY = input.readFloat();
data.mixScaleY = input.readFloat(); if ((flags & 16) != 0) data.mixScaleX = input.readFloat();
data.mixShearY = input.readFloat(); if ((flags & 32) != 0) data.mixScaleY = input.readFloat();
if ((flags & 64) != 0) data.mixShearY = input.readFloat();
skeletonData.transformConstraints.push(data); skeletonData.transformConstraints.push(data);
} }
@ -205,10 +217,11 @@ export class SkeletonBinary {
for (let ii = 0; ii < nn; ii++) for (let ii = 0; ii < nn; ii++)
data.bones.push(skeletonData.bones[input.readInt(true)]); data.bones.push(skeletonData.bones[input.readInt(true)]);
data.target = skeletonData.slots[input.readInt(true)]; data.target = skeletonData.slots[input.readInt(true)];
data.positionMode = input.readInt(true); const flags = input.readByte();
data.spacingMode = input.readInt(true); data.positionMode = flags & 1;
data.rotateMode = input.readInt(true); data.spacingMode = (flags >> 1) & 3;
data.offsetRotation = input.readFloat(); data.rotateMode = (flags >> 3) & 3;
if ((flags & 128) != 0) data.offsetRotation = input.readFloat();
data.position = input.readFloat(); data.position = input.readFloat();
if (data.positionMode == PositionMode.Fixed) data.position *= scale; if (data.positionMode == PositionMode.Fixed) data.position *= scale;
data.spacing = input.readFloat(); data.spacing = input.readFloat();
@ -234,14 +247,14 @@ export class SkeletonBinary {
if ((flags & 8) != 0) data.rotate = input.readFloat(); if ((flags & 8) != 0) data.rotate = input.readFloat();
if ((flags & 16) != 0) data.scaleX = input.readFloat(); if ((flags & 16) != 0) data.scaleX = input.readFloat();
if ((flags & 32) != 0) data.shearX = input.readFloat(); if ((flags & 32) != 0) data.shearX = input.readFloat();
data.limit = ((flags & 64) != 0 ? input.readFloat() : 5000) * scale;
data.step = 1 / input.readByte(); data.step = 1 / input.readByte();
data.inertia = input.readFloat(); data.inertia = input.readFloat();
data.strength = input.readFloat(); data.strength = input.readFloat();
data.damping = input.readFloat(); data.damping = input.readFloat();
data.massInverse = input.readFloat(); data.massInverse = (flags & 128) != 0 ? input.readFloat() : 1;
data.wind = input.readFloat() * scale; data.wind = input.readFloat();
data.gravity = input.readFloat() * scale; data.gravity = input.readFloat();
data.mix = input.readFloat();
flags = input.readByte(); flags = input.readByte();
if ((flags & 1) != 0) data.inertiaGlobal = true; if ((flags & 1) != 0) data.inertiaGlobal = true;
if ((flags & 2) != 0) data.strengthGlobal = true; if ((flags & 2) != 0) data.strengthGlobal = true;
@ -250,6 +263,7 @@ export class SkeletonBinary {
if ((flags & 16) != 0) data.windGlobal = true; if ((flags & 16) != 0) data.windGlobal = true;
if ((flags & 32) != 0) data.gravityGlobal = true; if ((flags & 32) != 0) data.gravityGlobal = true;
if ((flags & 64) != 0) data.mixGlobal = true; if ((flags & 64) != 0) data.mixGlobal = true;
data.mix = (flags & 128) != 0 ? input.readFloat() : 1;
skeletonData.physicsConstraints.push(data); skeletonData.physicsConstraints.push(data);
} }
@ -365,7 +379,7 @@ export class SkeletonBinary {
let path = (flags & 16) != 0 ? input.readStringRef() : null; let path = (flags & 16) != 0 ? input.readStringRef() : null;
const color = (flags & 32) != 0 ? input.readInt32() : 0xffffffff; const color = (flags & 32) != 0 ? input.readInt32() : 0xffffffff;
const sequence = (flags & 64) != 0 ? this.readSequence(input) : null; const sequence = (flags & 64) != 0 ? this.readSequence(input) : null;
let rotation = input.readFloat(); let rotation = (flags & 128) != 0 ? input.readFloat() : 0;
let x = input.readFloat(); let x = input.readFloat();
let y = input.readFloat(); let y = input.readFloat();
let scaleX = input.readFloat(); let scaleX = input.readFloat();
@ -827,19 +841,20 @@ export class SkeletonBinary {
for (let i = 0, n = input.readInt(true); i < n; i++) { for (let i = 0, n = input.readInt(true); i < n; i++) {
let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1; let index = input.readInt(true), frameCount = input.readInt(true), frameLast = frameCount - 1;
let timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index); let timeline = new IkConstraintTimeline(frameCount, input.readInt(true), index);
let time = input.readFloat(), mix = input.readFloat(), softness = input.readFloat() * scale; let flags = input.readByte();
let time = input.readFloat(), mix = (flags & 1) != 0 ? ((flags & 2) != 0 ? input.readFloat() : 1) : 0;
let softness = (flags & 4) != 0 ? input.readFloat() * scale : 0;
for (let frame = 0, bezier = 0; ; frame++) { for (let frame = 0, bezier = 0; ; frame++) {
const flags = input.readByte(); timeline.setFrame(frame, time, mix, softness, (flags & 8) != 0 ? 1 : -1, (flags & 16) != 0, (flags & 32) != 0);
timeline.setFrame(frame, time, mix, softness, input.readByte(), (flags & 1) != 0, (flags & 2) != 0);
if (frame == frameLast) break; if (frame == frameLast) break;
let time2 = input.readFloat(), mix2 = input.readFloat(), softness2 = input.readFloat() * scale; flags = input.readByte();
switch (input.readByte()) { const time2 = input.readFloat(), mix2 = (flags & 1) != 0 ? ((flags & 2) != 0 ? input.readFloat() : 1) : 0;
case CURVE_STEPPED: const softness2 = (flags & 4) != 0 ? input.readFloat() * scale : 0;
timeline.setStepped(frame); if ((flags & 64) != 0) {
break; timeline.setStepped(frame);
case CURVE_BEZIER: } else if ((flags & 128) != 0) {
setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1); setBezier(input, timeline, bezier++, frame, 0, time, time2, mix, mix2, 1);
setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale); setBezier(input, timeline, bezier++, frame, 1, time, time2, softness, softness2, scale);
} }
time = time2; time = time2;
mix = mix2; mix = mix2;
@ -953,10 +968,10 @@ export class SkeletonBinary {
timelines.push(readTimeline1(input, new PhysicsConstraintMassTimeline(frameCount, bezierCount, index), 1)); timelines.push(readTimeline1(input, new PhysicsConstraintMassTimeline(frameCount, bezierCount, index), 1));
break; break;
case PHYSICS_WIND: case PHYSICS_WIND:
timelines.push(readTimeline1(input, new PhysicsConstraintWindTimeline(frameCount, bezierCount, index), scale)); timelines.push(readTimeline1(input, new PhysicsConstraintWindTimeline(frameCount, bezierCount, index), 1));
break; break;
case PHYSICS_GRAVITY: case PHYSICS_GRAVITY:
timelines.push(readTimeline1(input, new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index), scale)); timelines.push(readTimeline1(input, new PhysicsConstraintGravityTimeline(frameCount, bezierCount, index), 1));
break; break;
case PHYSICS_MIX: case PHYSICS_MIX:
timelines.push(readTimeline1(input, new PhysicsConstraintMixTimeline(frameCount, bezierCount, index), 1)); timelines.push(readTimeline1(input, new PhysicsConstraintMixTimeline(frameCount, bezierCount, index), 1));

View File

@ -49,8 +49,9 @@ export class SkeletonData {
/** The skeleton's bones, sorted parent first. The root bone is always the first bone. */ /** The skeleton's bones, sorted parent first. The root bone is always the first bone. */
bones = new Array<BoneData>(); // Ordered parents first. bones = new Array<BoneData>(); // Ordered parents first.
/** The skeleton's slots. */ /** The skeleton's slots in the setup pose draw order. */
slots = new Array<SlotData>(); // Setup pose draw order. slots = new Array<SlotData>(); // Setup pose draw order.
skins = new Array<Skin>(); skins = new Array<Skin>();
/** The skeleton's default skin. By default this skin contains all attachments that were not in a skin in Spine. /** 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 {
/** The height of the skeleton's axis aligned bounding box in the setup pose. */ /** The height of the skeleton's axis aligned bounding box in the setup pose. */
height: number = 0; height: number = 0;
/** Baseline scale factor for applying distance-dependent effects on non-scalable properties, such as angle or scale. Default
* is 100. */
referenceScale = 100;
/** The Spine version used to export the skeleton data, or null. */ /** The Spine version used to export the skeleton data, or null. */
version: string | null = null; version: string | null = null;

View File

@ -79,8 +79,10 @@ export class SkeletonJson {
skeletonData.y = skeletonMap.y; skeletonData.y = skeletonMap.y;
skeletonData.width = skeletonMap.width; skeletonData.width = skeletonMap.width;
skeletonData.height = skeletonMap.height; skeletonData.height = skeletonMap.height;
skeletonData.referenceScale = getValue(skeletonMap, "referenceScale", 100) * scale;
skeletonData.fps = skeletonMap.fps; skeletonData.fps = skeletonMap.fps;
skeletonData.imagesPath = skeletonMap.images; skeletonData.imagesPath = skeletonMap.images ?? null;
skeletonData.audioPath = skeletonMap.audio ?? null;
} }
// Bones // Bones
@ -114,9 +116,16 @@ export class SkeletonJson {
if (root.slots) { if (root.slots) {
for (let i = 0; i < root.slots.length; i++) { for (let i = 0; i < root.slots.length; i++) {
let slotMap = root.slots[i]; let slotMap = root.slots[i];
let path: string | null = null;
let slotName = slotMap.name;
const slash = slotName.lastIndexOf('/');
if (slash != -1) {
path = slotName.substring(0, slash);
slotName = slotName.substring(slash + 1);
}
let boneData = skeletonData.findBone(slotMap.bone); let boneData = skeletonData.findBone(slotMap.bone);
if (!boneData) throw new Error(`Couldn't find bone ${slotMap.bone} for slot ${slotMap.name}`); if (!boneData) throw new Error(`Couldn't find bone ${slotMap.bone} for slot ${slotName}`);
let data = new SlotData(skeletonData.slots.length, slotMap.name, boneData); let data = new SlotData(skeletonData.slots.length, slotName, boneData);
let color: string = getValue(slotMap, "color", null); let color: string = getValue(slotMap, "color", null);
if (color) data.color.setFromString(color); if (color) data.color.setFromString(color);
@ -126,6 +135,8 @@ export class SkeletonJson {
data.attachmentName = getValue(slotMap, "attachment", null); data.attachmentName = getValue(slotMap, "attachment", null);
data.blendMode = Utils.enumValue(BlendMode, getValue(slotMap, "blend", "normal")); data.blendMode = Utils.enumValue(BlendMode, getValue(slotMap, "blend", "normal"));
data.visible = getValue(slotMap, "visible", true);
data.path = path;
skeletonData.slots.push(data); skeletonData.slots.push(data);
} }
} }
@ -253,13 +264,14 @@ export class SkeletonJson {
data.rotate = getValue(constraintMap, "rotate", 0); data.rotate = getValue(constraintMap, "rotate", 0);
data.scaleX = getValue(constraintMap, "scaleX", 0); data.scaleX = getValue(constraintMap, "scaleX", 0);
data.shearX = getValue(constraintMap, "shearX", 0); data.shearX = getValue(constraintMap, "shearX", 0);
data.limit = getValue(constraintMap, "limit", 5000) * scale;
data.step = 1 / getValue(constraintMap, "fps", 60); data.step = 1 / getValue(constraintMap, "fps", 60);
data.inertia = getValue(constraintMap, "inertia", 1); data.inertia = getValue(constraintMap, "inertia", 1);
data.strength = getValue(constraintMap, "strength", 100); data.strength = getValue(constraintMap, "strength", 100);
data.damping = getValue(constraintMap, "damping", 1); data.damping = getValue(constraintMap, "damping", 1);
data.massInverse = 1 / getValue(constraintMap, "mass", 1); data.massInverse = 1 / getValue(constraintMap, "mass", 1);
data.wind = getValue(constraintMap, "wind", 0) * scale; data.wind = getValue(constraintMap, "wind", 0);
data.gravity = getValue(constraintMap, "gravity", 0) * scale; data.gravity = getValue(constraintMap, "gravity", 0);
data.mix = getValue(constraintMap, "mix", 1); data.mix = getValue(constraintMap, "mix", 1);
data.inertiaGlobal = getValue(constraintMap, "inertiaGlobal", false); data.inertiaGlobal = getValue(constraintMap, "inertiaGlobal", false);
data.strengthGlobal = getValue(constraintMap, "strengthGlobal", false); data.strengthGlobal = getValue(constraintMap, "strengthGlobal", false);
@ -911,7 +923,6 @@ export class SkeletonJson {
} }
let timeline; let timeline;
let timelineScale = 1;
if (timelineName == "inertia") if (timelineName == "inertia")
timeline = new PhysicsConstraintInertiaTimeline(frames, frames, constraintIndex); timeline = new PhysicsConstraintInertiaTimeline(frames, frames, constraintIndex);
else if (timelineName == "strength") else if (timelineName == "strength")
@ -920,19 +931,15 @@ export class SkeletonJson {
timeline = new PhysicsConstraintDampingTimeline(frames, frames, constraintIndex); timeline = new PhysicsConstraintDampingTimeline(frames, frames, constraintIndex);
else if (timelineName == "mass") else if (timelineName == "mass")
timeline = new PhysicsConstraintMassTimeline(frames, frames, constraintIndex); timeline = new PhysicsConstraintMassTimeline(frames, frames, constraintIndex);
else if (timelineName == "wind") { else if (timelineName == "wind")
timeline = new PhysicsConstraintWindTimeline(frames, frames, constraintIndex); timeline = new PhysicsConstraintWindTimeline(frames, frames, constraintIndex);
timelineScale = scale; else if (timelineName == "gravity")
}
else if (timelineName == "gravity") {
timeline = new PhysicsConstraintGravityTimeline(frames, frames, constraintIndex); timeline = new PhysicsConstraintGravityTimeline(frames, frames, constraintIndex);
timelineScale = scale;
}
else if (timelineName == "mix") // else if (timelineName == "mix") //
timeline = new PhysicsConstraintMixTimeline(frames, frames, constraintIndex); timeline = new PhysicsConstraintMixTimeline(frames, frames, constraintIndex);
else else
continue; continue;
timelines.push(readTimeline1(timelineMap, timeline, 0, timelineScale)); timelines.push(readTimeline1(timelineMap, timeline, 0, 1));
} }
} }
} }

View File

@ -58,6 +58,10 @@ export class SlotData {
/** False if the slot was hidden in Spine and nonessential data was exported. Does not affect runtime rendering. */ /** False if the slot was hidden in Spine and nonessential data was exported. Does not affect runtime rendering. */
visible = true; visible = true;
/** The folders for this slot in the draw order, delimited by <code>/</code>, or null if nonessential data was not exported. */
path: string | null = null;
constructor (index: number, name: string, boneData: BoneData) { constructor (index: number, name: string, boneData: BoneData) {
if (index < 0) throw new Error("index must be >= 0."); if (index < 0) throw new Error("index must be >= 0.");
if (!name) throw new Error("name cannot be null."); if (!name) throw new Error("name cannot be null.");