rig-play 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -2
- package/dist/index.js +137 -39
- package/dist/index.js.map +1 -1
- package/dist/player.d.ts +28 -0
- package/dist/player.d.ts.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -300,9 +300,25 @@ player.play('wave', { track: 2, loop: false, mix: 0.1 }); // a one-shot…
|
|
|
300
300
|
player.queueClear(2, { mix: 0.1 }); // …mixed back out over its last 0.1 s
|
|
301
301
|
player.queue('idle_b', { track: 0, mix: 0.3 }); // after the current idle cycle completes
|
|
302
302
|
player.clear(1, { mix: 0.2 }); // the layer gone, the base showing through
|
|
303
|
-
player.entry(2); // { animation, loop, trackTime, animationTime, duration } | null
|
|
303
|
+
player.entry(2); // { animation, loop, trackTime, animationTime, duration, alpha, additive } | null
|
|
304
|
+
player.play('blink', { track: 3, alpha: 0.6 }); // the same clip at a weight…
|
|
305
|
+
player.setAlpha(3, 0.85); // …changed while it plays
|
|
306
|
+
player.play('breath', { track: 4, additive: true }); // adds to what the tracks below posed
|
|
304
307
|
```
|
|
305
308
|
|
|
309
|
+
**Weight and additive layers** (rig-c 2.39.0, the runtime's `TrackEntry.alpha` and
|
|
310
|
+
`additive`): `alpha` (0..1, default 1) multiplies every timeline the entry applies;
|
|
311
|
+
`setAlpha(track, alpha)` changes the current entry's weight from the next pose and
|
|
312
|
+
re-poses the held pose. It does not touch events — an entry fires at any alpha, 0
|
|
313
|
+
included, and one mixing out fires none — or attachments: the current entry keeps
|
|
314
|
+
its attachments at any alpha, one mixing out keeps none. `additive: true`, fixed
|
|
315
|
+
when the entry is set or queued, adds the entry's keyed bone and deform values to
|
|
316
|
+
what the tracks below posed instead of replacing them, so a layer keying the same
|
|
317
|
+
bone channels as an idle does not cover its motion; a rotation is added as it
|
|
318
|
+
stands, with no shortest-angle wrap, and an additive entry mixing out fades with its
|
|
319
|
+
alpha and is never held. Colour, attachment and event timelines apply as on a
|
|
320
|
+
replacing entry. A weight below 1 or an additive entry composes, as a mix does.
|
|
321
|
+
|
|
306
322
|
`play(name)` keeps meaning what it meant: a plain play on track 0 runs on rig-c's
|
|
307
323
|
single live track, which poses everything the core poses. The first call that
|
|
308
324
|
composes — a `mix`, a `track` above 0, a `queue`, a `clear` — moves the player onto
|
|
@@ -659,7 +675,7 @@ name, and unloading is still elements first, bitmaps second.
|
|
|
659
675
|
| `new Skeleton(assets.data)` and `new AnimationState(new AnimationStateData(assets.data))` | `new RigPlayer(root, assets, options?)` | The player holds the pose and the track. |
|
|
660
676
|
| `state.setAnimation(0, 'walk', true)` | `player.play('walk', { loop: true })` | `loop` defaults to true. `play` opens the animation at its first pose with the physics reset there. `player.current` names what is playing. |
|
|
661
677
|
| `skeleton.findSlot('x').attachment = null` to hide a slot, or a second skeleton with slots emptied | `RigPlayerOptions.slots`, `player.slots`, `player.posedFrame(slots)` | A draw filter since 1.6.0 — the pose is the whole rig's; a clipping slot left out does not clip. See *Drawing some slots only*. |
|
|
662
|
-
| `state.setAnimation(1, 'smile', false)` with a mix, `addAnimation`, `setEmptyAnimation`, `addEmptyAnimation`, `getCurrent(track)` | `player.play(name, { track, loop, mix })`, `player.queue(name, { track, loop, mix, delay })`, `player.clear(track, { mix })`, `player.queueClear(track, { mix, delay })`, `player.entry(track)` | Since 1.5.0, rig-c's live tracks: the runtime's mixing at tolerance 0. Constraint, sequence, draw-order and path-deform timelines are not mixed and are refused on the tracks. See *Several tracks*. |
|
|
678
|
+
| `state.setAnimation(1, 'smile', false)` with a mix, `addAnimation`, `setEmptyAnimation`, `addEmptyAnimation`, `getCurrent(track)`, `entry.alpha`, `entry.additive` | `player.play(name, { track, loop, mix, alpha, additive })`, `player.queue(name, { track, loop, mix, delay, alpha, additive })`, `player.clear(track, { mix })`, `player.queueClear(track, { mix, delay })`, `player.setAlpha(track, alpha)`, `player.entry(track)` | Since 1.5.0, rig-c's live tracks: the runtime's mixing at tolerance 0. Constraint, sequence, draw-order and path-deform timelines are not mixed and are refused on the tracks. See *Several tracks*. |
|
|
663
679
|
| `state.update(delta)`, `state.apply(skeleton)`, `skeleton.update(delta)`, `skeleton.updateWorldTransform(Physics.update)`, `renderer.render(skeleton)` | `player.frame(dt)` | Or `player.advance(dt)` then `player.draw()`, to time the two apart. |
|
|
664
680
|
| time scale (the caller's own, on the spine-core side) | `player.timeScale` | Multiplies every step. 0 holds the pose without re-posing it; a negative value is not stepped. |
|
|
665
681
|
| holding a pose (the caller stopped advancing) | `player.pause()`, `player.resume()` | `frame` keeps drawing the held pose. |
|
package/dist/index.js
CHANGED
|
@@ -379,7 +379,7 @@ function timelineIdentity(slot, shown) {
|
|
|
379
379
|
const nameOf = (a) => `${a.skin}/${a.slot}/${a.attachment}`;
|
|
380
380
|
function attachmentStates(doc, resolve, placeholders, sample, sliders, plant = {}, mixed) {
|
|
381
381
|
const evalDeform = plant.deform ?? deformAt;
|
|
382
|
-
const
|
|
382
|
+
const blendOf2 = plant.deformBlend ?? blendDeform;
|
|
383
383
|
const evalFrame = plant.sequence ?? sequenceFrameAt;
|
|
384
384
|
const shown = [];
|
|
385
385
|
const why = [];
|
|
@@ -410,7 +410,7 @@ function attachmentStates(doc, resolve, placeholders, sample, sliders, plant = {
|
|
|
410
410
|
if (!seen.includes(now2.identity)) seen.push(now2.identity);
|
|
411
411
|
if (a.deform !== null && now2.vertices !== null) {
|
|
412
412
|
const target = evalDeform(now2.vertices, a.deform, t);
|
|
413
|
-
if (target !== null) deform = blend === null ? target :
|
|
413
|
+
if (target !== null) deform = blend === null ? target : blendOf2(now2.vertices, deform, target, blend.alpha, blend.additive);
|
|
414
414
|
}
|
|
415
415
|
if (a.sequence !== null && now2.count !== void 0) {
|
|
416
416
|
const f = evalFrame(a.sequence, now2.count, t);
|
|
@@ -5072,6 +5072,9 @@ const FIRST = 2;
|
|
|
5072
5072
|
const MODE = 3;
|
|
5073
5073
|
const HOLD = 4;
|
|
5074
5074
|
const EMPTY = { name: null, duration: 0, timelines: [], ids: /* @__PURE__ */ new Set() };
|
|
5075
|
+
function addable(tl) {
|
|
5076
|
+
return tl.type === "deform" || tl.type === "bone" && tl.kind !== "inherit";
|
|
5077
|
+
}
|
|
5075
5078
|
const BONE_PROPERTIES = {
|
|
5076
5079
|
rotate: ["rotate"],
|
|
5077
5080
|
translate: ["x", "y"],
|
|
@@ -5141,6 +5144,8 @@ function newEntry(anim, loop) {
|
|
|
5141
5144
|
mixTime: 0,
|
|
5142
5145
|
mixDuration: 0,
|
|
5143
5146
|
totalAlpha: 0,
|
|
5147
|
+
alpha: 1,
|
|
5148
|
+
additive: false,
|
|
5144
5149
|
keepHold: false,
|
|
5145
5150
|
timelineMode: [],
|
|
5146
5151
|
timelineHoldMix: [],
|
|
@@ -5317,6 +5322,19 @@ class Mixer {
|
|
|
5317
5322
|
}
|
|
5318
5323
|
return this.plant.mixReversed === true ? 1 - m : m;
|
|
5319
5324
|
}
|
|
5325
|
+
/** `TrackEntry.alpha` — inverted or ignored under the plants. */
|
|
5326
|
+
alphaOf(e) {
|
|
5327
|
+
if (this.plant.alphaIgnored === true) return 1;
|
|
5328
|
+
return this.plant.alphaInverted === true ? 1 - e.alpha : e.alpha;
|
|
5329
|
+
}
|
|
5330
|
+
/** `TrackEntry.additive` — never under the plant that applies an additive entry as a replacing one. */
|
|
5331
|
+
additiveOf(e) {
|
|
5332
|
+
return e.additive && this.plant.additiveAsReplace !== true;
|
|
5333
|
+
}
|
|
5334
|
+
/** Whether `computeHold` reads an entry as additive — never under the plant that holds as if none were. */
|
|
5335
|
+
holdsAdditive(e) {
|
|
5336
|
+
return e !== null && this.additiveOf(e) && this.plant.additiveHeld !== true;
|
|
5337
|
+
}
|
|
5320
5338
|
update(delta) {
|
|
5321
5339
|
const tracks = this.tracks;
|
|
5322
5340
|
for (let i = 0, n = tracks.length; i < n; i++) {
|
|
@@ -5387,7 +5405,7 @@ class Mixer {
|
|
|
5387
5405
|
for (let i = 0, n = tracks.length; i < n; i++) {
|
|
5388
5406
|
const current = tracks[i];
|
|
5389
5407
|
if (current === null || current.delay > 0) continue;
|
|
5390
|
-
let alpha =
|
|
5408
|
+
let alpha = this.alphaOf(current);
|
|
5391
5409
|
if (current.mixingFrom !== null) alpha *= this.applyMixingFrom(current, pose, i);
|
|
5392
5410
|
else if (current.trackTime >= current.trackEnd && current.next === null) alpha = 0;
|
|
5393
5411
|
const animationLast = current.animationLast;
|
|
@@ -5397,18 +5415,22 @@ class Mixer {
|
|
|
5397
5415
|
if (i === 0 && alpha === 1) {
|
|
5398
5416
|
for (const tl of timelines) {
|
|
5399
5417
|
if (tl.type === "attachment") this.applyAttachment(pose, tl, animationTime, SETUP, true);
|
|
5400
|
-
else this.applyTimeline(pose, tl, animationLast, animationTime, true, 1, SETUP, false);
|
|
5418
|
+
else this.applyTimeline(pose, tl, animationLast, animationTime, true, 1, SETUP, false, false);
|
|
5401
5419
|
}
|
|
5402
5420
|
} else {
|
|
5403
5421
|
const modes = current.timelineMode;
|
|
5404
|
-
const retain = alpha >= 0;
|
|
5405
|
-
const
|
|
5422
|
+
const retain = this.plant.attachmentsBelowHalfAlpha === true ? alpha >= 0.5 : alpha >= 0;
|
|
5423
|
+
const add = this.additiveOf(current);
|
|
5424
|
+
const shortest = add && this.plant.additiveRotateRemembered !== true;
|
|
5425
|
+
const firstFrame = !shortest && current.timelinesRotation.length !== count << 1;
|
|
5406
5426
|
if (firstFrame) current.timelinesRotation.length = count << 1;
|
|
5407
5427
|
timelines.forEach((tl, ii) => {
|
|
5408
5428
|
const from = this.fromOf(modes[ii] & MODE, i);
|
|
5409
|
-
if (tl.type === "bone" && tl.kind === "rotate") this.applyRotate(pose, tl, animationTime, alpha, from, current.timelinesRotation, ii << 1, firstFrame);
|
|
5410
|
-
else if (tl.type === "attachment")
|
|
5411
|
-
|
|
5429
|
+
if (!shortest && tl.type === "bone" && tl.kind === "rotate") this.applyRotate(pose, tl, animationTime, alpha, from, current.timelinesRotation, ii << 1, firstFrame);
|
|
5430
|
+
else if (tl.type === "attachment") {
|
|
5431
|
+
if (add && this.plant.additiveAttachmentsDirect === true) this.applyAttachmentDirect(pose, tl, animationTime, from, false);
|
|
5432
|
+
else this.applyAttachment(pose, tl, animationTime, from, retain);
|
|
5433
|
+
} else this.applyTimeline(pose, tl, animationLast, animationTime, true, alpha, from, false, add);
|
|
5412
5434
|
});
|
|
5413
5435
|
}
|
|
5414
5436
|
if (i === 0) base = animationTime;
|
|
@@ -5428,15 +5450,17 @@ class Mixer {
|
|
|
5428
5450
|
const from = to.mixingFrom;
|
|
5429
5451
|
const fromMix = from.mixingFrom !== null ? this.applyMixingFrom(from, pose, track) : 1;
|
|
5430
5452
|
const mix = this.mixOf(to);
|
|
5431
|
-
const a =
|
|
5432
|
-
const keep = 1 - mix * 1;
|
|
5453
|
+
const a = this.alphaOf(from) * fromMix;
|
|
5454
|
+
const keep = 1 - mix * (this.plant.holdIgnoresEntryAlpha === true ? 1 : this.alphaOf(to));
|
|
5433
5455
|
const alphaMix = a * (1 - mix);
|
|
5434
5456
|
const alphaHold = keep > 0 ? alphaMix / keep : a;
|
|
5435
5457
|
const timelines = from.anim.timelines;
|
|
5436
5458
|
const count = timelines.length;
|
|
5437
5459
|
const modes = from.timelineMode;
|
|
5438
5460
|
const holdMix = from.timelineHoldMix;
|
|
5439
|
-
const
|
|
5461
|
+
const add = this.additiveOf(from);
|
|
5462
|
+
const shortest = add && this.plant.additiveRotateRemembered !== true;
|
|
5463
|
+
const firstFrame = !shortest && from.timelinesRotation.length !== count << 1;
|
|
5440
5464
|
if (firstFrame) from.timelinesRotation.length = count << 1;
|
|
5441
5465
|
const animationLast = from.animationLast;
|
|
5442
5466
|
const animationTime = animationTimeOf(from);
|
|
@@ -5451,9 +5475,11 @@ class Mixer {
|
|
|
5451
5475
|
alpha = h === null || h === void 0 ? alphaHold : alphaHold * (1 - this.mixOf(h));
|
|
5452
5476
|
} else alpha = alphaMix;
|
|
5453
5477
|
from.totalAlpha += alpha;
|
|
5454
|
-
if (tl.type === "bone" && tl.kind === "rotate") this.applyRotate(pose, tl, animationTime, alpha, mixFrom, from.timelinesRotation, i << 1, firstFrame);
|
|
5455
|
-
else if (tl.type === "attachment")
|
|
5456
|
-
|
|
5478
|
+
if (!shortest && tl.type === "bone" && tl.kind === "rotate") this.applyRotate(pose, tl, animationTime, alpha, mixFrom, from.timelinesRotation, i << 1, firstFrame);
|
|
5479
|
+
else if (tl.type === "attachment") {
|
|
5480
|
+
if (add && this.plant.additiveAttachmentsDirect === true) this.applyAttachmentDirect(pose, tl, animationTime, mixFrom, true);
|
|
5481
|
+
else this.applyAttachment(pose, tl, animationTime, mixFrom, false);
|
|
5482
|
+
} else this.applyTimeline(pose, tl, animationLast, animationTime, fire, alpha, mixFrom, true, add);
|
|
5457
5483
|
});
|
|
5458
5484
|
from.nextAnimationLast = animationTime;
|
|
5459
5485
|
from.nextTrackLast = from.trackTime;
|
|
@@ -5480,14 +5506,20 @@ class Mixer {
|
|
|
5480
5506
|
const holdMix = entry.timelineHoldMix;
|
|
5481
5507
|
holdMix.length = 0;
|
|
5482
5508
|
const to = entry.mixingTo;
|
|
5509
|
+
const add = this.holdsAdditive(entry);
|
|
5510
|
+
const toAdds = this.holdsAdditive(to);
|
|
5483
5511
|
timelines.forEach((tl, i) => {
|
|
5484
5512
|
const from = this.propertyFrom(track, tl.ids);
|
|
5513
|
+
if (add && addable(tl)) {
|
|
5514
|
+
modes[i] = from;
|
|
5515
|
+
return;
|
|
5516
|
+
}
|
|
5485
5517
|
let mode;
|
|
5486
|
-
if (to === null || tl.instant || !hasAny(to.anim, tl.ids)) mode = from;
|
|
5518
|
+
if (to === null || tl.instant || toAdds && addable(tl) || !hasAny(to.anim, tl.ids)) mode = from;
|
|
5487
5519
|
else {
|
|
5488
5520
|
mode = from | HOLD;
|
|
5489
5521
|
for (let next = to.mixingTo; next !== null; next = next.mixingTo) {
|
|
5490
|
-
if (!hasAny(next.anim, tl.ids)) {
|
|
5522
|
+
if (this.holdsAdditive(next) && addable(tl) || !hasAny(next.anim, tl.ids)) {
|
|
5491
5523
|
if (next.mixDuration > 0) holdMix[i] = next;
|
|
5492
5524
|
break;
|
|
5493
5525
|
}
|
|
@@ -5523,15 +5555,15 @@ class Mixer {
|
|
|
5523
5555
|
const channel2 = this.plant.channel ?? channelAt;
|
|
5524
5556
|
return keys[index].values.map((_v, c) => channel2(keys, index, c, t));
|
|
5525
5557
|
}
|
|
5526
|
-
/** `Timeline.apply` for every timeline but an attachment's and a rotation mixed below full weight. */
|
|
5527
|
-
applyTimeline(pose, tl, lastTime, time, fire, alpha, from, out) {
|
|
5558
|
+
/** `Timeline.apply` for every timeline but an attachment's and a rotation mixed below full weight; `add` is the entry's, read only by the timelines the runtime adds (`addable`). */
|
|
5559
|
+
applyTimeline(pose, tl, lastTime, time, fire, alpha, from, out, add) {
|
|
5528
5560
|
switch (tl.type) {
|
|
5529
5561
|
case "bone":
|
|
5530
|
-
return this.applyBone(pose, tl, time, alpha, from, out);
|
|
5562
|
+
return this.applyBone(pose, tl, time, alpha, from, out, add);
|
|
5531
5563
|
case "colour":
|
|
5532
5564
|
return this.applyColour(pose, tl, time, alpha, from);
|
|
5533
5565
|
case "deform":
|
|
5534
|
-
return this.applyDeform(pose, tl, time, alpha, from);
|
|
5566
|
+
return this.applyDeform(pose, tl, time, alpha, from, add);
|
|
5535
5567
|
case "events":
|
|
5536
5568
|
if (fire) pose.events.push(...eventsFired(tl.keys, lastTime, time, rawNumber, time < lastTime));
|
|
5537
5569
|
return;
|
|
@@ -5539,7 +5571,7 @@ class Mixer {
|
|
|
5539
5571
|
return;
|
|
5540
5572
|
}
|
|
5541
5573
|
}
|
|
5542
|
-
applyBone(pose, tl, time, alpha, from, out) {
|
|
5574
|
+
applyBone(pose, tl, time, alpha, from, out, add) {
|
|
5543
5575
|
const bone2 = pose.bone(tl.bone);
|
|
5544
5576
|
if (bone2 === null) return;
|
|
5545
5577
|
const setup = this.doc.bones[tl.bone];
|
|
@@ -5562,7 +5594,7 @@ class Mixer {
|
|
|
5562
5594
|
const relative = (field, i) => {
|
|
5563
5595
|
const s = setup[field] ?? 0;
|
|
5564
5596
|
const c = bone2[field] ?? 0;
|
|
5565
|
-
bone2[field] = v === null ? beforeFirstKey(from, alpha, c, s) : from === SETUP ? s + v[i] * alpha : c + (v[i] + s - c) * alpha;
|
|
5597
|
+
bone2[field] = v === null ? beforeFirstKey(from, alpha, c, s) : from === SETUP ? s + v[i] * alpha : c + (add ? v[i] : v[i] + s - c) * alpha;
|
|
5566
5598
|
};
|
|
5567
5599
|
const pair = (fx, fy) => {
|
|
5568
5600
|
const sx = setup[fx] ?? 0;
|
|
@@ -5580,6 +5612,9 @@ class Mixer {
|
|
|
5580
5612
|
if (from === SETUP) {
|
|
5581
5613
|
bone2[fx] = sx + v[0] * alpha;
|
|
5582
5614
|
bone2[fy] = sy + v[1] * alpha;
|
|
5615
|
+
} else if (add) {
|
|
5616
|
+
bone2[fx] = (bone2[fx] ?? 0) + v[0] * alpha;
|
|
5617
|
+
bone2[fy] = (bone2[fy] ?? 0) + v[1] * alpha;
|
|
5583
5618
|
} else {
|
|
5584
5619
|
bone2[fx] = (bone2[fx] ?? 0) + (sx + v[0] - (bone2[fx] ?? 0)) * alpha;
|
|
5585
5620
|
bone2[fy] = (bone2[fy] ?? 0) + (sy + v[1] - (bone2[fy] ?? 0)) * alpha;
|
|
@@ -5588,7 +5623,7 @@ class Mixer {
|
|
|
5588
5623
|
const scale = (field, i) => {
|
|
5589
5624
|
const s = setup[field] ?? 1;
|
|
5590
5625
|
const c = bone2[field] ?? 1;
|
|
5591
|
-
bone2[field] = v === null ? beforeFirstKey(from, alpha, c, s) : scaleValue(v[i] * s, alpha, from, out, c, s);
|
|
5626
|
+
bone2[field] = v === null ? beforeFirstKey(from, alpha, c, s) : scaleValue(v[i] * s, alpha, from, out, add, c, s);
|
|
5592
5627
|
};
|
|
5593
5628
|
switch (tl.kind) {
|
|
5594
5629
|
case "rotate":
|
|
@@ -5624,8 +5659,8 @@ class Mixer {
|
|
|
5624
5659
|
bone2.scaleY = (bone2.scaleY ?? 1) + (sy - (bone2.scaleY ?? 1)) * alpha;
|
|
5625
5660
|
}
|
|
5626
5661
|
} else {
|
|
5627
|
-
bone2.scaleX = scaleValue(v[0] * (setup.scaleX ?? 1), alpha, from, out, bone2.scaleX ?? 1, setup.scaleX ?? 1);
|
|
5628
|
-
bone2.scaleY = scaleValue(v[1] * (setup.scaleY ?? 1), alpha, from, out, bone2.scaleY ?? 1, setup.scaleY ?? 1);
|
|
5662
|
+
bone2.scaleX = scaleValue(v[0] * (setup.scaleX ?? 1), alpha, from, out, add, bone2.scaleX ?? 1, setup.scaleX ?? 1);
|
|
5663
|
+
bone2.scaleY = scaleValue(v[1] * (setup.scaleY ?? 1), alpha, from, out, add, bone2.scaleY ?? 1, setup.scaleY ?? 1);
|
|
5629
5664
|
}
|
|
5630
5665
|
break;
|
|
5631
5666
|
case "scalex":
|
|
@@ -5640,7 +5675,7 @@ class Mixer {
|
|
|
5640
5675
|
applyRotate(pose, tl, time, alpha, from, rotations, i, firstFrame) {
|
|
5641
5676
|
if (firstFrame) rotations[i] = 0;
|
|
5642
5677
|
if (alpha === 1) {
|
|
5643
|
-
this.applyBone(pose, tl, time, 1, from, false);
|
|
5678
|
+
this.applyBone(pose, tl, time, 1, from, false, false);
|
|
5644
5679
|
return;
|
|
5645
5680
|
}
|
|
5646
5681
|
const bone2 = pose.bone(tl.bone);
|
|
@@ -5712,6 +5747,14 @@ class Mixer {
|
|
|
5712
5747
|
if (retain) s.attachmentState = ATTACH_RETAIN;
|
|
5713
5748
|
else if (!setup) s.attachmentState = ATTACH_SETUP;
|
|
5714
5749
|
}
|
|
5750
|
+
/** `AttachmentTimeline.apply` — the plain timeline, writing no attachment state: only `additiveAttachmentsDirect` reaches it, the reading `CO52` rejects. */
|
|
5751
|
+
applyAttachmentDirect(pose, tl, time, from, out) {
|
|
5752
|
+
if (!pose.slotLive(tl.slot)) return;
|
|
5753
|
+
const index = (this.plant.search ?? keyIndexAt)(tl.keys, time);
|
|
5754
|
+
if (out || index < 0) {
|
|
5755
|
+
if (from !== CURRENT) pose.setAttachment(tl.slot, this.doc.slots[tl.slot].setup);
|
|
5756
|
+
} else pose.setAttachment(tl.slot, tl.keys[index].name ?? null);
|
|
5757
|
+
}
|
|
5715
5758
|
applyColour(pose, tl, time, alpha, from) {
|
|
5716
5759
|
if (!pose.slotLive(tl.slot)) return;
|
|
5717
5760
|
const slot = this.doc.slots[tl.slot];
|
|
@@ -5807,7 +5850,7 @@ class Mixer {
|
|
|
5807
5850
|
}
|
|
5808
5851
|
}
|
|
5809
5852
|
/** `DeformTimeline.apply`: every live slot showing the record the timeline keys, its deform array mixed. */
|
|
5810
|
-
applyDeform(pose, tl, time, alpha, from) {
|
|
5853
|
+
applyDeform(pose, tl, time, alpha, from, add) {
|
|
5811
5854
|
const keys = tl.timeline.deform;
|
|
5812
5855
|
if (keys === null) return;
|
|
5813
5856
|
pose.slots.forEach((s, k) => {
|
|
@@ -5837,12 +5880,26 @@ class Mixer {
|
|
|
5837
5880
|
}
|
|
5838
5881
|
return;
|
|
5839
5882
|
}
|
|
5840
|
-
|
|
5841
|
-
|
|
5883
|
+
const fromSetup = mixFrom === SETUP;
|
|
5884
|
+
if (alpha === 1) {
|
|
5885
|
+
if (add && !fromSetup) {
|
|
5886
|
+
const current = s.deform;
|
|
5887
|
+
if (!vertices.weighted) {
|
|
5888
|
+
const setup = setupArray(vertices);
|
|
5889
|
+
s.deform = current.map((c, i) => c + (target[i] - setup[i]));
|
|
5890
|
+
} else s.deform = current.map((c, i) => c + target[i]);
|
|
5891
|
+
} else s.deform = [...target];
|
|
5892
|
+
} else if (fromSetup) {
|
|
5842
5893
|
if (!vertices.weighted) {
|
|
5843
5894
|
const setup = setupArray(vertices);
|
|
5844
5895
|
s.deform = target.map((v, i) => setup[i] + (v - setup[i]) * alpha);
|
|
5845
5896
|
} else s.deform = target.map((v) => v * alpha);
|
|
5897
|
+
} else if (add) {
|
|
5898
|
+
const current = s.deform;
|
|
5899
|
+
if (!vertices.weighted) {
|
|
5900
|
+
const setup = setupArray(vertices);
|
|
5901
|
+
s.deform = current.map((c, i) => c + (target[i] - setup[i]) * alpha);
|
|
5902
|
+
} else s.deform = current.map((c, i) => c + target[i] * alpha);
|
|
5846
5903
|
} else {
|
|
5847
5904
|
const current = s.deform;
|
|
5848
5905
|
s.deform = current.map((c, i) => c + (target[i] - c) * alpha);
|
|
@@ -5855,9 +5912,10 @@ function beforeFirstKey(from, alpha, current, setup) {
|
|
|
5855
5912
|
if (from === FIRST) return current + (setup - current) * alpha;
|
|
5856
5913
|
return current;
|
|
5857
5914
|
}
|
|
5858
|
-
function scaleValue(value, alpha, from, out, current, setup) {
|
|
5859
|
-
if (alpha === 1) return value;
|
|
5915
|
+
function scaleValue(value, alpha, from, out, add, current, setup) {
|
|
5916
|
+
if (alpha === 1 && !add) return value;
|
|
5860
5917
|
let base = from === SETUP ? setup : current;
|
|
5918
|
+
if (add) return base + (value - setup) * alpha;
|
|
5861
5919
|
if (out) return base + (Math.abs(value) * Math.sign(base) - base) * alpha;
|
|
5862
5920
|
base = Math.abs(base) * Math.sign(value);
|
|
5863
5921
|
return base + (value - base) * alpha;
|
|
@@ -5878,6 +5936,16 @@ function loopOf(options, call) {
|
|
|
5878
5936
|
if (typeof options !== "object" || options === null || typeof options.loop !== "boolean") throw new CoreInputError(`the live tracks' ${call} options.loop is ${JSON.stringify(options?.loop)}, not true or false`);
|
|
5879
5937
|
return options.loop;
|
|
5880
5938
|
}
|
|
5939
|
+
function alphaValue(v, what, call) {
|
|
5940
|
+
if (typeof v !== "number" || !Number.isFinite(v) || v < 0 || v > 1) throw new CoreInputError(`the live tracks' ${call} ${what} is ${typeof v === "number" ? String(v) : JSON.stringify(v)}, not a number from 0 to 1`);
|
|
5941
|
+
return v;
|
|
5942
|
+
}
|
|
5943
|
+
function blendOf(options, call) {
|
|
5944
|
+
const alpha = options.alpha === void 0 ? 1 : alphaValue(options.alpha, "options.alpha", call);
|
|
5945
|
+
const additive = options.additive;
|
|
5946
|
+
if (additive !== void 0 && typeof additive !== "boolean") throw new CoreInputError(`the live tracks' ${call} options.additive is ${JSON.stringify(additive)}, not true or false — an entry adds or it does not`);
|
|
5947
|
+
return { alpha, additive: additive === true };
|
|
5948
|
+
}
|
|
5881
5949
|
function delayOf(v, call) {
|
|
5882
5950
|
if (typeof v !== "number" || !Number.isFinite(v)) throw new CoreInputError(`the live tracks' ${call} delay is ${String(v)}, not a finite number`);
|
|
5883
5951
|
return v;
|
|
@@ -5911,8 +5979,11 @@ class Tracks {
|
|
|
5911
5979
|
const i = trackIndex(track, "setAnimation");
|
|
5912
5980
|
const loop = loopOf(options, "setAnimation");
|
|
5913
5981
|
const mix = finiteAtOrAbove0(options.mixDuration, "mixDuration", "setAnimation");
|
|
5982
|
+
const blend = blendOf(options, "setAnimation");
|
|
5914
5983
|
const entry = this.mixer.setAnimation(i, this.mixer.animation(animation), loop);
|
|
5915
5984
|
entry.mixDuration = mix;
|
|
5985
|
+
entry.alpha = blend.alpha;
|
|
5986
|
+
entry.additive = blend.additive;
|
|
5916
5987
|
}
|
|
5917
5988
|
setEmptyAnimation(track, mixDuration) {
|
|
5918
5989
|
this.live("setEmptyAnimation");
|
|
@@ -5928,8 +5999,11 @@ class Tracks {
|
|
|
5928
5999
|
const loop = loopOf(options, "addAnimation");
|
|
5929
6000
|
const mix = finiteAtOrAbove0(options.mixDuration, "mixDuration", "addAnimation");
|
|
5930
6001
|
const delay = delayOf(options.delay, "addAnimation");
|
|
6002
|
+
const blend = blendOf(options, "addAnimation");
|
|
5931
6003
|
const entry = this.mixer.addAnimation(i, this.mixer.animation(animation), loop, delay);
|
|
5932
6004
|
this.mixer.setMixDuration(entry, mix, delay);
|
|
6005
|
+
entry.alpha = blend.alpha;
|
|
6006
|
+
entry.additive = blend.additive;
|
|
5933
6007
|
}
|
|
5934
6008
|
addEmptyAnimation(track, mixDuration, delay) {
|
|
5935
6009
|
this.live("addEmptyAnimation");
|
|
@@ -5941,10 +6015,18 @@ class Tracks {
|
|
|
5941
6015
|
entry.mixDuration = mix;
|
|
5942
6016
|
entry.trackEnd = mix;
|
|
5943
6017
|
}
|
|
6018
|
+
setAlpha(track, alpha) {
|
|
6019
|
+
this.live("setAlpha");
|
|
6020
|
+
const i = trackIndex(track, "setAlpha");
|
|
6021
|
+
const a = alphaValue(alpha, "alpha", "setAlpha");
|
|
6022
|
+
const e = this.mixer.tracks[i] ?? null;
|
|
6023
|
+
if (e === null) throw new CoreInputError(`the live tracks' setAlpha names track ${i}, which plays nothing — there is no entry to weigh`);
|
|
6024
|
+
e.alpha = a;
|
|
6025
|
+
}
|
|
5944
6026
|
current(track) {
|
|
5945
6027
|
const e = this.mixer.tracks[trackIndex(track, "current")] ?? null;
|
|
5946
6028
|
if (e === null) return null;
|
|
5947
|
-
return { animation: e.anim.name, loop: e.loop, trackTime: e.trackTime, animationTime: animationTimeOf(e), duration: e.anim.duration };
|
|
6029
|
+
return { animation: e.anim.name, loop: e.loop, trackTime: e.trackTime, animationTime: animationTimeOf(e), duration: e.anim.duration, alpha: e.alpha, additive: e.additive };
|
|
5948
6030
|
}
|
|
5949
6031
|
start() {
|
|
5950
6032
|
this.live("start");
|
|
@@ -8438,9 +8520,12 @@ class RigPlayer {
|
|
|
8438
8520
|
const track = options.track ?? 0;
|
|
8439
8521
|
const loop = options.loop ?? true;
|
|
8440
8522
|
const mix = options.mix ?? 0;
|
|
8523
|
+
const alpha = options.alpha ?? 1;
|
|
8524
|
+
const additive = options.additive ?? false;
|
|
8441
8525
|
this.paused = false;
|
|
8442
|
-
|
|
8443
|
-
if (this.
|
|
8526
|
+
const plain = track === 0 && mix === 0 && alpha === 1 && !additive;
|
|
8527
|
+
if (!plain) this.beyondBase = true;
|
|
8528
|
+
if (this.tracks === null && plain && !this.trackMode) {
|
|
8444
8529
|
this.animation = name;
|
|
8445
8530
|
this.loop = loop;
|
|
8446
8531
|
this.track = openTrack(this.view, name, { loop, reset: "animation", adjust: this.adjustHook });
|
|
@@ -8448,7 +8533,7 @@ class RigPlayer {
|
|
|
8448
8533
|
return;
|
|
8449
8534
|
}
|
|
8450
8535
|
const tracks = this.compose();
|
|
8451
|
-
tracks.setAnimation(track, name, { loop, mixDuration: mix });
|
|
8536
|
+
tracks.setAnimation(track, name, { loop, mixDuration: mix, alpha, additive });
|
|
8452
8537
|
this.trackCount = Math.max(this.trackCount, track + 1);
|
|
8453
8538
|
if (track === 0) {
|
|
8454
8539
|
this.animation = name;
|
|
@@ -8466,7 +8551,7 @@ class RigPlayer {
|
|
|
8466
8551
|
const track = options.track ?? 0;
|
|
8467
8552
|
this.beyondBase = true;
|
|
8468
8553
|
const tracks = this.compose();
|
|
8469
|
-
tracks.addAnimation(track, name, { loop: options.loop ?? true, mixDuration: options.mix ?? 0, delay: options.delay ?? 0 });
|
|
8554
|
+
tracks.addAnimation(track, name, { loop: options.loop ?? true, mixDuration: options.mix ?? 0, delay: options.delay ?? 0, alpha: options.alpha ?? 1, additive: options.additive ?? false });
|
|
8470
8555
|
this.trackCount = Math.max(this.trackCount, track + 1);
|
|
8471
8556
|
this.pose = tracks.step(0);
|
|
8472
8557
|
}
|
|
@@ -8495,15 +8580,28 @@ class RigPlayer {
|
|
|
8495
8580
|
this.trackCount = Math.max(this.trackCount, track + 1);
|
|
8496
8581
|
this.pose = tracks.step(0);
|
|
8497
8582
|
}
|
|
8583
|
+
/**
|
|
8584
|
+
* Sets the weight of `track`'s current entry, from 0 to 1, from the next pose
|
|
8585
|
+
* on — the pose the player holds is re-posed at once, so a held player shows
|
|
8586
|
+
* it on its next `draw`. An entry it is mixing from keeps its own weight. A
|
|
8587
|
+
* track playing nothing, or a weight outside 0..1, is refused in the core's
|
|
8588
|
+
* words. On the single track this composes: the player moves onto the tracks.
|
|
8589
|
+
*/
|
|
8590
|
+
setAlpha(track, alpha) {
|
|
8591
|
+
this.beyondBase = true;
|
|
8592
|
+
const tracks = this.compose();
|
|
8593
|
+
tracks.setAlpha(track, alpha);
|
|
8594
|
+
this.pose = tracks.step(0);
|
|
8595
|
+
}
|
|
8498
8596
|
/** `track`'s current entry, or null where it plays nothing (and before `play`). */
|
|
8499
8597
|
entry(track = 0) {
|
|
8500
8598
|
if (this.tracks !== null) {
|
|
8501
8599
|
const view = this.tracks.current(track);
|
|
8502
|
-
return view === null ? null : { animation: view.animation, loop: view.loop, trackTime: view.trackTime, animationTime: view.animationTime, duration: view.duration };
|
|
8600
|
+
return view === null ? null : { animation: view.animation, loop: view.loop, trackTime: view.trackTime, animationTime: view.animationTime, duration: view.duration, alpha: view.alpha, additive: view.additive };
|
|
8503
8601
|
}
|
|
8504
8602
|
const single = this.track;
|
|
8505
8603
|
if (track !== 0 || single === null) return null;
|
|
8506
|
-
return { animation: this.animation, loop: this.loop, trackTime: single.trackTime, animationTime: single.animationTime, duration: single.duration };
|
|
8604
|
+
return { animation: this.animation, loop: this.loop, trackTime: single.trackTime, animationTime: single.animationTime, duration: single.duration, alpha: 1, additive: false };
|
|
8507
8605
|
}
|
|
8508
8606
|
/** Whether the player is composed: on the live tracks rather than the single track. */
|
|
8509
8607
|
get composed() {
|