@rig-c/play 1.14.0 → 1.15.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 +61 -2
- package/dist/index.js +218 -17
- package/dist/index.js.map +1 -1
- package/dist/loadModel.d.ts +7 -0
- package/dist/loadModel.d.ts.map +1 -1
- package/dist/player.d.ts +60 -4
- package/dist/player.d.ts.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -556,6 +556,64 @@ active (`null`), track evaluation runs with zero extra work and produces
|
|
|
556
556
|
byte-identical poses. An unknown set name throws by name; on a build without
|
|
557
557
|
`sets.json`, `sets()` is empty (`[]`) and `setSet` throws.
|
|
558
558
|
|
|
559
|
+
**Several sets at once** (#168): sets that touch disjoint slots can be active
|
|
560
|
+
together — a mouth viseme driven by audio beside an arm gesture driven by
|
|
561
|
+
input, without building every combination as its own set:
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
player.setSets(['viseme_o', 'gesture_chin']); // exactly these, together
|
|
565
|
+
player.removeSet('viseme_o'); // the gesture stays
|
|
566
|
+
player.addSet('viseme_a'); // beside the active ones; already active = no change
|
|
567
|
+
player.activeSets; // ['viseme_a', 'gesture_chin'] in sets.json order
|
|
568
|
+
player.set; // null: several are active (one active → its name)
|
|
569
|
+
player.setSets([]); // restore base / track attachments
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
A set *touches* every slot it covers, every slot it `hides` and the
|
|
573
|
+
`<base>_still` companion it hides. Two active sets touching the same slot are
|
|
574
|
+
refused by name — `sets "viseme_o" and "talk_point" both touch slot "mouth"` —
|
|
575
|
+
and nothing changes; precedence is never guessed. Disjoint sets compose as the
|
|
576
|
+
union of each alone, whatever the order they were named or added in, and every
|
|
577
|
+
rule above (applied to the held pose, after track evaluation, uncovered slots
|
|
578
|
+
untouched) holds for the composition. `setSet(name)` keeps meaning *exactly this
|
|
579
|
+
one* (`setSets([name])`) and `setSet(null)` is `setSets([])`; an unknown or
|
|
580
|
+
repeated name throws by name. `slotIdentity()` reports the list as `sets`
|
|
581
|
+
beside `set`.
|
|
582
|
+
|
|
583
|
+
**Drawn bones** (#170): a set from `@rig-c/parts` that replaces a limb
|
|
584
|
+
(a *gesture* subset set) carries `drawn` — the world rotation, in degrees
|
|
585
|
+
(CCW, Y up), each of its bones was painted at — and its attachments are
|
|
586
|
+
placed in the frame of the bones posed there:
|
|
587
|
+
|
|
588
|
+
```json
|
|
589
|
+
"wave": {
|
|
590
|
+
"slots": { "arm_upper": "arm_upper_wave", "arm_lower": "arm_lower_wave" },
|
|
591
|
+
"drawn": { "shoulder": 50, "elbow": 95 }
|
|
592
|
+
}
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
While such a set is active its bones are posed at those angles, the way
|
|
596
|
+
`rig-parts check --set` poses them: in document order, each drawn bone's
|
|
597
|
+
local rotation is the drawn angle less its world rotation at local 0 under
|
|
598
|
+
its already-posed parents (no constraint applied), computed once when the set
|
|
599
|
+
is activated. On a track that local rotation replaces the clip's rotation
|
|
600
|
+
of that bone on every pose, before `adjust` (so `adjust` still has the last
|
|
601
|
+
word); every other bone keeps playing. Activating or clearing a drawn set
|
|
602
|
+
poses the held pose again at the same time. Two active sets drawing the same
|
|
603
|
+
bone are refused by name, as two sets touching one slot are. A `drawn` naming
|
|
604
|
+
a bone the document does not have is refused when `sets.json` is loaded.
|
|
605
|
+
|
|
606
|
+
`sets.json` keys are read strictly. Besides `slots`, `hides` and `drawn`, a
|
|
607
|
+
set may carry `drawnLength`, `registration` and `threshold`, which parts
|
|
608
|
+
writes and which place nothing under `check --set`: play reads past them by
|
|
609
|
+
name and gives them no meaning. Any other key, on a set or on the document
|
|
610
|
+
(which takes `spec`, `sets` and `threshold`), is refused by name rather than
|
|
611
|
+
ignored.
|
|
612
|
+
|
|
613
|
+
To switch one driver's set while another stays — a new viseme while a gesture
|
|
614
|
+
holds — name the new list in one call (`setSets(['viseme_a', 'gesture_chin'])`)
|
|
615
|
+
or `removeSet` then `addSet`; the first re-poses once, the second twice.
|
|
616
|
+
|
|
559
617
|
### Slot identity snapshot (`slotIdentity()`)
|
|
560
618
|
|
|
561
619
|
To inspect which attachment key, owning skin and visibility state each slot
|
|
@@ -566,7 +624,8 @@ holds on the current frame — for a parts explorer or review overlay — withou
|
|
|
566
624
|
const snap = player.slotIdentity();
|
|
567
625
|
// {
|
|
568
626
|
// generation: number, // matches player.generation and player.posedFrame().generation
|
|
569
|
-
// set: string | null, // active set
|
|
627
|
+
// set: string | null, // the one active set (player.set); null when none or several
|
|
628
|
+
// sets: readonly string[], // every active set (player.activeSets), sets.json order
|
|
570
629
|
// skin: string | null, // active skin name (player.skin ?? null)
|
|
571
630
|
// slots: readonly [ // every slot in posed draw order (z = 0 .. slots.length - 1)
|
|
572
631
|
// {
|
|
@@ -589,7 +648,7 @@ ones included) appears in `snap.slots`:
|
|
|
589
648
|
and the skin that owns it (`skin`).
|
|
590
649
|
- Non-drawn slots report `attachment: null`, `skin: null`, and why they are
|
|
591
650
|
absent from `posedFrame().order`:
|
|
592
|
-
- `'set-hide'` — hidden by
|
|
651
|
+
- `'set-hide'` — hidden by an active attachment set (`set.hides` or a
|
|
593
652
|
`<base>_still` companion of a slot the set covers or hides).
|
|
594
653
|
- `'timeline-null'` — cleared by an active attachment timeline key (`null`).
|
|
595
654
|
- `'no-attachment'` — no attachment in the setup pose or under the active skin
|
package/dist/index.js
CHANGED
|
@@ -8579,6 +8579,9 @@ function readSetupMesh(assets, target) {
|
|
|
8579
8579
|
return readSetupMesh$1(underNoSkin(assets.document.doc), target);
|
|
8580
8580
|
}
|
|
8581
8581
|
const SETS_SPECS = ["spine-parts-sets/1", "rig-parts-sets/1"];
|
|
8582
|
+
const SET_KEYS_NOT_POSED = ["drawnLength", "registration", "threshold"];
|
|
8583
|
+
const SET_KEYS = ["slots", "hides", "drawn", ...SET_KEYS_NOT_POSED];
|
|
8584
|
+
const SETS_DOCUMENT_KEYS = ["spec", "sets", "threshold"];
|
|
8582
8585
|
function isRecord(value) {
|
|
8583
8586
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8584
8587
|
}
|
|
@@ -8597,10 +8600,16 @@ function readSetsText(text, where, doc) {
|
|
|
8597
8600
|
`${where}: spec is ${JSON.stringify(raw.spec)}, not "${SETS_SPECS[0]}" or "${SETS_SPECS[1]}"`
|
|
8598
8601
|
);
|
|
8599
8602
|
}
|
|
8603
|
+
for (const key of Object.keys(raw)) {
|
|
8604
|
+
if (!SETS_DOCUMENT_KEYS.includes(key)) {
|
|
8605
|
+
throw new Error(`${where}: unknown key ${JSON.stringify(key)} (known: ${SETS_DOCUMENT_KEYS.join(", ")})`);
|
|
8606
|
+
}
|
|
8607
|
+
}
|
|
8600
8608
|
if (!isRecord(raw.sets) && !Array.isArray(raw.sets)) {
|
|
8601
8609
|
throw new Error(`${where}: "sets" must be an object or an array`);
|
|
8602
8610
|
}
|
|
8603
8611
|
const knownSlots = new Set(doc.slots.map((slot) => slot.name));
|
|
8612
|
+
const knownBones = new Set(doc.bones.map((bone2) => bone2.name));
|
|
8604
8613
|
const attachmentsBySlot = /* @__PURE__ */ new Map();
|
|
8605
8614
|
for (const skin of doc.skins) {
|
|
8606
8615
|
for (const [slotName, table] of Object.entries(skin.attachments)) {
|
|
@@ -8636,6 +8645,12 @@ function readSetsText(text, where, doc) {
|
|
|
8636
8645
|
throw new Error(`${where}: duplicate set "${name}"`);
|
|
8637
8646
|
}
|
|
8638
8647
|
seenNames.add(name);
|
|
8648
|
+
for (const key of Object.keys(item)) {
|
|
8649
|
+
if (Array.isArray(raw.sets) && key === "name") continue;
|
|
8650
|
+
if (!SET_KEYS.includes(key)) {
|
|
8651
|
+
throw new Error(`${where}: set "${name}" has unknown key ${JSON.stringify(key)} (known: ${SET_KEYS.join(", ")})`);
|
|
8652
|
+
}
|
|
8653
|
+
}
|
|
8639
8654
|
if (!isRecord(item.slots)) {
|
|
8640
8655
|
throw new Error(`${where}: set "${name}" must have a "slots" object`);
|
|
8641
8656
|
}
|
|
@@ -8670,11 +8685,28 @@ function readSetsText(text, where, doc) {
|
|
|
8670
8685
|
if (!hides.includes(hidden)) hides.push(hidden);
|
|
8671
8686
|
}
|
|
8672
8687
|
}
|
|
8688
|
+
let drawn;
|
|
8689
|
+
if (item.drawn !== void 0) {
|
|
8690
|
+
if (!isRecord(item.drawn)) {
|
|
8691
|
+
throw new Error(`${where}: set "${name}" "drawn" must be an object of bone name → degrees`);
|
|
8692
|
+
}
|
|
8693
|
+
drawn = {};
|
|
8694
|
+
for (const [boneName, degrees] of Object.entries(item.drawn)) {
|
|
8695
|
+
if (!knownBones.has(boneName)) {
|
|
8696
|
+
throw new Error(`${where}: set "${name}" draws unknown bone "${boneName}"`);
|
|
8697
|
+
}
|
|
8698
|
+
if (typeof degrees !== "number" || !Number.isFinite(degrees)) {
|
|
8699
|
+
throw new Error(`${where}: set "${name}" drawn bone "${boneName}" must be a finite number of degrees`);
|
|
8700
|
+
}
|
|
8701
|
+
drawn[boneName] = degrees;
|
|
8702
|
+
}
|
|
8703
|
+
}
|
|
8673
8704
|
out.push(
|
|
8674
8705
|
Object.freeze({
|
|
8675
8706
|
name,
|
|
8676
8707
|
slots: Object.freeze(slots),
|
|
8677
|
-
hides: Object.freeze(hides)
|
|
8708
|
+
hides: Object.freeze(hides),
|
|
8709
|
+
...drawn === void 0 || Object.keys(drawn).length === 0 ? {} : { drawn: Object.freeze(drawn) }
|
|
8678
8710
|
})
|
|
8679
8711
|
);
|
|
8680
8712
|
}
|
|
@@ -8942,6 +8974,47 @@ function hiddenBySet(slotName, set) {
|
|
|
8942
8974
|
}
|
|
8943
8975
|
return false;
|
|
8944
8976
|
}
|
|
8977
|
+
function touchedBySet(view, set) {
|
|
8978
|
+
const touched = /* @__PURE__ */ new Set();
|
|
8979
|
+
for (const slot of view.slots) {
|
|
8980
|
+
if (slot.name in set.slots || hiddenBySet(slot.name, set)) touched.add(slot.name);
|
|
8981
|
+
}
|
|
8982
|
+
return touched;
|
|
8983
|
+
}
|
|
8984
|
+
function normaliseDegrees(deg) {
|
|
8985
|
+
let v = (deg % 360 + 360) % 360;
|
|
8986
|
+
if (v > 180) v -= 360;
|
|
8987
|
+
return v;
|
|
8988
|
+
}
|
|
8989
|
+
function drawnLocals(view, drawn) {
|
|
8990
|
+
const names = new Set(view.bones.map((b) => b.name));
|
|
8991
|
+
const realParent = (parent) => parent !== void 0 && parent.endsWith("_d") && names.has(parent.slice(0, -2)) ? parent.slice(0, -2) : parent;
|
|
8992
|
+
const bones = view.bones.map((b) => ({ ...b, parent: realParent(b.parent) }));
|
|
8993
|
+
const locals = /* @__PURE__ */ new Map();
|
|
8994
|
+
for (let i = 0; i < bones.length; i++) {
|
|
8995
|
+
const target = drawn[bones[i].name];
|
|
8996
|
+
if (target === void 0) continue;
|
|
8997
|
+
bones[i] = { ...bones[i], rotation: 0 };
|
|
8998
|
+
const pose = poseRawSetup({ ...view, bones, slots: [], constraints: [] });
|
|
8999
|
+
const world = pose.bones.find((b) => b.name === bones[i].name);
|
|
9000
|
+
if (world === void 0) throw new Error(`drawn bone ${JSON.stringify(bones[i].name)} was not posed`);
|
|
9001
|
+
const rotation = normaliseDegrees(target - normaliseDegrees(Math.atan2(world.c, world.a) * 180 / Math.PI));
|
|
9002
|
+
bones[i] = { ...bones[i], rotation };
|
|
9003
|
+
locals.set(bones[i].name, rotation);
|
|
9004
|
+
}
|
|
9005
|
+
return locals;
|
|
9006
|
+
}
|
|
9007
|
+
function composeSets(sets) {
|
|
9008
|
+
if (sets.length === 0) return null;
|
|
9009
|
+
if (sets.length === 1) return sets[0];
|
|
9010
|
+
const drawn = sets.filter((s) => s.drawn !== void 0);
|
|
9011
|
+
return {
|
|
9012
|
+
name: sets.map((s) => s.name).join("+"),
|
|
9013
|
+
slots: Object.assign({}, ...sets.map((s) => s.slots)),
|
|
9014
|
+
hides: sets.flatMap((s) => s.hides),
|
|
9015
|
+
...drawn.length === 0 ? {} : { drawn: Object.assign({}, ...drawn.map((s) => s.drawn)) }
|
|
9016
|
+
};
|
|
9017
|
+
}
|
|
8945
9018
|
function applySetToPose(view, raw, set) {
|
|
8946
9019
|
const coveredNames = new Set(Object.keys(set.slots));
|
|
8947
9020
|
const overrideSlots = view.slots.flatMap((slot) => {
|
|
@@ -9182,7 +9255,12 @@ class RigPlayer {
|
|
|
9182
9255
|
cachedIdentityGen = -1;
|
|
9183
9256
|
setList;
|
|
9184
9257
|
setsByName;
|
|
9258
|
+
/** The active sets in declaration order (#168); `activeSet` is their composition. */
|
|
9259
|
+
activeList = [];
|
|
9185
9260
|
activeSet = null;
|
|
9261
|
+
touchedSets = /* @__PURE__ */ new Map();
|
|
9262
|
+
/** The active sets' drawn bones as local rotations, applied before `adjust` on every pose (#170); null when none draws. */
|
|
9263
|
+
drawnPose = null;
|
|
9186
9264
|
adjustFn = null;
|
|
9187
9265
|
/** Current preview patches, reapplied through the public core API on replay; no edit history. */
|
|
9188
9266
|
physicsPatches = /* @__PURE__ */ new Map();
|
|
@@ -9193,6 +9271,7 @@ class RigPlayer {
|
|
|
9193
9271
|
draws = () => true;
|
|
9194
9272
|
/** Handed to every track the player opens; reads the current `adjust` so setting it later needs no reopen. */
|
|
9195
9273
|
adjustHook = (locals, ctx) => {
|
|
9274
|
+
if (this.drawnPose !== null) for (const [bone2, rotation] of this.drawnPose) locals.bone(bone2).rotation = rotation;
|
|
9196
9275
|
if (this.adjustFn !== null) this.adjustFn(locals, ctx);
|
|
9197
9276
|
};
|
|
9198
9277
|
listeners = {
|
|
@@ -9726,8 +9805,7 @@ class RigPlayer {
|
|
|
9726
9805
|
this.reopen();
|
|
9727
9806
|
this.stepTo(time);
|
|
9728
9807
|
} else if (this.rawPose !== null) {
|
|
9729
|
-
this.
|
|
9730
|
-
this.setRawPose(this.setup);
|
|
9808
|
+
this.setRawPose(this.drawnSetup());
|
|
9731
9809
|
} else {
|
|
9732
9810
|
this.poseGeneration++;
|
|
9733
9811
|
this.cachedIdentity = null;
|
|
@@ -9741,9 +9819,17 @@ class RigPlayer {
|
|
|
9741
9819
|
sets() {
|
|
9742
9820
|
return this.setList;
|
|
9743
9821
|
}
|
|
9744
|
-
/**
|
|
9822
|
+
/**
|
|
9823
|
+
* The active attachment set's name when exactly one is active; `null` when
|
|
9824
|
+
* the base attachments are active or when several sets are (read
|
|
9825
|
+
* `activeSets` for the list).
|
|
9826
|
+
*/
|
|
9745
9827
|
get set() {
|
|
9746
|
-
return this.
|
|
9828
|
+
return this.activeList.length === 1 ? this.activeList[0].name : null;
|
|
9829
|
+
}
|
|
9830
|
+
/** Every active attachment set's name, in `sets.json` declaration order (`[]` when none). */
|
|
9831
|
+
get activeSets() {
|
|
9832
|
+
return this.activeList.map((s) => s.name);
|
|
9747
9833
|
}
|
|
9748
9834
|
/**
|
|
9749
9835
|
* Switches the active attachment set (#134): every slot the set covers takes
|
|
@@ -9757,6 +9843,12 @@ class RigPlayer {
|
|
|
9757
9843
|
* frames untouched; when no set is active (`null`), track evaluation runs
|
|
9758
9844
|
* with zero overhead and produces byte-identical poses.
|
|
9759
9845
|
*
|
|
9846
|
+
* A set with `drawn` (#170) also poses its bones at those world angles, as
|
|
9847
|
+
* `@rig-c/parts check --set` does, replacing a clip's rotation of them.
|
|
9848
|
+
*
|
|
9849
|
+
* `setSet(name)` means exactly this one set: any other active sets are
|
|
9850
|
+
* cleared (`setSets([name])`), and `setSet(null)` is `setSets([])`.
|
|
9851
|
+
*
|
|
9760
9852
|
* A name not in `sets.json` throws by name; a build without `sets.json` has
|
|
9761
9853
|
* `sets()` empty and `setSet` throws.
|
|
9762
9854
|
*/
|
|
@@ -9764,27 +9856,135 @@ class RigPlayer {
|
|
|
9764
9856
|
if (this.setList.length === 0) {
|
|
9765
9857
|
throw new Error(`cannot setSet(${JSON.stringify(name)}): this model has no sets.json`);
|
|
9766
9858
|
}
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9859
|
+
this.activate(name === null ? [] : [this.namedSet(name)]);
|
|
9860
|
+
}
|
|
9861
|
+
/**
|
|
9862
|
+
* Makes exactly `names` the active attachment sets (#168), together: each
|
|
9863
|
+
* set covers and hides its slots as it does alone, provided no two of them
|
|
9864
|
+
* touch the same slot (cover it, hide it, or hide it as a `<base>_still`
|
|
9865
|
+
* companion). Composition does not depend on the order of `names`. An
|
|
9866
|
+
* unknown name, a repeated name or two sets touching one slot throw by name
|
|
9867
|
+
* and leave the active sets as they were; `[]` restores the base.
|
|
9868
|
+
*/
|
|
9869
|
+
setSets(names) {
|
|
9870
|
+
if (this.setList.length === 0) {
|
|
9871
|
+
throw new Error(`cannot setSets(${JSON.stringify(names)}): this model has no sets.json`);
|
|
9872
|
+
}
|
|
9873
|
+
const sets = names.map((name) => this.namedSet(name));
|
|
9874
|
+
for (let i = 0; i < sets.length; i++) {
|
|
9875
|
+
for (let j = 0; j < i; j++) {
|
|
9876
|
+
if (sets[i] === sets[j]) throw new Error(`set ${JSON.stringify(sets[i].name)} is named twice in setSets`);
|
|
9877
|
+
}
|
|
9776
9878
|
}
|
|
9879
|
+
this.activate(sets);
|
|
9880
|
+
}
|
|
9881
|
+
/**
|
|
9882
|
+
* Activates `name` beside the sets already active (#168): a set already
|
|
9883
|
+
* active is left as it is; one touching a slot an active set touches throws
|
|
9884
|
+
* by name (the slot and both sets) and changes nothing.
|
|
9885
|
+
*/
|
|
9886
|
+
addSet(name) {
|
|
9887
|
+
if (this.setList.length === 0) {
|
|
9888
|
+
throw new Error(`cannot addSet(${JSON.stringify(name)}): this model has no sets.json`);
|
|
9889
|
+
}
|
|
9890
|
+
const found = this.namedSet(name);
|
|
9891
|
+
if (this.activeList.includes(found)) return;
|
|
9892
|
+
this.activate([...this.activeList, found]);
|
|
9893
|
+
}
|
|
9894
|
+
/**
|
|
9895
|
+
* Deactivates `name`, keeping every other active set (#168). An unknown name
|
|
9896
|
+
* throws by name; a set that is not active is left as it is.
|
|
9897
|
+
*/
|
|
9898
|
+
removeSet(name) {
|
|
9899
|
+
if (this.setList.length === 0) {
|
|
9900
|
+
throw new Error(`cannot removeSet(${JSON.stringify(name)}): this model has no sets.json`);
|
|
9901
|
+
}
|
|
9902
|
+
const found = this.namedSet(name);
|
|
9903
|
+
if (!this.activeList.includes(found)) return;
|
|
9904
|
+
this.activate(this.activeList.filter((s) => s !== found));
|
|
9905
|
+
}
|
|
9906
|
+
/** The declared set named `name`, or a throw naming it and the available sets. */
|
|
9907
|
+
namedSet(name) {
|
|
9777
9908
|
const found = this.setsByName.get(name);
|
|
9778
9909
|
if (found === void 0) {
|
|
9779
9910
|
throw new Error(
|
|
9780
9911
|
`no set named ${JSON.stringify(name)} in sets.json (available: ${this.setList.map((s) => s.name).join(", ")})`
|
|
9781
9912
|
);
|
|
9782
9913
|
}
|
|
9783
|
-
|
|
9914
|
+
return found;
|
|
9915
|
+
}
|
|
9916
|
+
/**
|
|
9917
|
+
* Poses again after the drawn bones changed (#170): through the tracks at
|
|
9918
|
+
* the same time when the player has played, else the setup pose with the
|
|
9919
|
+
* drawn bones at their rotations.
|
|
9920
|
+
*/
|
|
9921
|
+
reposeDrawn() {
|
|
9922
|
+
const stepper = this.tracks ?? this.track;
|
|
9923
|
+
if (stepper !== null && this.pose !== null) {
|
|
9924
|
+
this.setRawPose(stepper.step(0));
|
|
9925
|
+
return;
|
|
9926
|
+
}
|
|
9927
|
+
this.setRawPose(this.drawnSetup());
|
|
9928
|
+
}
|
|
9929
|
+
/** The setup pose with the active sets' drawn bones at their rotations (#170); the plain setup pose when none draws. */
|
|
9930
|
+
drawnSetup() {
|
|
9931
|
+
const setup = this.setup ?? (this.setup = poseRawSetup(this.view));
|
|
9932
|
+
if (this.drawnPose === null) return setup;
|
|
9933
|
+
return poseRawSetup({
|
|
9934
|
+
...this.view,
|
|
9935
|
+
bones: this.view.bones.map((b) => {
|
|
9936
|
+
const rotation = this.drawnPose?.get(b.name);
|
|
9937
|
+
return rotation === void 0 ? b : { ...b, rotation };
|
|
9938
|
+
})
|
|
9939
|
+
});
|
|
9940
|
+
}
|
|
9941
|
+
/** The slots `set` touches, computed once per set. */
|
|
9942
|
+
touched(set) {
|
|
9943
|
+
let touched = this.touchedSets.get(set.name);
|
|
9944
|
+
if (touched === void 0) {
|
|
9945
|
+
touched = touchedBySet(this.view, set);
|
|
9946
|
+
this.touchedSets.set(set.name, touched);
|
|
9947
|
+
}
|
|
9948
|
+
return touched;
|
|
9949
|
+
}
|
|
9950
|
+
/**
|
|
9951
|
+
* Makes `sets` (distinct) the active sets: refuses any two touching one slot
|
|
9952
|
+
* before changing anything, then applies their composition to the held pose.
|
|
9953
|
+
*/
|
|
9954
|
+
activate(sets) {
|
|
9955
|
+
for (let i = 0; i < sets.length; i++) {
|
|
9956
|
+
for (let j = 0; j < i; j++) {
|
|
9957
|
+
const other = this.touched(sets[j]);
|
|
9958
|
+
for (const slot of this.touched(sets[i])) {
|
|
9959
|
+
if (other.has(slot)) {
|
|
9960
|
+
throw new Error(
|
|
9961
|
+
`sets ${JSON.stringify(sets[j].name)} and ${JSON.stringify(sets[i].name)} both touch slot ${JSON.stringify(slot)}; active sets must cover and hide disjoint slots`
|
|
9962
|
+
);
|
|
9963
|
+
}
|
|
9964
|
+
}
|
|
9965
|
+
const otherDrawn = sets[j].drawn;
|
|
9966
|
+
for (const bone2 of Object.keys(sets[i].drawn ?? {})) {
|
|
9967
|
+
if (otherDrawn !== void 0 && bone2 in otherDrawn) {
|
|
9968
|
+
throw new Error(
|
|
9969
|
+
`sets ${JSON.stringify(sets[j].name)} and ${JSON.stringify(sets[i].name)} both pose bone ${JSON.stringify(bone2)}; active sets must draw disjoint bones`
|
|
9970
|
+
);
|
|
9971
|
+
}
|
|
9972
|
+
}
|
|
9973
|
+
}
|
|
9974
|
+
}
|
|
9975
|
+
const drawnBefore = this.activeSet?.drawn;
|
|
9976
|
+
this.activeList = this.setList.filter((s) => sets.includes(s));
|
|
9977
|
+
this.activeSet = composeSets(this.activeList);
|
|
9784
9978
|
this.clearSeekCache();
|
|
9979
|
+
const drawn = this.activeSet?.drawn;
|
|
9980
|
+
if (drawn !== drawnBefore && !(drawn === void 0 && drawnBefore === void 0)) {
|
|
9981
|
+
this.drawnPose = drawn === void 0 ? null : drawnLocals(this.view, drawn);
|
|
9982
|
+
this.reposeDrawn();
|
|
9983
|
+
return;
|
|
9984
|
+
}
|
|
9785
9985
|
const base = this.rawPose ?? (this.setup ?? (this.setup = poseRawSetup(this.view)));
|
|
9786
9986
|
this.rawPose = base;
|
|
9787
|
-
this.pose = applySetToPose(this.view, base,
|
|
9987
|
+
this.pose = this.activeSet === null ? base : applySetToPose(this.view, base, this.activeSet);
|
|
9788
9988
|
this.poseGeneration++;
|
|
9789
9989
|
this.cachedIdentity = null;
|
|
9790
9990
|
}
|
|
@@ -10020,7 +10220,8 @@ class RigPlayer {
|
|
|
10020
10220
|
}
|
|
10021
10221
|
const snapshot = Object.freeze({
|
|
10022
10222
|
generation: this.poseGeneration,
|
|
10023
|
-
set: this.
|
|
10223
|
+
set: this.set,
|
|
10224
|
+
sets: Object.freeze(this.activeSets),
|
|
10024
10225
|
skin: this.skinName ?? null,
|
|
10025
10226
|
slots: Object.freeze(slots)
|
|
10026
10227
|
});
|