@ikijs/editor 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -14
- package/dist/index.d.mts +74 -17
- package/dist/index.d.ts +74 -17
- package/dist/index.js +574 -157
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +575 -155
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -62,6 +62,7 @@ __export(index_exports, {
|
|
|
62
62
|
createLayerSetMeasurer: () => createLayerSetMeasurer,
|
|
63
63
|
denseCoreOf: () => denseCoreOf,
|
|
64
64
|
detectAlphaBbox: () => detectAlphaBbox,
|
|
65
|
+
forearmPoseGeometry: () => forearmPoseGeometry,
|
|
65
66
|
foregroundSpan: () => foregroundSpan,
|
|
66
67
|
generateIkiFromLayerSet: () => generateIkiFromLayerSet,
|
|
67
68
|
headHalfOf: () => headHalfOf,
|
|
@@ -1641,7 +1642,16 @@ var ROW_RUN_ROLES = /* @__PURE__ */ new Set([
|
|
|
1641
1642
|
"hair_back",
|
|
1642
1643
|
"body",
|
|
1643
1644
|
"arm_L",
|
|
1644
|
-
"arm_R"
|
|
1645
|
+
"arm_R",
|
|
1646
|
+
"forearm_pose_L",
|
|
1647
|
+
"forearm_pose_R",
|
|
1648
|
+
"mouth_inner",
|
|
1649
|
+
"lip_lower",
|
|
1650
|
+
"lip_upper"
|
|
1651
|
+
]);
|
|
1652
|
+
var REST_HIDDEN_ROLES = /* @__PURE__ */ new Set([
|
|
1653
|
+
"forearm_pose_L",
|
|
1654
|
+
"forearm_pose_R"
|
|
1645
1655
|
]);
|
|
1646
1656
|
function createLayerSetMeasurer(canvas) {
|
|
1647
1657
|
for (const field of ["width", "height"]) {
|
|
@@ -1672,6 +1682,7 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1672
1682
|
if (bbox === null) return null;
|
|
1673
1683
|
const rowLeft = new Int32Array(canvasH).fill(canvasW);
|
|
1674
1684
|
const rowRight = new Int32Array(canvasH).fill(-1);
|
|
1685
|
+
const hidden = REST_HIDDEN_ROLES.has(role);
|
|
1675
1686
|
const rowRuns = ROW_RUN_ROLES.has(role) ? [] : void 0;
|
|
1676
1687
|
for (let y = 0; y < canvasH; y++) {
|
|
1677
1688
|
let runs;
|
|
@@ -1687,14 +1698,14 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1687
1698
|
runs.push(x);
|
|
1688
1699
|
inRun = on;
|
|
1689
1700
|
}
|
|
1690
|
-
if (!on) continue;
|
|
1701
|
+
if (!on || hidden) continue;
|
|
1691
1702
|
opaque[p] = 1;
|
|
1692
1703
|
if (x < rowLeft[y]) rowLeft[y] = x;
|
|
1693
1704
|
if (x > rowRight[y]) rowRight[y] = x;
|
|
1694
1705
|
}
|
|
1695
1706
|
if (runs !== void 0 && inRun) runs.push(canvasW);
|
|
1696
1707
|
}
|
|
1697
|
-
rowSpansByRole.push({ role, rowLeft, rowRight });
|
|
1708
|
+
if (!hidden) rowSpansByRole.push({ role, rowLeft, rowRight });
|
|
1698
1709
|
const layer = {
|
|
1699
1710
|
role,
|
|
1700
1711
|
fileName,
|
|
@@ -2249,6 +2260,29 @@ function gridMesh(cols, rows) {
|
|
|
2249
2260
|
}
|
|
2250
2261
|
return { vertices, uvs, indices };
|
|
2251
2262
|
}
|
|
2263
|
+
function columnMesh(b, grid) {
|
|
2264
|
+
const { xs } = grid;
|
|
2265
|
+
const rows = grid.ys.length - 1;
|
|
2266
|
+
const vertices = [];
|
|
2267
|
+
const uvs = [];
|
|
2268
|
+
for (let r = 0; r <= rows; r++) {
|
|
2269
|
+
for (const x of xs) {
|
|
2270
|
+
const u = (x - b.x0) / bw(b);
|
|
2271
|
+
vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - r / rows, 1e-5));
|
|
2272
|
+
uvs.push(roundTo(u, 1e-5), roundTo(r / rows, 1e-5));
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
const n = xs.length;
|
|
2276
|
+
const indices = [];
|
|
2277
|
+
for (let r = 0; r < rows; r++) {
|
|
2278
|
+
for (let c = 0; c + 1 < n; c++) {
|
|
2279
|
+
const tl = r * n + c;
|
|
2280
|
+
const bl = tl + n;
|
|
2281
|
+
indices.push(bl, bl + 1, tl, tl, bl + 1, tl + 1);
|
|
2282
|
+
}
|
|
2283
|
+
}
|
|
2284
|
+
return { vertices, uvs, indices };
|
|
2285
|
+
}
|
|
2252
2286
|
function meshPoints(mesh, box) {
|
|
2253
2287
|
const out = [];
|
|
2254
2288
|
for (let i = 0; i < mesh.vertices.length; i += 2) {
|
|
@@ -2372,10 +2406,13 @@ function browBindings(side, reach) {
|
|
|
2372
2406
|
];
|
|
2373
2407
|
}
|
|
2374
2408
|
var BROW_TILT = 15;
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2409
|
+
var mouthFrameOf = (box) => ({
|
|
2410
|
+
c: cx(box),
|
|
2411
|
+
half: bw(box) / 2,
|
|
2412
|
+
amp: 0.3 * bh(box)
|
|
2413
|
+
});
|
|
2414
|
+
function mouthForm(mesh, box, frame = mouthFrameOf(box)) {
|
|
2415
|
+
const { c, half, amp: a } = frame;
|
|
2379
2416
|
return localWarp(import_format3.StandardParameter.MouthForm, mesh, box, [-1, 0, 1], ([x], v) => {
|
|
2380
2417
|
if (v === 0) return [0, 0];
|
|
2381
2418
|
const u = (x - c) / half;
|
|
@@ -2393,8 +2430,8 @@ function mouthOpenGrow(mesh, box) {
|
|
|
2393
2430
|
([x, y], v) => v === 1 ? [(AMPLITUDE.mouthOpenWidth - 1) * (x - c), 0] : [0, box.y1 + (y - box.y1) * 0.2 - y]
|
|
2394
2431
|
);
|
|
2395
2432
|
}
|
|
2396
|
-
function mouthWiden(mesh, box) {
|
|
2397
|
-
const c =
|
|
2433
|
+
function mouthWiden(mesh, box, frame = mouthFrameOf(box)) {
|
|
2434
|
+
const c = frame.c;
|
|
2398
2435
|
return localWarp(import_format3.StandardParameter.MouthOpen, mesh, box, [0, 1], ([x], v) => [
|
|
2399
2436
|
v * (AMPLITUDE.mouthOpenWidth - 1) * (x - c),
|
|
2400
2437
|
0
|
|
@@ -2777,40 +2814,56 @@ var ARM_IDS = {
|
|
|
2777
2814
|
forearmDeformer: "forearmDeformer_R"
|
|
2778
2815
|
}
|
|
2779
2816
|
};
|
|
2780
|
-
function
|
|
2817
|
+
function endPivot(layer, end) {
|
|
2781
2818
|
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2782
|
-
const
|
|
2783
|
-
const
|
|
2819
|
+
const span = runSpan(layer);
|
|
2820
|
+
const from = (k) => end === "top" ? k : bbox.h - 1 - k;
|
|
2821
|
+
const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
|
|
2822
|
+
const firstK = end === "top" ? painted[0] ?? 0 : painted.at(-1) ?? bbox.h - 1;
|
|
2823
|
+
const lo = Math.ceil(CAP_ROWS[0] * bbox.h);
|
|
2824
|
+
const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
|
|
2825
|
+
const widths = [];
|
|
2826
|
+
for (let k = lo; k <= hi; k++) {
|
|
2827
|
+
const [s2, e2] = span(from(k));
|
|
2828
|
+
widths.push(e2 - s2);
|
|
2829
|
+
}
|
|
2830
|
+
widths.sort((p, q) => p - q);
|
|
2831
|
+
const radius = widths[widths.length >> 1] / 2;
|
|
2832
|
+
const ks = end === "top" ? firstK + radius : firstK - radius;
|
|
2833
|
+
const [s, e] = span(Math.round(ks));
|
|
2834
|
+
return {
|
|
2835
|
+
point: {
|
|
2836
|
+
x: roundTo((s + e) / 2 - canvasW / 2, 0.01),
|
|
2837
|
+
y: roundTo(canvasH / 2 - (bbox.y + ks + 0.5), 0.01)
|
|
2838
|
+
},
|
|
2839
|
+
radius,
|
|
2840
|
+
row: ks
|
|
2841
|
+
};
|
|
2842
|
+
}
|
|
2843
|
+
function runSpan(layer) {
|
|
2844
|
+
const { bbox, rowRuns } = layer;
|
|
2845
|
+
return (k) => {
|
|
2784
2846
|
const runs = rowRuns?.[k] ?? [];
|
|
2785
2847
|
if (runs.length === 0) return [bbox.x, bbox.x + bbox.w];
|
|
2786
|
-
let [
|
|
2848
|
+
let [s, e] = [runs[0], runs[1]];
|
|
2787
2849
|
for (let i = 2; i < runs.length; i += 2) {
|
|
2788
|
-
if (runs[i + 1] - runs[i] >
|
|
2850
|
+
if (runs[i + 1] - runs[i] > e - s) [s, e] = [runs[i], runs[i + 1]];
|
|
2789
2851
|
}
|
|
2790
|
-
return [
|
|
2852
|
+
return [s, e];
|
|
2791
2853
|
};
|
|
2854
|
+
}
|
|
2855
|
+
function armGeometry(layer, bodyAxisX) {
|
|
2856
|
+
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2857
|
+
const b = boxOfLayer(layer);
|
|
2858
|
+
const span = runSpan(layer);
|
|
2792
2859
|
const centreX = (k) => {
|
|
2793
2860
|
const [s2, e2] = span(k);
|
|
2794
2861
|
return (s2 + e2) / 2 - canvasW / 2;
|
|
2795
2862
|
};
|
|
2796
2863
|
const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
|
|
2797
2864
|
const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
|
|
2798
|
-
const first = painted[0] ?? 0;
|
|
2799
2865
|
const last = painted.at(-1) ?? bbox.h - 1;
|
|
2800
|
-
const
|
|
2801
|
-
const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
|
|
2802
|
-
const widths = [];
|
|
2803
|
-
for (let k = lo; k <= hi; k++) {
|
|
2804
|
-
const [s2, e2] = span(k);
|
|
2805
|
-
widths.push(e2 - s2);
|
|
2806
|
-
}
|
|
2807
|
-
widths.sort((p, q) => p - q);
|
|
2808
|
-
const ru = widths[widths.length >> 1] / 2;
|
|
2809
|
-
const ks = first + ru;
|
|
2810
|
-
const shoulder = {
|
|
2811
|
-
x: roundTo(centreX(Math.round(ks)), 0.01),
|
|
2812
|
-
y: roundTo(rowY(ks), 0.01)
|
|
2813
|
-
};
|
|
2866
|
+
const { point: shoulder, radius: ru, row: ks } = endPivot(layer, "top");
|
|
2814
2867
|
const ke = Math.round(ks + ELBOW_AT * (last - ks));
|
|
2815
2868
|
if (ke <= ks) {
|
|
2816
2869
|
throw new LayerGeometryError(
|
|
@@ -2858,7 +2911,7 @@ function meshOf(vertices, indices) {
|
|
|
2858
2911
|
);
|
|
2859
2912
|
return { vertices, uvs, indices };
|
|
2860
2913
|
}
|
|
2861
|
-
function armParts(role, layer, g, order) {
|
|
2914
|
+
function armParts(role, layer, g, order, poseSwitch) {
|
|
2862
2915
|
const b = boxOfLayer(layer);
|
|
2863
2916
|
const lx = (x) => roundTo((x - cx(b)) / bw(b), 1e-5);
|
|
2864
2917
|
const ly = (y) => roundTo((y - cy(b)) / bh(b), 1e-5);
|
|
@@ -2911,7 +2964,7 @@ function armParts(role, layer, g, order) {
|
|
|
2911
2964
|
const [p, q] = [ringInner + j, ringOuter + j];
|
|
2912
2965
|
ring.push(p, q, q + 1, p, q + 1, p + 1);
|
|
2913
2966
|
}
|
|
2914
|
-
const part = (id, deformer, z, mesh) => ({
|
|
2967
|
+
const part = (id, deformer, z, mesh, swaps = false) => ({
|
|
2915
2968
|
id,
|
|
2916
2969
|
color: [1, 1, 1, 1],
|
|
2917
2970
|
width: bw(b),
|
|
@@ -2919,50 +2972,124 @@ function armParts(role, layer, g, order) {
|
|
|
2919
2972
|
transform: { x: cx(b), y: cy(b) },
|
|
2920
2973
|
order: z,
|
|
2921
2974
|
deformer,
|
|
2922
|
-
mesh
|
|
2975
|
+
mesh,
|
|
2976
|
+
...swaps && poseSwitch !== void 0 ? {
|
|
2977
|
+
bindings: [
|
|
2978
|
+
{
|
|
2979
|
+
parameter: poseSwitch,
|
|
2980
|
+
channel: "opacity",
|
|
2981
|
+
from: 1,
|
|
2982
|
+
to: 0
|
|
2983
|
+
}
|
|
2984
|
+
]
|
|
2985
|
+
} : {}
|
|
2923
2986
|
});
|
|
2924
2987
|
const ids = ARM_IDS[role];
|
|
2925
2988
|
return [
|
|
2926
|
-
part(
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2989
|
+
part(
|
|
2990
|
+
ids.cap,
|
|
2991
|
+
ids.forearmDeformer,
|
|
2992
|
+
order,
|
|
2993
|
+
{
|
|
2994
|
+
vertices,
|
|
2995
|
+
uvs,
|
|
2996
|
+
indices: [...fan, ...ring]
|
|
2997
|
+
},
|
|
2998
|
+
true
|
|
2999
|
+
),
|
|
2931
3000
|
part(role, ids.armDeformer, order + 1, meshOf(band(0.5, seam), quad)),
|
|
2932
3001
|
part(
|
|
2933
3002
|
ids.forearm,
|
|
2934
3003
|
ids.forearmDeformer,
|
|
2935
3004
|
order + 2,
|
|
2936
|
-
meshOf(band(seam, -0.5), quad)
|
|
3005
|
+
meshOf(band(seam, -0.5), quad),
|
|
3006
|
+
true
|
|
2937
3007
|
)
|
|
2938
3008
|
];
|
|
2939
3009
|
}
|
|
3010
|
+
var rotateBinding = (parameter, range, sign) => ({
|
|
3011
|
+
parameter,
|
|
3012
|
+
channel: "rotate",
|
|
3013
|
+
from: sign * range[0],
|
|
3014
|
+
to: sign * range[1]
|
|
3015
|
+
});
|
|
2940
3016
|
function armDeformers(role, g) {
|
|
2941
3017
|
const ids = ARM_IDS[role];
|
|
2942
3018
|
const params = ARM_PARAMS[role];
|
|
2943
|
-
const rotate = (parameter, range, sign) => ({
|
|
2944
|
-
parameter,
|
|
2945
|
-
channel: "rotate",
|
|
2946
|
-
from: sign * range[0],
|
|
2947
|
-
to: sign * range[1]
|
|
2948
|
-
});
|
|
2949
3019
|
return [
|
|
2950
3020
|
{
|
|
2951
3021
|
id: ids.armDeformer,
|
|
2952
3022
|
parent: BODY_WARP_ID,
|
|
2953
3023
|
pivot: { ...g.shoulder },
|
|
2954
|
-
bindings: [
|
|
3024
|
+
bindings: [rotateBinding(params.arm, ARM_RANGE, g.side)]
|
|
2955
3025
|
},
|
|
2956
3026
|
{
|
|
2957
3027
|
id: ids.forearmDeformer,
|
|
2958
3028
|
parent: ids.armDeformer,
|
|
2959
3029
|
pivot: { ...g.elbow },
|
|
2960
|
-
bindings: [
|
|
3030
|
+
bindings: [rotateBinding(params.elbow, ELBOW_RANGE, -g.side)]
|
|
2961
3031
|
}
|
|
2962
3032
|
];
|
|
2963
3033
|
}
|
|
2964
3034
|
|
|
3035
|
+
// src/auto-rig/forearm-pose.ts
|
|
3036
|
+
var import_format6 = require("@ikijs/format");
|
|
3037
|
+
var ARM_OF_POSE = {
|
|
3038
|
+
forearm_pose_L: "arm_L",
|
|
3039
|
+
forearm_pose_R: "arm_R"
|
|
3040
|
+
};
|
|
3041
|
+
var POSE_OF_ARM = {
|
|
3042
|
+
arm_L: "forearm_pose_L",
|
|
3043
|
+
arm_R: "forearm_pose_R"
|
|
3044
|
+
};
|
|
3045
|
+
var POSE_PARAMS = {
|
|
3046
|
+
forearm_pose_L: { pose: import_format6.StandardParameter.ArmPoseLeft, angle: import_format6.StandardParameter.ArmPoseAngleLeft },
|
|
3047
|
+
forearm_pose_R: { pose: import_format6.StandardParameter.ArmPoseRight, angle: import_format6.StandardParameter.ArmPoseAngleRight }
|
|
3048
|
+
};
|
|
3049
|
+
var POSE_IDS = {
|
|
3050
|
+
forearm_pose_L: "armPoseDeformer_L",
|
|
3051
|
+
forearm_pose_R: "armPoseDeformer_R"
|
|
3052
|
+
};
|
|
3053
|
+
var POSE_ANGLE_RANGE = [-15, 15];
|
|
3054
|
+
function forearmPoseGeometry(layer) {
|
|
3055
|
+
const { point, radius } = endPivot(layer, "bottom");
|
|
3056
|
+
const first = layer.rowRuns?.findIndex((runs) => runs.length > 0) ?? -1;
|
|
3057
|
+
const k = first < 0 ? 0 : first;
|
|
3058
|
+
return {
|
|
3059
|
+
pivot: point,
|
|
3060
|
+
radius,
|
|
3061
|
+
top: roundTo(layer.canvasH / 2 - (layer.bbox.y + k + 0.5), 0.01)
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
function forearmPosePart(role, layer, order) {
|
|
3065
|
+
const b = boxOfLayer(layer);
|
|
3066
|
+
return {
|
|
3067
|
+
id: role,
|
|
3068
|
+
color: [1, 1, 1, 1],
|
|
3069
|
+
width: bw(b),
|
|
3070
|
+
height: bh(b),
|
|
3071
|
+
transform: { x: cx(b), y: cy(b) },
|
|
3072
|
+
order,
|
|
3073
|
+
deformer: POSE_IDS[role],
|
|
3074
|
+
bindings: [
|
|
3075
|
+
{ parameter: POSE_PARAMS[role].pose, channel: "opacity", from: 0, to: 1 }
|
|
3076
|
+
]
|
|
3077
|
+
};
|
|
3078
|
+
}
|
|
3079
|
+
function forearmPoseDeformer(role, arm) {
|
|
3080
|
+
return {
|
|
3081
|
+
id: POSE_IDS[role],
|
|
3082
|
+
parent: ARM_IDS[ARM_OF_POSE[role]].armDeformer,
|
|
3083
|
+
pivot: { ...arm.elbow },
|
|
3084
|
+
bindings: [
|
|
3085
|
+
rotateBinding(POSE_PARAMS[role].angle, POSE_ANGLE_RANGE, -arm.side)
|
|
3086
|
+
]
|
|
3087
|
+
};
|
|
3088
|
+
}
|
|
3089
|
+
|
|
2965
3090
|
// src/auto-rig/roles.ts
|
|
3091
|
+
var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
|
|
3092
|
+
var isLipRole = (role) => LIP_ROLES.includes(role);
|
|
2966
3093
|
var ROLE_TABLE = [
|
|
2967
3094
|
{ role: "hair_back", family: "hair_back" },
|
|
2968
3095
|
{ role: "body", family: "body" },
|
|
@@ -2972,8 +3099,14 @@ var ROLE_TABLE = [
|
|
|
2972
3099
|
{ role: "blush_L", family: "face" },
|
|
2973
3100
|
{ role: "blush_R", family: "face" },
|
|
2974
3101
|
{ role: "nose", family: "nose" },
|
|
2975
|
-
{ role: "mouth", family: "mouth"
|
|
3102
|
+
{ role: "mouth", family: "mouth" },
|
|
2976
3103
|
{ role: "mouth_open", family: "mouth" },
|
|
3104
|
+
// The lip set, back to front: the interior, the lower lip's skin, the upper
|
|
3105
|
+
// lip and its line over both. It folds open instead of crossfading
|
|
3106
|
+
// (`mouth.ts`), so it stands in for `mouth`, never beside it.
|
|
3107
|
+
{ role: "mouth_inner", family: "mouth" },
|
|
3108
|
+
{ role: "lip_lower", family: "mouth" },
|
|
3109
|
+
{ role: "lip_upper", family: "mouth" },
|
|
2977
3110
|
{ role: "eye_L", family: "eye_L", required: true },
|
|
2978
3111
|
{ role: "eye_R", family: "eye_R", required: true },
|
|
2979
3112
|
{ role: "iris_L", family: "eye_L" },
|
|
@@ -2988,7 +3121,10 @@ var ROLE_TABLE = [
|
|
|
2988
3121
|
{ role: "lash_R", family: "eye_R" },
|
|
2989
3122
|
{ role: "brow_L", family: "brow_L" },
|
|
2990
3123
|
{ role: "brow_R", family: "brow_R" },
|
|
2991
|
-
{ role: "hair_front", family: "hair_front" }
|
|
3124
|
+
{ role: "hair_front", family: "hair_front" },
|
|
3125
|
+
// Drawn above the face and the hair: a raised hand passes them.
|
|
3126
|
+
{ role: "forearm_pose_L", family: "forearm_pose" },
|
|
3127
|
+
{ role: "forearm_pose_R", family: "forearm_pose" }
|
|
2992
3128
|
];
|
|
2993
3129
|
var REQUIRED_ROLES = ROLE_TABLE.filter(
|
|
2994
3130
|
(r) => r.required
|
|
@@ -3028,6 +3164,39 @@ function checkArmsHaveBody(roles) {
|
|
|
3028
3164
|
);
|
|
3029
3165
|
}
|
|
3030
3166
|
}
|
|
3167
|
+
function checkMouth(roles) {
|
|
3168
|
+
const has = new Set(roles);
|
|
3169
|
+
const present = LIP_ROLES.filter((r) => has.has(r));
|
|
3170
|
+
if (present.length === 0) {
|
|
3171
|
+
if (!has.has("mouth")) {
|
|
3172
|
+
throw new Error(
|
|
3173
|
+
`auto-rig: missing required role mouth (or the lip set ${LIP_ROLES.join(", ")})`
|
|
3174
|
+
);
|
|
3175
|
+
}
|
|
3176
|
+
return;
|
|
3177
|
+
}
|
|
3178
|
+
if (present.length < LIP_ROLES.length) {
|
|
3179
|
+
throw new Error(
|
|
3180
|
+
`auto-rig: the lip set is partial: missing ${LIP_ROLES.filter((r) => !has.has(r)).join(", ")}`
|
|
3181
|
+
);
|
|
3182
|
+
}
|
|
3183
|
+
const mixed = ["mouth", "mouth_open"].filter((r) => has.has(r));
|
|
3184
|
+
if (mixed.length > 0) {
|
|
3185
|
+
throw new Error(
|
|
3186
|
+
`auto-rig: the lip set cannot be mixed with ${mixed.join(", ")} (the lip set replaces the mouth drawings; use one or the other)`
|
|
3187
|
+
);
|
|
3188
|
+
}
|
|
3189
|
+
}
|
|
3190
|
+
function checkPosesHaveArms(roles) {
|
|
3191
|
+
const has = new Set(roles);
|
|
3192
|
+
for (const side of ["L", "R"]) {
|
|
3193
|
+
if (has.has(`forearm_pose_${side}`) && !has.has(`arm_${side}`)) {
|
|
3194
|
+
throw new Error(
|
|
3195
|
+
`auto-rig: forearm_pose_${side} needs an arm_${side} layer (a pose forearm hangs from its arm's elbow)`
|
|
3196
|
+
);
|
|
3197
|
+
}
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3031
3200
|
function parseLayerRoles(fileNames) {
|
|
3032
3201
|
const seen = /* @__PURE__ */ new Map();
|
|
3033
3202
|
const out = [];
|
|
@@ -3053,37 +3222,39 @@ function parseLayerRoles(fileNames) {
|
|
|
3053
3222
|
`auto-rig: missing required role${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`
|
|
3054
3223
|
);
|
|
3055
3224
|
}
|
|
3225
|
+
checkMouth(seen.keys());
|
|
3056
3226
|
checkArmsHaveBody(seen.keys());
|
|
3227
|
+
checkPosesHaveArms(seen.keys());
|
|
3057
3228
|
return out;
|
|
3058
3229
|
}
|
|
3059
3230
|
|
|
3060
3231
|
// src/auto-rig/generate.ts
|
|
3061
|
-
var
|
|
3232
|
+
var import_format10 = require("@ikijs/format");
|
|
3062
3233
|
|
|
3063
3234
|
// src/auto-rig/animations.ts
|
|
3064
|
-
var
|
|
3235
|
+
var import_format7 = require("@ikijs/format");
|
|
3065
3236
|
function browsY(y) {
|
|
3066
3237
|
return [
|
|
3067
|
-
{ parameter:
|
|
3068
|
-
{ parameter:
|
|
3238
|
+
{ parameter: import_format7.StandardParameter.BrowLeftY, value: y, blend: "add" },
|
|
3239
|
+
{ parameter: import_format7.StandardParameter.BrowRightY, value: y, blend: "add" }
|
|
3069
3240
|
];
|
|
3070
3241
|
}
|
|
3071
3242
|
function browsTilt(innerDown) {
|
|
3072
3243
|
return [
|
|
3073
|
-
{ parameter:
|
|
3074
|
-
{ parameter:
|
|
3244
|
+
{ parameter: import_format7.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
3245
|
+
{ parameter: import_format7.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
3075
3246
|
];
|
|
3076
3247
|
}
|
|
3077
3248
|
function cheek(value) {
|
|
3078
|
-
return { parameter:
|
|
3249
|
+
return { parameter: import_format7.StandardParameter.Cheek, value, blend: "add" };
|
|
3079
3250
|
}
|
|
3080
|
-
var BROWS = [
|
|
3251
|
+
var BROWS = [import_format7.StandardParameter.BrowLeftY, import_format7.StandardParameter.BrowRightY, import_format7.StandardParameter.BrowLeftAngle, import_format7.StandardParameter.BrowRightAngle];
|
|
3081
3252
|
var EXPRESSIONS = [
|
|
3082
3253
|
{
|
|
3083
3254
|
id: "smile",
|
|
3084
3255
|
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
3085
3256
|
parameters: [
|
|
3086
|
-
{ parameter:
|
|
3257
|
+
{ parameter: import_format7.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3087
3258
|
...browsY(0.25),
|
|
3088
3259
|
cheek(0.4)
|
|
3089
3260
|
]
|
|
@@ -3092,10 +3263,10 @@ var EXPRESSIONS = [
|
|
|
3092
3263
|
id: "laugh",
|
|
3093
3264
|
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
3094
3265
|
parameters: [
|
|
3095
|
-
{ parameter:
|
|
3096
|
-
{ parameter:
|
|
3097
|
-
{ parameter:
|
|
3098
|
-
{ parameter:
|
|
3266
|
+
{ parameter: import_format7.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
|
|
3267
|
+
{ parameter: import_format7.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
|
|
3268
|
+
{ parameter: import_format7.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3269
|
+
{ parameter: import_format7.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
|
|
3099
3270
|
...browsY(0.4),
|
|
3100
3271
|
cheek(0.65)
|
|
3101
3272
|
]
|
|
@@ -3107,7 +3278,7 @@ var EXPRESSIONS = [
|
|
|
3107
3278
|
parameters: [
|
|
3108
3279
|
...browsY(-0.5),
|
|
3109
3280
|
...browsTilt(0.8),
|
|
3110
|
-
{ parameter:
|
|
3281
|
+
{ parameter: import_format7.StandardParameter.MouthForm, value: -0.9, blend: "add" }
|
|
3111
3282
|
]
|
|
3112
3283
|
},
|
|
3113
3284
|
{
|
|
@@ -3117,7 +3288,7 @@ var EXPRESSIONS = [
|
|
|
3117
3288
|
parameters: [
|
|
3118
3289
|
...browsY(0.35),
|
|
3119
3290
|
...browsTilt(-0.8),
|
|
3120
|
-
{ parameter:
|
|
3291
|
+
{ parameter: import_format7.StandardParameter.MouthForm, value: -0.8, blend: "add" }
|
|
3121
3292
|
]
|
|
3122
3293
|
},
|
|
3123
3294
|
{
|
|
@@ -3125,18 +3296,18 @@ var EXPRESSIONS = [
|
|
|
3125
3296
|
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
3126
3297
|
parameters: [
|
|
3127
3298
|
...browsY(0.8),
|
|
3128
|
-
{ parameter:
|
|
3299
|
+
{ parameter: import_format7.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
3129
3300
|
]
|
|
3130
3301
|
},
|
|
3131
3302
|
{
|
|
3132
3303
|
id: "shy",
|
|
3133
3304
|
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
3134
|
-
needs: [
|
|
3305
|
+
needs: [import_format7.StandardParameter.Cheek],
|
|
3135
3306
|
parameters: [
|
|
3136
3307
|
cheek(1),
|
|
3137
3308
|
// Gaze down.
|
|
3138
|
-
{ parameter:
|
|
3139
|
-
{ parameter:
|
|
3309
|
+
{ parameter: import_format7.StandardParameter.EyeballY, value: -0.65, blend: "add" },
|
|
3310
|
+
{ parameter: import_format7.StandardParameter.MouthForm, value: 0.4, blend: "add" },
|
|
3140
3311
|
...browsTilt(-0.25)
|
|
3141
3312
|
]
|
|
3142
3313
|
}
|
|
@@ -3149,14 +3320,14 @@ function defaultExpressions(declared) {
|
|
|
3149
3320
|
parameters: e.parameters.filter((t) => declared.has(t.parameter))
|
|
3150
3321
|
}));
|
|
3151
3322
|
}
|
|
3152
|
-
var
|
|
3323
|
+
var HEAD_MOTIONS = {
|
|
3153
3324
|
Nod: [
|
|
3154
3325
|
{
|
|
3155
3326
|
description: "Nods yes: agreement, acknowledgement, understanding.",
|
|
3156
3327
|
duration: 1,
|
|
3157
3328
|
curves: [
|
|
3158
3329
|
{
|
|
3159
|
-
parameter:
|
|
3330
|
+
parameter: import_format7.StandardParameter.AngleY,
|
|
3160
3331
|
keys: [
|
|
3161
3332
|
[0, 0],
|
|
3162
3333
|
[0.3, -18],
|
|
@@ -3177,7 +3348,7 @@ var DEFAULT_MOTIONS = {
|
|
|
3177
3348
|
fadeIn: 0.15,
|
|
3178
3349
|
curves: [
|
|
3179
3350
|
{
|
|
3180
|
-
parameter:
|
|
3351
|
+
parameter: import_format7.StandardParameter.AngleX,
|
|
3181
3352
|
keys: [
|
|
3182
3353
|
[0, 0],
|
|
3183
3354
|
[0.2, -18],
|
|
@@ -3196,7 +3367,7 @@ var DEFAULT_MOTIONS = {
|
|
|
3196
3367
|
duration: 1.4,
|
|
3197
3368
|
curves: [
|
|
3198
3369
|
{
|
|
3199
|
-
parameter:
|
|
3370
|
+
parameter: import_format7.StandardParameter.AngleZ,
|
|
3200
3371
|
keys: [
|
|
3201
3372
|
[0, 0],
|
|
3202
3373
|
[0.35, 20],
|
|
@@ -3208,6 +3379,55 @@ var DEFAULT_MOTIONS = {
|
|
|
3208
3379
|
}
|
|
3209
3380
|
]
|
|
3210
3381
|
};
|
|
3382
|
+
var WAVE = {
|
|
3383
|
+
Wave: [
|
|
3384
|
+
{
|
|
3385
|
+
description: "Waves hello with the right hand: greeting, goodbye, getting attention.",
|
|
3386
|
+
duration: 2.2,
|
|
3387
|
+
fadeIn: 0.15,
|
|
3388
|
+
fadeOut: 0.15,
|
|
3389
|
+
curves: [
|
|
3390
|
+
{
|
|
3391
|
+
parameter: import_format7.StandardParameter.ArmPoseRight,
|
|
3392
|
+
interpolation: "linear",
|
|
3393
|
+
keys: [
|
|
3394
|
+
[0, 0],
|
|
3395
|
+
[0.15, 1],
|
|
3396
|
+
[2.05, 1],
|
|
3397
|
+
[2.2, 0]
|
|
3398
|
+
]
|
|
3399
|
+
},
|
|
3400
|
+
{
|
|
3401
|
+
parameter: import_format7.StandardParameter.ArmRight,
|
|
3402
|
+
keys: [
|
|
3403
|
+
[0, 0],
|
|
3404
|
+
[0.4, 10],
|
|
3405
|
+
[1.8, 10],
|
|
3406
|
+
[2.2, 0]
|
|
3407
|
+
]
|
|
3408
|
+
},
|
|
3409
|
+
{
|
|
3410
|
+
parameter: import_format7.StandardParameter.ArmPoseAngleRight,
|
|
3411
|
+
keys: [
|
|
3412
|
+
[0, 0],
|
|
3413
|
+
[0.5, 0],
|
|
3414
|
+
[0.7, 12],
|
|
3415
|
+
[1, -12],
|
|
3416
|
+
[1.3, 12],
|
|
3417
|
+
[1.7, 0],
|
|
3418
|
+
[2.2, 0]
|
|
3419
|
+
]
|
|
3420
|
+
}
|
|
3421
|
+
]
|
|
3422
|
+
}
|
|
3423
|
+
]
|
|
3424
|
+
};
|
|
3425
|
+
function defaultMotions(declared) {
|
|
3426
|
+
const waves = [import_format7.StandardParameter.ArmRight, import_format7.StandardParameter.ArmPoseRight, import_format7.StandardParameter.ArmPoseAngleRight].every(
|
|
3427
|
+
(id) => declared.has(id)
|
|
3428
|
+
);
|
|
3429
|
+
return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
|
|
3430
|
+
}
|
|
3211
3431
|
|
|
3212
3432
|
// src/auto-rig/checks.ts
|
|
3213
3433
|
var finite = (v) => typeof v === "number" && Number.isFinite(v);
|
|
@@ -3312,7 +3532,16 @@ function checkLayers(layers, canvas) {
|
|
|
3312
3532
|
if (missing.length > 0) {
|
|
3313
3533
|
throw new Error(`auto-rig: missing required role(s) ${missing.join(", ")}`);
|
|
3314
3534
|
}
|
|
3535
|
+
checkMouth(seen);
|
|
3536
|
+
for (const l of layers) {
|
|
3537
|
+
if (l.rowRuns === void 0 && isLipRole(l.role)) {
|
|
3538
|
+
throw new Error(
|
|
3539
|
+
`auto-rig: layer "${l.fileName}": the lip set needs rowRuns (the fold reads the opening's rows off them; measure with createLayerSetMeasurer)`
|
|
3540
|
+
);
|
|
3541
|
+
}
|
|
3542
|
+
}
|
|
3315
3543
|
checkArmsHaveBody(seen);
|
|
3544
|
+
checkPosesHaveArms(seen);
|
|
3316
3545
|
}
|
|
3317
3546
|
function checkHeadEdges(edges, byRole) {
|
|
3318
3547
|
if (edges === void 0) return;
|
|
@@ -3430,7 +3659,180 @@ function checkStrandEdges(strand, irises, hair) {
|
|
|
3430
3659
|
}
|
|
3431
3660
|
|
|
3432
3661
|
// src/auto-rig/context.ts
|
|
3433
|
-
var
|
|
3662
|
+
var import_format9 = require("@ikijs/format");
|
|
3663
|
+
|
|
3664
|
+
// src/auto-rig/mouth.ts
|
|
3665
|
+
var import_format8 = require("@ikijs/format");
|
|
3666
|
+
var UPPER_SHARE = 0.3;
|
|
3667
|
+
var MOUTH_KNOT_PX = 4;
|
|
3668
|
+
var isLipSet = (byRole) => LIP_ROLES.every((r) => byRole.has(r));
|
|
3669
|
+
function columnRuns(layer) {
|
|
3670
|
+
const cols = /* @__PURE__ */ new Map();
|
|
3671
|
+
(layer.rowRuns ?? []).forEach((runs, k) => {
|
|
3672
|
+
for (let i = 0; i < runs.length; i += 2) {
|
|
3673
|
+
for (let c = runs[i]; c < runs[i + 1]; c++) {
|
|
3674
|
+
const list = cols.get(c) ?? [];
|
|
3675
|
+
const last = list[list.length - 1];
|
|
3676
|
+
if (last !== void 0 && last[1] === k) last[1] = k + 1;
|
|
3677
|
+
else list.push([k, k + 1]);
|
|
3678
|
+
cols.set(c, list);
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
});
|
|
3682
|
+
return cols;
|
|
3683
|
+
}
|
|
3684
|
+
var edgesOf = (layer, run) => ({
|
|
3685
|
+
top: layer.canvasH / 2 - (layer.bbox.y + run[0]),
|
|
3686
|
+
bottom: layer.canvasH / 2 - (layer.bbox.y + run[1])
|
|
3687
|
+
});
|
|
3688
|
+
function mouthOpening(byRole) {
|
|
3689
|
+
const inner = byRole.get("mouth_inner");
|
|
3690
|
+
const upper = byRole.get("lip_upper");
|
|
3691
|
+
const lower = byRole.get("lip_lower");
|
|
3692
|
+
const innerCols = columnRuns(inner);
|
|
3693
|
+
if (innerCols.size === 0) {
|
|
3694
|
+
throw new LayerGeometryError(
|
|
3695
|
+
`auto-rig: layer "${inner.fileName}": mouth_inner has no opaque pixel (alpha \u2265 128) to read the opening off`
|
|
3696
|
+
);
|
|
3697
|
+
}
|
|
3698
|
+
const upperCols = columnRuns(upper);
|
|
3699
|
+
const lowerCols = columnRuns(lower);
|
|
3700
|
+
const cols = [...innerCols.keys()];
|
|
3701
|
+
const x0 = Math.min(...cols);
|
|
3702
|
+
const x1 = Math.max(...cols);
|
|
3703
|
+
const read = (col) => {
|
|
3704
|
+
const runs = innerCols.get(col);
|
|
3705
|
+
if (runs === void 0) return void 0;
|
|
3706
|
+
const T = edgesOf(inner, runs[0]).top;
|
|
3707
|
+
const Bb = edgesOf(inner, runs[runs.length - 1]).bottom;
|
|
3708
|
+
const line = [...upperCols.get(col) ?? []].reverse().map((r) => edgesOf(upper, r)).find((e) => e.top >= T);
|
|
3709
|
+
const Tu = line?.bottom ?? T;
|
|
3710
|
+
const lineTop = line?.top ?? T;
|
|
3711
|
+
const skin = lowerCols.get(col)?.[0];
|
|
3712
|
+
return {
|
|
3713
|
+
T,
|
|
3714
|
+
Bb,
|
|
3715
|
+
Tu,
|
|
3716
|
+
lineTop,
|
|
3717
|
+
lineH: lineTop - Tu,
|
|
3718
|
+
Bl: skin === void 0 ? Bb : edgesOf(lower, skin).top,
|
|
3719
|
+
H: T - Bb,
|
|
3720
|
+
seam: T - UPPER_SHARE * (T - Bb)
|
|
3721
|
+
};
|
|
3722
|
+
};
|
|
3723
|
+
const reads = [];
|
|
3724
|
+
for (let c = x0; c <= x1; c++) {
|
|
3725
|
+
let r;
|
|
3726
|
+
for (let d = 0; (r = read(c - d) ?? read(c + d)) === void 0; d++) ;
|
|
3727
|
+
reads.push(r);
|
|
3728
|
+
}
|
|
3729
|
+
const base = (col) => reads[Math.min(x1, Math.max(x0, col)) - x0];
|
|
3730
|
+
const centre = Math.round((x0 + x1) / 2);
|
|
3731
|
+
const w = base(centre).lineH;
|
|
3732
|
+
return {
|
|
3733
|
+
x0,
|
|
3734
|
+
x1,
|
|
3735
|
+
centre,
|
|
3736
|
+
canvasW: inner.canvasW,
|
|
3737
|
+
w,
|
|
3738
|
+
at(col) {
|
|
3739
|
+
const b = base(col);
|
|
3740
|
+
return { ...b, overlap: Math.max(0, Math.min(w, b.lineH)) };
|
|
3741
|
+
}
|
|
3742
|
+
};
|
|
3743
|
+
}
|
|
3744
|
+
function mouthFold(role, opening) {
|
|
3745
|
+
return (col, y, v) => {
|
|
3746
|
+
if (v === 1) return 0;
|
|
3747
|
+
if (role !== "mouth_inner" && (col < opening.x0 || col > opening.x1))
|
|
3748
|
+
return 0;
|
|
3749
|
+
const c = opening.at(col);
|
|
3750
|
+
const k = 1 - v;
|
|
3751
|
+
switch (role) {
|
|
3752
|
+
case "lip_upper":
|
|
3753
|
+
return (c.seam - c.Tu) * k;
|
|
3754
|
+
case "lip_lower":
|
|
3755
|
+
return (c.seam + c.overlap - c.Bl) * k;
|
|
3756
|
+
case "mouth_inner":
|
|
3757
|
+
return (c.seam + c.overlap - y) * k;
|
|
3758
|
+
}
|
|
3759
|
+
};
|
|
3760
|
+
}
|
|
3761
|
+
function columnOfKnot(knot, opening, role) {
|
|
3762
|
+
const boundary = Math.round(knot + opening.canvasW / 2);
|
|
3763
|
+
const col = boundary === opening.x1 + 1 ? opening.x1 : boundary;
|
|
3764
|
+
if (col >= opening.x0 && col <= opening.x1) return col;
|
|
3765
|
+
return role === "mouth_inner" ? Math.min(opening.x1, Math.max(opening.x0, col)) : void 0;
|
|
3766
|
+
}
|
|
3767
|
+
function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
3768
|
+
const fold = mouthFold(role, opening);
|
|
3769
|
+
return localWarp(import_format8.StandardParameter.MouthOpen, mesh, box, [0, 1], (p, v, i) => {
|
|
3770
|
+
const col = columnOfKnot(xs[i % xs.length], opening, role);
|
|
3771
|
+
return [0, col === void 0 ? 0 : fold(col, p[1], v)];
|
|
3772
|
+
});
|
|
3773
|
+
}
|
|
3774
|
+
function mouthKnots(union, opening) {
|
|
3775
|
+
const half = opening.canvasW / 2;
|
|
3776
|
+
const step = MOUTH_KNOT_PX;
|
|
3777
|
+
const xs = [union.x0];
|
|
3778
|
+
while (xs[xs.length - 1] + step < union.x1 - 0.3 * step)
|
|
3779
|
+
xs.push(xs[xs.length - 1] + step);
|
|
3780
|
+
xs.push(union.x1);
|
|
3781
|
+
const fixed = /* @__PURE__ */ new Set([union.x0, union.x1]);
|
|
3782
|
+
for (const x of [opening.x0 - half, opening.x1 + 1 - half]) {
|
|
3783
|
+
fixed.add(x);
|
|
3784
|
+
if (xs.includes(x)) continue;
|
|
3785
|
+
const near = xs.findIndex((v) => Math.abs(v - x) < 0.3 * step);
|
|
3786
|
+
if (near >= 0 && !fixed.has(xs[near])) xs[near] = x;
|
|
3787
|
+
else xs.push(x);
|
|
3788
|
+
}
|
|
3789
|
+
xs.sort((p, q) => p - q);
|
|
3790
|
+
for (const x of xs) {
|
|
3791
|
+
if (!Number.isInteger(x + half)) {
|
|
3792
|
+
throw new Error(
|
|
3793
|
+
`auto-rig: mouth knot ${x} is not an integer canvas boundary`
|
|
3794
|
+
);
|
|
3795
|
+
}
|
|
3796
|
+
}
|
|
3797
|
+
return xs;
|
|
3798
|
+
}
|
|
3799
|
+
function mouthMesh(box, knots) {
|
|
3800
|
+
const xs = [box.x0, ...knots.filter((k) => k > box.x0 && k < box.x1), box.x1];
|
|
3801
|
+
const rows = cellsFor(bh(box), MOUTH_KNOT_PX, 2, 40);
|
|
3802
|
+
const ys = Array.from(
|
|
3803
|
+
{ length: rows + 1 },
|
|
3804
|
+
(_, r) => box.y1 - bh(box) * r / rows
|
|
3805
|
+
);
|
|
3806
|
+
return { mesh: columnMesh(box, { xs, ys }), xs };
|
|
3807
|
+
}
|
|
3808
|
+
function buildMouthRig(byRole) {
|
|
3809
|
+
const opening = mouthOpening(byRole);
|
|
3810
|
+
const union = unionBoxes(LIP_ROLES.map((r) => boxOfLayer(byRole.get(r))));
|
|
3811
|
+
return {
|
|
3812
|
+
opening,
|
|
3813
|
+
frame: mouthFrameOf(union),
|
|
3814
|
+
knots: mouthKnots(union, opening),
|
|
3815
|
+
union
|
|
3816
|
+
};
|
|
3817
|
+
}
|
|
3818
|
+
function mouthAnchor(byRole) {
|
|
3819
|
+
const box = (role) => boxOfLayer(byRole.get(role));
|
|
3820
|
+
if (isLipSet(byRole)) {
|
|
3821
|
+
const opening = mouthOpening(byRole);
|
|
3822
|
+
return {
|
|
3823
|
+
at: {
|
|
3824
|
+
x: cx(box("mouth_inner")),
|
|
3825
|
+
y: opening.at(opening.centre).seam
|
|
3826
|
+
},
|
|
3827
|
+
box: unionBoxes(LIP_ROLES.map(box))
|
|
3828
|
+
};
|
|
3829
|
+
}
|
|
3830
|
+
const m = box("mouth");
|
|
3831
|
+
return {
|
|
3832
|
+
at: { x: cx(m), y: cy(m) },
|
|
3833
|
+
box: byRole.has("mouth_open") ? unionBoxes([m, box("mouth_open")]) : m
|
|
3834
|
+
};
|
|
3835
|
+
}
|
|
3434
3836
|
|
|
3435
3837
|
// src/auto-rig/head.ts
|
|
3436
3838
|
function outerEdge(runs, axisX, side) {
|
|
@@ -3510,7 +3912,7 @@ function buildHeadFrame(layers, opts) {
|
|
|
3510
3912
|
return ya === void 0 || yb === void 0 ? void 0 : (ya + yb) / 2;
|
|
3511
3913
|
};
|
|
3512
3914
|
const eyeY = pair("iris_L", "iris_R") ?? pair("eye_L", "eye_R");
|
|
3513
|
-
const mouthY =
|
|
3915
|
+
const mouthY = mouthAnchor(byRole).at.y;
|
|
3514
3916
|
const rowY = (r) => canvasH / 2 - (faceLayer.bbox.y + r + 0.5);
|
|
3515
3917
|
const raw = filledProfile(faceLayer.rowHalfWidths);
|
|
3516
3918
|
const profile = raw === null ? null : smoothProfile(raw);
|
|
@@ -4462,6 +4864,7 @@ function familyField(m, family) {
|
|
|
4462
4864
|
return hairField(m, m.hairBack, NOD.hairBack);
|
|
4463
4865
|
case "body":
|
|
4464
4866
|
case "arm":
|
|
4867
|
+
case "forearm_pose":
|
|
4465
4868
|
return () => [0, 0];
|
|
4466
4869
|
}
|
|
4467
4870
|
}
|
|
@@ -4482,7 +4885,6 @@ function sideIrises(byRole) {
|
|
|
4482
4885
|
return pair === void 0 ? {} : { left: pair.left.box, right: pair.right.box };
|
|
4483
4886
|
}
|
|
4484
4887
|
function solveContext(frame, byRole, options, hasNose) {
|
|
4485
|
-
const box = (role) => boxOfLayer(byRole.get(role));
|
|
4486
4888
|
const row = (s, strand2, sideSign) => {
|
|
4487
4889
|
if (strand2 !== void 0) {
|
|
4488
4890
|
const lo = sideSign < 0 ? strand2.irisOuter : strand2.irisInner;
|
|
@@ -4531,8 +4933,7 @@ function solveContext(frame, byRole, options, hasNose) {
|
|
|
4531
4933
|
noseBox = nose.denseCore ? boxOfPixels(nose.denseCore, nose.canvasW, nose.canvasH) : boxOfLayer(nose);
|
|
4532
4934
|
noseAt = { x: cx(noseBox), y: cy(noseBox) };
|
|
4533
4935
|
}
|
|
4534
|
-
const
|
|
4535
|
-
const m = box("mouth");
|
|
4936
|
+
const mouth = mouthAnchor(byRole);
|
|
4536
4937
|
return {
|
|
4537
4938
|
frame,
|
|
4538
4939
|
left,
|
|
@@ -4544,8 +4945,8 @@ function solveContext(frame, byRole, options, hasNose) {
|
|
|
4544
4945
|
eyeBoxes: { left: eyes.left.box, right: eyes.right.box },
|
|
4545
4946
|
noseAt,
|
|
4546
4947
|
noseBox,
|
|
4547
|
-
mouthAt:
|
|
4548
|
-
mouthBox
|
|
4948
|
+
mouthAt: mouth.at,
|
|
4949
|
+
mouthBox: mouth.box
|
|
4549
4950
|
};
|
|
4550
4951
|
}
|
|
4551
4952
|
function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
@@ -4561,7 +4962,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
|
4561
4962
|
const family = roleSpec(role).family;
|
|
4562
4963
|
const part = parts.find((p) => p.id === role);
|
|
4563
4964
|
const g = grids.get(family);
|
|
4564
|
-
const warp = part?.warps?.find((w) => w.parameter ===
|
|
4965
|
+
const warp = part?.warps?.find((w) => w.parameter === import_format9.StandardParameter.AngleX);
|
|
4565
4966
|
if (part === void 0 || part.mesh === void 0 || !g && !warp) {
|
|
4566
4967
|
return x + field(role)(x, y, ax, 0)[0];
|
|
4567
4968
|
}
|
|
@@ -5297,6 +5698,7 @@ var WARP_ID = {
|
|
|
5297
5698
|
};
|
|
5298
5699
|
var isGridFamily = (f) => f in WARP_ID;
|
|
5299
5700
|
var isArm = (spec) => spec.family === "arm";
|
|
5701
|
+
var isPose = (spec) => spec.family === "forearm_pose";
|
|
5300
5702
|
var GRID_PAD = 6;
|
|
5301
5703
|
function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
5302
5704
|
checkLayers(layers, canvas);
|
|
@@ -5357,6 +5759,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5357
5759
|
hh: frame.hh,
|
|
5358
5760
|
pivots: [...arms.values()].map((g) => g.shoulder)
|
|
5359
5761
|
}) : void 0;
|
|
5762
|
+
const lips = isLipSet(byRole) ? buildMouthRig(byRole) : void 0;
|
|
5360
5763
|
const parts = [];
|
|
5361
5764
|
const reach = /* @__PURE__ */ new Map();
|
|
5362
5765
|
let regionOf = [];
|
|
@@ -5365,11 +5768,22 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5365
5768
|
const layer = byRole.get(spec.role);
|
|
5366
5769
|
if (layer === void 0) continue;
|
|
5367
5770
|
if (isArm(spec)) {
|
|
5771
|
+
const pose = POSE_OF_ARM[spec.role];
|
|
5368
5772
|
parts.push(
|
|
5369
|
-
...armParts(
|
|
5773
|
+
...armParts(
|
|
5774
|
+
spec.role,
|
|
5775
|
+
layer,
|
|
5776
|
+
arms.get(spec.role),
|
|
5777
|
+
parts.length,
|
|
5778
|
+
has(pose) ? POSE_PARAMS[pose].pose : void 0
|
|
5779
|
+
)
|
|
5370
5780
|
);
|
|
5371
5781
|
continue;
|
|
5372
5782
|
}
|
|
5783
|
+
if (isPose(spec)) {
|
|
5784
|
+
parts.push(forearmPosePart(spec.role, layer, parts.length));
|
|
5785
|
+
continue;
|
|
5786
|
+
}
|
|
5373
5787
|
const built = buildPart(
|
|
5374
5788
|
spec,
|
|
5375
5789
|
boxOfLayer(layer),
|
|
@@ -5377,7 +5791,8 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5377
5791
|
frame,
|
|
5378
5792
|
byRole,
|
|
5379
5793
|
style,
|
|
5380
|
-
landmarks.hairFrontGrid
|
|
5794
|
+
landmarks.hairFrontGrid,
|
|
5795
|
+
lips
|
|
5381
5796
|
);
|
|
5382
5797
|
if (built.region !== void 0) {
|
|
5383
5798
|
regionOf = built.region;
|
|
@@ -5422,7 +5837,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5422
5837
|
part.mesh = bodyWarp.mesh;
|
|
5423
5838
|
continue;
|
|
5424
5839
|
}
|
|
5425
|
-
if (family === "arm") continue;
|
|
5840
|
+
if (family === "arm" || family === "forearm_pose") continue;
|
|
5426
5841
|
part.deformer = "headDeformer";
|
|
5427
5842
|
if (part.mesh === void 0) continue;
|
|
5428
5843
|
const own = familyField(turn, family);
|
|
@@ -5434,17 +5849,17 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5434
5849
|
}
|
|
5435
5850
|
const parameters = declareParameters(new Set(byRole.keys()));
|
|
5436
5851
|
const model = {
|
|
5437
|
-
version:
|
|
5852
|
+
version: import_format10.IKI_FORMAT_VERSION,
|
|
5438
5853
|
name: "auto-rigged",
|
|
5439
5854
|
canvas: { width: canvas.width, height: canvas.height },
|
|
5440
5855
|
parameters,
|
|
5441
5856
|
parts,
|
|
5442
|
-
deformers: deformers(frame, grids, bodyWarp?.deformer, arms),
|
|
5857
|
+
deformers: deformers(frame, grids, bodyWarp?.deformer, arms, byRole),
|
|
5443
5858
|
...has("hair_front") ? { physics: hairPhysics() } : {},
|
|
5444
5859
|
expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
|
|
5445
|
-
motions:
|
|
5860
|
+
motions: defaultMotions(new Set(parameters.map((p) => p.id)))
|
|
5446
5861
|
};
|
|
5447
|
-
const valid = (0,
|
|
5862
|
+
const valid = (0, import_format10.parseIkiModel)(model);
|
|
5448
5863
|
if (hasNose && options.onTurnSolved) {
|
|
5449
5864
|
options.onTurnSolved(
|
|
5450
5865
|
turnReport(
|
|
@@ -5459,14 +5874,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5459
5874
|
function turnWarps(mesh, b, fieldOf) {
|
|
5460
5875
|
return [
|
|
5461
5876
|
localWarp(
|
|
5462
|
-
|
|
5877
|
+
import_format10.StandardParameter.AngleX,
|
|
5463
5878
|
mesh,
|
|
5464
5879
|
b,
|
|
5465
5880
|
X_STOPS,
|
|
5466
5881
|
([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
|
|
5467
5882
|
),
|
|
5468
5883
|
localWarp(
|
|
5469
|
-
|
|
5884
|
+
import_format10.StandardParameter.AngleY,
|
|
5470
5885
|
mesh,
|
|
5471
5886
|
b,
|
|
5472
5887
|
Y_STOPS,
|
|
@@ -5479,7 +5894,7 @@ function plateFields(turn, region, head) {
|
|
|
5479
5894
|
const neck = neckField(turn);
|
|
5480
5895
|
return (i) => region[i] === "ear" ? ear : region[i] === "neck" ? neck : head;
|
|
5481
5896
|
}
|
|
5482
|
-
function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
5897
|
+
function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
5483
5898
|
const box = (role) => boxOfLayer(byRole.get(role));
|
|
5484
5899
|
const part = {
|
|
5485
5900
|
id: spec.role,
|
|
@@ -5494,6 +5909,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5494
5909
|
const bindings = [];
|
|
5495
5910
|
let extra = [0, 0];
|
|
5496
5911
|
let mesh;
|
|
5912
|
+
let knotXs;
|
|
5497
5913
|
let region;
|
|
5498
5914
|
let headStart = 0;
|
|
5499
5915
|
if (spec.role === "face") {
|
|
@@ -5504,6 +5920,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5504
5920
|
headStart = fm.headStart;
|
|
5505
5921
|
} else if (spec.role === "hair_front") {
|
|
5506
5922
|
mesh = columnMesh(b, frontGrid);
|
|
5923
|
+
} else if (lips !== void 0 && isLipRole(spec.role)) {
|
|
5924
|
+
({ mesh, xs: knotXs } = mouthMesh(b, lips.knots));
|
|
5507
5925
|
} else if (spec.family !== "body") {
|
|
5508
5926
|
const cells = MESH_CELLS[spec.role] ?? FEATURE_MESH_CELLS;
|
|
5509
5927
|
const px = Math.min(cells.px, meshScale(spec.role, frame));
|
|
@@ -5514,7 +5932,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5514
5932
|
}
|
|
5515
5933
|
if (mesh !== void 0) part.mesh = mesh;
|
|
5516
5934
|
const side = spec.role.endsWith("_L") ? "L" : "R";
|
|
5517
|
-
const eyeOpen = side === "L" ?
|
|
5935
|
+
const eyeOpen = side === "L" ? import_format10.StandardParameter.EyeOpenLeft : import_format10.StandardParameter.EyeOpenRight;
|
|
5518
5936
|
const hh = frame.hh;
|
|
5519
5937
|
switch (spec.role) {
|
|
5520
5938
|
case "face":
|
|
@@ -5529,7 +5947,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5529
5947
|
headOwnRoll(byRole.has("body"))
|
|
5530
5948
|
),
|
|
5531
5949
|
localWarp(
|
|
5532
|
-
|
|
5950
|
+
import_format10.StandardParameter.Breath,
|
|
5533
5951
|
mesh,
|
|
5534
5952
|
b,
|
|
5535
5953
|
[0, 1],
|
|
@@ -5576,8 +5994,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5576
5994
|
if (byRole.has("mouth_open")) {
|
|
5577
5995
|
warps.push(mouthWiden(mesh, b));
|
|
5578
5996
|
bindings.push(
|
|
5579
|
-
{ parameter:
|
|
5580
|
-
{ parameter:
|
|
5997
|
+
{ parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
|
|
5998
|
+
{ parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
|
|
5581
5999
|
);
|
|
5582
6000
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
5583
6001
|
} else {
|
|
@@ -5587,19 +6005,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5587
6005
|
);
|
|
5588
6006
|
bindings.push(
|
|
5589
6007
|
{
|
|
5590
|
-
parameter:
|
|
6008
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5591
6009
|
channel: "scaleY",
|
|
5592
6010
|
from: 0,
|
|
5593
6011
|
to: roundTo(grow, 0.01)
|
|
5594
6012
|
},
|
|
5595
6013
|
{
|
|
5596
|
-
parameter:
|
|
6014
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5597
6015
|
channel: "scaleX",
|
|
5598
6016
|
from: 0,
|
|
5599
6017
|
to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
|
|
5600
6018
|
},
|
|
5601
6019
|
{
|
|
5602
|
-
parameter:
|
|
6020
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5603
6021
|
channel: "translateY",
|
|
5604
6022
|
from: 0,
|
|
5605
6023
|
to: -roundTo(grow * bh(b) / 2, 0.01)
|
|
@@ -5611,17 +6029,26 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5611
6029
|
case "mouth_open":
|
|
5612
6030
|
warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
|
|
5613
6031
|
bindings.push({
|
|
5614
|
-
parameter:
|
|
6032
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5615
6033
|
channel: "opacity",
|
|
5616
6034
|
from: 0,
|
|
5617
6035
|
to: 1
|
|
5618
6036
|
});
|
|
5619
6037
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
5620
6038
|
break;
|
|
6039
|
+
case "mouth_inner":
|
|
6040
|
+
case "lip_lower":
|
|
6041
|
+
case "lip_upper":
|
|
6042
|
+
warps.push(
|
|
6043
|
+
mouthFoldWarp(spec.role, mesh, knotXs, b, lips.opening),
|
|
6044
|
+
mouthForm(mesh, b, lips.frame),
|
|
6045
|
+
mouthWiden(mesh, b, lips.frame)
|
|
6046
|
+
);
|
|
6047
|
+
break;
|
|
5621
6048
|
case "blush_L":
|
|
5622
6049
|
case "blush_R":
|
|
5623
6050
|
bindings.push({
|
|
5624
|
-
parameter:
|
|
6051
|
+
parameter: import_format10.StandardParameter.Cheek,
|
|
5625
6052
|
channel: "opacity",
|
|
5626
6053
|
from: AMPLITUDE.blushRest,
|
|
5627
6054
|
to: 1
|
|
@@ -5632,8 +6059,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5632
6059
|
if (byRole.has("hair_front")) {
|
|
5633
6060
|
const amp = style.sway * AMPLITUDE.sway * hh;
|
|
5634
6061
|
warps.push(
|
|
5635
|
-
hairSway(
|
|
5636
|
-
hairSway(
|
|
6062
|
+
hairSway(import_format10.StandardParameter.HairSwayX, mesh, b, amp),
|
|
6063
|
+
hairSway(import_format10.StandardParameter.HairSwayZ, mesh, b, amp)
|
|
5637
6064
|
);
|
|
5638
6065
|
}
|
|
5639
6066
|
warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
|
|
@@ -5674,29 +6101,6 @@ function hairFrontGrid(b, frame, boxes) {
|
|
|
5674
6101
|
);
|
|
5675
6102
|
return { xs, ys };
|
|
5676
6103
|
}
|
|
5677
|
-
function columnMesh(b, grid) {
|
|
5678
|
-
const { xs } = grid;
|
|
5679
|
-
const rows = grid.ys.length - 1;
|
|
5680
|
-
const vertices = [];
|
|
5681
|
-
const uvs = [];
|
|
5682
|
-
for (let r = 0; r <= rows; r++) {
|
|
5683
|
-
for (const x of xs) {
|
|
5684
|
-
const u = (x - b.x0) / bw(b);
|
|
5685
|
-
vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - r / rows, 1e-5));
|
|
5686
|
-
uvs.push(roundTo(u, 1e-5), roundTo(r / rows, 1e-5));
|
|
5687
|
-
}
|
|
5688
|
-
}
|
|
5689
|
-
const n = xs.length;
|
|
5690
|
-
const indices = [];
|
|
5691
|
-
for (let r = 0; r < rows; r++) {
|
|
5692
|
-
for (let c = 0; c + 1 < n; c++) {
|
|
5693
|
-
const tl = r * n + c;
|
|
5694
|
-
const bl = tl + n;
|
|
5695
|
-
indices.push(bl, bl + 1, tl, tl, bl + 1, tl + 1);
|
|
5696
|
-
}
|
|
5697
|
-
}
|
|
5698
|
-
return { vertices, uvs, indices };
|
|
5699
|
-
}
|
|
5700
6104
|
function meshScale(role, frame) {
|
|
5701
6105
|
switch (role) {
|
|
5702
6106
|
case "hair_front":
|
|
@@ -5706,10 +6110,14 @@ function meshScale(role, frame) {
|
|
|
5706
6110
|
return Infinity;
|
|
5707
6111
|
}
|
|
5708
6112
|
}
|
|
5709
|
-
function deformers(frame, grids, bodyWarp, arms) {
|
|
6113
|
+
function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
5710
6114
|
const out = [];
|
|
5711
6115
|
if (bodyWarp !== void 0) out.push(bodyWarp);
|
|
5712
6116
|
for (const [role, g] of arms) out.push(...armDeformers(role, g));
|
|
6117
|
+
for (const [role, arm] of arms) {
|
|
6118
|
+
const pose = POSE_OF_ARM[role];
|
|
6119
|
+
if (byRole.has(pose)) out.push(forearmPoseDeformer(pose, arm));
|
|
6120
|
+
}
|
|
5713
6121
|
const hasBody = bodyWarp !== void 0;
|
|
5714
6122
|
const roll = headOwnRoll(hasBody);
|
|
5715
6123
|
out.push({
|
|
@@ -5718,9 +6126,9 @@ function deformers(frame, grids, bodyWarp, arms) {
|
|
|
5718
6126
|
pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
|
|
5719
6127
|
bindings: [
|
|
5720
6128
|
// AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
|
|
5721
|
-
{ parameter:
|
|
6129
|
+
{ parameter: import_format10.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
|
|
5722
6130
|
{
|
|
5723
|
-
parameter:
|
|
6131
|
+
parameter: import_format10.StandardParameter.Breath,
|
|
5724
6132
|
channel: "translateY",
|
|
5725
6133
|
from: 0,
|
|
5726
6134
|
to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
|
|
@@ -5738,8 +6146,8 @@ function deformers(frame, grids, bodyWarp, arms) {
|
|
|
5738
6146
|
points: g.lattice.points
|
|
5739
6147
|
},
|
|
5740
6148
|
warp2d: {
|
|
5741
|
-
parameter:
|
|
5742
|
-
parameterY:
|
|
6149
|
+
parameter: import_format10.StandardParameter.AngleX,
|
|
6150
|
+
parameterY: import_format10.StandardParameter.AngleY,
|
|
5743
6151
|
valuesX: X_STOPS,
|
|
5744
6152
|
valuesY: Y_STOPS,
|
|
5745
6153
|
keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
|
|
@@ -5752,16 +6160,16 @@ function hairPhysics() {
|
|
|
5752
6160
|
return [
|
|
5753
6161
|
{
|
|
5754
6162
|
id: "hairSway",
|
|
5755
|
-
input: { parameter:
|
|
5756
|
-
output: { parameter:
|
|
6163
|
+
input: { parameter: import_format10.StandardParameter.AngleX, weight: 1 },
|
|
6164
|
+
output: { parameter: import_format10.StandardParameter.HairSwayX, scale: 5 },
|
|
5757
6165
|
mass: 1,
|
|
5758
6166
|
stiffness: 30,
|
|
5759
6167
|
damping: 3
|
|
5760
6168
|
},
|
|
5761
6169
|
{
|
|
5762
6170
|
id: "hairTilt",
|
|
5763
|
-
input: { parameter:
|
|
5764
|
-
output: { parameter:
|
|
6171
|
+
input: { parameter: import_format10.StandardParameter.AngleZ, weight: 1 },
|
|
6172
|
+
output: { parameter: import_format10.StandardParameter.HairSwayZ, scale: 8 },
|
|
5765
6173
|
mass: 1,
|
|
5766
6174
|
stiffness: 25,
|
|
5767
6175
|
damping: 2.5
|
|
@@ -5771,47 +6179,55 @@ function hairPhysics() {
|
|
|
5771
6179
|
function declareParameters(roles) {
|
|
5772
6180
|
const out = [];
|
|
5773
6181
|
const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
|
|
5774
|
-
add(
|
|
5775
|
-
add(
|
|
5776
|
-
add(
|
|
6182
|
+
add(import_format10.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
|
|
6183
|
+
add(import_format10.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
|
|
6184
|
+
add(import_format10.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
|
|
5777
6185
|
if (roles.has("body")) {
|
|
5778
|
-
add(
|
|
5779
|
-
add(
|
|
5780
|
-
add(
|
|
6186
|
+
add(import_format10.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
|
|
6187
|
+
add(import_format10.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
|
|
6188
|
+
add(import_format10.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
|
|
5781
6189
|
}
|
|
6190
|
+
const addPose = (side, pose, angle) => {
|
|
6191
|
+
add(pose, `Arm Pose ${side}`, 0, 1, 0);
|
|
6192
|
+
add(angle, `Arm Pose Angle ${side}`, ...POSE_ANGLE_RANGE, 0);
|
|
6193
|
+
};
|
|
5782
6194
|
const [armMin, armMax] = ARM_RANGE;
|
|
5783
6195
|
const [elbowMin, elbowMax] = ELBOW_RANGE;
|
|
5784
6196
|
if (roles.has("arm_L")) {
|
|
5785
|
-
add(
|
|
5786
|
-
add(
|
|
6197
|
+
add(import_format10.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
|
|
6198
|
+
add(import_format10.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
|
|
6199
|
+
if (roles.has("forearm_pose_L"))
|
|
6200
|
+
addPose("L", import_format10.StandardParameter.ArmPoseLeft, import_format10.StandardParameter.ArmPoseAngleLeft);
|
|
5787
6201
|
}
|
|
5788
6202
|
if (roles.has("arm_R")) {
|
|
5789
|
-
add(
|
|
5790
|
-
add(
|
|
6203
|
+
add(import_format10.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
|
|
6204
|
+
add(import_format10.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
|
|
6205
|
+
if (roles.has("forearm_pose_R"))
|
|
6206
|
+
addPose("R", import_format10.StandardParameter.ArmPoseRight, import_format10.StandardParameter.ArmPoseAngleRight);
|
|
5791
6207
|
}
|
|
5792
|
-
add(
|
|
5793
|
-
add(
|
|
6208
|
+
add(import_format10.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
|
|
6209
|
+
add(import_format10.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
|
|
5794
6210
|
if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
|
|
5795
|
-
add(
|
|
5796
|
-
add(
|
|
6211
|
+
add(import_format10.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
|
|
6212
|
+
add(import_format10.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
|
|
5797
6213
|
}
|
|
5798
|
-
add(
|
|
5799
|
-
add(
|
|
6214
|
+
add(import_format10.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
|
|
6215
|
+
add(import_format10.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
|
|
5800
6216
|
if (roles.has("brow_L")) {
|
|
5801
|
-
add(
|
|
5802
|
-
add(
|
|
6217
|
+
add(import_format10.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
|
|
6218
|
+
add(import_format10.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
|
|
5803
6219
|
}
|
|
5804
6220
|
if (roles.has("brow_R")) {
|
|
5805
|
-
add(
|
|
5806
|
-
add(
|
|
6221
|
+
add(import_format10.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
|
|
6222
|
+
add(import_format10.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
|
|
5807
6223
|
}
|
|
5808
6224
|
if (roles.has("blush_L") || roles.has("blush_R")) {
|
|
5809
|
-
add(
|
|
6225
|
+
add(import_format10.StandardParameter.Cheek, "Cheek", 0, 1, 0);
|
|
5810
6226
|
}
|
|
5811
|
-
add(
|
|
6227
|
+
add(import_format10.StandardParameter.Breath, "Breath", 0, 1, 0);
|
|
5812
6228
|
if (roles.has("hair_front")) {
|
|
5813
|
-
add(
|
|
5814
|
-
add(
|
|
6229
|
+
add(import_format10.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
6230
|
+
add(import_format10.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
5815
6231
|
}
|
|
5816
6232
|
return out;
|
|
5817
6233
|
}
|
|
@@ -5859,6 +6275,7 @@ function declareParameters(roles) {
|
|
|
5859
6275
|
createLayerSetMeasurer,
|
|
5860
6276
|
denseCoreOf,
|
|
5861
6277
|
detectAlphaBbox,
|
|
6278
|
+
forearmPoseGeometry,
|
|
5862
6279
|
foregroundSpan,
|
|
5863
6280
|
generateIkiFromLayerSet,
|
|
5864
6281
|
headHalfOf,
|