mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-25 22:23:42 +08:00
[ts] Port of cf45806b: Added DrawOrderFolderTimeline.
This commit is contained in:
parent
409bf9d639
commit
7f24ed23af
@ -1792,7 +1792,7 @@ export class DrawOrderTimeline extends Timeline {
|
||||
static propertyIds = [`${Property.drawOrder}`];
|
||||
|
||||
/** The draw order for each key frame. See {@link #setFrame(int, float, int[])}. */
|
||||
drawOrders: Array<Array<number> | null>;
|
||||
private readonly drawOrders: Array<Array<number> | null>;
|
||||
|
||||
constructor (frameCount: number) {
|
||||
super(frameCount, ...DrawOrderTimeline.propertyIds);
|
||||
@ -1804,7 +1804,7 @@ export class DrawOrderTimeline extends Timeline {
|
||||
}
|
||||
|
||||
/** Sets the time in seconds and the draw order for the specified key frame.
|
||||
* @param drawOrder For each slot in {@link Skeleton#slots}, the index of the new draw order. May be null to use setup pose
|
||||
* @param drawOrder Ordered {@link Skeleton#slots} indices, or null to use setup pose
|
||||
* draw order. */
|
||||
setFrame (frame: number, time: number, drawOrder: Array<number> | null) {
|
||||
this.frames[frame] = time;
|
||||
@ -1837,6 +1837,85 @@ export class DrawOrderTimeline extends Timeline {
|
||||
}
|
||||
}
|
||||
|
||||
/** Changes a subset of a skeleton's {@link Skeleton#getDrawOrder()}. */
|
||||
export class DrawOrderFolderTimeline extends Timeline {
|
||||
private readonly slots: number[];
|
||||
private readonly inFolder: boolean[];
|
||||
private readonly drawOrders: Array<Array<number> | null>;
|
||||
|
||||
/** @param slots {@link Skeleton#slots} indices controlled by this timeline, in setup order.
|
||||
* @param slotCount The maximum number of slots in the skeleton. */
|
||||
constructor (frameCount: number, slots: number[], slotCount: number) {
|
||||
super(frameCount, ...DrawOrderTimeline.propertyIds);
|
||||
this.slots = slots;
|
||||
this.drawOrders = new Array(frameCount);
|
||||
this.inFolder = new Array(slotCount);
|
||||
for (const i of slots)
|
||||
this.inFolder[i] = true;
|
||||
}
|
||||
|
||||
getFrameCount (): number {
|
||||
return this.frames.length;
|
||||
}
|
||||
|
||||
/** The {@link Skeleton#getSlots()} indices that this timeline affects, in setup order. */
|
||||
getSlots (): number[] {
|
||||
return this.slots;
|
||||
}
|
||||
|
||||
/** The draw order for each frame. See {@link #setFrame(int, float, int[])}. */
|
||||
getDrawOrders (): Array<Array<number> | null> {
|
||||
return this.drawOrders;
|
||||
}
|
||||
|
||||
/** Sets the time and draw order for the specified frame.
|
||||
* @param frame Between 0 and <code>frameCount</code>, inclusive.
|
||||
* @param time The frame time in seconds.
|
||||
* @param drawOrder Ordered {@link #getSlots()} indices, or null to use setup pose order. */
|
||||
setFrame (frame: number, time: number, drawOrder: Array<number> | null): void {
|
||||
this.frames[frame] = time;
|
||||
this.drawOrders[frame] = drawOrder;
|
||||
}
|
||||
|
||||
apply (skeleton: Skeleton, lastTime: number, time: number, events: Array<Event>, alpha: number, blend: MixBlend,
|
||||
direction: MixDirection, appliedPose: boolean): void {
|
||||
|
||||
if (direction === MixDirection.out) {
|
||||
if (blend === MixBlend.setup) this.setup(skeleton);
|
||||
} else if (time < this.frames[0]) {
|
||||
if (blend === MixBlend.setup || blend === MixBlend.first) this.setup(skeleton);
|
||||
} else {
|
||||
const order = this.drawOrders[Timeline.search(this.frames, time)];
|
||||
if (!order)
|
||||
this.setup(skeleton);
|
||||
else
|
||||
this.apply1(skeleton, order);
|
||||
}
|
||||
}
|
||||
|
||||
private setup (skeleton: Skeleton): void {
|
||||
const { inFolder, slots } = this;
|
||||
const { drawOrder, slots: allSlots } = skeleton;
|
||||
for (let i = 0, found = 0, done = slots.length; ; i++) {
|
||||
if (inFolder[drawOrder[i].data.index]) {
|
||||
drawOrder[i] = allSlots[slots[found]];
|
||||
if (++found === done) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private apply1 (skeleton: Skeleton, order: number[]): void {
|
||||
const { inFolder, slots } = this;
|
||||
const { drawOrder, slots: allSlots } = skeleton;
|
||||
for (let i = 0, found = 0, done = slots.length; ; i++) {
|
||||
if (inFolder[drawOrder[i].data.index]) {
|
||||
drawOrder[i] = allSlots[slots[order[found]]];
|
||||
if (++found === done) break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface ConstraintTimeline {
|
||||
/** The index of the constraint in {@link Skeleton.constraints} that will be changed when this timeline is applied, or
|
||||
* -1 if a specific constraint will not be changed. */
|
||||
|
||||
@ -29,7 +29,7 @@
|
||||
|
||||
/** biome-ignore-all lint/style/noNonNullAssertion: reference runtime expects some nullable to not be null */
|
||||
|
||||
import { Animation, AttachmentTimeline, DrawOrderTimeline, EventTimeline, MixBlend, MixDirection, RotateTimeline, Timeline } from "./Animation.js";
|
||||
import { Animation, AttachmentTimeline, DrawOrderFolderTimeline, DrawOrderTimeline, EventTimeline, MixBlend, MixDirection, RotateTimeline, Timeline } from "./Animation.js";
|
||||
import type { AnimationStateData } from "./AnimationStateData.js";
|
||||
import type { Event } from "./Event.js";
|
||||
import type { Skeleton } from "./Skeleton.js";
|
||||
@ -788,7 +788,8 @@ export class AnimationState {
|
||||
if (!propertyIDs.addAll(ids))
|
||||
timelineMode[i] = SUBSEQUENT;
|
||||
else if (!to || timeline instanceof AttachmentTimeline || timeline instanceof DrawOrderTimeline
|
||||
|| timeline instanceof EventTimeline || !to.animation!.hasTimeline(ids)) {
|
||||
|| timeline instanceof DrawOrderFolderTimeline || timeline instanceof EventTimeline
|
||||
|| !to.animation!.hasTimeline(ids)) {
|
||||
timelineMode[i] = FIRST;
|
||||
} else {
|
||||
for (let next = to.mixingTo; next; next = next!.mixingTo) {
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { AlphaTimeline, Animation, AttachmentTimeline, type BoneTimeline2, type CurveTimeline, CurveTimeline1, DeformTimeline, DrawOrderTimeline, EventTimeline, IkConstraintTimeline, InheritTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintMixTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintWindTimeline, RGB2Timeline, RGBA2Timeline, RGBATimeline, RGBTimeline, RotateTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, SequenceTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, SliderMixTimeline, SliderTimeline, type Timeline, TransformConstraintTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline } from "./Animation.js";
|
||||
import { AlphaTimeline, Animation, AttachmentTimeline, type BoneTimeline2, type CurveTimeline, CurveTimeline1, DeformTimeline, DrawOrderFolderTimeline, DrawOrderTimeline, EventTimeline, IkConstraintTimeline, InheritTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintMixTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintWindTimeline, RGB2Timeline, RGBA2Timeline, RGBATimeline, RGBTimeline, RotateTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, SequenceTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, SliderMixTimeline, SliderTimeline, type Timeline, TransformConstraintTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline } from "./Animation.js";
|
||||
import type { Attachment, VertexAttachment } from "./attachments/Attachment.js";
|
||||
import type { AttachmentLoader } from "./attachments/AttachmentLoader.js";
|
||||
import type { HasSequence } from "./attachments/HasSequence.js";
|
||||
@ -1130,34 +1130,26 @@ export class SkeletonBinary {
|
||||
}
|
||||
|
||||
// Draw order timeline.
|
||||
const slotCount = skeletonData.slots.length;
|
||||
const drawOrderCount = input.readInt(true);
|
||||
if (drawOrderCount > 0) {
|
||||
const timeline = new DrawOrderTimeline(drawOrderCount);
|
||||
const slotCount = skeletonData.slots.length;
|
||||
for (let i = 0; i < drawOrderCount; i++) {
|
||||
const time = input.readFloat();
|
||||
const offsetCount = input.readInt(true);
|
||||
const drawOrder = Utils.newArray(slotCount, 0);
|
||||
for (let ii = slotCount - 1; ii >= 0; ii--)
|
||||
drawOrder[ii] = -1;
|
||||
const unchanged = Utils.newArray(slotCount - offsetCount, 0);
|
||||
let originalIndex = 0, unchangedIndex = 0;
|
||||
for (let ii = 0; ii < offsetCount; ii++) {
|
||||
const slotIndex = input.readInt(true);
|
||||
// Collect unchanged items.
|
||||
while (originalIndex !== slotIndex)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Set changed items.
|
||||
drawOrder[originalIndex + input.readInt(true)] = originalIndex++;
|
||||
}
|
||||
// Collect remaining unchanged items.
|
||||
while (originalIndex < slotCount)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Fill in unchanged items.
|
||||
for (let ii = slotCount - 1; ii >= 0; ii--)
|
||||
if (drawOrder[ii] === -1) drawOrder[ii] = unchanged[--unchangedIndex];
|
||||
timeline.setFrame(i, time, drawOrder);
|
||||
}
|
||||
for (let i = 0; i < drawOrderCount; i++)
|
||||
timeline.setFrame(i, input.readFloat(), readDrawOrder(input, slotCount));
|
||||
timelines.push(timeline);
|
||||
}
|
||||
|
||||
// Draw order folder timelines.
|
||||
const folderCount = input.readInt(true);
|
||||
for (let i = 0; i < folderCount; i++) {
|
||||
const folderSlotCount = input.readInt(true);
|
||||
const folderSlots = new Array<number>(folderSlotCount);
|
||||
for (let ii = 0; ii < folderSlotCount; ii++)
|
||||
folderSlots[ii] = input.readInt(true);
|
||||
const keyCount = input.readInt(true);
|
||||
const timeline = new DrawOrderFolderTimeline(keyCount, folderSlots, slotCount);
|
||||
for (let ii = 0; ii < keyCount; ii++)
|
||||
timeline.setFrame(ii, input.readFloat(), readDrawOrder(input, folderSlotCount));
|
||||
timelines.push(timeline);
|
||||
}
|
||||
|
||||
@ -1351,6 +1343,29 @@ function readTimeline2 (input: BinaryInput, timelines: Array<Timeline>, timeline
|
||||
timelines.push(timeline);
|
||||
}
|
||||
|
||||
function readDrawOrder (input: BinaryInput, slotCount: number): number[] | null {
|
||||
const changeCount = input.readInt(true);
|
||||
if (changeCount === 0) return null;
|
||||
const drawOrder = new Array<number>(slotCount).fill(-1);
|
||||
const unchanged = new Array<number>(slotCount - changeCount);
|
||||
let originalIndex = 0, unchangedIndex = 0;
|
||||
for (let i = 0; i < changeCount; i++) {
|
||||
const slotIndex = input.readInt(true);
|
||||
// Collect unchanged items.
|
||||
while (originalIndex !== slotIndex)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Set changed items.
|
||||
drawOrder[originalIndex + input.readInt(true)] = originalIndex++;
|
||||
}
|
||||
// Collect remaining unchanged items.
|
||||
while (originalIndex < slotCount)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Fill in unchanged items.
|
||||
for (let i = slotCount - 1; i >= 0; i--)
|
||||
if (drawOrder[i] === -1) drawOrder[i] = unchanged[--unchangedIndex];
|
||||
return drawOrder;
|
||||
}
|
||||
|
||||
function setBezier (input: BinaryInput, timeline: CurveTimeline, bezier: number, frame: number, value: number,
|
||||
time1: number, time2: number, value1: number, value2: number, scale: number) {
|
||||
timeline.setBezier(bezier, frame, value, time1, value1, input.readFloat(), input.readFloat() * scale, input.readFloat(), input.readFloat() * scale, time2, value2);
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { AlphaTimeline, Animation, AttachmentTimeline, type BoneTimeline2, type CurveTimeline, type CurveTimeline1, DeformTimeline, DrawOrderTimeline, EventTimeline, IkConstraintTimeline, InheritTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintMixTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintWindTimeline, RGB2Timeline, RGBA2Timeline, RGBATimeline, RGBTimeline, RotateTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, SequenceTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, SliderMixTimeline, SliderTimeline, type Timeline, TransformConstraintTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline } from "./Animation.js";
|
||||
import { AlphaTimeline, Animation, AttachmentTimeline, type BoneTimeline2, type CurveTimeline, type CurveTimeline1, DeformTimeline, DrawOrderFolderTimeline, DrawOrderTimeline, EventTimeline, IkConstraintTimeline, InheritTimeline, PathConstraintMixTimeline, PathConstraintPositionTimeline, PathConstraintSpacingTimeline, PhysicsConstraintDampingTimeline, PhysicsConstraintGravityTimeline, PhysicsConstraintInertiaTimeline, PhysicsConstraintMassTimeline, PhysicsConstraintMixTimeline, PhysicsConstraintResetTimeline, PhysicsConstraintStrengthTimeline, PhysicsConstraintWindTimeline, RGB2Timeline, RGBA2Timeline, RGBATimeline, RGBTimeline, RotateTimeline, ScaleTimeline, ScaleXTimeline, ScaleYTimeline, SequenceTimeline, ShearTimeline, ShearXTimeline, ShearYTimeline, SliderMixTimeline, SliderTimeline, type Timeline, TransformConstraintTimeline, TranslateTimeline, TranslateXTimeline, TranslateYTimeline } from "./Animation.js";
|
||||
import type { Attachment, VertexAttachment } from "./attachments/Attachment.js";
|
||||
import type { AttachmentLoader } from "./attachments/AttachmentLoader.js";
|
||||
import type { HasSequence } from "./attachments/HasSequence.js";
|
||||
@ -419,7 +419,7 @@ export class SkeletonJson {
|
||||
|
||||
for (const slotName in skinMap.attachments) {
|
||||
const slot = skeletonData.findSlot(slotName);
|
||||
if (!slot) throw new Error(`Couldn't find slot ${slotName} for skin ${skinMap.name}.`);
|
||||
if (!slot) throw new Error(`Couldn't find skin slot ${slotName} for skin ${skinMap.name}.`);
|
||||
const slotMap = skinMap.attachments[slotName];
|
||||
for (const entryName in slotMap) {
|
||||
const attachment = this.readAttachment(slotMap[entryName], skin, slot.index, entryName, skeletonData);
|
||||
@ -1101,7 +1101,7 @@ export class SkeletonJson {
|
||||
for (const slotMapName in attachmentsMap) {
|
||||
const slotMap = attachmentsMap[slotMapName];
|
||||
const slot = skeletonData.findSlot(slotMapName);
|
||||
if (!slot) throw new Error(`Slot not found: ${slotMapName}`);
|
||||
if (!slot) throw new Error(`Attachment slot not found: ${slotMapName}`);
|
||||
const slotIndex = slot.index;
|
||||
for (const attachmentMapName in slotMap) {
|
||||
const attachmentMap = slotMap[attachmentMapName];
|
||||
@ -1172,42 +1172,39 @@ export class SkeletonJson {
|
||||
}
|
||||
}
|
||||
|
||||
// Draw order timelines.
|
||||
// Draw order timeline.
|
||||
if (map.drawOrder) {
|
||||
const timeline = new DrawOrderTimeline(map.drawOrder.length);
|
||||
const slotCount = skeletonData.slots.length;
|
||||
let frame = 0;
|
||||
for (let i = 0; i < map.drawOrder.length; i++, frame++) {
|
||||
const drawOrderMap = map.drawOrder[i];
|
||||
let drawOrder: Array<number> | null = null;
|
||||
const offsets = getValue(drawOrderMap, "offsets", null);
|
||||
if (offsets) {
|
||||
drawOrder = Utils.newArray<number>(slotCount, -1);
|
||||
const unchanged = Utils.newArray<number>(slotCount - offsets.length, 0);
|
||||
let originalIndex = 0, unchangedIndex = 0;
|
||||
for (let ii = 0; ii < offsets.length; ii++) {
|
||||
const offsetMap = offsets[ii];
|
||||
const slot = skeletonData.findSlot(offsetMap.slot);
|
||||
if (!slot) throw new Error(`Slot not found: ${slot}`);
|
||||
const slotIndex = slot.index;
|
||||
// Collect unchanged items.
|
||||
while (originalIndex !== slotIndex)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Set changed items.
|
||||
drawOrder[originalIndex + offsetMap.offset] = originalIndex++;
|
||||
}
|
||||
// Collect remaining unchanged items.
|
||||
while (originalIndex < slotCount)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Fill in unchanged items.
|
||||
for (let ii = slotCount - 1; ii >= 0; ii--)
|
||||
if (drawOrder[ii] === -1) drawOrder[ii] = unchanged[--unchangedIndex];
|
||||
}
|
||||
timeline.setFrame(frame, getValue(drawOrderMap, "time", 0), drawOrder);
|
||||
let frame = 0
|
||||
for (const drawOrderMap of (map.drawOrder as DrawOrderKeysType[])) {
|
||||
timeline.setFrame(frame++, getValue(drawOrderMap, "time", 0), readDrawOrder(skeletonData, drawOrderMap, slotCount, null));
|
||||
}
|
||||
timelines.push(timeline);
|
||||
}
|
||||
|
||||
// Draw order folder timelines.
|
||||
if (map.drawOrderFolder) {
|
||||
for (const timelineMap of map.drawOrderFolder) {
|
||||
const slotEntries = getValue(timelineMap, "slots", []) as string[];
|
||||
const folderSlots = new Array<number>(slotEntries.length);
|
||||
let ii = 0;
|
||||
for (const slotEntry of slotEntries) {
|
||||
const slot = skeletonData.findSlot(slotEntry);
|
||||
if (!slot) throw new Error(`Draw order folder slot not found: ${slotEntry}`);
|
||||
folderSlots[ii++] = slot.index;
|
||||
}
|
||||
|
||||
const drawOrderFolderEntries = getValue(timelineMap, "keys", []) as DrawOrderKeysType[];
|
||||
const timeline = new DrawOrderFolderTimeline(drawOrderFolderEntries.length, folderSlots, skeletonData.slots.length);
|
||||
let frame = 0;
|
||||
for (const drawOrderFolderMap of drawOrderFolderEntries) {
|
||||
timeline.setFrame(frame++, getValue(drawOrderFolderMap, "time", 0), readDrawOrder(skeletonData, drawOrderFolderMap, folderSlots.length, folderSlots));
|
||||
}
|
||||
timelines.push(timeline);
|
||||
}
|
||||
}
|
||||
|
||||
// Event timelines.
|
||||
if (map.events) {
|
||||
const timeline = new EventTimeline(map.events.length);
|
||||
@ -1307,6 +1304,46 @@ function readTimeline2 (timelines: Array<Timeline>, keys: Timeline2KeysType[], t
|
||||
}
|
||||
}
|
||||
|
||||
type DrawOrderKeysType = { offsets?: { slot: string, offset: number }[] };
|
||||
|
||||
/** @param folderSlots Slot names are resolved to positions within this array. If null, slot indices are used as positions. */
|
||||
function readDrawOrder (skeletonData: SkeletonData, keys: DrawOrderKeysType, slotCount: number, folderSlots: number[] | null): number[] | null {
|
||||
const changes = keys.offsets;
|
||||
if (!changes) return null; // Setup draw order.
|
||||
const drawOrder = new Array<number>(slotCount).fill(-1);
|
||||
const unchanged = new Array<number>(slotCount - changes.length);
|
||||
let originalIndex = 0, unchangedIndex = 0;
|
||||
for (const offsetMap of changes) {
|
||||
const slot = skeletonData.findSlot(offsetMap.slot);
|
||||
if (slot == null) throw new Error(`Draw order slot not found: ${offsetMap.slot}`);
|
||||
let index = 0;
|
||||
if (!folderSlots)
|
||||
index = slot.index;
|
||||
else {
|
||||
index = -1;
|
||||
for (let i = 0; i < slotCount; i++) {
|
||||
if (folderSlots[i] === slot.index) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index === -1) throw new Error(`Slot not in folder: ${offsetMap.slot}`);
|
||||
}
|
||||
// Collect unchanged items.
|
||||
while (originalIndex !== index)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Set changed items.
|
||||
drawOrder[originalIndex + offsetMap.offset] = originalIndex++;
|
||||
}
|
||||
// Collect remaining unchanged items.
|
||||
while (originalIndex < slotCount)
|
||||
unchanged[unchangedIndex++] = originalIndex++;
|
||||
// Fill in unchanged items.
|
||||
for (let i = slotCount - 1; i >= 0; i--)
|
||||
if (drawOrder[i] === -1) drawOrder[i] = unchanged[--unchangedIndex];
|
||||
return drawOrder;
|
||||
}
|
||||
|
||||
function readCurve (curve: [number, number, number, number] | "stepped", timeline: CurveTimeline, bezier: number, frame: number, value: number, time1: number, time2: number,
|
||||
value1: number, value2: number, scale: number) {
|
||||
if (curve === "stepped") {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user