@ikijs/editor 0.24.1 → 0.25.1
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 +16 -14
- package/dist/index.d.mts +78 -3
- package/dist/index.d.ts +78 -3
- package/dist/index.js +437 -125
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +435 -123
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -36,6 +36,8 @@ __export(index_exports, {
|
|
|
36
36
|
LIP_INSIDE_ROLES: () => LIP_INSIDE_ROLES,
|
|
37
37
|
LIP_ROLES: () => LIP_ROLES,
|
|
38
38
|
LayerGeometryError: () => LayerGeometryError,
|
|
39
|
+
PatchError: () => PatchError,
|
|
40
|
+
SHUT_BY: () => SHUT_BY,
|
|
39
41
|
SPECK_CORE_FRACTION: () => SPECK_CORE_FRACTION,
|
|
40
42
|
SetDeformerBindings: () => SetDeformerBindings,
|
|
41
43
|
SetDeformerParent: () => SetDeformerParent,
|
|
@@ -1560,8 +1562,8 @@ function foregroundSpan(mask, width, height, rowLo, rowHi) {
|
|
|
1560
1562
|
}
|
|
1561
1563
|
return { left, right };
|
|
1562
1564
|
}
|
|
1563
|
-
function headHalfOf(
|
|
1564
|
-
return (
|
|
1565
|
+
function headHalfOf(span2) {
|
|
1566
|
+
return (span2.right - span2.left + 1) / 2;
|
|
1565
1567
|
}
|
|
1566
1568
|
var DARK_SHARE = 0.55;
|
|
1567
1569
|
function jawRowsOf(rgba, canvasW, bbox, rowLeft, rowRight) {
|
|
@@ -1571,9 +1573,9 @@ function jawRowsOf(rgba, canvasW, bbox, rowLeft, rowRight) {
|
|
|
1571
1573
|
let widest = bbox.y;
|
|
1572
1574
|
let widestSpan = -1;
|
|
1573
1575
|
for (let y = bbox.y; y < bbox.y + bbox.h; y++) {
|
|
1574
|
-
const
|
|
1575
|
-
if (
|
|
1576
|
-
widestSpan =
|
|
1576
|
+
const span2 = rowRight[y] - rowLeft[y];
|
|
1577
|
+
if (span2 >= widestSpan) {
|
|
1578
|
+
widestSpan = span2;
|
|
1577
1579
|
widest = y;
|
|
1578
1580
|
}
|
|
1579
1581
|
for (let x = bbox.x; x < bbox.x + bbox.w; x++) {
|
|
@@ -1742,14 +1744,14 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1742
1744
|
}
|
|
1743
1745
|
function finish() {
|
|
1744
1746
|
const eyeRow = eyeBandRowOf(layerInputs);
|
|
1745
|
-
const
|
|
1747
|
+
const span2 = foregroundSpan(
|
|
1746
1748
|
opaque,
|
|
1747
1749
|
canvasW,
|
|
1748
1750
|
canvasH,
|
|
1749
1751
|
eyeRow - HEAD_BAND,
|
|
1750
1752
|
eyeRow + HEAD_BAND
|
|
1751
1753
|
);
|
|
1752
|
-
const headHalfWidth =
|
|
1754
|
+
const headHalfWidth = span2.right > span2.left ? headHalfOf(span2) : void 0;
|
|
1753
1755
|
const headHalfWidthApplied = headHalfWidth !== void 0 && headHalfWidth > facePlateHalfOf(layerInputs);
|
|
1754
1756
|
let headEdges;
|
|
1755
1757
|
if (headHalfWidthApplied) {
|
|
@@ -1777,9 +1779,9 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1777
1779
|
const centre = iris.bbox.x + iris.bbox.w / 2;
|
|
1778
1780
|
const otherX = other.bbox.x + other.bbox.w / 2 - half;
|
|
1779
1781
|
const c = side < 0 ? Math.ceil(centre) - 1 : Math.floor(centre);
|
|
1780
|
-
const
|
|
1781
|
-
if (
|
|
1782
|
-
const minRun = STRAND_MIN_RUN_FRACTION * (
|
|
1782
|
+
const span3 = rowSpansByRole.find((s) => s.role === iris.role);
|
|
1783
|
+
if (span3.rowRight[r] < span3.rowLeft[r]) return void 0;
|
|
1784
|
+
const minRun = STRAND_MIN_RUN_FRACTION * (span3.rowRight[r] + 1 - span3.rowLeft[r]);
|
|
1783
1785
|
const strand = new Uint8Array(canvasW);
|
|
1784
1786
|
const runs = r >= hairTop && r < hairTop + hairRuns.length ? hairRuns[r - hairTop] : [];
|
|
1785
1787
|
for (let k = 0; k < runs.length; k += 2) {
|
|
@@ -1804,8 +1806,8 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1804
1806
|
while (strandAt(x)) x += side;
|
|
1805
1807
|
return {
|
|
1806
1808
|
y: canvasH / 2 - (r + 0.5),
|
|
1807
|
-
irisOuter: (side < 0 ?
|
|
1808
|
-
irisInner: (side < 0 ?
|
|
1809
|
+
irisOuter: (side < 0 ? span3.rowLeft[r] : span3.rowRight[r] + 1) - half,
|
|
1810
|
+
irisInner: (side < 0 ? span3.rowRight[r] + 1 : span3.rowLeft[r]) - half,
|
|
1809
1811
|
runOuter: outerBoundary(x - side),
|
|
1810
1812
|
runFace
|
|
1811
1813
|
};
|
|
@@ -2209,6 +2211,12 @@ var DEFAULT_TURN_TARGETS = Object.freeze({
|
|
|
2209
2211
|
farEyeRatio: round2(TURN.eyeFarScale / TURN.eyeNearScale),
|
|
2210
2212
|
silhouetteRatio: 1
|
|
2211
2213
|
});
|
|
2214
|
+
var PatchError = class extends Error {
|
|
2215
|
+
constructor(message) {
|
|
2216
|
+
super(message);
|
|
2217
|
+
this.name = "PatchError";
|
|
2218
|
+
}
|
|
2219
|
+
};
|
|
2212
2220
|
|
|
2213
2221
|
// src/auto-rig/arms.ts
|
|
2214
2222
|
var import_format5 = require("@ikijs/format");
|
|
@@ -2279,15 +2287,16 @@ function gridMesh(cols, rows) {
|
|
|
2279
2287
|
return { vertices, uvs, indices };
|
|
2280
2288
|
}
|
|
2281
2289
|
function columnMesh(b, grid) {
|
|
2282
|
-
const { xs } = grid;
|
|
2283
|
-
const rows =
|
|
2290
|
+
const { xs, ys } = grid;
|
|
2291
|
+
const rows = ys.length - 1;
|
|
2284
2292
|
const vertices = [];
|
|
2285
2293
|
const uvs = [];
|
|
2286
2294
|
for (let r = 0; r <= rows; r++) {
|
|
2295
|
+
const v = (ys[0] - ys[r]) / (ys[0] - ys[rows]);
|
|
2287
2296
|
for (const x of xs) {
|
|
2288
2297
|
const u = (x - b.x0) / bw(b);
|
|
2289
|
-
vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 -
|
|
2290
|
-
uvs.push(roundTo(u, 1e-5), roundTo(
|
|
2298
|
+
vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - v, 1e-5));
|
|
2299
|
+
uvs.push(roundTo(u, 1e-5), roundTo(v, 1e-5));
|
|
2291
2300
|
}
|
|
2292
2301
|
}
|
|
2293
2302
|
const n = xs.length;
|
|
@@ -2834,7 +2843,7 @@ var ARM_IDS = {
|
|
|
2834
2843
|
};
|
|
2835
2844
|
function endPivot(layer, end) {
|
|
2836
2845
|
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2837
|
-
const
|
|
2846
|
+
const span2 = runSpan(layer);
|
|
2838
2847
|
const from = (k) => end === "top" ? k : bbox.h - 1 - k;
|
|
2839
2848
|
const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
|
|
2840
2849
|
const firstK = end === "top" ? painted[0] ?? 0 : painted.at(-1) ?? bbox.h - 1;
|
|
@@ -2842,13 +2851,13 @@ function endPivot(layer, end) {
|
|
|
2842
2851
|
const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
|
|
2843
2852
|
const widths = [];
|
|
2844
2853
|
for (let k = lo; k <= hi; k++) {
|
|
2845
|
-
const [s2, e2] =
|
|
2854
|
+
const [s2, e2] = span2(from(k));
|
|
2846
2855
|
widths.push(e2 - s2);
|
|
2847
2856
|
}
|
|
2848
2857
|
widths.sort((p, q) => p - q);
|
|
2849
2858
|
const radius = widths[widths.length >> 1] / 2;
|
|
2850
2859
|
const ks = end === "top" ? firstK + radius : firstK - radius;
|
|
2851
|
-
const [s, e] =
|
|
2860
|
+
const [s, e] = span2(Math.round(ks));
|
|
2852
2861
|
return {
|
|
2853
2862
|
point: {
|
|
2854
2863
|
x: roundTo((s + e) / 2 - canvasW / 2, 0.01),
|
|
@@ -2873,9 +2882,9 @@ function runSpan(layer) {
|
|
|
2873
2882
|
function armGeometry(layer, bodyAxisX) {
|
|
2874
2883
|
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2875
2884
|
const b = boxOfLayer(layer);
|
|
2876
|
-
const
|
|
2885
|
+
const span2 = runSpan(layer);
|
|
2877
2886
|
const centreX = (k) => {
|
|
2878
|
-
const [s2, e2] =
|
|
2887
|
+
const [s2, e2] = span2(k);
|
|
2879
2888
|
return (s2 + e2) / 2 - canvasW / 2;
|
|
2880
2889
|
};
|
|
2881
2890
|
const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
|
|
@@ -2889,7 +2898,7 @@ function armGeometry(layer, bodyAxisX) {
|
|
|
2889
2898
|
);
|
|
2890
2899
|
}
|
|
2891
2900
|
const elbow = { x: roundTo(centreX(ke), 0.01), y: roundTo(rowY(ke), 0.01) };
|
|
2892
|
-
const [s, e] =
|
|
2901
|
+
const [s, e] = span2(ke);
|
|
2893
2902
|
const capRadius = Math.min(
|
|
2894
2903
|
(e - s) / 2 + 1,
|
|
2895
2904
|
elbow.x - b.x0,
|
|
@@ -2899,7 +2908,7 @@ function armGeometry(layer, bodyAxisX) {
|
|
|
2899
2908
|
const inside = (x, y) => {
|
|
2900
2909
|
const k = Math.floor(y) - bbox.y;
|
|
2901
2910
|
if (k < 0 || k >= bbox.h) return false;
|
|
2902
|
-
const [rs, re] =
|
|
2911
|
+
const [rs, re] = span2(k);
|
|
2903
2912
|
return rs < x && x < re;
|
|
2904
2913
|
};
|
|
2905
2914
|
const capRays = Array.from({ length: CAP_SEGMENTS + 1 }, (_, i) => {
|
|
@@ -3106,13 +3115,24 @@ function forearmPoseDeformer(role, arm) {
|
|
|
3106
3115
|
}
|
|
3107
3116
|
|
|
3108
3117
|
// src/auto-rig/mouth.ts
|
|
3109
|
-
var
|
|
3118
|
+
var import_format8 = require("@ikijs/format");
|
|
3110
3119
|
|
|
3111
3120
|
// src/auto-rig/roles.ts
|
|
3121
|
+
var import_format7 = require("@ikijs/format");
|
|
3112
3122
|
var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
|
|
3113
3123
|
var isLipRole = (role) => LIP_ROLES.includes(role);
|
|
3114
3124
|
var LIP_INSIDE_ROLES = ["mouth_tongue", "mouth_teeth"];
|
|
3115
3125
|
var isLipInsideRole = (role) => LIP_INSIDE_ROLES.includes(role);
|
|
3126
|
+
var SHUT_BY = new Map([
|
|
3127
|
+
["eye_L", import_format7.StandardParameter.EyeOpenLeft],
|
|
3128
|
+
["lash_lower_L", import_format7.StandardParameter.EyeOpenLeft],
|
|
3129
|
+
["eye_R", import_format7.StandardParameter.EyeOpenRight],
|
|
3130
|
+
["lash_lower_R", import_format7.StandardParameter.EyeOpenRight],
|
|
3131
|
+
...[...LIP_ROLES, ...LIP_INSIDE_ROLES].map((id) => [
|
|
3132
|
+
id,
|
|
3133
|
+
import_format7.StandardParameter.MouthOpen
|
|
3134
|
+
])
|
|
3135
|
+
]);
|
|
3116
3136
|
var ROLE_TABLE = [
|
|
3117
3137
|
{ role: "hair_back", family: "hair_back" },
|
|
3118
3138
|
{ role: "body", family: "body" },
|
|
@@ -3457,7 +3477,7 @@ function columnOfKnot(knot, opening) {
|
|
|
3457
3477
|
function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
3458
3478
|
const fold = mouthFold(role, opening);
|
|
3459
3479
|
return localWarp(
|
|
3460
|
-
|
|
3480
|
+
import_format8.StandardParameter.MouthOpen,
|
|
3461
3481
|
mesh,
|
|
3462
3482
|
box,
|
|
3463
3483
|
[0, 1],
|
|
@@ -3466,13 +3486,13 @@ function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
|
3466
3486
|
}
|
|
3467
3487
|
function mouthRestShift(byRole) {
|
|
3468
3488
|
const rig = buildMouthRig(byRole);
|
|
3469
|
-
const
|
|
3489
|
+
const fields2 = /* @__PURE__ */ new Map();
|
|
3470
3490
|
for (const role of ["lip_upper", "lip_lower"]) {
|
|
3471
3491
|
const box = boxOfLayer(byRole.get(role));
|
|
3472
3492
|
const { mesh, xs } = mouthMesh(box, rig.knots);
|
|
3473
3493
|
const warp = mouthFoldWarp(role, mesh, xs, box, rig.opening);
|
|
3474
3494
|
const offsets = warp.keyforms[0].offsets;
|
|
3475
|
-
|
|
3495
|
+
fields2.set(role, {
|
|
3476
3496
|
box,
|
|
3477
3497
|
xs,
|
|
3478
3498
|
rows: offsets.length / 2 / xs.length - 1,
|
|
@@ -3481,7 +3501,7 @@ function mouthRestShift(byRole) {
|
|
|
3481
3501
|
}
|
|
3482
3502
|
const { canvasW, canvasH } = byRole.get("lip_upper");
|
|
3483
3503
|
return (role, col, row) => {
|
|
3484
|
-
const { box, xs, rows, dy } =
|
|
3504
|
+
const { box, xs, rows, dy } = fields2.get(role);
|
|
3485
3505
|
const x = col + 0.5 - canvasW / 2;
|
|
3486
3506
|
const y = canvasH / 2 - (row + 0.5);
|
|
3487
3507
|
if (x < box.x0 || x > box.x1 || y < box.y0 || y > box.y1) return void 0;
|
|
@@ -3570,32 +3590,32 @@ function mouthAnchor(byRole) {
|
|
|
3570
3590
|
}
|
|
3571
3591
|
|
|
3572
3592
|
// src/auto-rig/generate.ts
|
|
3573
|
-
var
|
|
3593
|
+
var import_format12 = require("@ikijs/format");
|
|
3574
3594
|
|
|
3575
3595
|
// src/auto-rig/animations.ts
|
|
3576
|
-
var
|
|
3596
|
+
var import_format9 = require("@ikijs/format");
|
|
3577
3597
|
function browsY(y) {
|
|
3578
3598
|
return [
|
|
3579
|
-
{ parameter:
|
|
3580
|
-
{ parameter:
|
|
3599
|
+
{ parameter: import_format9.StandardParameter.BrowLeftY, value: y, blend: "add" },
|
|
3600
|
+
{ parameter: import_format9.StandardParameter.BrowRightY, value: y, blend: "add" }
|
|
3581
3601
|
];
|
|
3582
3602
|
}
|
|
3583
3603
|
function browsTilt(innerDown) {
|
|
3584
3604
|
return [
|
|
3585
|
-
{ parameter:
|
|
3586
|
-
{ parameter:
|
|
3605
|
+
{ parameter: import_format9.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
3606
|
+
{ parameter: import_format9.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
3587
3607
|
];
|
|
3588
3608
|
}
|
|
3589
3609
|
function cheek(value) {
|
|
3590
|
-
return { parameter:
|
|
3610
|
+
return { parameter: import_format9.StandardParameter.Cheek, value, blend: "add" };
|
|
3591
3611
|
}
|
|
3592
|
-
var BROWS = [
|
|
3612
|
+
var BROWS = [import_format9.StandardParameter.BrowLeftY, import_format9.StandardParameter.BrowRightY, import_format9.StandardParameter.BrowLeftAngle, import_format9.StandardParameter.BrowRightAngle];
|
|
3593
3613
|
var EXPRESSIONS = [
|
|
3594
3614
|
{
|
|
3595
3615
|
id: "smile",
|
|
3596
3616
|
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
3597
3617
|
parameters: [
|
|
3598
|
-
{ parameter:
|
|
3618
|
+
{ parameter: import_format9.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3599
3619
|
...browsY(0.25),
|
|
3600
3620
|
cheek(0.4)
|
|
3601
3621
|
]
|
|
@@ -3604,10 +3624,10 @@ var EXPRESSIONS = [
|
|
|
3604
3624
|
id: "laugh",
|
|
3605
3625
|
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
3606
3626
|
parameters: [
|
|
3607
|
-
{ parameter:
|
|
3608
|
-
{ parameter:
|
|
3609
|
-
{ parameter:
|
|
3610
|
-
{ parameter:
|
|
3627
|
+
{ parameter: import_format9.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
|
|
3628
|
+
{ parameter: import_format9.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
|
|
3629
|
+
{ parameter: import_format9.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3630
|
+
{ parameter: import_format9.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
|
|
3611
3631
|
...browsY(0.4),
|
|
3612
3632
|
cheek(0.65)
|
|
3613
3633
|
]
|
|
@@ -3619,7 +3639,7 @@ var EXPRESSIONS = [
|
|
|
3619
3639
|
parameters: [
|
|
3620
3640
|
...browsY(-0.5),
|
|
3621
3641
|
...browsTilt(0.8),
|
|
3622
|
-
{ parameter:
|
|
3642
|
+
{ parameter: import_format9.StandardParameter.MouthForm, value: -0.9, blend: "add" }
|
|
3623
3643
|
]
|
|
3624
3644
|
},
|
|
3625
3645
|
{
|
|
@@ -3629,7 +3649,7 @@ var EXPRESSIONS = [
|
|
|
3629
3649
|
parameters: [
|
|
3630
3650
|
...browsY(0.35),
|
|
3631
3651
|
...browsTilt(-0.8),
|
|
3632
|
-
{ parameter:
|
|
3652
|
+
{ parameter: import_format9.StandardParameter.MouthForm, value: -0.8, blend: "add" }
|
|
3633
3653
|
]
|
|
3634
3654
|
},
|
|
3635
3655
|
{
|
|
@@ -3637,18 +3657,18 @@ var EXPRESSIONS = [
|
|
|
3637
3657
|
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
3638
3658
|
parameters: [
|
|
3639
3659
|
...browsY(0.8),
|
|
3640
|
-
{ parameter:
|
|
3660
|
+
{ parameter: import_format9.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
3641
3661
|
]
|
|
3642
3662
|
},
|
|
3643
3663
|
{
|
|
3644
3664
|
id: "shy",
|
|
3645
3665
|
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
3646
|
-
needs: [
|
|
3666
|
+
needs: [import_format9.StandardParameter.Cheek],
|
|
3647
3667
|
parameters: [
|
|
3648
3668
|
cheek(1),
|
|
3649
3669
|
// Gaze down.
|
|
3650
|
-
{ parameter:
|
|
3651
|
-
{ parameter:
|
|
3670
|
+
{ parameter: import_format9.StandardParameter.EyeballY, value: -0.65, blend: "add" },
|
|
3671
|
+
{ parameter: import_format9.StandardParameter.MouthForm, value: 0.4, blend: "add" },
|
|
3652
3672
|
...browsTilt(-0.25)
|
|
3653
3673
|
]
|
|
3654
3674
|
}
|
|
@@ -3668,7 +3688,7 @@ var HEAD_MOTIONS = {
|
|
|
3668
3688
|
duration: 1,
|
|
3669
3689
|
curves: [
|
|
3670
3690
|
{
|
|
3671
|
-
parameter:
|
|
3691
|
+
parameter: import_format9.StandardParameter.AngleY,
|
|
3672
3692
|
keys: [
|
|
3673
3693
|
[0, 0],
|
|
3674
3694
|
[0.3, -18],
|
|
@@ -3689,7 +3709,7 @@ var HEAD_MOTIONS = {
|
|
|
3689
3709
|
fadeIn: 0.15,
|
|
3690
3710
|
curves: [
|
|
3691
3711
|
{
|
|
3692
|
-
parameter:
|
|
3712
|
+
parameter: import_format9.StandardParameter.AngleX,
|
|
3693
3713
|
keys: [
|
|
3694
3714
|
[0, 0],
|
|
3695
3715
|
[0.2, -18],
|
|
@@ -3708,7 +3728,7 @@ var HEAD_MOTIONS = {
|
|
|
3708
3728
|
duration: 1.4,
|
|
3709
3729
|
curves: [
|
|
3710
3730
|
{
|
|
3711
|
-
parameter:
|
|
3731
|
+
parameter: import_format9.StandardParameter.AngleZ,
|
|
3712
3732
|
keys: [
|
|
3713
3733
|
[0, 0],
|
|
3714
3734
|
[0.35, 20],
|
|
@@ -3729,7 +3749,7 @@ var WAVE = {
|
|
|
3729
3749
|
fadeOut: 0.15,
|
|
3730
3750
|
curves: [
|
|
3731
3751
|
{
|
|
3732
|
-
parameter:
|
|
3752
|
+
parameter: import_format9.StandardParameter.ArmPoseRight,
|
|
3733
3753
|
interpolation: "linear",
|
|
3734
3754
|
keys: [
|
|
3735
3755
|
[0, 0],
|
|
@@ -3739,7 +3759,7 @@ var WAVE = {
|
|
|
3739
3759
|
]
|
|
3740
3760
|
},
|
|
3741
3761
|
{
|
|
3742
|
-
parameter:
|
|
3762
|
+
parameter: import_format9.StandardParameter.ArmRight,
|
|
3743
3763
|
keys: [
|
|
3744
3764
|
[0, 0],
|
|
3745
3765
|
[0.4, 10],
|
|
@@ -3748,7 +3768,7 @@ var WAVE = {
|
|
|
3748
3768
|
]
|
|
3749
3769
|
},
|
|
3750
3770
|
{
|
|
3751
|
-
parameter:
|
|
3771
|
+
parameter: import_format9.StandardParameter.ArmPoseAngleRight,
|
|
3752
3772
|
keys: [
|
|
3753
3773
|
[0, 0],
|
|
3754
3774
|
[0.5, 0],
|
|
@@ -3764,7 +3784,7 @@ var WAVE = {
|
|
|
3764
3784
|
]
|
|
3765
3785
|
};
|
|
3766
3786
|
function defaultMotions(declared) {
|
|
3767
|
-
const waves = [
|
|
3787
|
+
const waves = [import_format9.StandardParameter.ArmRight, import_format9.StandardParameter.ArmPoseRight, import_format9.StandardParameter.ArmPoseAngleRight].every(
|
|
3768
3788
|
(id) => declared.has(id)
|
|
3769
3789
|
);
|
|
3770
3790
|
return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
|
|
@@ -4000,7 +4020,7 @@ function checkStrandEdges(strand, irises, hair) {
|
|
|
4000
4020
|
}
|
|
4001
4021
|
|
|
4002
4022
|
// src/auto-rig/context.ts
|
|
4003
|
-
var
|
|
4023
|
+
var import_format10 = require("@ikijs/format");
|
|
4004
4024
|
|
|
4005
4025
|
// src/auto-rig/head.ts
|
|
4006
4026
|
function outerEdge(runs, axisX, side) {
|
|
@@ -4109,14 +4129,14 @@ function buildHeadFrame(layers, opts) {
|
|
|
4109
4129
|
const rawRows = raw;
|
|
4110
4130
|
const earless = (chin) => {
|
|
4111
4131
|
const rowOf = (y) => Math.round(canvasH / 2 - y - faceLayer.bbox.y - 0.5);
|
|
4112
|
-
const
|
|
4132
|
+
const span2 = Math.max(1, eyeY - chin);
|
|
4113
4133
|
const found = findEars(
|
|
4114
4134
|
rawRows,
|
|
4115
4135
|
wMax,
|
|
4116
4136
|
Math.round(0.2 * rawRows.length),
|
|
4117
4137
|
Math.min(rawRows.length - 1, rowOf(chin)),
|
|
4118
|
-
Math.round(0.1 *
|
|
4119
|
-
Math.round(0.8 *
|
|
4138
|
+
Math.round(0.1 * span2),
|
|
4139
|
+
Math.round(0.8 * span2)
|
|
4120
4140
|
);
|
|
4121
4141
|
if (found === void 0) return profile;
|
|
4122
4142
|
const { top, bottom } = found;
|
|
@@ -4150,16 +4170,16 @@ function buildHeadFrame(layers, opts) {
|
|
|
4150
4170
|
}
|
|
4151
4171
|
}
|
|
4152
4172
|
if (neckRow >= 0 && n - neckRow >= 0.12 * n) {
|
|
4153
|
-
let
|
|
4173
|
+
let span2 = 0;
|
|
4154
4174
|
let waist = Infinity;
|
|
4155
4175
|
for (let r = neckRow; r < n; r++) {
|
|
4156
|
-
|
|
4176
|
+
span2 = Math.max(span2, profile[r]);
|
|
4157
4177
|
if (r < n - tail) waist = Math.min(waist, profile[r]);
|
|
4158
4178
|
}
|
|
4159
4179
|
neck = {
|
|
4160
4180
|
topY: rowY(neckRow),
|
|
4161
4181
|
half: profile[neckRow],
|
|
4162
|
-
span,
|
|
4182
|
+
span: span2,
|
|
4163
4183
|
waist: Math.min(waist, profile[neckRow])
|
|
4164
4184
|
};
|
|
4165
4185
|
}
|
|
@@ -4359,22 +4379,22 @@ function earField(m) {
|
|
|
4359
4379
|
const dy = face(x, y, 0, ay)[1];
|
|
4360
4380
|
const d = Math.abs(x - f.axisX);
|
|
4361
4381
|
const attach = ears.attachAt(y);
|
|
4362
|
-
const
|
|
4382
|
+
const span2 = Math.max(1, ears.outer - attach);
|
|
4363
4383
|
if (sideOf(m, x, s) === "far") {
|
|
4364
4384
|
const rim2 = TURN.earFar * m.face;
|
|
4365
4385
|
const root2 = earRoot(m, face, y, s);
|
|
4366
4386
|
const lead = Math.max(
|
|
4367
4387
|
0,
|
|
4368
|
-
Math.min((1 - m.earFarScale) *
|
|
4388
|
+
Math.min((1 - m.earFarScale) * span2, root2 - rim2)
|
|
4369
4389
|
);
|
|
4370
|
-
let along = rim2 + lead * (ears.outer - d) /
|
|
4390
|
+
let along = rim2 + lead * (ears.outer - d) / span2;
|
|
4371
4391
|
if (d < attach) along = Math.min(along, s * face(x, y, 30 * s, 0)[0]);
|
|
4372
4392
|
return [s * a * along, dy];
|
|
4373
4393
|
}
|
|
4374
4394
|
if (d <= attach) return [face(x, y, ax, 0)[0], dy];
|
|
4375
4395
|
const root = face(f.axisX + Math.sign(x - f.axisX) * attach, y, ax, 0)[0];
|
|
4376
4396
|
const rim = s * a * TURN.earNear * m.face;
|
|
4377
|
-
const t = Math.min(1, (d - attach) /
|
|
4397
|
+
const t = Math.min(1, (d - attach) / span2);
|
|
4378
4398
|
return [root + (rim - root) * t, dy];
|
|
4379
4399
|
};
|
|
4380
4400
|
}
|
|
@@ -4538,7 +4558,17 @@ function hairFrontField(m) {
|
|
|
4538
4558
|
);
|
|
4539
4559
|
return core + (m.hairOuter - core) * out;
|
|
4540
4560
|
};
|
|
4541
|
-
const ride = outerRide(
|
|
4561
|
+
const ride = outerRide(
|
|
4562
|
+
m,
|
|
4563
|
+
inner,
|
|
4564
|
+
coreAt,
|
|
4565
|
+
followAt,
|
|
4566
|
+
eyeY + 1.5 * eyeH,
|
|
4567
|
+
eyeY + eyeH / 2,
|
|
4568
|
+
Math.max(
|
|
4569
|
+
...[m.boxes.iris_L, m.boxes.iris_R].map((b) => b?.y1 ?? -Infinity)
|
|
4570
|
+
)
|
|
4571
|
+
);
|
|
4542
4572
|
const followOf = (x, y, s) => followAt(x, y, s) + (ride === void 0 ? 0 : ride.lift(x, y, s));
|
|
4543
4573
|
const drift = Math.abs(m.hairBack);
|
|
4544
4574
|
const crown = crownRide(m, cap, (x, y, s) => followOf(x, y, s) + drift);
|
|
@@ -4565,9 +4595,12 @@ function crownBlend(m, y) {
|
|
|
4565
4595
|
}
|
|
4566
4596
|
function gridRow(ys, y, snap = false) {
|
|
4567
4597
|
const n = ys.length;
|
|
4568
|
-
|
|
4598
|
+
const yc = clamp(y, ys[n - 1], ys[0]);
|
|
4599
|
+
let r = 0;
|
|
4600
|
+
while (r < n - 2 && ys[r + 1] >= yc) r++;
|
|
4601
|
+
let v = r + (ys[r] - yc) / (ys[r] - ys[r + 1]);
|
|
4569
4602
|
if (snap && Math.abs(v - Math.round(v)) < 1e-3) v = Math.round(v);
|
|
4570
|
-
|
|
4603
|
+
r = Math.min(n - 2, Math.floor(v));
|
|
4571
4604
|
return { r, t: v - r };
|
|
4572
4605
|
}
|
|
4573
4606
|
function gridCol(xs, x) {
|
|
@@ -4657,7 +4690,7 @@ function paints(runs, px) {
|
|
|
4657
4690
|
}
|
|
4658
4691
|
return false;
|
|
4659
4692
|
}
|
|
4660
|
-
function outerRide(m, inner, coreAt, followAt, capLimit) {
|
|
4693
|
+
function outerRide(m, inner, coreAt, followAt, capLimit, eyeTop, irisTop) {
|
|
4661
4694
|
const f = m.frame;
|
|
4662
4695
|
const grid = m.hairFrontGrid;
|
|
4663
4696
|
if (grid === void 0) return void 0;
|
|
@@ -4844,10 +4877,25 @@ function outerRide(m, inner, coreAt, followAt, capLimit) {
|
|
|
4844
4877
|
}
|
|
4845
4878
|
for (let j = 0; j < 2; j++) {
|
|
4846
4879
|
for (let r = n - 2; r >= 0; r--) {
|
|
4847
|
-
if (wr[r] < 1)
|
|
4880
|
+
if (r <= last || wr[r] < 1)
|
|
4848
4881
|
blend[r * 2 + j] = Math.max(blend[r * 2 + j], blend[(r + 1) * 2 + j]);
|
|
4849
4882
|
}
|
|
4850
4883
|
}
|
|
4884
|
+
let zero = last + 1;
|
|
4885
|
+
while (zero < n && ys[zero] > eyeTop) zero++;
|
|
4886
|
+
while (zero > last + 1 && zero < n && ys[zero] < irisTop - 0.5) zero--;
|
|
4887
|
+
const endY = zero < n ? ys[zero] : Math.min(Math.max(eyeTop, irisTop - 0.5), ys[n - 1] - 1);
|
|
4888
|
+
for (let j = 0; j < 2 && last < n - 1; j++) {
|
|
4889
|
+
for (let r = 1; r < zero; r++) {
|
|
4890
|
+
for (let q = 0; q < r && q <= last; q++) {
|
|
4891
|
+
if (wr[q] < 1) continue;
|
|
4892
|
+
blend[r * 2 + j] = Math.max(
|
|
4893
|
+
blend[r * 2 + j],
|
|
4894
|
+
blend[q * 2 + j] * (ys[r] - endY) / (ys[q] - endY)
|
|
4895
|
+
);
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
}
|
|
4851
4899
|
const cutRow = (r, j, x) => r > last ? 0 : along(cuts.subarray((r * 2 + j) * nc, (r * 2 + j + 1) * nc), x);
|
|
4852
4900
|
return {
|
|
4853
4901
|
lift: liftAt,
|
|
@@ -5130,7 +5178,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
|
5130
5178
|
const family = roleSpec(role).family;
|
|
5131
5179
|
const part = parts.find((p) => p.id === role);
|
|
5132
5180
|
const g = grids.get(family);
|
|
5133
|
-
const warp = part?.warps?.find((w) => w.parameter ===
|
|
5181
|
+
const warp = part?.warps?.find((w) => w.parameter === import_format10.StandardParameter.AngleX);
|
|
5134
5182
|
if (part === void 0 || part.mesh === void 0 || !g && !warp) {
|
|
5135
5183
|
return x + field(role)(x, y, ax, 0)[0];
|
|
5136
5184
|
}
|
|
@@ -5269,11 +5317,11 @@ function buildFaceMesh(frame, b, head, neckCells) {
|
|
|
5269
5317
|
const indices = [];
|
|
5270
5318
|
const neck = frame.neck;
|
|
5271
5319
|
if (neck !== void 0) {
|
|
5272
|
-
const
|
|
5273
|
-
const nc = cellsFor(2 *
|
|
5320
|
+
const span2 = neck.span + pad;
|
|
5321
|
+
const nc = cellsFor(2 * span2, neckCells.px, neckCells.min, neckCells.max);
|
|
5274
5322
|
const xs2 = columns(
|
|
5275
|
-
Math.max(b.x0, frame.axisX -
|
|
5276
|
-
Math.min(b.x1, frame.axisX +
|
|
5323
|
+
Math.max(b.x0, frame.axisX - span2),
|
|
5324
|
+
Math.min(b.x1, frame.axisX + span2),
|
|
5277
5325
|
nc
|
|
5278
5326
|
);
|
|
5279
5327
|
const sampleBelow = (x) => Math.max(
|
|
@@ -5409,6 +5457,9 @@ function buildFaceMesh(frame, b, head, neckCells) {
|
|
|
5409
5457
|
return { mesh: { vertices, uvs, indices }, region, headStart };
|
|
5410
5458
|
}
|
|
5411
5459
|
|
|
5460
|
+
// src/auto-rig/patches.ts
|
|
5461
|
+
var import_format11 = require("@ikijs/format");
|
|
5462
|
+
|
|
5412
5463
|
// src/auto-rig/solve.ts
|
|
5413
5464
|
var RANGES = {
|
|
5414
5465
|
// [lo, hi, lo exclusive]
|
|
@@ -5839,6 +5890,235 @@ function buildTurn(frame, landmarks, q) {
|
|
|
5839
5890
|
};
|
|
5840
5891
|
}
|
|
5841
5892
|
|
|
5893
|
+
// src/auto-rig/patches.ts
|
|
5894
|
+
var OP_KEYS = ["target", "at", "region", "by", "falloff", "why"];
|
|
5895
|
+
var REGION_KEYS = ["role", "x", "y"];
|
|
5896
|
+
var BY_KEYS = ["dx", "dy"];
|
|
5897
|
+
var ROLLS = /* @__PURE__ */ new Set([import_format11.StandardParameter.AngleZ, import_format11.StandardParameter.BodyAngleZ]);
|
|
5898
|
+
var REST_IS_ART = "rest is the drawn art, which no patch changes (change it with the layout or by regenerating the layer)";
|
|
5899
|
+
function applyPatches(patches, ctx) {
|
|
5900
|
+
if (!Array.isArray(patches)) {
|
|
5901
|
+
throw new PatchError(
|
|
5902
|
+
`patches must be an array, got ${patches === null ? "null" : typeof patches}`
|
|
5903
|
+
);
|
|
5904
|
+
}
|
|
5905
|
+
const sums = /* @__PURE__ */ new Map();
|
|
5906
|
+
const reports = patches.map((op, i) => replay(op, i, ctx, sums));
|
|
5907
|
+
for (const [offsets, s] of sums) {
|
|
5908
|
+
s.terms.forEach((t, k) => {
|
|
5909
|
+
if (t.length === 0) return;
|
|
5910
|
+
const d = t.sort((a, b) => a - b).reduce((a, b) => a + b, 0);
|
|
5911
|
+
offsets[k] = roundTo(offsets[k] + d / s.scale[k % 2], s.step);
|
|
5912
|
+
});
|
|
5913
|
+
}
|
|
5914
|
+
return reports;
|
|
5915
|
+
}
|
|
5916
|
+
function replay(op, i, ctx, sums) {
|
|
5917
|
+
const name = `patches[${i}]`;
|
|
5918
|
+
const o = fields(name, op, OP_KEYS);
|
|
5919
|
+
const target = text(`${name}.target`, o.target);
|
|
5920
|
+
const at = numbers(`${name}.at`, o.at);
|
|
5921
|
+
const region = fields(`${name}.region`, o.region, REGION_KEYS);
|
|
5922
|
+
const role = text(`${name}.region.role`, region.role);
|
|
5923
|
+
const x = span(`${name}.region.x`, region.x);
|
|
5924
|
+
const y = span(`${name}.region.y`, region.y);
|
|
5925
|
+
const by = fields(`${name}.by`, o.by, BY_KEYS);
|
|
5926
|
+
const dx = finite2(`${name}.by.dx`, by.dx);
|
|
5927
|
+
const dy = finite2(`${name}.by.dy`, by.dy);
|
|
5928
|
+
const falloff = o.falloff === void 0 ? 0 : finite2(`${name}.falloff`, o.falloff, 0);
|
|
5929
|
+
if (o.why !== void 0) text(`${name}.why`, o.why);
|
|
5930
|
+
const box = ctx.boxOf(role);
|
|
5931
|
+
if (box === void 0) {
|
|
5932
|
+
const roles = [...new Set(ctx.parts.map((p) => roleOfPart(p.id)))].filter(
|
|
5933
|
+
(r) => ctx.boxOf(r) !== void 0
|
|
5934
|
+
);
|
|
5935
|
+
throw new PatchError(
|
|
5936
|
+
`${name}.region.role "${role}" is not a layer of this set (roles: ${roles.join(", ")})`
|
|
5937
|
+
);
|
|
5938
|
+
}
|
|
5939
|
+
const stop = resolveStop(name, target, at, ctx);
|
|
5940
|
+
const { points } = stop;
|
|
5941
|
+
const weights = regionWeights(points, box, x, y, falloff * ctx.faceWidth);
|
|
5942
|
+
const hit = weights.flatMap((w, k) => w > 0 ? [k] : []);
|
|
5943
|
+
if (hit.length === 0) {
|
|
5944
|
+
const fx = points.map(([px]) => (px - box.x0) / bw(box));
|
|
5945
|
+
const fy = points.map(([, py]) => (box.y1 - py) / bh(box));
|
|
5946
|
+
const range = (v) => `[${roundTo(Math.min(...v), 0.01)}, ${roundTo(Math.max(...v), 0.01)}]`;
|
|
5947
|
+
throw new PatchError(
|
|
5948
|
+
`${name}.region (falloff ${falloff}) catches none of ${target}'s ${points.length} points; at rest they span x ${range(fx)}, y ${range(fy)} of ${role}'s box`
|
|
5949
|
+
);
|
|
5950
|
+
}
|
|
5951
|
+
let sum = sums.get(stop.offsets);
|
|
5952
|
+
if (sum === void 0) {
|
|
5953
|
+
sum = {
|
|
5954
|
+
terms: stop.offsets.map(() => []),
|
|
5955
|
+
scale: stop.scale,
|
|
5956
|
+
step: stop.step
|
|
5957
|
+
};
|
|
5958
|
+
sums.set(stop.offsets, sum);
|
|
5959
|
+
}
|
|
5960
|
+
const ddx = dx * ctx.faceWidth;
|
|
5961
|
+
const ddy = -dy * ctx.faceWidth;
|
|
5962
|
+
for (const k of hit) {
|
|
5963
|
+
sum.terms[2 * k].push(ddx * weights[k]);
|
|
5964
|
+
sum.terms[2 * k + 1].push(ddy * weights[k]);
|
|
5965
|
+
}
|
|
5966
|
+
const maxW = Math.max(...hit.map((k) => weights[k]));
|
|
5967
|
+
const report = {
|
|
5968
|
+
index: i,
|
|
5969
|
+
target,
|
|
5970
|
+
at,
|
|
5971
|
+
moved: hit.length,
|
|
5972
|
+
of: points.length,
|
|
5973
|
+
maxPx: roundTo(maxW * Math.hypot(dx, dy) * ctx.faceWidth, 0.1)
|
|
5974
|
+
};
|
|
5975
|
+
if (stop.stride !== void 0) {
|
|
5976
|
+
const { stride } = stop;
|
|
5977
|
+
const cols = hit.map((k) => k % stride);
|
|
5978
|
+
const rows = hit.map((k) => Math.floor(k / stride));
|
|
5979
|
+
report.cols = [Math.min(...cols), Math.max(...cols)];
|
|
5980
|
+
report.rows = [Math.min(...rows), Math.max(...rows)];
|
|
5981
|
+
}
|
|
5982
|
+
return report;
|
|
5983
|
+
}
|
|
5984
|
+
function resolveStop(name, target, at, ctx) {
|
|
5985
|
+
const grid = ctx.grids.get(target);
|
|
5986
|
+
if (grid !== void 0) {
|
|
5987
|
+
const l = grid.lattice.points;
|
|
5988
|
+
const points = [];
|
|
5989
|
+
for (let k = 0; k < l.length; k += 2) points.push([l[k], l[k + 1]]);
|
|
5990
|
+
return {
|
|
5991
|
+
offsets: gridStop(name, at, target, grid.keyforms),
|
|
5992
|
+
points,
|
|
5993
|
+
scale: [1, 1],
|
|
5994
|
+
step: 0.1,
|
|
5995
|
+
stride: grid.lattice.cols + 1
|
|
5996
|
+
};
|
|
5997
|
+
}
|
|
5998
|
+
const part = ctx.parts.find((p) => p.id === target);
|
|
5999
|
+
const b = ctx.boxOf(target);
|
|
6000
|
+
if (part?.mesh === void 0 || part.warps === void 0 || b === void 0) {
|
|
6001
|
+
const parts = ctx.parts.filter(
|
|
6002
|
+
(p) => p.mesh !== void 0 && p.warps !== void 0 && ctx.boxOf(p.id) !== void 0
|
|
6003
|
+
);
|
|
6004
|
+
throw new PatchError(
|
|
6005
|
+
`${name}.target "${target}" is neither a feature grid (${[...ctx.grids.keys()].join(", ")}) nor a mesh part with warps (${parts.map((p) => p.id).join(", ")}); bodyWarp and the matrix deformers are not patchable`
|
|
6006
|
+
);
|
|
6007
|
+
}
|
|
6008
|
+
return {
|
|
6009
|
+
offsets: partStop(name, at, part, part.warps, ctx),
|
|
6010
|
+
points: meshPoints(part.mesh, b),
|
|
6011
|
+
scale: [bw(b), bh(b)],
|
|
6012
|
+
step: 1e-4
|
|
6013
|
+
};
|
|
6014
|
+
}
|
|
6015
|
+
function gridStop(name, at, id, keyforms) {
|
|
6016
|
+
const i = X_STOPS.indexOf(at[import_format11.StandardParameter.AngleX]);
|
|
6017
|
+
const j = Y_STOPS.indexOf(at[import_format11.StandardParameter.AngleY]);
|
|
6018
|
+
if (Object.keys(at).length !== 2 || i < 0 || j < 0) {
|
|
6019
|
+
throw new PatchError(
|
|
6020
|
+
`${name}.at must name ${import_format11.StandardParameter.AngleX} in [${X_STOPS.join(", ")}] and ${import_format11.StandardParameter.AngleY} in [${Y_STOPS.join(", ")}] for the grid ${id}, got ${JSON.stringify(at)}`
|
|
6021
|
+
);
|
|
6022
|
+
}
|
|
6023
|
+
if (X_STOPS[i] === 0 && Y_STOPS[j] === 0) {
|
|
6024
|
+
throw new PatchError(
|
|
6025
|
+
`${name}.at ${import_format11.StandardParameter.AngleX}=0 ${import_format11.StandardParameter.AngleY}=0 is ${id}'s rest stop: ${REST_IS_ART}; patch a stop with ${import_format11.StandardParameter.AngleX} or ${import_format11.StandardParameter.AngleY} at \xB130 instead`
|
|
6026
|
+
);
|
|
6027
|
+
}
|
|
6028
|
+
return keyforms[j * X_STOPS.length + i];
|
|
6029
|
+
}
|
|
6030
|
+
function partStop(name, at, part, warps, ctx) {
|
|
6031
|
+
const keys = Object.keys(at);
|
|
6032
|
+
const [param] = keys;
|
|
6033
|
+
const value = at[param];
|
|
6034
|
+
const keyform = keys.length === 1 ? warps.filter((w) => w.parameter === param).flatMap((w) => w.keyforms).find((k) => k.value === value) : void 0;
|
|
6035
|
+
if (keyform === void 0) {
|
|
6036
|
+
throw new PatchError(
|
|
6037
|
+
`${name}.at must name one parameter at one of ${part.id}'s warp stops (${listStops(warps, () => true)}), got ${JSON.stringify(at)}`
|
|
6038
|
+
);
|
|
6039
|
+
}
|
|
6040
|
+
if (ROLLS.has(param)) {
|
|
6041
|
+
throw new PatchError(
|
|
6042
|
+
`${name}.at ${param}: that parameter rolls ${part.id}'s deformer chain, so a displacement at its stop is not on screen axes; patch another of its stops (${listStops(warps, (p) => !ROLLS.has(p))}) or its feature grid instead`
|
|
6043
|
+
);
|
|
6044
|
+
}
|
|
6045
|
+
const isRest = (p, v) => v === ctx.parameters.find((q) => q.id === p)?.default;
|
|
6046
|
+
const isShut = (p, v) => SHUT_BY.get(part.id) === p && v === 0;
|
|
6047
|
+
if (!isRest(param, value) && !isShut(param, value)) return keyform.offsets;
|
|
6048
|
+
const open = listStops(warps, (p, v) => !isRest(p, v) && !isShut(p, v));
|
|
6049
|
+
const grid = part.deformer !== void 0 && ctx.grids.has(part.deformer) ? `its feature grid ${part.deformer} at a ${import_format11.StandardParameter.AngleX} \xD7 ${import_format11.StandardParameter.AngleY} stop` : "";
|
|
6050
|
+
const instead = [open && `another of its stops (${open})`, grid].filter((s) => s !== "").join(" or ");
|
|
6051
|
+
const hint = instead === "" ? "" : `; patch ${instead} instead`;
|
|
6052
|
+
throw new PatchError(
|
|
6053
|
+
isRest(param, value) ? `${name}.at ${param}=${value} is that parameter's default, ${part.id}'s rest stop: ${REST_IS_ART}${hint}` : `${name}.at ${param}=0 is where the rig shuts ${part.id} on purpose, a closure the fold check exempts, so a fold a patch made there would never be reported${hint}`
|
|
6054
|
+
);
|
|
6055
|
+
}
|
|
6056
|
+
function listStops(warps, keep) {
|
|
6057
|
+
const stops = /* @__PURE__ */ new Map();
|
|
6058
|
+
for (const w of warps) {
|
|
6059
|
+
for (const k of w.keyforms) {
|
|
6060
|
+
if (!keep(w.parameter, k.value)) continue;
|
|
6061
|
+
const s = stops.get(w.parameter) ?? /* @__PURE__ */ new Set();
|
|
6062
|
+
s.add(k.value);
|
|
6063
|
+
stops.set(w.parameter, s);
|
|
6064
|
+
}
|
|
6065
|
+
}
|
|
6066
|
+
return [...stops].map(([p, s]) => `${p}@${[...s].sort((a, b) => a - b).join(",")}`).join(" ");
|
|
6067
|
+
}
|
|
6068
|
+
function regionWeights(points, box, x, y, fall) {
|
|
6069
|
+
const x0 = box.x0 + x[0] * bw(box);
|
|
6070
|
+
const x1 = box.x0 + x[1] * bw(box);
|
|
6071
|
+
const top = box.y1 - y[0] * bh(box);
|
|
6072
|
+
const bottom = box.y1 - y[1] * bh(box);
|
|
6073
|
+
return points.map(([px, py]) => {
|
|
6074
|
+
const d = Math.hypot(
|
|
6075
|
+
Math.max(x0 - px, 0, px - x1),
|
|
6076
|
+
Math.max(bottom - py, 0, py - top)
|
|
6077
|
+
);
|
|
6078
|
+
return d === 0 ? 1 : d < fall ? smoothstep(fall, 0, d) : 0;
|
|
6079
|
+
});
|
|
6080
|
+
}
|
|
6081
|
+
function asPatch(check) {
|
|
6082
|
+
try {
|
|
6083
|
+
return check();
|
|
6084
|
+
} catch (e) {
|
|
6085
|
+
throw e instanceof TurnTargetError ? new PatchError(e.message) : e;
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
function fields(name, v, known) {
|
|
6089
|
+
if (v === void 0) throw new PatchError(`${name} is missing`);
|
|
6090
|
+
asPatch(() => checkedKeys(name, v, known));
|
|
6091
|
+
return v;
|
|
6092
|
+
}
|
|
6093
|
+
function numbers(name, v) {
|
|
6094
|
+
const o = fields(name, v, Object.keys(v ?? {}));
|
|
6095
|
+
return Object.fromEntries(
|
|
6096
|
+
Object.entries(o).map(([k, n]) => [k, finite2(`${name}.${k}`, n)])
|
|
6097
|
+
);
|
|
6098
|
+
}
|
|
6099
|
+
var finite2 = (name, v, lo = -Infinity) => asPatch(() => checkedNumber(name, v, lo, Infinity));
|
|
6100
|
+
function text(name, v) {
|
|
6101
|
+
if (typeof v !== "string") {
|
|
6102
|
+
throw new PatchError(`${name} must be a string, got ${String(v)}`);
|
|
6103
|
+
}
|
|
6104
|
+
return v;
|
|
6105
|
+
}
|
|
6106
|
+
function span(name, v) {
|
|
6107
|
+
if (!Array.isArray(v) || v.length !== 2) {
|
|
6108
|
+
throw new PatchError(
|
|
6109
|
+
`${name} must be [from, to], got ${JSON.stringify(v)}`
|
|
6110
|
+
);
|
|
6111
|
+
}
|
|
6112
|
+
const lo = finite2(`${name}[0]`, v[0]);
|
|
6113
|
+
const hi = finite2(`${name}[1]`, v[1]);
|
|
6114
|
+
if (lo > hi) {
|
|
6115
|
+
throw new PatchError(
|
|
6116
|
+
`${name} [${lo}, ${hi}] runs backwards: ${name}[0] must not exceed ${name}[1]`
|
|
6117
|
+
);
|
|
6118
|
+
}
|
|
6119
|
+
return [lo, hi];
|
|
6120
|
+
}
|
|
6121
|
+
|
|
5842
6122
|
// src/auto-rig/generate.ts
|
|
5843
6123
|
var MESH_CELLS = {
|
|
5844
6124
|
face: { px: 28, min: 4, max: 24 },
|
|
@@ -5896,7 +6176,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5896
6176
|
const solveFrame = buildHeadFrame(layers, frameOptions);
|
|
5897
6177
|
const ctx = solveContext(solveFrame, byRole, turnOptions, hasNose);
|
|
5898
6178
|
const boxes = Object.fromEntries(
|
|
5899
|
-
["eye_L", "eye_R", "brow_L", "brow_R"].filter(has).map((r) => [r, box(r)])
|
|
6179
|
+
["eye_L", "eye_R", "iris_L", "iris_R", "brow_L", "brow_R"].filter(has).map((r) => [r, box(r)])
|
|
5900
6180
|
);
|
|
5901
6181
|
const landmarks = {
|
|
5902
6182
|
boxes,
|
|
@@ -6016,8 +6296,17 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
6016
6296
|
];
|
|
6017
6297
|
}
|
|
6018
6298
|
const parameters = declareParameters(new Set(byRole.keys()));
|
|
6299
|
+
const reports = options.patches === void 0 ? void 0 : applyPatches(options.patches, {
|
|
6300
|
+
grids: new Map(
|
|
6301
|
+
[...grids].map(([family, g]) => [WARP_ID[family], g])
|
|
6302
|
+
),
|
|
6303
|
+
parts,
|
|
6304
|
+
boxOf: (role) => has(role) ? box(role) : void 0,
|
|
6305
|
+
faceWidth: byRole.get("face").cropW,
|
|
6306
|
+
parameters
|
|
6307
|
+
});
|
|
6019
6308
|
const model = {
|
|
6020
|
-
version:
|
|
6309
|
+
version: import_format12.IKI_FORMAT_VERSION,
|
|
6021
6310
|
name: "auto-rigged",
|
|
6022
6311
|
canvas: { width: canvas.width, height: canvas.height },
|
|
6023
6312
|
parameters,
|
|
@@ -6027,7 +6316,8 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
6027
6316
|
expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
|
|
6028
6317
|
motions: defaultMotions(new Set(parameters.map((p) => p.id)))
|
|
6029
6318
|
};
|
|
6030
|
-
const valid = (0,
|
|
6319
|
+
const valid = (0, import_format12.parseIkiModel)(model);
|
|
6320
|
+
if (reports !== void 0) options.onPatchesApplied?.(reports);
|
|
6031
6321
|
if (hasNose && options.onTurnSolved) {
|
|
6032
6322
|
options.onTurnSolved(
|
|
6033
6323
|
turnReport(
|
|
@@ -6042,14 +6332,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
6042
6332
|
function turnWarps(mesh, b, fieldOf) {
|
|
6043
6333
|
return [
|
|
6044
6334
|
localWarp(
|
|
6045
|
-
|
|
6335
|
+
import_format12.StandardParameter.AngleX,
|
|
6046
6336
|
mesh,
|
|
6047
6337
|
b,
|
|
6048
6338
|
X_STOPS,
|
|
6049
6339
|
([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
|
|
6050
6340
|
),
|
|
6051
6341
|
localWarp(
|
|
6052
|
-
|
|
6342
|
+
import_format12.StandardParameter.AngleY,
|
|
6053
6343
|
mesh,
|
|
6054
6344
|
b,
|
|
6055
6345
|
Y_STOPS,
|
|
@@ -6100,7 +6390,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6100
6390
|
}
|
|
6101
6391
|
if (mesh !== void 0) part.mesh = mesh;
|
|
6102
6392
|
const side = spec.role.endsWith("_L") ? "L" : "R";
|
|
6103
|
-
const eyeOpen = side === "L" ?
|
|
6393
|
+
const eyeOpen = side === "L" ? import_format12.StandardParameter.EyeOpenLeft : import_format12.StandardParameter.EyeOpenRight;
|
|
6104
6394
|
const hh = frame.hh;
|
|
6105
6395
|
switch (spec.role) {
|
|
6106
6396
|
case "face":
|
|
@@ -6115,7 +6405,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6115
6405
|
headOwnRoll(byRole.has("body"))
|
|
6116
6406
|
),
|
|
6117
6407
|
localWarp(
|
|
6118
|
-
|
|
6408
|
+
import_format12.StandardParameter.Breath,
|
|
6119
6409
|
mesh,
|
|
6120
6410
|
b,
|
|
6121
6411
|
[0, 1],
|
|
@@ -6162,8 +6452,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6162
6452
|
if (byRole.has("mouth_open")) {
|
|
6163
6453
|
warps.push(mouthWiden(mesh, b));
|
|
6164
6454
|
bindings.push(
|
|
6165
|
-
{ parameter:
|
|
6166
|
-
{ parameter:
|
|
6455
|
+
{ parameter: import_format12.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
|
|
6456
|
+
{ parameter: import_format12.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
|
|
6167
6457
|
);
|
|
6168
6458
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
6169
6459
|
} else {
|
|
@@ -6173,19 +6463,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6173
6463
|
);
|
|
6174
6464
|
bindings.push(
|
|
6175
6465
|
{
|
|
6176
|
-
parameter:
|
|
6466
|
+
parameter: import_format12.StandardParameter.MouthOpen,
|
|
6177
6467
|
channel: "scaleY",
|
|
6178
6468
|
from: 0,
|
|
6179
6469
|
to: roundTo(grow, 0.01)
|
|
6180
6470
|
},
|
|
6181
6471
|
{
|
|
6182
|
-
parameter:
|
|
6472
|
+
parameter: import_format12.StandardParameter.MouthOpen,
|
|
6183
6473
|
channel: "scaleX",
|
|
6184
6474
|
from: 0,
|
|
6185
6475
|
to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
|
|
6186
6476
|
},
|
|
6187
6477
|
{
|
|
6188
|
-
parameter:
|
|
6478
|
+
parameter: import_format12.StandardParameter.MouthOpen,
|
|
6189
6479
|
channel: "translateY",
|
|
6190
6480
|
from: 0,
|
|
6191
6481
|
to: -roundTo(grow * bh(b) / 2, 0.01)
|
|
@@ -6197,7 +6487,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6197
6487
|
case "mouth_open":
|
|
6198
6488
|
warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
|
|
6199
6489
|
bindings.push({
|
|
6200
|
-
parameter:
|
|
6490
|
+
parameter: import_format12.StandardParameter.MouthOpen,
|
|
6201
6491
|
channel: "opacity",
|
|
6202
6492
|
from: 0,
|
|
6203
6493
|
to: 1
|
|
@@ -6219,7 +6509,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6219
6509
|
case "blush_L":
|
|
6220
6510
|
case "blush_R":
|
|
6221
6511
|
bindings.push({
|
|
6222
|
-
parameter:
|
|
6512
|
+
parameter: import_format12.StandardParameter.Cheek,
|
|
6223
6513
|
channel: "opacity",
|
|
6224
6514
|
from: AMPLITUDE.blushRest,
|
|
6225
6515
|
to: 1
|
|
@@ -6230,8 +6520,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6230
6520
|
if (byRole.has("hair_front")) {
|
|
6231
6521
|
const amp = style.sway * AMPLITUDE.sway * hh;
|
|
6232
6522
|
warps.push(
|
|
6233
|
-
hairSway(
|
|
6234
|
-
hairSway(
|
|
6523
|
+
hairSway(import_format12.StandardParameter.HairSwayX, mesh, b, amp),
|
|
6524
|
+
hairSway(import_format12.StandardParameter.HairSwayZ, mesh, b, amp)
|
|
6235
6525
|
);
|
|
6236
6526
|
}
|
|
6237
6527
|
warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
|
|
@@ -6246,6 +6536,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6246
6536
|
headStart
|
|
6247
6537
|
};
|
|
6248
6538
|
}
|
|
6539
|
+
var RAMP_ROWS = 3;
|
|
6540
|
+
var MIN_RAMP_CELL = 4;
|
|
6249
6541
|
function hairFrontGrid(b, frame, boxes) {
|
|
6250
6542
|
const cells = MESH_CELLS.hair_front;
|
|
6251
6543
|
const px = Math.min(cells.px, meshScale("hair_front", frame));
|
|
@@ -6270,6 +6562,24 @@ function hairFrontGrid(b, frame, boxes) {
|
|
|
6270
6562
|
{ length: rows + 1 },
|
|
6271
6563
|
(_, r) => b.y1 - bh(b) * r / rows
|
|
6272
6564
|
);
|
|
6565
|
+
const eyes = [boxes.eye_L, boxes.eye_R].filter((e) => e !== void 0);
|
|
6566
|
+
const irises = [boxes.iris_L, boxes.iris_R].filter((e) => e !== void 0);
|
|
6567
|
+
if (eyes.length > 0 && irises.length > 0) {
|
|
6568
|
+
const eyeY = eyes.reduce((t, e) => t + (e.y0 + e.y1) / 2, 0) / eyes.length;
|
|
6569
|
+
const eyeH = eyes.reduce((t, e) => t + (e.y1 - e.y0), 0) / eyes.length;
|
|
6570
|
+
const hi = Math.min(eyeY + 1.5 * eyeH, b.y1);
|
|
6571
|
+
const lo = Math.max(Math.max(...irises.map((e) => e.y1)) - 0.5, b.y0);
|
|
6572
|
+
for (; ; ) {
|
|
6573
|
+
const edges = [hi, ...ys.filter((y) => y < hi && y > lo), lo];
|
|
6574
|
+
if (edges.length - 2 >= RAMP_ROWS) break;
|
|
6575
|
+
let gap = 0;
|
|
6576
|
+
for (let i = 1; i + 1 < edges.length; i++)
|
|
6577
|
+
if (edges[i] - edges[i + 1] > edges[gap] - edges[gap + 1]) gap = i;
|
|
6578
|
+
if (edges[gap] - edges[gap + 1] < 2 * MIN_RAMP_CELL) break;
|
|
6579
|
+
ys.push((edges[gap] + edges[gap + 1]) / 2);
|
|
6580
|
+
ys.sort((p, q) => q - p);
|
|
6581
|
+
}
|
|
6582
|
+
}
|
|
6273
6583
|
return { xs, ys };
|
|
6274
6584
|
}
|
|
6275
6585
|
function meshScale(role, frame) {
|
|
@@ -6297,9 +6607,9 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
|
6297
6607
|
pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
|
|
6298
6608
|
bindings: [
|
|
6299
6609
|
// AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
|
|
6300
|
-
{ parameter:
|
|
6610
|
+
{ parameter: import_format12.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
|
|
6301
6611
|
{
|
|
6302
|
-
parameter:
|
|
6612
|
+
parameter: import_format12.StandardParameter.Breath,
|
|
6303
6613
|
channel: "translateY",
|
|
6304
6614
|
from: 0,
|
|
6305
6615
|
to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
|
|
@@ -6317,8 +6627,8 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
|
6317
6627
|
points: g.lattice.points
|
|
6318
6628
|
},
|
|
6319
6629
|
warp2d: {
|
|
6320
|
-
parameter:
|
|
6321
|
-
parameterY:
|
|
6630
|
+
parameter: import_format12.StandardParameter.AngleX,
|
|
6631
|
+
parameterY: import_format12.StandardParameter.AngleY,
|
|
6322
6632
|
valuesX: X_STOPS,
|
|
6323
6633
|
valuesY: Y_STOPS,
|
|
6324
6634
|
keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
|
|
@@ -6331,16 +6641,16 @@ function hairPhysics() {
|
|
|
6331
6641
|
return [
|
|
6332
6642
|
{
|
|
6333
6643
|
id: "hairSway",
|
|
6334
|
-
input: { parameter:
|
|
6335
|
-
output: { parameter:
|
|
6644
|
+
input: { parameter: import_format12.StandardParameter.AngleX, weight: 1 },
|
|
6645
|
+
output: { parameter: import_format12.StandardParameter.HairSwayX, scale: 5 },
|
|
6336
6646
|
mass: 1,
|
|
6337
6647
|
stiffness: 30,
|
|
6338
6648
|
damping: 3
|
|
6339
6649
|
},
|
|
6340
6650
|
{
|
|
6341
6651
|
id: "hairTilt",
|
|
6342
|
-
input: { parameter:
|
|
6343
|
-
output: { parameter:
|
|
6652
|
+
input: { parameter: import_format12.StandardParameter.AngleZ, weight: 1 },
|
|
6653
|
+
output: { parameter: import_format12.StandardParameter.HairSwayZ, scale: 8 },
|
|
6344
6654
|
mass: 1,
|
|
6345
6655
|
stiffness: 25,
|
|
6346
6656
|
damping: 2.5
|
|
@@ -6350,13 +6660,13 @@ function hairPhysics() {
|
|
|
6350
6660
|
function declareParameters(roles) {
|
|
6351
6661
|
const out = [];
|
|
6352
6662
|
const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
|
|
6353
|
-
add(
|
|
6354
|
-
add(
|
|
6355
|
-
add(
|
|
6663
|
+
add(import_format12.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
|
|
6664
|
+
add(import_format12.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
|
|
6665
|
+
add(import_format12.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
|
|
6356
6666
|
if (roles.has("body")) {
|
|
6357
|
-
add(
|
|
6358
|
-
add(
|
|
6359
|
-
add(
|
|
6667
|
+
add(import_format12.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
|
|
6668
|
+
add(import_format12.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
|
|
6669
|
+
add(import_format12.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
|
|
6360
6670
|
}
|
|
6361
6671
|
const addPose = (side, pose, angle) => {
|
|
6362
6672
|
add(pose, `Arm Pose ${side}`, 0, 1, 0);
|
|
@@ -6365,40 +6675,40 @@ function declareParameters(roles) {
|
|
|
6365
6675
|
const [armMin, armMax] = ARM_RANGE;
|
|
6366
6676
|
const [elbowMin, elbowMax] = ELBOW_RANGE;
|
|
6367
6677
|
if (roles.has("arm_L")) {
|
|
6368
|
-
add(
|
|
6369
|
-
add(
|
|
6678
|
+
add(import_format12.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
|
|
6679
|
+
add(import_format12.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
|
|
6370
6680
|
if (roles.has("forearm_pose_L"))
|
|
6371
|
-
addPose("L",
|
|
6681
|
+
addPose("L", import_format12.StandardParameter.ArmPoseLeft, import_format12.StandardParameter.ArmPoseAngleLeft);
|
|
6372
6682
|
}
|
|
6373
6683
|
if (roles.has("arm_R")) {
|
|
6374
|
-
add(
|
|
6375
|
-
add(
|
|
6684
|
+
add(import_format12.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
|
|
6685
|
+
add(import_format12.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
|
|
6376
6686
|
if (roles.has("forearm_pose_R"))
|
|
6377
|
-
addPose("R",
|
|
6687
|
+
addPose("R", import_format12.StandardParameter.ArmPoseRight, import_format12.StandardParameter.ArmPoseAngleRight);
|
|
6378
6688
|
}
|
|
6379
|
-
add(
|
|
6380
|
-
add(
|
|
6689
|
+
add(import_format12.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
|
|
6690
|
+
add(import_format12.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
|
|
6381
6691
|
if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
|
|
6382
|
-
add(
|
|
6383
|
-
add(
|
|
6692
|
+
add(import_format12.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
|
|
6693
|
+
add(import_format12.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
|
|
6384
6694
|
}
|
|
6385
|
-
add(
|
|
6386
|
-
add(
|
|
6695
|
+
add(import_format12.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
|
|
6696
|
+
add(import_format12.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
|
|
6387
6697
|
if (roles.has("brow_L")) {
|
|
6388
|
-
add(
|
|
6389
|
-
add(
|
|
6698
|
+
add(import_format12.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
|
|
6699
|
+
add(import_format12.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
|
|
6390
6700
|
}
|
|
6391
6701
|
if (roles.has("brow_R")) {
|
|
6392
|
-
add(
|
|
6393
|
-
add(
|
|
6702
|
+
add(import_format12.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
|
|
6703
|
+
add(import_format12.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
|
|
6394
6704
|
}
|
|
6395
6705
|
if (roles.has("blush_L") || roles.has("blush_R")) {
|
|
6396
|
-
add(
|
|
6706
|
+
add(import_format12.StandardParameter.Cheek, "Cheek", 0, 1, 0);
|
|
6397
6707
|
}
|
|
6398
|
-
add(
|
|
6708
|
+
add(import_format12.StandardParameter.Breath, "Breath", 0, 1, 0);
|
|
6399
6709
|
if (roles.has("hair_front")) {
|
|
6400
|
-
add(
|
|
6401
|
-
add(
|
|
6710
|
+
add(import_format12.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
6711
|
+
add(import_format12.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
6402
6712
|
}
|
|
6403
6713
|
return out;
|
|
6404
6714
|
}
|
|
@@ -6420,6 +6730,8 @@ function declareParameters(roles) {
|
|
|
6420
6730
|
LIP_INSIDE_ROLES,
|
|
6421
6731
|
LIP_ROLES,
|
|
6422
6732
|
LayerGeometryError,
|
|
6733
|
+
PatchError,
|
|
6734
|
+
SHUT_BY,
|
|
6423
6735
|
SPECK_CORE_FRACTION,
|
|
6424
6736
|
SetDeformerBindings,
|
|
6425
6737
|
SetDeformerParent,
|