@ikijs/editor 0.24.1 → 0.25.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 +16 -14
- package/dist/index.d.mts +78 -3
- package/dist/index.d.ts +78 -3
- package/dist/index.js +378 -115
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +376 -113
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1476,8 +1476,8 @@ function foregroundSpan(mask, width, height, rowLo, rowHi) {
|
|
|
1476
1476
|
}
|
|
1477
1477
|
return { left, right };
|
|
1478
1478
|
}
|
|
1479
|
-
function headHalfOf(
|
|
1480
|
-
return (
|
|
1479
|
+
function headHalfOf(span2) {
|
|
1480
|
+
return (span2.right - span2.left + 1) / 2;
|
|
1481
1481
|
}
|
|
1482
1482
|
var DARK_SHARE = 0.55;
|
|
1483
1483
|
function jawRowsOf(rgba, canvasW, bbox, rowLeft, rowRight) {
|
|
@@ -1487,9 +1487,9 @@ function jawRowsOf(rgba, canvasW, bbox, rowLeft, rowRight) {
|
|
|
1487
1487
|
let widest = bbox.y;
|
|
1488
1488
|
let widestSpan = -1;
|
|
1489
1489
|
for (let y = bbox.y; y < bbox.y + bbox.h; y++) {
|
|
1490
|
-
const
|
|
1491
|
-
if (
|
|
1492
|
-
widestSpan =
|
|
1490
|
+
const span2 = rowRight[y] - rowLeft[y];
|
|
1491
|
+
if (span2 >= widestSpan) {
|
|
1492
|
+
widestSpan = span2;
|
|
1493
1493
|
widest = y;
|
|
1494
1494
|
}
|
|
1495
1495
|
for (let x = bbox.x; x < bbox.x + bbox.w; x++) {
|
|
@@ -1658,14 +1658,14 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1658
1658
|
}
|
|
1659
1659
|
function finish() {
|
|
1660
1660
|
const eyeRow = eyeBandRowOf(layerInputs);
|
|
1661
|
-
const
|
|
1661
|
+
const span2 = foregroundSpan(
|
|
1662
1662
|
opaque,
|
|
1663
1663
|
canvasW,
|
|
1664
1664
|
canvasH,
|
|
1665
1665
|
eyeRow - HEAD_BAND,
|
|
1666
1666
|
eyeRow + HEAD_BAND
|
|
1667
1667
|
);
|
|
1668
|
-
const headHalfWidth =
|
|
1668
|
+
const headHalfWidth = span2.right > span2.left ? headHalfOf(span2) : void 0;
|
|
1669
1669
|
const headHalfWidthApplied = headHalfWidth !== void 0 && headHalfWidth > facePlateHalfOf(layerInputs);
|
|
1670
1670
|
let headEdges;
|
|
1671
1671
|
if (headHalfWidthApplied) {
|
|
@@ -1693,9 +1693,9 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1693
1693
|
const centre = iris.bbox.x + iris.bbox.w / 2;
|
|
1694
1694
|
const otherX = other.bbox.x + other.bbox.w / 2 - half;
|
|
1695
1695
|
const c = side < 0 ? Math.ceil(centre) - 1 : Math.floor(centre);
|
|
1696
|
-
const
|
|
1697
|
-
if (
|
|
1698
|
-
const minRun = STRAND_MIN_RUN_FRACTION * (
|
|
1696
|
+
const span3 = rowSpansByRole.find((s) => s.role === iris.role);
|
|
1697
|
+
if (span3.rowRight[r] < span3.rowLeft[r]) return void 0;
|
|
1698
|
+
const minRun = STRAND_MIN_RUN_FRACTION * (span3.rowRight[r] + 1 - span3.rowLeft[r]);
|
|
1699
1699
|
const strand = new Uint8Array(canvasW);
|
|
1700
1700
|
const runs = r >= hairTop && r < hairTop + hairRuns.length ? hairRuns[r - hairTop] : [];
|
|
1701
1701
|
for (let k = 0; k < runs.length; k += 2) {
|
|
@@ -1720,8 +1720,8 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1720
1720
|
while (strandAt(x)) x += side;
|
|
1721
1721
|
return {
|
|
1722
1722
|
y: canvasH / 2 - (r + 0.5),
|
|
1723
|
-
irisOuter: (side < 0 ?
|
|
1724
|
-
irisInner: (side < 0 ?
|
|
1723
|
+
irisOuter: (side < 0 ? span3.rowLeft[r] : span3.rowRight[r] + 1) - half,
|
|
1724
|
+
irisInner: (side < 0 ? span3.rowRight[r] + 1 : span3.rowLeft[r]) - half,
|
|
1725
1725
|
runOuter: outerBoundary(x - side),
|
|
1726
1726
|
runFace
|
|
1727
1727
|
};
|
|
@@ -2125,6 +2125,12 @@ var DEFAULT_TURN_TARGETS = Object.freeze({
|
|
|
2125
2125
|
farEyeRatio: round2(TURN.eyeFarScale / TURN.eyeNearScale),
|
|
2126
2126
|
silhouetteRatio: 1
|
|
2127
2127
|
});
|
|
2128
|
+
var PatchError = class extends Error {
|
|
2129
|
+
constructor(message) {
|
|
2130
|
+
super(message);
|
|
2131
|
+
this.name = "PatchError";
|
|
2132
|
+
}
|
|
2133
|
+
};
|
|
2128
2134
|
|
|
2129
2135
|
// src/auto-rig/arms.ts
|
|
2130
2136
|
import {
|
|
@@ -2756,7 +2762,7 @@ var ARM_IDS = {
|
|
|
2756
2762
|
};
|
|
2757
2763
|
function endPivot(layer, end) {
|
|
2758
2764
|
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2759
|
-
const
|
|
2765
|
+
const span2 = runSpan(layer);
|
|
2760
2766
|
const from = (k) => end === "top" ? k : bbox.h - 1 - k;
|
|
2761
2767
|
const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
|
|
2762
2768
|
const firstK = end === "top" ? painted[0] ?? 0 : painted.at(-1) ?? bbox.h - 1;
|
|
@@ -2764,13 +2770,13 @@ function endPivot(layer, end) {
|
|
|
2764
2770
|
const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
|
|
2765
2771
|
const widths = [];
|
|
2766
2772
|
for (let k = lo; k <= hi; k++) {
|
|
2767
|
-
const [s2, e2] =
|
|
2773
|
+
const [s2, e2] = span2(from(k));
|
|
2768
2774
|
widths.push(e2 - s2);
|
|
2769
2775
|
}
|
|
2770
2776
|
widths.sort((p, q) => p - q);
|
|
2771
2777
|
const radius = widths[widths.length >> 1] / 2;
|
|
2772
2778
|
const ks = end === "top" ? firstK + radius : firstK - radius;
|
|
2773
|
-
const [s, e] =
|
|
2779
|
+
const [s, e] = span2(Math.round(ks));
|
|
2774
2780
|
return {
|
|
2775
2781
|
point: {
|
|
2776
2782
|
x: roundTo((s + e) / 2 - canvasW / 2, 0.01),
|
|
@@ -2795,9 +2801,9 @@ function runSpan(layer) {
|
|
|
2795
2801
|
function armGeometry(layer, bodyAxisX) {
|
|
2796
2802
|
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2797
2803
|
const b = boxOfLayer(layer);
|
|
2798
|
-
const
|
|
2804
|
+
const span2 = runSpan(layer);
|
|
2799
2805
|
const centreX = (k) => {
|
|
2800
|
-
const [s2, e2] =
|
|
2806
|
+
const [s2, e2] = span2(k);
|
|
2801
2807
|
return (s2 + e2) / 2 - canvasW / 2;
|
|
2802
2808
|
};
|
|
2803
2809
|
const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
|
|
@@ -2811,7 +2817,7 @@ function armGeometry(layer, bodyAxisX) {
|
|
|
2811
2817
|
);
|
|
2812
2818
|
}
|
|
2813
2819
|
const elbow = { x: roundTo(centreX(ke), 0.01), y: roundTo(rowY(ke), 0.01) };
|
|
2814
|
-
const [s, e] =
|
|
2820
|
+
const [s, e] = span2(ke);
|
|
2815
2821
|
const capRadius = Math.min(
|
|
2816
2822
|
(e - s) / 2 + 1,
|
|
2817
2823
|
elbow.x - b.x0,
|
|
@@ -2821,7 +2827,7 @@ function armGeometry(layer, bodyAxisX) {
|
|
|
2821
2827
|
const inside = (x, y) => {
|
|
2822
2828
|
const k = Math.floor(y) - bbox.y;
|
|
2823
2829
|
if (k < 0 || k >= bbox.h) return false;
|
|
2824
|
-
const [rs, re] =
|
|
2830
|
+
const [rs, re] = span2(k);
|
|
2825
2831
|
return rs < x && x < re;
|
|
2826
2832
|
};
|
|
2827
2833
|
const capRays = Array.from({ length: CAP_SEGMENTS + 1 }, (_, i) => {
|
|
@@ -3031,14 +3037,25 @@ function forearmPoseDeformer(role, arm) {
|
|
|
3031
3037
|
|
|
3032
3038
|
// src/auto-rig/mouth.ts
|
|
3033
3039
|
import {
|
|
3034
|
-
StandardParameter as
|
|
3040
|
+
StandardParameter as P6
|
|
3035
3041
|
} from "@ikijs/format";
|
|
3036
3042
|
|
|
3037
3043
|
// src/auto-rig/roles.ts
|
|
3044
|
+
import { StandardParameter as P5 } from "@ikijs/format";
|
|
3038
3045
|
var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
|
|
3039
3046
|
var isLipRole = (role) => LIP_ROLES.includes(role);
|
|
3040
3047
|
var LIP_INSIDE_ROLES = ["mouth_tongue", "mouth_teeth"];
|
|
3041
3048
|
var isLipInsideRole = (role) => LIP_INSIDE_ROLES.includes(role);
|
|
3049
|
+
var SHUT_BY = new Map([
|
|
3050
|
+
["eye_L", P5.EyeOpenLeft],
|
|
3051
|
+
["lash_lower_L", P5.EyeOpenLeft],
|
|
3052
|
+
["eye_R", P5.EyeOpenRight],
|
|
3053
|
+
["lash_lower_R", P5.EyeOpenRight],
|
|
3054
|
+
...[...LIP_ROLES, ...LIP_INSIDE_ROLES].map((id) => [
|
|
3055
|
+
id,
|
|
3056
|
+
P5.MouthOpen
|
|
3057
|
+
])
|
|
3058
|
+
]);
|
|
3042
3059
|
var ROLE_TABLE = [
|
|
3043
3060
|
{ role: "hair_back", family: "hair_back" },
|
|
3044
3061
|
{ role: "body", family: "body" },
|
|
@@ -3383,7 +3400,7 @@ function columnOfKnot(knot, opening) {
|
|
|
3383
3400
|
function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
3384
3401
|
const fold = mouthFold(role, opening);
|
|
3385
3402
|
return localWarp(
|
|
3386
|
-
|
|
3403
|
+
P6.MouthOpen,
|
|
3387
3404
|
mesh,
|
|
3388
3405
|
box,
|
|
3389
3406
|
[0, 1],
|
|
@@ -3392,13 +3409,13 @@ function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
|
3392
3409
|
}
|
|
3393
3410
|
function mouthRestShift(byRole) {
|
|
3394
3411
|
const rig = buildMouthRig(byRole);
|
|
3395
|
-
const
|
|
3412
|
+
const fields2 = /* @__PURE__ */ new Map();
|
|
3396
3413
|
for (const role of ["lip_upper", "lip_lower"]) {
|
|
3397
3414
|
const box = boxOfLayer(byRole.get(role));
|
|
3398
3415
|
const { mesh, xs } = mouthMesh(box, rig.knots);
|
|
3399
3416
|
const warp = mouthFoldWarp(role, mesh, xs, box, rig.opening);
|
|
3400
3417
|
const offsets = warp.keyforms[0].offsets;
|
|
3401
|
-
|
|
3418
|
+
fields2.set(role, {
|
|
3402
3419
|
box,
|
|
3403
3420
|
xs,
|
|
3404
3421
|
rows: offsets.length / 2 / xs.length - 1,
|
|
@@ -3407,7 +3424,7 @@ function mouthRestShift(byRole) {
|
|
|
3407
3424
|
}
|
|
3408
3425
|
const { canvasW, canvasH } = byRole.get("lip_upper");
|
|
3409
3426
|
return (role, col, row) => {
|
|
3410
|
-
const { box, xs, rows, dy } =
|
|
3427
|
+
const { box, xs, rows, dy } = fields2.get(role);
|
|
3411
3428
|
const x = col + 0.5 - canvasW / 2;
|
|
3412
3429
|
const y = canvasH / 2 - (row + 0.5);
|
|
3413
3430
|
if (x < box.x0 || x > box.x1 || y < box.y0 || y > box.y1) return void 0;
|
|
@@ -3498,36 +3515,36 @@ function mouthAnchor(byRole) {
|
|
|
3498
3515
|
// src/auto-rig/generate.ts
|
|
3499
3516
|
import {
|
|
3500
3517
|
IKI_FORMAT_VERSION as IKI_FORMAT_VERSION2,
|
|
3501
|
-
StandardParameter as
|
|
3518
|
+
StandardParameter as P10,
|
|
3502
3519
|
parseIkiModel as parseIkiModel3
|
|
3503
3520
|
} from "@ikijs/format";
|
|
3504
3521
|
|
|
3505
3522
|
// src/auto-rig/animations.ts
|
|
3506
3523
|
import {
|
|
3507
|
-
StandardParameter as
|
|
3524
|
+
StandardParameter as P7
|
|
3508
3525
|
} from "@ikijs/format";
|
|
3509
3526
|
function browsY(y) {
|
|
3510
3527
|
return [
|
|
3511
|
-
{ parameter:
|
|
3512
|
-
{ parameter:
|
|
3528
|
+
{ parameter: P7.BrowLeftY, value: y, blend: "add" },
|
|
3529
|
+
{ parameter: P7.BrowRightY, value: y, blend: "add" }
|
|
3513
3530
|
];
|
|
3514
3531
|
}
|
|
3515
3532
|
function browsTilt(innerDown) {
|
|
3516
3533
|
return [
|
|
3517
|
-
{ parameter:
|
|
3518
|
-
{ parameter:
|
|
3534
|
+
{ parameter: P7.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
3535
|
+
{ parameter: P7.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
3519
3536
|
];
|
|
3520
3537
|
}
|
|
3521
3538
|
function cheek(value) {
|
|
3522
|
-
return { parameter:
|
|
3539
|
+
return { parameter: P7.Cheek, value, blend: "add" };
|
|
3523
3540
|
}
|
|
3524
|
-
var BROWS = [
|
|
3541
|
+
var BROWS = [P7.BrowLeftY, P7.BrowRightY, P7.BrowLeftAngle, P7.BrowRightAngle];
|
|
3525
3542
|
var EXPRESSIONS = [
|
|
3526
3543
|
{
|
|
3527
3544
|
id: "smile",
|
|
3528
3545
|
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
3529
3546
|
parameters: [
|
|
3530
|
-
{ parameter:
|
|
3547
|
+
{ parameter: P7.MouthForm, value: 1, blend: "add" },
|
|
3531
3548
|
...browsY(0.25),
|
|
3532
3549
|
cheek(0.4)
|
|
3533
3550
|
]
|
|
@@ -3536,10 +3553,10 @@ var EXPRESSIONS = [
|
|
|
3536
3553
|
id: "laugh",
|
|
3537
3554
|
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
3538
3555
|
parameters: [
|
|
3539
|
-
{ parameter:
|
|
3540
|
-
{ parameter:
|
|
3541
|
-
{ parameter:
|
|
3542
|
-
{ parameter:
|
|
3556
|
+
{ parameter: P7.EyeOpenLeft, value: 0, blend: "multiply" },
|
|
3557
|
+
{ parameter: P7.EyeOpenRight, value: 0, blend: "multiply" },
|
|
3558
|
+
{ parameter: P7.MouthForm, value: 1, blend: "add" },
|
|
3559
|
+
{ parameter: P7.MouthOpen, value: 0.8, blend: "overwrite" },
|
|
3543
3560
|
...browsY(0.4),
|
|
3544
3561
|
cheek(0.65)
|
|
3545
3562
|
]
|
|
@@ -3551,7 +3568,7 @@ var EXPRESSIONS = [
|
|
|
3551
3568
|
parameters: [
|
|
3552
3569
|
...browsY(-0.5),
|
|
3553
3570
|
...browsTilt(0.8),
|
|
3554
|
-
{ parameter:
|
|
3571
|
+
{ parameter: P7.MouthForm, value: -0.9, blend: "add" }
|
|
3555
3572
|
]
|
|
3556
3573
|
},
|
|
3557
3574
|
{
|
|
@@ -3561,7 +3578,7 @@ var EXPRESSIONS = [
|
|
|
3561
3578
|
parameters: [
|
|
3562
3579
|
...browsY(0.35),
|
|
3563
3580
|
...browsTilt(-0.8),
|
|
3564
|
-
{ parameter:
|
|
3581
|
+
{ parameter: P7.MouthForm, value: -0.8, blend: "add" }
|
|
3565
3582
|
]
|
|
3566
3583
|
},
|
|
3567
3584
|
{
|
|
@@ -3569,18 +3586,18 @@ var EXPRESSIONS = [
|
|
|
3569
3586
|
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
3570
3587
|
parameters: [
|
|
3571
3588
|
...browsY(0.8),
|
|
3572
|
-
{ parameter:
|
|
3589
|
+
{ parameter: P7.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
3573
3590
|
]
|
|
3574
3591
|
},
|
|
3575
3592
|
{
|
|
3576
3593
|
id: "shy",
|
|
3577
3594
|
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
3578
|
-
needs: [
|
|
3595
|
+
needs: [P7.Cheek],
|
|
3579
3596
|
parameters: [
|
|
3580
3597
|
cheek(1),
|
|
3581
3598
|
// Gaze down.
|
|
3582
|
-
{ parameter:
|
|
3583
|
-
{ parameter:
|
|
3599
|
+
{ parameter: P7.EyeballY, value: -0.65, blend: "add" },
|
|
3600
|
+
{ parameter: P7.MouthForm, value: 0.4, blend: "add" },
|
|
3584
3601
|
...browsTilt(-0.25)
|
|
3585
3602
|
]
|
|
3586
3603
|
}
|
|
@@ -3600,7 +3617,7 @@ var HEAD_MOTIONS = {
|
|
|
3600
3617
|
duration: 1,
|
|
3601
3618
|
curves: [
|
|
3602
3619
|
{
|
|
3603
|
-
parameter:
|
|
3620
|
+
parameter: P7.AngleY,
|
|
3604
3621
|
keys: [
|
|
3605
3622
|
[0, 0],
|
|
3606
3623
|
[0.3, -18],
|
|
@@ -3621,7 +3638,7 @@ var HEAD_MOTIONS = {
|
|
|
3621
3638
|
fadeIn: 0.15,
|
|
3622
3639
|
curves: [
|
|
3623
3640
|
{
|
|
3624
|
-
parameter:
|
|
3641
|
+
parameter: P7.AngleX,
|
|
3625
3642
|
keys: [
|
|
3626
3643
|
[0, 0],
|
|
3627
3644
|
[0.2, -18],
|
|
@@ -3640,7 +3657,7 @@ var HEAD_MOTIONS = {
|
|
|
3640
3657
|
duration: 1.4,
|
|
3641
3658
|
curves: [
|
|
3642
3659
|
{
|
|
3643
|
-
parameter:
|
|
3660
|
+
parameter: P7.AngleZ,
|
|
3644
3661
|
keys: [
|
|
3645
3662
|
[0, 0],
|
|
3646
3663
|
[0.35, 20],
|
|
@@ -3661,7 +3678,7 @@ var WAVE = {
|
|
|
3661
3678
|
fadeOut: 0.15,
|
|
3662
3679
|
curves: [
|
|
3663
3680
|
{
|
|
3664
|
-
parameter:
|
|
3681
|
+
parameter: P7.ArmPoseRight,
|
|
3665
3682
|
interpolation: "linear",
|
|
3666
3683
|
keys: [
|
|
3667
3684
|
[0, 0],
|
|
@@ -3671,7 +3688,7 @@ var WAVE = {
|
|
|
3671
3688
|
]
|
|
3672
3689
|
},
|
|
3673
3690
|
{
|
|
3674
|
-
parameter:
|
|
3691
|
+
parameter: P7.ArmRight,
|
|
3675
3692
|
keys: [
|
|
3676
3693
|
[0, 0],
|
|
3677
3694
|
[0.4, 10],
|
|
@@ -3680,7 +3697,7 @@ var WAVE = {
|
|
|
3680
3697
|
]
|
|
3681
3698
|
},
|
|
3682
3699
|
{
|
|
3683
|
-
parameter:
|
|
3700
|
+
parameter: P7.ArmPoseAngleRight,
|
|
3684
3701
|
keys: [
|
|
3685
3702
|
[0, 0],
|
|
3686
3703
|
[0.5, 0],
|
|
@@ -3696,7 +3713,7 @@ var WAVE = {
|
|
|
3696
3713
|
]
|
|
3697
3714
|
};
|
|
3698
3715
|
function defaultMotions(declared) {
|
|
3699
|
-
const waves = [
|
|
3716
|
+
const waves = [P7.ArmRight, P7.ArmPoseRight, P7.ArmPoseAngleRight].every(
|
|
3700
3717
|
(id) => declared.has(id)
|
|
3701
3718
|
);
|
|
3702
3719
|
return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
|
|
@@ -3932,7 +3949,7 @@ function checkStrandEdges(strand, irises, hair) {
|
|
|
3932
3949
|
}
|
|
3933
3950
|
|
|
3934
3951
|
// src/auto-rig/context.ts
|
|
3935
|
-
import { StandardParameter as
|
|
3952
|
+
import { StandardParameter as P8 } from "@ikijs/format";
|
|
3936
3953
|
|
|
3937
3954
|
// src/auto-rig/head.ts
|
|
3938
3955
|
function outerEdge(runs, axisX, side) {
|
|
@@ -4041,14 +4058,14 @@ function buildHeadFrame(layers, opts) {
|
|
|
4041
4058
|
const rawRows = raw;
|
|
4042
4059
|
const earless = (chin) => {
|
|
4043
4060
|
const rowOf = (y) => Math.round(canvasH / 2 - y - faceLayer.bbox.y - 0.5);
|
|
4044
|
-
const
|
|
4061
|
+
const span2 = Math.max(1, eyeY - chin);
|
|
4045
4062
|
const found = findEars(
|
|
4046
4063
|
rawRows,
|
|
4047
4064
|
wMax,
|
|
4048
4065
|
Math.round(0.2 * rawRows.length),
|
|
4049
4066
|
Math.min(rawRows.length - 1, rowOf(chin)),
|
|
4050
|
-
Math.round(0.1 *
|
|
4051
|
-
Math.round(0.8 *
|
|
4067
|
+
Math.round(0.1 * span2),
|
|
4068
|
+
Math.round(0.8 * span2)
|
|
4052
4069
|
);
|
|
4053
4070
|
if (found === void 0) return profile;
|
|
4054
4071
|
const { top, bottom } = found;
|
|
@@ -4082,16 +4099,16 @@ function buildHeadFrame(layers, opts) {
|
|
|
4082
4099
|
}
|
|
4083
4100
|
}
|
|
4084
4101
|
if (neckRow >= 0 && n - neckRow >= 0.12 * n) {
|
|
4085
|
-
let
|
|
4102
|
+
let span2 = 0;
|
|
4086
4103
|
let waist = Infinity;
|
|
4087
4104
|
for (let r = neckRow; r < n; r++) {
|
|
4088
|
-
|
|
4105
|
+
span2 = Math.max(span2, profile[r]);
|
|
4089
4106
|
if (r < n - tail) waist = Math.min(waist, profile[r]);
|
|
4090
4107
|
}
|
|
4091
4108
|
neck = {
|
|
4092
4109
|
topY: rowY(neckRow),
|
|
4093
4110
|
half: profile[neckRow],
|
|
4094
|
-
span,
|
|
4111
|
+
span: span2,
|
|
4095
4112
|
waist: Math.min(waist, profile[neckRow])
|
|
4096
4113
|
};
|
|
4097
4114
|
}
|
|
@@ -4291,22 +4308,22 @@ function earField(m) {
|
|
|
4291
4308
|
const dy = face(x, y, 0, ay)[1];
|
|
4292
4309
|
const d = Math.abs(x - f.axisX);
|
|
4293
4310
|
const attach = ears.attachAt(y);
|
|
4294
|
-
const
|
|
4311
|
+
const span2 = Math.max(1, ears.outer - attach);
|
|
4295
4312
|
if (sideOf(m, x, s) === "far") {
|
|
4296
4313
|
const rim2 = TURN.earFar * m.face;
|
|
4297
4314
|
const root2 = earRoot(m, face, y, s);
|
|
4298
4315
|
const lead = Math.max(
|
|
4299
4316
|
0,
|
|
4300
|
-
Math.min((1 - m.earFarScale) *
|
|
4317
|
+
Math.min((1 - m.earFarScale) * span2, root2 - rim2)
|
|
4301
4318
|
);
|
|
4302
|
-
let along = rim2 + lead * (ears.outer - d) /
|
|
4319
|
+
let along = rim2 + lead * (ears.outer - d) / span2;
|
|
4303
4320
|
if (d < attach) along = Math.min(along, s * face(x, y, 30 * s, 0)[0]);
|
|
4304
4321
|
return [s * a * along, dy];
|
|
4305
4322
|
}
|
|
4306
4323
|
if (d <= attach) return [face(x, y, ax, 0)[0], dy];
|
|
4307
4324
|
const root = face(f.axisX + Math.sign(x - f.axisX) * attach, y, ax, 0)[0];
|
|
4308
4325
|
const rim = s * a * TURN.earNear * m.face;
|
|
4309
|
-
const t = Math.min(1, (d - attach) /
|
|
4326
|
+
const t = Math.min(1, (d - attach) / span2);
|
|
4310
4327
|
return [root + (rim - root) * t, dy];
|
|
4311
4328
|
};
|
|
4312
4329
|
}
|
|
@@ -5062,7 +5079,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
|
5062
5079
|
const family = roleSpec(role).family;
|
|
5063
5080
|
const part = parts.find((p) => p.id === role);
|
|
5064
5081
|
const g = grids.get(family);
|
|
5065
|
-
const warp = part?.warps?.find((w) => w.parameter ===
|
|
5082
|
+
const warp = part?.warps?.find((w) => w.parameter === P8.AngleX);
|
|
5066
5083
|
if (part === void 0 || part.mesh === void 0 || !g && !warp) {
|
|
5067
5084
|
return x + field(role)(x, y, ax, 0)[0];
|
|
5068
5085
|
}
|
|
@@ -5201,11 +5218,11 @@ function buildFaceMesh(frame, b, head, neckCells) {
|
|
|
5201
5218
|
const indices = [];
|
|
5202
5219
|
const neck = frame.neck;
|
|
5203
5220
|
if (neck !== void 0) {
|
|
5204
|
-
const
|
|
5205
|
-
const nc = cellsFor(2 *
|
|
5221
|
+
const span2 = neck.span + pad;
|
|
5222
|
+
const nc = cellsFor(2 * span2, neckCells.px, neckCells.min, neckCells.max);
|
|
5206
5223
|
const xs2 = columns(
|
|
5207
|
-
Math.max(b.x0, frame.axisX -
|
|
5208
|
-
Math.min(b.x1, frame.axisX +
|
|
5224
|
+
Math.max(b.x0, frame.axisX - span2),
|
|
5225
|
+
Math.min(b.x1, frame.axisX + span2),
|
|
5209
5226
|
nc
|
|
5210
5227
|
);
|
|
5211
5228
|
const sampleBelow = (x) => Math.max(
|
|
@@ -5341,6 +5358,11 @@ function buildFaceMesh(frame, b, head, neckCells) {
|
|
|
5341
5358
|
return { mesh: { vertices, uvs, indices }, region, headStart };
|
|
5342
5359
|
}
|
|
5343
5360
|
|
|
5361
|
+
// src/auto-rig/patches.ts
|
|
5362
|
+
import {
|
|
5363
|
+
StandardParameter as P9
|
|
5364
|
+
} from "@ikijs/format";
|
|
5365
|
+
|
|
5344
5366
|
// src/auto-rig/solve.ts
|
|
5345
5367
|
var RANGES = {
|
|
5346
5368
|
// [lo, hi, lo exclusive]
|
|
@@ -5771,6 +5793,235 @@ function buildTurn(frame, landmarks, q) {
|
|
|
5771
5793
|
};
|
|
5772
5794
|
}
|
|
5773
5795
|
|
|
5796
|
+
// src/auto-rig/patches.ts
|
|
5797
|
+
var OP_KEYS = ["target", "at", "region", "by", "falloff", "why"];
|
|
5798
|
+
var REGION_KEYS = ["role", "x", "y"];
|
|
5799
|
+
var BY_KEYS = ["dx", "dy"];
|
|
5800
|
+
var ROLLS = /* @__PURE__ */ new Set([P9.AngleZ, P9.BodyAngleZ]);
|
|
5801
|
+
var REST_IS_ART = "rest is the drawn art, which no patch changes (change it with the layout or by regenerating the layer)";
|
|
5802
|
+
function applyPatches(patches, ctx) {
|
|
5803
|
+
if (!Array.isArray(patches)) {
|
|
5804
|
+
throw new PatchError(
|
|
5805
|
+
`patches must be an array, got ${patches === null ? "null" : typeof patches}`
|
|
5806
|
+
);
|
|
5807
|
+
}
|
|
5808
|
+
const sums = /* @__PURE__ */ new Map();
|
|
5809
|
+
const reports = patches.map((op, i) => replay(op, i, ctx, sums));
|
|
5810
|
+
for (const [offsets, s] of sums) {
|
|
5811
|
+
s.terms.forEach((t, k) => {
|
|
5812
|
+
if (t.length === 0) return;
|
|
5813
|
+
const d = t.sort((a, b) => a - b).reduce((a, b) => a + b, 0);
|
|
5814
|
+
offsets[k] = roundTo(offsets[k] + d / s.scale[k % 2], s.step);
|
|
5815
|
+
});
|
|
5816
|
+
}
|
|
5817
|
+
return reports;
|
|
5818
|
+
}
|
|
5819
|
+
function replay(op, i, ctx, sums) {
|
|
5820
|
+
const name = `patches[${i}]`;
|
|
5821
|
+
const o = fields(name, op, OP_KEYS);
|
|
5822
|
+
const target = text(`${name}.target`, o.target);
|
|
5823
|
+
const at = numbers(`${name}.at`, o.at);
|
|
5824
|
+
const region = fields(`${name}.region`, o.region, REGION_KEYS);
|
|
5825
|
+
const role = text(`${name}.region.role`, region.role);
|
|
5826
|
+
const x = span(`${name}.region.x`, region.x);
|
|
5827
|
+
const y = span(`${name}.region.y`, region.y);
|
|
5828
|
+
const by = fields(`${name}.by`, o.by, BY_KEYS);
|
|
5829
|
+
const dx = finite2(`${name}.by.dx`, by.dx);
|
|
5830
|
+
const dy = finite2(`${name}.by.dy`, by.dy);
|
|
5831
|
+
const falloff = o.falloff === void 0 ? 0 : finite2(`${name}.falloff`, o.falloff, 0);
|
|
5832
|
+
if (o.why !== void 0) text(`${name}.why`, o.why);
|
|
5833
|
+
const box = ctx.boxOf(role);
|
|
5834
|
+
if (box === void 0) {
|
|
5835
|
+
const roles = [...new Set(ctx.parts.map((p) => roleOfPart(p.id)))].filter(
|
|
5836
|
+
(r) => ctx.boxOf(r) !== void 0
|
|
5837
|
+
);
|
|
5838
|
+
throw new PatchError(
|
|
5839
|
+
`${name}.region.role "${role}" is not a layer of this set (roles: ${roles.join(", ")})`
|
|
5840
|
+
);
|
|
5841
|
+
}
|
|
5842
|
+
const stop = resolveStop(name, target, at, ctx);
|
|
5843
|
+
const { points } = stop;
|
|
5844
|
+
const weights = regionWeights(points, box, x, y, falloff * ctx.faceWidth);
|
|
5845
|
+
const hit = weights.flatMap((w, k) => w > 0 ? [k] : []);
|
|
5846
|
+
if (hit.length === 0) {
|
|
5847
|
+
const fx = points.map(([px]) => (px - box.x0) / bw(box));
|
|
5848
|
+
const fy = points.map(([, py]) => (box.y1 - py) / bh(box));
|
|
5849
|
+
const range = (v) => `[${roundTo(Math.min(...v), 0.01)}, ${roundTo(Math.max(...v), 0.01)}]`;
|
|
5850
|
+
throw new PatchError(
|
|
5851
|
+
`${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`
|
|
5852
|
+
);
|
|
5853
|
+
}
|
|
5854
|
+
let sum = sums.get(stop.offsets);
|
|
5855
|
+
if (sum === void 0) {
|
|
5856
|
+
sum = {
|
|
5857
|
+
terms: stop.offsets.map(() => []),
|
|
5858
|
+
scale: stop.scale,
|
|
5859
|
+
step: stop.step
|
|
5860
|
+
};
|
|
5861
|
+
sums.set(stop.offsets, sum);
|
|
5862
|
+
}
|
|
5863
|
+
const ddx = dx * ctx.faceWidth;
|
|
5864
|
+
const ddy = -dy * ctx.faceWidth;
|
|
5865
|
+
for (const k of hit) {
|
|
5866
|
+
sum.terms[2 * k].push(ddx * weights[k]);
|
|
5867
|
+
sum.terms[2 * k + 1].push(ddy * weights[k]);
|
|
5868
|
+
}
|
|
5869
|
+
const maxW = Math.max(...hit.map((k) => weights[k]));
|
|
5870
|
+
const report = {
|
|
5871
|
+
index: i,
|
|
5872
|
+
target,
|
|
5873
|
+
at,
|
|
5874
|
+
moved: hit.length,
|
|
5875
|
+
of: points.length,
|
|
5876
|
+
maxPx: roundTo(maxW * Math.hypot(dx, dy) * ctx.faceWidth, 0.1)
|
|
5877
|
+
};
|
|
5878
|
+
if (stop.stride !== void 0) {
|
|
5879
|
+
const { stride } = stop;
|
|
5880
|
+
const cols = hit.map((k) => k % stride);
|
|
5881
|
+
const rows = hit.map((k) => Math.floor(k / stride));
|
|
5882
|
+
report.cols = [Math.min(...cols), Math.max(...cols)];
|
|
5883
|
+
report.rows = [Math.min(...rows), Math.max(...rows)];
|
|
5884
|
+
}
|
|
5885
|
+
return report;
|
|
5886
|
+
}
|
|
5887
|
+
function resolveStop(name, target, at, ctx) {
|
|
5888
|
+
const grid = ctx.grids.get(target);
|
|
5889
|
+
if (grid !== void 0) {
|
|
5890
|
+
const l = grid.lattice.points;
|
|
5891
|
+
const points = [];
|
|
5892
|
+
for (let k = 0; k < l.length; k += 2) points.push([l[k], l[k + 1]]);
|
|
5893
|
+
return {
|
|
5894
|
+
offsets: gridStop(name, at, target, grid.keyforms),
|
|
5895
|
+
points,
|
|
5896
|
+
scale: [1, 1],
|
|
5897
|
+
step: 0.1,
|
|
5898
|
+
stride: grid.lattice.cols + 1
|
|
5899
|
+
};
|
|
5900
|
+
}
|
|
5901
|
+
const part = ctx.parts.find((p) => p.id === target);
|
|
5902
|
+
const b = ctx.boxOf(target);
|
|
5903
|
+
if (part?.mesh === void 0 || part.warps === void 0 || b === void 0) {
|
|
5904
|
+
const parts = ctx.parts.filter(
|
|
5905
|
+
(p) => p.mesh !== void 0 && p.warps !== void 0 && ctx.boxOf(p.id) !== void 0
|
|
5906
|
+
);
|
|
5907
|
+
throw new PatchError(
|
|
5908
|
+
`${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`
|
|
5909
|
+
);
|
|
5910
|
+
}
|
|
5911
|
+
return {
|
|
5912
|
+
offsets: partStop(name, at, part, part.warps, ctx),
|
|
5913
|
+
points: meshPoints(part.mesh, b),
|
|
5914
|
+
scale: [bw(b), bh(b)],
|
|
5915
|
+
step: 1e-4
|
|
5916
|
+
};
|
|
5917
|
+
}
|
|
5918
|
+
function gridStop(name, at, id, keyforms) {
|
|
5919
|
+
const i = X_STOPS.indexOf(at[P9.AngleX]);
|
|
5920
|
+
const j = Y_STOPS.indexOf(at[P9.AngleY]);
|
|
5921
|
+
if (Object.keys(at).length !== 2 || i < 0 || j < 0) {
|
|
5922
|
+
throw new PatchError(
|
|
5923
|
+
`${name}.at must name ${P9.AngleX} in [${X_STOPS.join(", ")}] and ${P9.AngleY} in [${Y_STOPS.join(", ")}] for the grid ${id}, got ${JSON.stringify(at)}`
|
|
5924
|
+
);
|
|
5925
|
+
}
|
|
5926
|
+
if (X_STOPS[i] === 0 && Y_STOPS[j] === 0) {
|
|
5927
|
+
throw new PatchError(
|
|
5928
|
+
`${name}.at ${P9.AngleX}=0 ${P9.AngleY}=0 is ${id}'s rest stop: ${REST_IS_ART}; patch a stop with ${P9.AngleX} or ${P9.AngleY} at \xB130 instead`
|
|
5929
|
+
);
|
|
5930
|
+
}
|
|
5931
|
+
return keyforms[j * X_STOPS.length + i];
|
|
5932
|
+
}
|
|
5933
|
+
function partStop(name, at, part, warps, ctx) {
|
|
5934
|
+
const keys = Object.keys(at);
|
|
5935
|
+
const [param] = keys;
|
|
5936
|
+
const value = at[param];
|
|
5937
|
+
const keyform = keys.length === 1 ? warps.filter((w) => w.parameter === param).flatMap((w) => w.keyforms).find((k) => k.value === value) : void 0;
|
|
5938
|
+
if (keyform === void 0) {
|
|
5939
|
+
throw new PatchError(
|
|
5940
|
+
`${name}.at must name one parameter at one of ${part.id}'s warp stops (${listStops(warps, () => true)}), got ${JSON.stringify(at)}`
|
|
5941
|
+
);
|
|
5942
|
+
}
|
|
5943
|
+
if (ROLLS.has(param)) {
|
|
5944
|
+
throw new PatchError(
|
|
5945
|
+
`${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`
|
|
5946
|
+
);
|
|
5947
|
+
}
|
|
5948
|
+
const isRest = (p, v) => v === ctx.parameters.find((q) => q.id === p)?.default;
|
|
5949
|
+
const isShut = (p, v) => SHUT_BY.get(part.id) === p && v === 0;
|
|
5950
|
+
if (!isRest(param, value) && !isShut(param, value)) return keyform.offsets;
|
|
5951
|
+
const open = listStops(warps, (p, v) => !isRest(p, v) && !isShut(p, v));
|
|
5952
|
+
const grid = part.deformer !== void 0 && ctx.grids.has(part.deformer) ? `its feature grid ${part.deformer} at a ${P9.AngleX} \xD7 ${P9.AngleY} stop` : "";
|
|
5953
|
+
const instead = [open && `another of its stops (${open})`, grid].filter((s) => s !== "").join(" or ");
|
|
5954
|
+
const hint = instead === "" ? "" : `; patch ${instead} instead`;
|
|
5955
|
+
throw new PatchError(
|
|
5956
|
+
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}`
|
|
5957
|
+
);
|
|
5958
|
+
}
|
|
5959
|
+
function listStops(warps, keep) {
|
|
5960
|
+
const stops = /* @__PURE__ */ new Map();
|
|
5961
|
+
for (const w of warps) {
|
|
5962
|
+
for (const k of w.keyforms) {
|
|
5963
|
+
if (!keep(w.parameter, k.value)) continue;
|
|
5964
|
+
const s = stops.get(w.parameter) ?? /* @__PURE__ */ new Set();
|
|
5965
|
+
s.add(k.value);
|
|
5966
|
+
stops.set(w.parameter, s);
|
|
5967
|
+
}
|
|
5968
|
+
}
|
|
5969
|
+
return [...stops].map(([p, s]) => `${p}@${[...s].sort((a, b) => a - b).join(",")}`).join(" ");
|
|
5970
|
+
}
|
|
5971
|
+
function regionWeights(points, box, x, y, fall) {
|
|
5972
|
+
const x0 = box.x0 + x[0] * bw(box);
|
|
5973
|
+
const x1 = box.x0 + x[1] * bw(box);
|
|
5974
|
+
const top = box.y1 - y[0] * bh(box);
|
|
5975
|
+
const bottom = box.y1 - y[1] * bh(box);
|
|
5976
|
+
return points.map(([px, py]) => {
|
|
5977
|
+
const d = Math.hypot(
|
|
5978
|
+
Math.max(x0 - px, 0, px - x1),
|
|
5979
|
+
Math.max(bottom - py, 0, py - top)
|
|
5980
|
+
);
|
|
5981
|
+
return d === 0 ? 1 : d < fall ? smoothstep(fall, 0, d) : 0;
|
|
5982
|
+
});
|
|
5983
|
+
}
|
|
5984
|
+
function asPatch(check) {
|
|
5985
|
+
try {
|
|
5986
|
+
return check();
|
|
5987
|
+
} catch (e) {
|
|
5988
|
+
throw e instanceof TurnTargetError ? new PatchError(e.message) : e;
|
|
5989
|
+
}
|
|
5990
|
+
}
|
|
5991
|
+
function fields(name, v, known) {
|
|
5992
|
+
if (v === void 0) throw new PatchError(`${name} is missing`);
|
|
5993
|
+
asPatch(() => checkedKeys(name, v, known));
|
|
5994
|
+
return v;
|
|
5995
|
+
}
|
|
5996
|
+
function numbers(name, v) {
|
|
5997
|
+
const o = fields(name, v, Object.keys(v ?? {}));
|
|
5998
|
+
return Object.fromEntries(
|
|
5999
|
+
Object.entries(o).map(([k, n]) => [k, finite2(`${name}.${k}`, n)])
|
|
6000
|
+
);
|
|
6001
|
+
}
|
|
6002
|
+
var finite2 = (name, v, lo = -Infinity) => asPatch(() => checkedNumber(name, v, lo, Infinity));
|
|
6003
|
+
function text(name, v) {
|
|
6004
|
+
if (typeof v !== "string") {
|
|
6005
|
+
throw new PatchError(`${name} must be a string, got ${String(v)}`);
|
|
6006
|
+
}
|
|
6007
|
+
return v;
|
|
6008
|
+
}
|
|
6009
|
+
function span(name, v) {
|
|
6010
|
+
if (!Array.isArray(v) || v.length !== 2) {
|
|
6011
|
+
throw new PatchError(
|
|
6012
|
+
`${name} must be [from, to], got ${JSON.stringify(v)}`
|
|
6013
|
+
);
|
|
6014
|
+
}
|
|
6015
|
+
const lo = finite2(`${name}[0]`, v[0]);
|
|
6016
|
+
const hi = finite2(`${name}[1]`, v[1]);
|
|
6017
|
+
if (lo > hi) {
|
|
6018
|
+
throw new PatchError(
|
|
6019
|
+
`${name} [${lo}, ${hi}] runs backwards: ${name}[0] must not exceed ${name}[1]`
|
|
6020
|
+
);
|
|
6021
|
+
}
|
|
6022
|
+
return [lo, hi];
|
|
6023
|
+
}
|
|
6024
|
+
|
|
5774
6025
|
// src/auto-rig/generate.ts
|
|
5775
6026
|
var MESH_CELLS = {
|
|
5776
6027
|
face: { px: 28, min: 4, max: 24 },
|
|
@@ -5948,6 +6199,15 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5948
6199
|
];
|
|
5949
6200
|
}
|
|
5950
6201
|
const parameters = declareParameters(new Set(byRole.keys()));
|
|
6202
|
+
const reports = options.patches === void 0 ? void 0 : applyPatches(options.patches, {
|
|
6203
|
+
grids: new Map(
|
|
6204
|
+
[...grids].map(([family, g]) => [WARP_ID[family], g])
|
|
6205
|
+
),
|
|
6206
|
+
parts,
|
|
6207
|
+
boxOf: (role) => has(role) ? box(role) : void 0,
|
|
6208
|
+
faceWidth: byRole.get("face").cropW,
|
|
6209
|
+
parameters
|
|
6210
|
+
});
|
|
5951
6211
|
const model = {
|
|
5952
6212
|
version: IKI_FORMAT_VERSION2,
|
|
5953
6213
|
name: "auto-rigged",
|
|
@@ -5960,6 +6220,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5960
6220
|
motions: defaultMotions(new Set(parameters.map((p) => p.id)))
|
|
5961
6221
|
};
|
|
5962
6222
|
const valid = parseIkiModel3(model);
|
|
6223
|
+
if (reports !== void 0) options.onPatchesApplied?.(reports);
|
|
5963
6224
|
if (hasNose && options.onTurnSolved) {
|
|
5964
6225
|
options.onTurnSolved(
|
|
5965
6226
|
turnReport(
|
|
@@ -5974,14 +6235,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5974
6235
|
function turnWarps(mesh, b, fieldOf) {
|
|
5975
6236
|
return [
|
|
5976
6237
|
localWarp(
|
|
5977
|
-
|
|
6238
|
+
P10.AngleX,
|
|
5978
6239
|
mesh,
|
|
5979
6240
|
b,
|
|
5980
6241
|
X_STOPS,
|
|
5981
6242
|
([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
|
|
5982
6243
|
),
|
|
5983
6244
|
localWarp(
|
|
5984
|
-
|
|
6245
|
+
P10.AngleY,
|
|
5985
6246
|
mesh,
|
|
5986
6247
|
b,
|
|
5987
6248
|
Y_STOPS,
|
|
@@ -6032,7 +6293,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6032
6293
|
}
|
|
6033
6294
|
if (mesh !== void 0) part.mesh = mesh;
|
|
6034
6295
|
const side = spec.role.endsWith("_L") ? "L" : "R";
|
|
6035
|
-
const eyeOpen = side === "L" ?
|
|
6296
|
+
const eyeOpen = side === "L" ? P10.EyeOpenLeft : P10.EyeOpenRight;
|
|
6036
6297
|
const hh = frame.hh;
|
|
6037
6298
|
switch (spec.role) {
|
|
6038
6299
|
case "face":
|
|
@@ -6047,7 +6308,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6047
6308
|
headOwnRoll(byRole.has("body"))
|
|
6048
6309
|
),
|
|
6049
6310
|
localWarp(
|
|
6050
|
-
|
|
6311
|
+
P10.Breath,
|
|
6051
6312
|
mesh,
|
|
6052
6313
|
b,
|
|
6053
6314
|
[0, 1],
|
|
@@ -6094,8 +6355,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6094
6355
|
if (byRole.has("mouth_open")) {
|
|
6095
6356
|
warps.push(mouthWiden(mesh, b));
|
|
6096
6357
|
bindings.push(
|
|
6097
|
-
{ parameter:
|
|
6098
|
-
{ parameter:
|
|
6358
|
+
{ parameter: P10.MouthOpen, channel: "opacity", from: 1, to: 0 },
|
|
6359
|
+
{ parameter: P10.MouthOpen, channel: "opacity", from: 1, to: 0 }
|
|
6099
6360
|
);
|
|
6100
6361
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
6101
6362
|
} else {
|
|
@@ -6105,19 +6366,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6105
6366
|
);
|
|
6106
6367
|
bindings.push(
|
|
6107
6368
|
{
|
|
6108
|
-
parameter:
|
|
6369
|
+
parameter: P10.MouthOpen,
|
|
6109
6370
|
channel: "scaleY",
|
|
6110
6371
|
from: 0,
|
|
6111
6372
|
to: roundTo(grow, 0.01)
|
|
6112
6373
|
},
|
|
6113
6374
|
{
|
|
6114
|
-
parameter:
|
|
6375
|
+
parameter: P10.MouthOpen,
|
|
6115
6376
|
channel: "scaleX",
|
|
6116
6377
|
from: 0,
|
|
6117
6378
|
to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
|
|
6118
6379
|
},
|
|
6119
6380
|
{
|
|
6120
|
-
parameter:
|
|
6381
|
+
parameter: P10.MouthOpen,
|
|
6121
6382
|
channel: "translateY",
|
|
6122
6383
|
from: 0,
|
|
6123
6384
|
to: -roundTo(grow * bh(b) / 2, 0.01)
|
|
@@ -6129,7 +6390,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6129
6390
|
case "mouth_open":
|
|
6130
6391
|
warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
|
|
6131
6392
|
bindings.push({
|
|
6132
|
-
parameter:
|
|
6393
|
+
parameter: P10.MouthOpen,
|
|
6133
6394
|
channel: "opacity",
|
|
6134
6395
|
from: 0,
|
|
6135
6396
|
to: 1
|
|
@@ -6151,7 +6412,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6151
6412
|
case "blush_L":
|
|
6152
6413
|
case "blush_R":
|
|
6153
6414
|
bindings.push({
|
|
6154
|
-
parameter:
|
|
6415
|
+
parameter: P10.Cheek,
|
|
6155
6416
|
channel: "opacity",
|
|
6156
6417
|
from: AMPLITUDE.blushRest,
|
|
6157
6418
|
to: 1
|
|
@@ -6162,8 +6423,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6162
6423
|
if (byRole.has("hair_front")) {
|
|
6163
6424
|
const amp = style.sway * AMPLITUDE.sway * hh;
|
|
6164
6425
|
warps.push(
|
|
6165
|
-
hairSway(
|
|
6166
|
-
hairSway(
|
|
6426
|
+
hairSway(P10.HairSwayX, mesh, b, amp),
|
|
6427
|
+
hairSway(P10.HairSwayZ, mesh, b, amp)
|
|
6167
6428
|
);
|
|
6168
6429
|
}
|
|
6169
6430
|
warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
|
|
@@ -6229,9 +6490,9 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
|
6229
6490
|
pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
|
|
6230
6491
|
bindings: [
|
|
6231
6492
|
// AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
|
|
6232
|
-
{ parameter:
|
|
6493
|
+
{ parameter: P10.AngleZ, channel: "rotate", from: roll, to: -roll },
|
|
6233
6494
|
{
|
|
6234
|
-
parameter:
|
|
6495
|
+
parameter: P10.Breath,
|
|
6235
6496
|
channel: "translateY",
|
|
6236
6497
|
from: 0,
|
|
6237
6498
|
to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
|
|
@@ -6249,8 +6510,8 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
|
6249
6510
|
points: g.lattice.points
|
|
6250
6511
|
},
|
|
6251
6512
|
warp2d: {
|
|
6252
|
-
parameter:
|
|
6253
|
-
parameterY:
|
|
6513
|
+
parameter: P10.AngleX,
|
|
6514
|
+
parameterY: P10.AngleY,
|
|
6254
6515
|
valuesX: X_STOPS,
|
|
6255
6516
|
valuesY: Y_STOPS,
|
|
6256
6517
|
keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
|
|
@@ -6263,16 +6524,16 @@ function hairPhysics() {
|
|
|
6263
6524
|
return [
|
|
6264
6525
|
{
|
|
6265
6526
|
id: "hairSway",
|
|
6266
|
-
input: { parameter:
|
|
6267
|
-
output: { parameter:
|
|
6527
|
+
input: { parameter: P10.AngleX, weight: 1 },
|
|
6528
|
+
output: { parameter: P10.HairSwayX, scale: 5 },
|
|
6268
6529
|
mass: 1,
|
|
6269
6530
|
stiffness: 30,
|
|
6270
6531
|
damping: 3
|
|
6271
6532
|
},
|
|
6272
6533
|
{
|
|
6273
6534
|
id: "hairTilt",
|
|
6274
|
-
input: { parameter:
|
|
6275
|
-
output: { parameter:
|
|
6535
|
+
input: { parameter: P10.AngleZ, weight: 1 },
|
|
6536
|
+
output: { parameter: P10.HairSwayZ, scale: 8 },
|
|
6276
6537
|
mass: 1,
|
|
6277
6538
|
stiffness: 25,
|
|
6278
6539
|
damping: 2.5
|
|
@@ -6282,13 +6543,13 @@ function hairPhysics() {
|
|
|
6282
6543
|
function declareParameters(roles) {
|
|
6283
6544
|
const out = [];
|
|
6284
6545
|
const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
|
|
6285
|
-
add(
|
|
6286
|
-
add(
|
|
6287
|
-
add(
|
|
6546
|
+
add(P10.AngleX, "Head Angle", -30, 30, 0);
|
|
6547
|
+
add(P10.AngleY, "Head Angle Y", -30, 30, 0);
|
|
6548
|
+
add(P10.AngleZ, "Head Angle Z", -30, 30, 0);
|
|
6288
6549
|
if (roles.has("body")) {
|
|
6289
|
-
add(
|
|
6290
|
-
add(
|
|
6291
|
-
add(
|
|
6550
|
+
add(P10.BodyAngleX, "Body Angle X", -10, 10, 0);
|
|
6551
|
+
add(P10.BodyAngleY, "Body Angle Y", -10, 10, 0);
|
|
6552
|
+
add(P10.BodyAngleZ, "Body Angle Z", -10, 10, 0);
|
|
6292
6553
|
}
|
|
6293
6554
|
const addPose = (side, pose, angle) => {
|
|
6294
6555
|
add(pose, `Arm Pose ${side}`, 0, 1, 0);
|
|
@@ -6297,40 +6558,40 @@ function declareParameters(roles) {
|
|
|
6297
6558
|
const [armMin, armMax] = ARM_RANGE;
|
|
6298
6559
|
const [elbowMin, elbowMax] = ELBOW_RANGE;
|
|
6299
6560
|
if (roles.has("arm_L")) {
|
|
6300
|
-
add(
|
|
6301
|
-
add(
|
|
6561
|
+
add(P10.ArmLeft, "Arm L", armMin, armMax, 0);
|
|
6562
|
+
add(P10.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
|
|
6302
6563
|
if (roles.has("forearm_pose_L"))
|
|
6303
|
-
addPose("L",
|
|
6564
|
+
addPose("L", P10.ArmPoseLeft, P10.ArmPoseAngleLeft);
|
|
6304
6565
|
}
|
|
6305
6566
|
if (roles.has("arm_R")) {
|
|
6306
|
-
add(
|
|
6307
|
-
add(
|
|
6567
|
+
add(P10.ArmRight, "Arm R", armMin, armMax, 0);
|
|
6568
|
+
add(P10.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
|
|
6308
6569
|
if (roles.has("forearm_pose_R"))
|
|
6309
|
-
addPose("R",
|
|
6570
|
+
addPose("R", P10.ArmPoseRight, P10.ArmPoseAngleRight);
|
|
6310
6571
|
}
|
|
6311
|
-
add(
|
|
6312
|
-
add(
|
|
6572
|
+
add(P10.EyeOpenLeft, "Eye L", 0, 1, 1);
|
|
6573
|
+
add(P10.EyeOpenRight, "Eye R", 0, 1, 1);
|
|
6313
6574
|
if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
|
|
6314
|
-
add(
|
|
6315
|
-
add(
|
|
6575
|
+
add(P10.EyeballX, "Gaze X", -1, 1, 0);
|
|
6576
|
+
add(P10.EyeballY, "Gaze Y", -1, 1, 0);
|
|
6316
6577
|
}
|
|
6317
|
-
add(
|
|
6318
|
-
add(
|
|
6578
|
+
add(P10.MouthOpen, "Mouth Open", 0, 1, 0);
|
|
6579
|
+
add(P10.MouthForm, "Mouth Form", -1, 1, 0);
|
|
6319
6580
|
if (roles.has("brow_L")) {
|
|
6320
|
-
add(
|
|
6321
|
-
add(
|
|
6581
|
+
add(P10.BrowLeftY, "Brow L Y", -1, 1, 0);
|
|
6582
|
+
add(P10.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
|
|
6322
6583
|
}
|
|
6323
6584
|
if (roles.has("brow_R")) {
|
|
6324
|
-
add(
|
|
6325
|
-
add(
|
|
6585
|
+
add(P10.BrowRightY, "Brow R Y", -1, 1, 0);
|
|
6586
|
+
add(P10.BrowRightAngle, "Brow R Angle", -1, 1, 0);
|
|
6326
6587
|
}
|
|
6327
6588
|
if (roles.has("blush_L") || roles.has("blush_R")) {
|
|
6328
|
-
add(
|
|
6589
|
+
add(P10.Cheek, "Cheek", 0, 1, 0);
|
|
6329
6590
|
}
|
|
6330
|
-
add(
|
|
6591
|
+
add(P10.Breath, "Breath", 0, 1, 0);
|
|
6331
6592
|
if (roles.has("hair_front")) {
|
|
6332
|
-
add(
|
|
6333
|
-
add(
|
|
6593
|
+
add(P10.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
6594
|
+
add(P10.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
6334
6595
|
}
|
|
6335
6596
|
return out;
|
|
6336
6597
|
}
|
|
@@ -6351,6 +6612,8 @@ export {
|
|
|
6351
6612
|
LIP_INSIDE_ROLES,
|
|
6352
6613
|
LIP_ROLES,
|
|
6353
6614
|
LayerGeometryError,
|
|
6615
|
+
PatchError,
|
|
6616
|
+
SHUT_BY,
|
|
6354
6617
|
SPECK_CORE_FRACTION,
|
|
6355
6618
|
SetDeformerBindings,
|
|
6356
6619
|
SetDeformerParent,
|