From 03d78d4381b2f6b645db70e2a84fcebac3e38778 Mon Sep 17 00:00:00 2001 From: Davide Tantillo Date: Thu, 28 Mar 2024 17:17:50 +0100 Subject: [PATCH] [ts] Port of commits c83a867, f4f22cd, 1a83e96 (inherit timeline and minor fixes). --- spine-ts/spine-core/src/Animation.ts | 80 ++++++++++++++++------- spine-ts/spine-core/src/Bone.ts | 31 +++++---- spine-ts/spine-core/src/BoneData.ts | 4 +- spine-ts/spine-core/src/IkConstraint.ts | 15 +++-- spine-ts/spine-core/src/Skeleton.ts | 2 +- spine-ts/spine-core/src/SkeletonBinary.ts | 18 +++-- spine-ts/spine-core/src/SkeletonJson.ts | 13 +++- 7 files changed, 110 insertions(+), 53 deletions(-) diff --git a/spine-ts/spine-core/src/Animation.ts b/spine-ts/spine-core/src/Animation.ts index afe054ef4..2be3f2ac6 100644 --- a/spine-ts/spine-core/src/Animation.ts +++ b/spine-ts/spine-core/src/Animation.ts @@ -39,6 +39,7 @@ import { HasTextureRegion } from "./attachments/HasTextureRegion.js"; import { SequenceMode, SequenceModeValues } from "./attachments/Sequence.js"; import { PhysicsConstraint } from "./PhysicsConstraint.js"; import { PhysicsConstraintData } from "./PhysicsConstraintData.js"; +import { Inherit } from "./BoneData.js"; /** A simple container for a list of timelines and a name. */ export class Animation { @@ -134,34 +135,35 @@ const Property = { scaleY: 4, shearX: 5, shearY: 6, + inherit: 7, - rgb: 7, - alpha: 8, - rgb2: 9, + rgb: 8, + alpha: 9, + rgb2: 10, - attachment: 10, - deform: 11, + attachment: 11, + deform: 12, - event: 12, - drawOrder: 13, + event: 13, + drawOrder: 14, - ikConstraint: 14, - transformConstraint: 15, + ikConstraint: 15, + transformConstraint: 16, - pathConstraintPosition: 16, - pathConstraintSpacing: 17, - pathConstraintMix: 18, + pathConstraintPosition: 17, + pathConstraintSpacing: 18, + pathConstraintMix: 19, - physicsConstraintInertia: 19, - physicsConstraintStrength: 20, - physicsConstraintDamping: 21, - physicsConstraintMass: 22, - physicsConstraintWind: 23, - physicsConstraintGravity: 24, - physicsConstraintMix: 25, - physicsConstraintReset: 26, + physicsConstraintInertia: 20, + physicsConstraintStrength: 21, + physicsConstraintDamping: 22, + physicsConstraintMass: 23, + physicsConstraintWind: 24, + physicsConstraintGravity: 25, + physicsConstraintMix: 26, + physicsConstraintReset: 27, - sequence: 27, + sequence: 28, } /** The interface for all timelines. */ @@ -804,7 +806,41 @@ export class ShearYTimeline extends CurveTimeline1 implements BoneTimeline { apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { let bone = skeleton.bones[this.boneIndex]; - if (bone.active) bone.shearY = this.getRelativeValue(time, alpha, blend, bone.shearX, bone.data.shearY); + if (bone.active) bone.shearY = this.getRelativeValue(time, alpha, blend, bone.shearY, bone.data.shearY); + } +} + +export class InheritTimeline extends Timeline implements BoneTimeline { + boneIndex = 0; + + constructor (frameCount: number, boneIndex: number) { + super(frameCount, [Property.inherit + "|" + boneIndex]); + this.boneIndex = boneIndex; + } + + public getFrameEntries () { + return 2/*ENTRIES*/; + } + + /** Sets the transform mode for the specified frame. + * @param frame Between 0 and frameCount, inclusive. + * @param time The frame time in seconds. */ + public setFrame (frame: number, time: number, inherit: Inherit) { + frame *= 2/*ENTRIES*/; + this.frames[frame] = time; + this.frames[frame + 1/*INHERIT*/] = inherit; + } + + public apply (skeleton: Skeleton, lastTime: number, time: number, events: Array, alpha: number, blend: MixBlend, direction: MixDirection) { + let bone = skeleton.bones[this.boneIndex]; + if (!bone.active) return; + + let frames = this.frames; + if (time < frames[0]) { + if (blend == MixBlend.setup || blend == MixBlend.first) bone.inherit = bone.data.inherit; + return; + } + bone.inherit = this.frames[Timeline.search(frames, time, 2/*ENTRIES*/) + 1/*INHERIT*/]; } } diff --git a/spine-ts/spine-core/src/Bone.ts b/spine-ts/spine-core/src/Bone.ts index c6aa2f215..f2b7272be 100644 --- a/spine-ts/spine-core/src/Bone.ts +++ b/spine-ts/spine-core/src/Bone.ts @@ -27,7 +27,7 @@ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -import { BoneData, TransformMode } from "./BoneData.js"; +import { BoneData, Inherit } from "./BoneData.js"; import { Physics, Skeleton } from "./Skeleton.js"; import { Updatable } from "./Updatable.js"; import { MathUtils, Vector2 } from "./Utils.js"; @@ -110,6 +110,8 @@ export class Bone implements Updatable { /** The world Y position. If changed, {@link #updateAppliedTransform()} should be called. */ worldX = 0; + inherit: Inherit = Inherit.Normal; + sorted = false; active = false; @@ -174,8 +176,8 @@ export class Bone implements Updatable { this.worldX = pa * x + pb * y + parent.worldX; this.worldY = pc * x + pd * y + parent.worldY; - switch (this.data.transformMode) { - case TransformMode.Normal: { + switch (this.inherit) { + case Inherit.Normal: { const rx = (rotation + shearX) * MathUtils.degRad; const ry = (rotation + 90 + shearY) * MathUtils.degRad; const la = Math.cos(rx) * scaleX; @@ -188,7 +190,7 @@ export class Bone implements Updatable { this.d = pc * lb + pd * ld; return; } - case TransformMode.OnlyTranslation: { + case Inherit.OnlyTranslation: { const rx = (rotation + shearX) * MathUtils.degRad; const ry = (rotation + 90 + shearY) * MathUtils.degRad; this.a = Math.cos(rx) * scaleX; @@ -197,7 +199,7 @@ export class Bone implements Updatable { this.d = Math.sin(ry) * scaleY; break; } - case TransformMode.NoRotationOrReflection: { + case Inherit.NoRotationOrReflection: { let s = pa * pa + pc * pc; let prx = 0; if (s > 0.0001) { @@ -224,8 +226,8 @@ export class Bone implements Updatable { this.d = pc * lb + pd * ld; break; } - case TransformMode.NoScale: - case TransformMode.NoScaleOrReflection: { + case Inherit.NoScale: + case Inherit.NoScaleOrReflection: { rotation *= MathUtils.degRad; const cos = Math.cos(rotation), sin = Math.sin(rotation); let za = (pa * cos + pb * sin) / this.skeleton.scaleX; @@ -235,7 +237,7 @@ export class Bone implements Updatable { za *= s; zc *= s; s = Math.sqrt(za * za + zc * zc); - if (this.data.transformMode == TransformMode.NoScale + if (this.inherit == Inherit.NoScale && (pa * pd - pb * pc < 0) != (this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0)) s = -s; rotation = Math.PI / 2 + Math.atan2(zc, za); const zb = Math.cos(rotation) * s; @@ -269,6 +271,7 @@ export class Bone implements Updatable { this.scaleY = data.scaleY; this.shearX = data.shearX; this.shearY = data.shearY; + this.inherit = data.inherit; } /** Computes the applied transform values from the world transform. @@ -299,14 +302,14 @@ export class Bone implements Updatable { this.ay = (dy * id - dx * ic); let ra, rb, rc, rd; - if (this.data.transformMode == TransformMode.OnlyTranslation) { + if (this.inherit == Inherit.OnlyTranslation) { ra = this.a; rb = this.b; rc = this.c; rd = this.d; } else { - switch (this.data.transformMode) { - case TransformMode.NoRotationOrReflection: { + switch (this.inherit) { + case Inherit.NoRotationOrReflection: { let s = Math.abs(pa * pd - pb * pc) / (pa * pa + pc * pc); let sa = pa / this.skeleton.scaleX; let sc = pc / this.skeleton.scaleY; @@ -317,8 +320,8 @@ export class Bone implements Updatable { ib = pb * pid; break; } - case TransformMode.NoScale: - case TransformMode.NoScaleOrReflection: + case Inherit.NoScale: + case Inherit.NoScaleOrReflection: let cos = MathUtils.cosDeg(this.rotation), sin = MathUtils.sinDeg(this.rotation); pa = (pa * cos + pb * sin) / this.skeleton.scaleX; pc = (pc * cos + pd * sin) / this.skeleton.scaleY; @@ -327,7 +330,7 @@ export class Bone implements Updatable { pa *= s; pc *= s; s = Math.sqrt(pa * pa + pc * pc); - if (this.data.transformMode == TransformMode.NoScale && pid < 0 != (this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0)) s = -s; + if (this.inherit == Inherit.NoScale && pid < 0 != (this.skeleton.scaleX < 0 != this.skeleton.scaleY < 0)) s = -s; let r = MathUtils.PI / 2 + Math.atan2(pc, pa); pb = Math.cos(r) * s; pd = Math.sin(r) * s; diff --git a/spine-ts/spine-core/src/BoneData.ts b/spine-ts/spine-core/src/BoneData.ts index ac79827b0..ddbed669f 100644 --- a/spine-ts/spine-core/src/BoneData.ts +++ b/spine-ts/spine-core/src/BoneData.ts @@ -65,7 +65,7 @@ export class BoneData { shearY = 0; /** The transform mode for how parent world transforms affect this bone. */ - transformMode = TransformMode.Normal; + inherit = Inherit.Normal; /** When true, {@link Skeleton#updateWorldTransform()} only updates this bone if the {@link Skeleton#skin} contains this * bone. @@ -92,4 +92,4 @@ export class BoneData { } /** Determines how a bone inherits world transforms from parent bones. */ -export enum TransformMode { Normal, OnlyTranslation, NoRotationOrReflection, NoScale, NoScaleOrReflection } +export enum Inherit { Normal, OnlyTranslation, NoRotationOrReflection, NoScale, NoScaleOrReflection } diff --git a/spine-ts/spine-core/src/IkConstraint.ts b/spine-ts/spine-core/src/IkConstraint.ts index 9fc98ff63..4652d5c52 100644 --- a/spine-ts/spine-core/src/IkConstraint.ts +++ b/spine-ts/spine-core/src/IkConstraint.ts @@ -28,7 +28,7 @@ *****************************************************************************/ import { Bone } from "./Bone.js"; -import { TransformMode } from "./BoneData.js"; +import { Inherit } from "./BoneData.js"; import { IkConstraintData } from "./IkConstraintData.js"; import { Physics, Skeleton } from "./Skeleton.js"; import { Updatable } from "./Updatable.js"; @@ -120,12 +120,12 @@ export class IkConstraint implements Updatable { let pa = p.a, pb = p.b, pc = p.c, pd = p.d; let rotationIK = -bone.ashearX - bone.arotation, tx = 0, ty = 0; - switch (bone.data.transformMode) { - case TransformMode.OnlyTranslation: + switch (bone.inherit) { + case Inherit.OnlyTranslation: tx = (targetX - bone.worldX) * MathUtils.signum(bone.skeleton.scaleX); ty = (targetY - bone.worldY) * MathUtils.signum(bone.skeleton.scaleY); break; - case TransformMode.NoRotationOrReflection: + case Inherit.NoRotationOrReflection: let s = Math.abs(pa * pd - pb * pc) / Math.max(0.0001, pa * pa + pc * pc); let sa = pa / bone.skeleton.scaleX; let sc = pc / bone.skeleton.scaleY; @@ -152,9 +152,9 @@ export class IkConstraint implements Updatable { rotationIK += 360; let sx = bone.ascaleX, sy = bone.ascaleY; if (compress || stretch) { - switch (bone.data.transformMode) { - case TransformMode.NoScale: - case TransformMode.NoScaleOrReflection: + switch (bone.inherit) { + case Inherit.NoScale: + case Inherit.NoScaleOrReflection: tx = targetX - bone.worldX; ty = targetY - bone.worldY; } @@ -175,6 +175,7 @@ export class IkConstraint implements Updatable { /** Applies 2 bone IK. The target is specified in the world coordinate system. * @param child A direct descendant of the parent bone. */ apply2 (parent: Bone, child: Bone, targetX: number, targetY: number, bendDir: number, stretch: boolean, uniform: boolean, softness: number, alpha: number) { + if (parent.inherit != Inherit.Normal || child.inherit != Inherit.Normal) return; let px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, sx = psx, sy = psy, csx = child.ascaleX; let os1 = 0, os2 = 0, s2 = 0; if (psx < 0) { diff --git a/spine-ts/spine-core/src/Skeleton.ts b/spine-ts/spine-core/src/Skeleton.ts index 0e12bbd55..277a21d4f 100644 --- a/spine-ts/spine-core/src/Skeleton.ts +++ b/spine-ts/spine-core/src/Skeleton.ts @@ -46,7 +46,7 @@ import { Color, Utils, MathUtils, Vector2, NumberArrayLike } from "./Utils.js"; * * See [Instance objects](http://esotericsoftware.com/spine-runtime-architecture#Instance-objects) in the Spine Runtimes Guide. */ export class Skeleton { - static yDown = false;; + static yDown = false; /** The skeleton's setup pose data. */ data: SkeletonData; diff --git a/spine-ts/spine-core/src/SkeletonBinary.ts b/spine-ts/spine-core/src/SkeletonBinary.ts index f26949e0e..21b2313ac 100644 --- a/spine-ts/spine-core/src/SkeletonBinary.ts +++ b/spine-ts/spine-core/src/SkeletonBinary.ts @@ -27,7 +27,7 @@ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -import { Animation, Timeline, AttachmentTimeline, RGBATimeline, RGBTimeline, RGBA2Timeline, RGB2Timeline, AlphaTimeline, RotateTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, IkConstraintTimeline, TransformConstraintTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PathConstraintMixTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, CurveTimeline1, CurveTimeline2, CurveTimeline, SequenceTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintWindTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintMixTimeline } from "./Animation.js"; +import { Animation, Timeline, InheritTimeline, AttachmentTimeline, RGBATimeline, RGBTimeline, RGBA2Timeline, RGB2Timeline, AlphaTimeline, RotateTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, IkConstraintTimeline, TransformConstraintTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PathConstraintMixTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, CurveTimeline1, CurveTimeline2, CurveTimeline, SequenceTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintWindTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintMixTimeline } from "./Animation.js"; import { VertexAttachment, Attachment } from "./attachments/Attachment.js"; import { AttachmentLoader } from "./attachments/AttachmentLoader.js"; import { HasTextureRegion } from "./attachments/HasTextureRegion.js"; @@ -113,7 +113,7 @@ export class SkeletonBinary { data.shearX = input.readFloat(); data.shearY = input.readFloat(); data.length = input.readFloat() * scale; - data.transformMode = input.readInt(true); + data.inherit = input.readByte(); data.skinRequired = input.readBoolean(); if (nonessential) { Color.rgba8888ToColor(data.color, input.readInt32()); @@ -248,7 +248,7 @@ export class SkeletonBinary { if ((flags & 16) != 0) data.scaleX = 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.readUnsignedByte(); data.inertia = input.readFloat(); data.strength = input.readFloat(); data.damping = input.readFloat(); @@ -802,7 +802,16 @@ export class SkeletonBinary { for (let i = 0, n = input.readInt(true); i < n; i++) { let boneIndex = input.readInt(true); for (let ii = 0, nn = input.readInt(true); ii < nn; ii++) { - let type = input.readByte(), frameCount = input.readInt(true), bezierCount = input.readInt(true); + let type = input.readByte(), frameCount = input.readInt(true); + if (type == BONE_INHERIT) { + let timeline = new InheritTimeline(frameCount, boneIndex); + for (let frame = 0; frame < frameCount; frame++) { + timeline.setFrame(frame, input.readFloat(), input.readByte()); + } + timelines.push(timeline); + continue; + } + let bezierCount = input.readInt(true); switch (type) { case BONE_ROTATE: timelines.push(readTimeline1(input, new RotateTimeline(frameCount, bezierCount, boneIndex), 1)); @@ -1286,6 +1295,7 @@ const BONE_SCALEY = 6; const BONE_SHEAR = 7; const BONE_SHEARX = 8; const BONE_SHEARY = 9; +const BONE_INHERIT = 10; const SLOT_ATTACHMENT = 0; const SLOT_RGBA = 1; diff --git a/spine-ts/spine-core/src/SkeletonJson.ts b/spine-ts/spine-core/src/SkeletonJson.ts index f422c3820..82703134c 100644 --- a/spine-ts/spine-core/src/SkeletonJson.ts +++ b/spine-ts/spine-core/src/SkeletonJson.ts @@ -27,11 +27,11 @@ * SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *****************************************************************************/ -import { Animation, Timeline, AttachmentTimeline, RGBATimeline, RGBTimeline, AlphaTimeline, RGBA2Timeline, RGB2Timeline, RotateTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, IkConstraintTimeline, TransformConstraintTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PathConstraintMixTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, CurveTimeline1, CurveTimeline2, CurveTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintWindTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintMixTimeline } from "./Animation.js"; +import { Animation, Timeline, InheritTimeline, AttachmentTimeline, RGBATimeline, RGBTimeline, AlphaTimeline, RGBA2Timeline, RGB2Timeline, RotateTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, IkConstraintTimeline, TransformConstraintTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PathConstraintMixTimeline, DeformTimeline, DrawOrderTimeline, EventTimeline, CurveTimeline1, CurveTimeline2, CurveTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintWindTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintMixTimeline } from "./Animation.js"; import { VertexAttachment, Attachment } from "./attachments/Attachment.js"; import { AttachmentLoader } from "./attachments/AttachmentLoader.js"; import { MeshAttachment } from "./attachments/MeshAttachment.js"; -import { BoneData, TransformMode } from "./BoneData.js"; +import { BoneData, Inherit } from "./BoneData.js"; import { EventData } from "./EventData.js"; import { Event } from "./Event.js"; import { IkConstraintData } from "./IkConstraintData.js"; @@ -102,7 +102,7 @@ export class SkeletonJson { data.scaleY = getValue(boneMap, "scaleY", 1); data.shearX = getValue(boneMap, "shearX", 0); data.shearY = getValue(boneMap, "shearY", 0); - data.transformMode = Utils.enumValue(TransformMode, getValue(boneMap, "transform", "Normal")); + data.inherit = Utils.enumValue(Inherit, getValue(boneMap, "inherit", "Normal")); data.skinRequired = getValue(boneMap, "skin", false); let color = getValue(boneMap, "color", null); @@ -739,6 +739,13 @@ export class SkeletonJson { } else if (timelineName === "sheary") { let timeline = new ShearYTimeline(frames, frames, boneIndex); timelines.push(readTimeline1(timelineMap, timeline, 0, 1)); + } else if (timelineName === "inherit") { + let timeline = new InheritTimeline(frames, bone.index); + for (let frame = 0; frame < timelineMap.length; frame++) { + let aFrame = timelineMap[frame]; + timeline.setFrame(frame, getValue(aFrame, "time", 0), Utils.enumValue(Inherit, getValue(aFrame, "inherit", "Normal"))); + } + timelines.push(timeline); } } }