@lovelace_lol/loom3 1.0.4 → 1.0.7
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 +1 -1
- package/dist/index.cjs +149 -131
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -32
- package/dist/index.d.ts +22 -32
- package/dist/index.js +149 -131
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -5,11 +5,53 @@ var __defProp = Object.defineProperty;
|
|
|
5
5
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
6
6
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
7
7
|
|
|
8
|
+
// src/core/compositeAxis.ts
|
|
9
|
+
function toAUList(value) {
|
|
10
|
+
if (value === void 0) return [];
|
|
11
|
+
return Array.isArray(value) ? value : [value];
|
|
12
|
+
}
|
|
13
|
+
function findBoneBindingForNode(auToBones, auId, nodeKey) {
|
|
14
|
+
return auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
|
|
15
|
+
}
|
|
16
|
+
function getCompositeAxisValue(axisConfig, getValue) {
|
|
17
|
+
if (!axisConfig) return 0;
|
|
18
|
+
const negativeAUs = toAUList(axisConfig.negative);
|
|
19
|
+
const positiveAUs = toAUList(axisConfig.positive);
|
|
20
|
+
if (negativeAUs.length > 0 && positiveAUs.length > 0) {
|
|
21
|
+
const negativeValue = Math.max(...negativeAUs.map(getValue), 0);
|
|
22
|
+
const positiveValue = Math.max(...positiveAUs.map(getValue), 0);
|
|
23
|
+
return positiveValue - negativeValue;
|
|
24
|
+
}
|
|
25
|
+
if (axisConfig.aus.length > 1) {
|
|
26
|
+
return Math.max(...axisConfig.aus.map(getValue), 0);
|
|
27
|
+
}
|
|
28
|
+
return getValue(axisConfig.aus[0]);
|
|
29
|
+
}
|
|
30
|
+
function getCompositeAxisBinding(nodeKey, axisConfig, direction, getValue, auToBones) {
|
|
31
|
+
if (!axisConfig) return null;
|
|
32
|
+
const directionalAUs = direction < 0 ? toAUList(axisConfig.negative) : toAUList(axisConfig.positive);
|
|
33
|
+
const candidates = directionalAUs.length > 0 ? directionalAUs : axisConfig.aus;
|
|
34
|
+
const ranked = [...candidates].sort((a, b) => getValue(b) - getValue(a));
|
|
35
|
+
for (const auId of ranked) {
|
|
36
|
+
const binding = findBoneBindingForNode(auToBones, auId, nodeKey);
|
|
37
|
+
if (binding) return binding;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
8
42
|
// src/engines/three/balanceUtils.ts
|
|
9
43
|
function clampBalance(value) {
|
|
10
44
|
if (!Number.isFinite(value)) return 0;
|
|
11
45
|
return Math.max(-1, Math.min(1, value));
|
|
12
46
|
}
|
|
47
|
+
function getSideScale(balance, side) {
|
|
48
|
+
if (side !== "left" && side !== "right") return 1;
|
|
49
|
+
const clamped = clampBalance(balance);
|
|
50
|
+
if (side === "left") {
|
|
51
|
+
return clamped > 0 ? 1 - clamped : 1;
|
|
52
|
+
}
|
|
53
|
+
return clamped < 0 ? 1 + clamped : 1;
|
|
54
|
+
}
|
|
13
55
|
function resolveCurveBalance(curveId, globalBalance, balanceMap) {
|
|
14
56
|
if (balanceMap && Object.prototype.hasOwnProperty.call(balanceMap, curveId)) {
|
|
15
57
|
return clampBalance(Number(balanceMap[curveId]));
|
|
@@ -533,43 +575,23 @@ var BakedAnimationController = class {
|
|
|
533
575
|
Object.keys(curves).filter(isNumericAU).map((id) => Number(id))
|
|
534
576
|
);
|
|
535
577
|
const getAxisBinding = (nodeKey, axisConfig, axisValue, t) => {
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
let maxVal = sampleCurve(String(maxAU), t);
|
|
544
|
-
for (const auId2 of axisConfig.aus) {
|
|
545
|
-
const val = sampleCurve(String(auId2), t);
|
|
546
|
-
if (val > maxVal) {
|
|
547
|
-
maxVal = val;
|
|
548
|
-
maxAU = auId2;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
return config.auToBones[maxAU]?.find((b) => b.node === nodeKey) ?? null;
|
|
552
|
-
}
|
|
553
|
-
const auId = axisConfig.aus[0];
|
|
554
|
-
return config.auToBones[auId]?.find((b) => b.node === nodeKey) ?? null;
|
|
578
|
+
return getCompositeAxisBinding(
|
|
579
|
+
nodeKey,
|
|
580
|
+
axisConfig,
|
|
581
|
+
axisValue,
|
|
582
|
+
(auId) => getAxisSampleForNode(auId, nodeKey, t),
|
|
583
|
+
config.auToBones
|
|
584
|
+
);
|
|
555
585
|
};
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
if (
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
if (axisConfig.aus.length > 1) {
|
|
564
|
-
let maxVal = 0;
|
|
565
|
-
for (const auId of axisConfig.aus) {
|
|
566
|
-
const val = sampleCurve(String(auId), t);
|
|
567
|
-
if (val > maxVal) maxVal = val;
|
|
568
|
-
}
|
|
569
|
-
return maxVal;
|
|
570
|
-
}
|
|
571
|
-
return sampleCurve(String(axisConfig.aus[0]), t);
|
|
586
|
+
const getAxisSampleForNode = (auId, nodeKey, t) => {
|
|
587
|
+
const rawValue = sampleCurve(String(auId), t);
|
|
588
|
+
if (rawValue <= 1e-6) return 0;
|
|
589
|
+
const binding = config.auToBones[auId]?.find((b) => b.node === nodeKey) ?? null;
|
|
590
|
+
if (!binding?.side) return rawValue;
|
|
591
|
+
const curveBalance = resolveCurveBalance(String(auId), globalBalance, balanceMap);
|
|
592
|
+
return rawValue * getSideScale(curveBalance, binding.side);
|
|
572
593
|
};
|
|
594
|
+
const getAxisValue = (nodeKey, axisConfig, t) => getCompositeAxisValue(axisConfig, (auId) => getAxisSampleForNode(auId, nodeKey, t));
|
|
573
595
|
const autoVisemeJawHandledJaw = autoVisemeJaw && jawScale > 0 && visemeJawAmounts && options?.snippetCategory === "visemeSnippet";
|
|
574
596
|
for (const composite of compositeRotations) {
|
|
575
597
|
const nodeKey = composite.node;
|
|
@@ -590,7 +612,7 @@ var BakedAnimationController = class {
|
|
|
590
612
|
const compositeQ = new Quaternion().copy(entry.baseQuat);
|
|
591
613
|
const applyAxis = (axisConfig) => {
|
|
592
614
|
if (!axisConfig) return;
|
|
593
|
-
let axisValue = getAxisValue(axisConfig, t);
|
|
615
|
+
let axisValue = getAxisValue(nodeKey, axisConfig, t);
|
|
594
616
|
if (Math.abs(axisValue) <= 1e-6) return;
|
|
595
617
|
const binding = getAxisBinding(nodeKey, axisConfig, axisValue, t);
|
|
596
618
|
if (!binding?.maxDegrees || !binding.channel) return;
|
|
@@ -1193,24 +1215,24 @@ var AU_TO_MORPHS = {
|
|
|
1193
1215
|
center: []
|
|
1194
1216
|
},
|
|
1195
1217
|
61: {
|
|
1196
|
-
left: ["Eye_L_Look_L"
|
|
1197
|
-
right: [],
|
|
1218
|
+
left: ["Eye_L_Look_L"],
|
|
1219
|
+
right: ["Eye_R_Look_L"],
|
|
1198
1220
|
center: []
|
|
1199
1221
|
},
|
|
1200
1222
|
62: {
|
|
1201
|
-
left: [],
|
|
1202
|
-
right: ["
|
|
1223
|
+
left: ["Eye_L_Look_R"],
|
|
1224
|
+
right: ["Eye_R_Look_R"],
|
|
1203
1225
|
center: []
|
|
1204
1226
|
},
|
|
1205
1227
|
63: {
|
|
1206
|
-
left: [],
|
|
1207
|
-
right: [],
|
|
1208
|
-
center: [
|
|
1228
|
+
left: ["Eye_L_Look_Up"],
|
|
1229
|
+
right: ["Eye_R_Look_Up"],
|
|
1230
|
+
center: []
|
|
1209
1231
|
},
|
|
1210
1232
|
64: {
|
|
1211
|
-
left: [],
|
|
1212
|
-
right: [],
|
|
1213
|
-
center: [
|
|
1233
|
+
left: ["Eye_L_Look_Down"],
|
|
1234
|
+
right: ["Eye_R_Look_Down"],
|
|
1235
|
+
center: []
|
|
1214
1236
|
},
|
|
1215
1237
|
65: {
|
|
1216
1238
|
left: ["Eye_L_Look_L"],
|
|
@@ -1438,6 +1460,14 @@ var BONE_AU_TO_BINDINGS = {
|
|
|
1438
1460
|
{ node: "EYE_L", channel: "rx", scale: 1, maxDegrees: 20, side: "left" },
|
|
1439
1461
|
{ node: "EYE_R", channel: "rx", scale: 1, maxDegrees: 20, side: "right" }
|
|
1440
1462
|
],
|
|
1463
|
+
65: [{ node: "EYE_L", channel: "rz", scale: 1, maxDegrees: 25, side: "left" }],
|
|
1464
|
+
66: [{ node: "EYE_L", channel: "rz", scale: -1, maxDegrees: 25, side: "left" }],
|
|
1465
|
+
67: [{ node: "EYE_L", channel: "rx", scale: -1, maxDegrees: 20, side: "left" }],
|
|
1466
|
+
68: [{ node: "EYE_L", channel: "rx", scale: 1, maxDegrees: 20, side: "left" }],
|
|
1467
|
+
69: [{ node: "EYE_R", channel: "rz", scale: 1, maxDegrees: 25, side: "right" }],
|
|
1468
|
+
70: [{ node: "EYE_R", channel: "rz", scale: -1, maxDegrees: 25, side: "right" }],
|
|
1469
|
+
71: [{ node: "EYE_R", channel: "rx", scale: -1, maxDegrees: 20, side: "right" }],
|
|
1470
|
+
72: [{ node: "EYE_R", channel: "rx", scale: 1, maxDegrees: 20, side: "right" }],
|
|
1441
1471
|
// Tongue controls (optional, for rigs that expose them)
|
|
1442
1472
|
37: [{ node: "TONGUE", channel: "rz", scale: 1, maxDegrees: 20 }],
|
|
1443
1473
|
38: [{ node: "TONGUE", channel: "rz", scale: -1, maxDegrees: 20 }],
|
|
@@ -1525,18 +1555,18 @@ var COMPOSITE_ROTATIONS = [
|
|
|
1525
1555
|
},
|
|
1526
1556
|
{
|
|
1527
1557
|
node: "EYE_L",
|
|
1528
|
-
pitch: { aus: [64, 63], axis: "rx", negative: 64, positive: 63 },
|
|
1558
|
+
pitch: { aus: [64, 63, 68, 67], axis: "rx", negative: [64, 68], positive: [63, 67] },
|
|
1529
1559
|
// Eyes down/up
|
|
1530
|
-
yaw: { aus: [61, 62], axis: "rz", negative: 61, positive: 62 },
|
|
1560
|
+
yaw: { aus: [61, 62, 65, 66], axis: "rz", negative: [61, 65], positive: [62, 66] },
|
|
1531
1561
|
// Eyes left/right (rz for CC4)
|
|
1532
1562
|
roll: null
|
|
1533
1563
|
// Eyes don't have roll
|
|
1534
1564
|
},
|
|
1535
1565
|
{
|
|
1536
1566
|
node: "EYE_R",
|
|
1537
|
-
pitch: { aus: [64, 63], axis: "rx", negative: 64, positive: 63 },
|
|
1567
|
+
pitch: { aus: [64, 63, 72, 71], axis: "rx", negative: [64, 72], positive: [63, 71] },
|
|
1538
1568
|
// Eyes down/up
|
|
1539
|
-
yaw: { aus: [61, 62], axis: "rz", negative: 61, positive: 62 },
|
|
1569
|
+
yaw: { aus: [61, 62, 69, 70], axis: "rz", negative: [61, 69], positive: [62, 70] },
|
|
1540
1570
|
// Eyes left/right (rz for CC4)
|
|
1541
1571
|
roll: null
|
|
1542
1572
|
// Eyes don't have roll
|
|
@@ -1555,9 +1585,17 @@ var CONTINUUM_PAIRS_MAP = {
|
|
|
1555
1585
|
// Eyes horizontal - both eyes share same AUs (yaw maps to rz via COMPOSITE_ROTATIONS)
|
|
1556
1586
|
61: { pairId: 62, isNegative: true, axis: "yaw", node: "EYE_L" },
|
|
1557
1587
|
62: { pairId: 61, isNegative: false, axis: "yaw", node: "EYE_L" },
|
|
1588
|
+
65: { pairId: 66, isNegative: true, axis: "yaw", node: "EYE_L" },
|
|
1589
|
+
66: { pairId: 65, isNegative: false, axis: "yaw", node: "EYE_L" },
|
|
1590
|
+
69: { pairId: 70, isNegative: true, axis: "yaw", node: "EYE_R" },
|
|
1591
|
+
70: { pairId: 69, isNegative: false, axis: "yaw", node: "EYE_R" },
|
|
1558
1592
|
// Eyes vertical (pitch)
|
|
1559
1593
|
64: { pairId: 63, isNegative: true, axis: "pitch", node: "EYE_L" },
|
|
1560
1594
|
63: { pairId: 64, isNegative: false, axis: "pitch", node: "EYE_L" },
|
|
1595
|
+
68: { pairId: 67, isNegative: true, axis: "pitch", node: "EYE_L" },
|
|
1596
|
+
67: { pairId: 68, isNegative: false, axis: "pitch", node: "EYE_L" },
|
|
1597
|
+
72: { pairId: 71, isNegative: true, axis: "pitch", node: "EYE_R" },
|
|
1598
|
+
71: { pairId: 72, isNegative: false, axis: "pitch", node: "EYE_R" },
|
|
1561
1599
|
// Head yaw (turn left/right)
|
|
1562
1600
|
51: { pairId: 52, isNegative: true, axis: "yaw", node: "HEAD" },
|
|
1563
1601
|
52: { pairId: 51, isNegative: false, axis: "yaw", node: "HEAD" },
|
|
@@ -1590,6 +1628,10 @@ var CONTINUUM_PAIRS_MAP = {
|
|
|
1590
1628
|
var CONTINUUM_LABELS = {
|
|
1591
1629
|
"61-62": "Eyes \u2014 Horizontal",
|
|
1592
1630
|
"64-63": "Eyes \u2014 Vertical",
|
|
1631
|
+
"65-66": "Left Eye \u2014 Horizontal",
|
|
1632
|
+
"68-67": "Left Eye \u2014 Vertical",
|
|
1633
|
+
"69-70": "Right Eye \u2014 Horizontal",
|
|
1634
|
+
"72-71": "Right Eye \u2014 Vertical",
|
|
1593
1635
|
"51-52": "Head \u2014 Horizontal",
|
|
1594
1636
|
"54-53": "Head \u2014 Vertical",
|
|
1595
1637
|
"55-56": "Head \u2014 Tilt",
|
|
@@ -3834,37 +3876,12 @@ var _Loom3 = class _Loom3 {
|
|
|
3834
3876
|
}
|
|
3835
3877
|
const compositeInfo = this.auToCompositeMap.get(id);
|
|
3836
3878
|
if (compositeInfo) {
|
|
3837
|
-
const storedBalance = this.auBalances[id] ?? 0;
|
|
3838
|
-
const { left: leftVal, right: rightVal } = this.computeSideValues(clamp012(v), storedBalance);
|
|
3839
3879
|
for (const nodeKey of compositeInfo.nodes) {
|
|
3840
3880
|
const config = this.compositeRotations.find((c) => c.node === nodeKey);
|
|
3841
3881
|
if (!config) continue;
|
|
3842
3882
|
const axisConfig = config[compositeInfo.axis];
|
|
3843
3883
|
if (!axisConfig) continue;
|
|
3844
|
-
|
|
3845
|
-
if (axisConfig.negative !== void 0 && axisConfig.positive !== void 0) {
|
|
3846
|
-
const negValue = this.auValues[axisConfig.negative] ?? 0;
|
|
3847
|
-
const posValue = this.auValues[axisConfig.positive] ?? 0;
|
|
3848
|
-
axisValue = posValue - negValue;
|
|
3849
|
-
} else if (axisConfig.aus.length > 1) {
|
|
3850
|
-
axisValue = Math.max(...axisConfig.aus.map((auId) => this.auValues[auId] ?? 0));
|
|
3851
|
-
} else {
|
|
3852
|
-
axisValue = v;
|
|
3853
|
-
}
|
|
3854
|
-
const auBoneBindings = this.config.auToBones[id] || [];
|
|
3855
|
-
const sideByNode = /* @__PURE__ */ new Map();
|
|
3856
|
-
for (const binding of auBoneBindings) {
|
|
3857
|
-
if (binding.side === "left" || binding.side === "right") {
|
|
3858
|
-
sideByNode.set(binding.node, binding.side);
|
|
3859
|
-
}
|
|
3860
|
-
}
|
|
3861
|
-
const side = sideByNode.get(nodeKey);
|
|
3862
|
-
if (side) {
|
|
3863
|
-
const baseValue = clamp012(v);
|
|
3864
|
-
const balanceValue = side === "left" ? leftVal : rightVal;
|
|
3865
|
-
const denom = baseValue > 0 ? baseValue : 1;
|
|
3866
|
-
axisValue = axisValue * (balanceValue / denom);
|
|
3867
|
-
}
|
|
3884
|
+
const axisValue = this.getCompositeAxisValueForNode(nodeKey, axisConfig);
|
|
3868
3885
|
this.updateBoneRotation(nodeKey, compositeInfo.axis, axisValue);
|
|
3869
3886
|
this.pendingCompositeNodes.add(nodeKey);
|
|
3870
3887
|
}
|
|
@@ -3892,11 +3909,15 @@ var _Loom3 = class _Loom3 {
|
|
|
3892
3909
|
}
|
|
3893
3910
|
}
|
|
3894
3911
|
const target = clamp012(to);
|
|
3912
|
+
if (balance !== void 0) {
|
|
3913
|
+
this.auBalances[numId] = balance;
|
|
3914
|
+
}
|
|
3915
|
+
const storedBalance = this.auBalances[numId] ?? 0;
|
|
3895
3916
|
const { left: leftKeys, right: rightKeys, center: centerKeys } = this.getAUMorphsBySide(numId);
|
|
3896
3917
|
const bindings = this.config.auToBones[numId] || [];
|
|
3897
3918
|
const mixWeight = this.isMixedAU(numId) ? this.getAUMixWeight(numId) : 1;
|
|
3898
3919
|
const base = target * mixWeight;
|
|
3899
|
-
const { left: leftVal, right: rightVal } = this.computeSideValues(base,
|
|
3920
|
+
const { left: leftVal, right: rightVal } = this.computeSideValues(base, storedBalance);
|
|
3900
3921
|
this.auValues[numId] = target;
|
|
3901
3922
|
const handles = [];
|
|
3902
3923
|
const meshNames = this.getMeshNamesForAU(numId);
|
|
@@ -3924,16 +3945,7 @@ var _Loom3 = class _Loom3 {
|
|
|
3924
3945
|
if (!config) continue;
|
|
3925
3946
|
const axisConfig = config[compositeInfo.axis];
|
|
3926
3947
|
if (!axisConfig) continue;
|
|
3927
|
-
|
|
3928
|
-
if (axisConfig.negative !== void 0 && axisConfig.positive !== void 0) {
|
|
3929
|
-
const negValue = this.auValues[axisConfig.negative] ?? 0;
|
|
3930
|
-
const posValue = this.auValues[axisConfig.positive] ?? 0;
|
|
3931
|
-
axisValue = posValue - negValue;
|
|
3932
|
-
} else if (axisConfig.aus.length > 1) {
|
|
3933
|
-
axisValue = Math.max(...axisConfig.aus.map((auId) => this.auValues[auId] ?? 0));
|
|
3934
|
-
} else {
|
|
3935
|
-
axisValue = target;
|
|
3936
|
-
}
|
|
3948
|
+
const axisValue = this.getCompositeAxisValueForNode(nodeKey, axisConfig);
|
|
3937
3949
|
handles.push(this.transitionBoneRotation(nodeKey, compositeInfo.axis, axisValue, durationMs));
|
|
3938
3950
|
}
|
|
3939
3951
|
}
|
|
@@ -4567,6 +4579,25 @@ var _Loom3 = class _Loom3 {
|
|
|
4567
4579
|
const hasMorphs = !!(morphs?.left?.length || morphs?.right?.length || morphs?.center?.length);
|
|
4568
4580
|
return !!(hasMorphs && this.config.auToBones[id]?.length);
|
|
4569
4581
|
}
|
|
4582
|
+
getEffectiveBoneAUValue(auId, nodeKey) {
|
|
4583
|
+
const rawValue = clamp012(this.auValues[auId] ?? 0);
|
|
4584
|
+
if (rawValue <= 1e-6) return 0;
|
|
4585
|
+
const binding = this.config.auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
|
|
4586
|
+
if (!binding?.side) return rawValue;
|
|
4587
|
+
return rawValue * getSideScale(this.auBalances[auId] ?? 0, binding.side);
|
|
4588
|
+
}
|
|
4589
|
+
getCompositeAxisValueForNode(nodeKey, axisConfig) {
|
|
4590
|
+
return getCompositeAxisValue(axisConfig, (auId) => this.getEffectiveBoneAUValue(auId, nodeKey));
|
|
4591
|
+
}
|
|
4592
|
+
getCompositeAxisBindingForNode(nodeKey, axisConfig, direction) {
|
|
4593
|
+
return getCompositeAxisBinding(
|
|
4594
|
+
nodeKey,
|
|
4595
|
+
axisConfig,
|
|
4596
|
+
direction,
|
|
4597
|
+
(auId) => this.getEffectiveBoneAUValue(auId, nodeKey),
|
|
4598
|
+
this.config.auToBones
|
|
4599
|
+
);
|
|
4600
|
+
}
|
|
4570
4601
|
initBoneRotations() {
|
|
4571
4602
|
this.rotations = {};
|
|
4572
4603
|
this.pendingCompositeNodes.clear();
|
|
@@ -4642,30 +4673,10 @@ var _Loom3 = class _Loom3 {
|
|
|
4642
4673
|
if (!config) {
|
|
4643
4674
|
return;
|
|
4644
4675
|
}
|
|
4645
|
-
const getBindingForAxis = (axisConfig, direction) => {
|
|
4646
|
-
if (!axisConfig) return null;
|
|
4647
|
-
if (axisConfig.negative !== void 0 && axisConfig.positive !== void 0) {
|
|
4648
|
-
const auId = direction < 0 ? axisConfig.negative : axisConfig.positive;
|
|
4649
|
-
return this.config.auToBones[auId]?.find((b) => b.node === nodeKey);
|
|
4650
|
-
}
|
|
4651
|
-
if (axisConfig.aus.length > 1) {
|
|
4652
|
-
let maxAU = axisConfig.aus[0];
|
|
4653
|
-
let maxValue = this.auValues[maxAU] ?? 0;
|
|
4654
|
-
for (const auId of axisConfig.aus) {
|
|
4655
|
-
const val = this.auValues[auId] ?? 0;
|
|
4656
|
-
if (val > maxValue) {
|
|
4657
|
-
maxValue = val;
|
|
4658
|
-
maxAU = auId;
|
|
4659
|
-
}
|
|
4660
|
-
}
|
|
4661
|
-
return this.config.auToBones[maxAU]?.find((b) => b.node === nodeKey);
|
|
4662
|
-
}
|
|
4663
|
-
return this.config.auToBones[axisConfig.aus[0]]?.find((b) => b.node === nodeKey);
|
|
4664
|
-
};
|
|
4665
4676
|
const getAxis = (channel) => channel === "rx" ? X_AXIS2 : channel === "ry" ? Y_AXIS2 : Z_AXIS2;
|
|
4666
4677
|
const compositeQ = new Quaternion().copy(baseQuat);
|
|
4667
4678
|
if (config.yaw && rotState.yaw !== 0) {
|
|
4668
|
-
const binding =
|
|
4679
|
+
const binding = this.getCompositeAxisBindingForNode(nodeKey, config.yaw, rotState.yaw);
|
|
4669
4680
|
if (binding?.maxDegrees && binding.channel) {
|
|
4670
4681
|
const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.yaw) * binding.scale;
|
|
4671
4682
|
const axis = getAxis(binding.channel);
|
|
@@ -4674,7 +4685,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4674
4685
|
}
|
|
4675
4686
|
}
|
|
4676
4687
|
if (config.pitch && rotState.pitch !== 0) {
|
|
4677
|
-
const binding =
|
|
4688
|
+
const binding = this.getCompositeAxisBindingForNode(nodeKey, config.pitch, rotState.pitch);
|
|
4678
4689
|
if (binding?.maxDegrees && binding.channel) {
|
|
4679
4690
|
const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.pitch) * binding.scale;
|
|
4680
4691
|
const axis = getAxis(binding.channel);
|
|
@@ -4683,7 +4694,7 @@ var _Loom3 = class _Loom3 {
|
|
|
4683
4694
|
}
|
|
4684
4695
|
}
|
|
4685
4696
|
if (config.roll && rotState.roll !== 0) {
|
|
4686
|
-
const binding =
|
|
4697
|
+
const binding = this.getCompositeAxisBindingForNode(nodeKey, config.roll, rotState.roll);
|
|
4687
4698
|
if (binding?.maxDegrees && binding.channel) {
|
|
4688
4699
|
const radians = deg2rad(binding.maxDegrees) * Math.abs(rotState.roll) * binding.scale;
|
|
4689
4700
|
const axis = getAxis(binding.channel);
|
|
@@ -5552,7 +5563,7 @@ function findMatches(targetNames, candidateNames, prefix, suffix, suffixPattern)
|
|
|
5552
5563
|
return { found, missing };
|
|
5553
5564
|
}
|
|
5554
5565
|
function collectAxisConfigs(axisConfigs) {
|
|
5555
|
-
return axisConfigs.filter((entry) => entry.config);
|
|
5566
|
+
return axisConfigs.filter((entry) => entry.config !== null);
|
|
5556
5567
|
}
|
|
5557
5568
|
function isEyeNodeKey(nodeKey) {
|
|
5558
5569
|
return nodeKey === "EYE_L" || nodeKey === "EYE_R";
|
|
@@ -5622,28 +5633,35 @@ function validateMappingConfig(config) {
|
|
|
5622
5633
|
);
|
|
5623
5634
|
continue;
|
|
5624
5635
|
}
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5636
|
+
for (const auId of toAUList(axisConfig.negative)) {
|
|
5637
|
+
if (!axisConfig.aus.includes(auId)) {
|
|
5638
|
+
push(
|
|
5639
|
+
"error",
|
|
5640
|
+
"COMPOSITE_AU_MISSING",
|
|
5641
|
+
`Composite axis for "${composite.node}" is missing negative AU ${auId} in aus list`,
|
|
5642
|
+
{ node: composite.node, auId }
|
|
5643
|
+
);
|
|
5644
|
+
}
|
|
5632
5645
|
}
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5646
|
+
for (const auId of toAUList(axisConfig.positive)) {
|
|
5647
|
+
if (!axisConfig.aus.includes(auId)) {
|
|
5648
|
+
push(
|
|
5649
|
+
"error",
|
|
5650
|
+
"COMPOSITE_AU_MISSING",
|
|
5651
|
+
`Composite axis for "${composite.node}" is missing positive AU ${auId} in aus list`,
|
|
5652
|
+
{ node: composite.node, auId }
|
|
5653
|
+
);
|
|
5654
|
+
}
|
|
5640
5655
|
}
|
|
5641
|
-
|
|
5656
|
+
const negativeAUs = toAUList(axisConfig.negative);
|
|
5657
|
+
const positiveAUs = toAUList(axisConfig.positive);
|
|
5658
|
+
const overlappingAUs = negativeAUs.filter((auId) => positiveAUs.includes(auId));
|
|
5659
|
+
if (overlappingAUs.length > 0) {
|
|
5642
5660
|
push(
|
|
5643
5661
|
"error",
|
|
5644
5662
|
"COMPOSITE_AU_DUPLICATE",
|
|
5645
|
-
`Composite axis for "${composite.node}"
|
|
5646
|
-
{ node: composite.node, auId:
|
|
5663
|
+
`Composite axis for "${composite.node}" reuses AU ${overlappingAUs[0]} in both negative and positive groups`,
|
|
5664
|
+
{ node: composite.node, auId: overlappingAUs[0] }
|
|
5647
5665
|
);
|
|
5648
5666
|
}
|
|
5649
5667
|
}
|
|
@@ -5733,11 +5751,11 @@ function validateMappingConfig(config) {
|
|
|
5733
5751
|
);
|
|
5734
5752
|
continue;
|
|
5735
5753
|
}
|
|
5736
|
-
const expectedNeg = axisConfig.negative;
|
|
5737
|
-
const expectedPos = axisConfig.positive;
|
|
5754
|
+
const expectedNeg = toAUList(axisConfig.negative);
|
|
5755
|
+
const expectedPos = toAUList(axisConfig.positive);
|
|
5738
5756
|
const negId = info.isNegative ? Number(auIdStr) : info.pairId;
|
|
5739
5757
|
const posId = info.isNegative ? info.pairId : Number(auIdStr);
|
|
5740
|
-
if (negId
|
|
5758
|
+
if (!expectedNeg.includes(negId) || !expectedPos.includes(posId)) {
|
|
5741
5759
|
push(
|
|
5742
5760
|
"warning",
|
|
5743
5761
|
"CONTINUUM_COMPOSITE_MISMATCH",
|