@ikijs/editor 0.20.0 → 0.22.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 +14 -14
- package/dist/index.d.mts +140 -11
- package/dist/index.d.ts +140 -11
- package/dist/index.js +415 -116
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +410 -114
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -33,6 +33,7 @@ __export(index_exports, {
|
|
|
33
33
|
DeletePhysicsRig: () => DeletePhysicsRig,
|
|
34
34
|
EditorDocument: () => EditorDocument,
|
|
35
35
|
HEAD_BAND: () => HEAD_BAND,
|
|
36
|
+
LIP_ROLES: () => LIP_ROLES,
|
|
36
37
|
LayerGeometryError: () => LayerGeometryError,
|
|
37
38
|
SPECK_CORE_FRACTION: () => SPECK_CORE_FRACTION,
|
|
38
39
|
SetDeformerBindings: () => SetDeformerBindings,
|
|
@@ -54,6 +55,7 @@ __export(index_exports, {
|
|
|
54
55
|
UV_INSET_PX: () => UV_INSET_PX,
|
|
55
56
|
armGeometry: () => armGeometry,
|
|
56
57
|
captureBindingEndpoint: () => captureBindingEndpoint,
|
|
58
|
+
columnRuns: () => columnRuns,
|
|
57
59
|
computeGridOffsets: () => computeGridOffsets,
|
|
58
60
|
createDefaultMatrixDeformer: () => createDefaultMatrixDeformer,
|
|
59
61
|
createDefaultPart: () => createDefaultPart,
|
|
@@ -68,6 +70,9 @@ __export(index_exports, {
|
|
|
68
70
|
headHalfOf: () => headHalfOf,
|
|
69
71
|
interpolateGridOffsets: () => interpolateGridOffsets,
|
|
70
72
|
isSpeckCore: () => isSpeckCore,
|
|
73
|
+
mouthFold: () => mouthFold,
|
|
74
|
+
mouthOpening: () => mouthOpening,
|
|
75
|
+
mouthRestShift: () => mouthRestShift,
|
|
71
76
|
packAtlas: () => packAtlas,
|
|
72
77
|
parseLayerRoles: () => parseLayerRoles,
|
|
73
78
|
partIdsOfRole: () => partIdsOfRole,
|
|
@@ -1644,7 +1649,10 @@ var ROW_RUN_ROLES = /* @__PURE__ */ new Set([
|
|
|
1644
1649
|
"arm_L",
|
|
1645
1650
|
"arm_R",
|
|
1646
1651
|
"forearm_pose_L",
|
|
1647
|
-
"forearm_pose_R"
|
|
1652
|
+
"forearm_pose_R",
|
|
1653
|
+
"mouth_inner",
|
|
1654
|
+
"lip_lower",
|
|
1655
|
+
"lip_upper"
|
|
1648
1656
|
]);
|
|
1649
1657
|
var REST_HIDDEN_ROLES = /* @__PURE__ */ new Set([
|
|
1650
1658
|
"forearm_pose_L",
|
|
@@ -2257,6 +2265,29 @@ function gridMesh(cols, rows) {
|
|
|
2257
2265
|
}
|
|
2258
2266
|
return { vertices, uvs, indices };
|
|
2259
2267
|
}
|
|
2268
|
+
function columnMesh(b, grid) {
|
|
2269
|
+
const { xs } = grid;
|
|
2270
|
+
const rows = grid.ys.length - 1;
|
|
2271
|
+
const vertices = [];
|
|
2272
|
+
const uvs = [];
|
|
2273
|
+
for (let r = 0; r <= rows; r++) {
|
|
2274
|
+
for (const x of xs) {
|
|
2275
|
+
const u = (x - b.x0) / bw(b);
|
|
2276
|
+
vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - r / rows, 1e-5));
|
|
2277
|
+
uvs.push(roundTo(u, 1e-5), roundTo(r / rows, 1e-5));
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
const n = xs.length;
|
|
2281
|
+
const indices = [];
|
|
2282
|
+
for (let r = 0; r < rows; r++) {
|
|
2283
|
+
for (let c = 0; c + 1 < n; c++) {
|
|
2284
|
+
const tl = r * n + c;
|
|
2285
|
+
const bl = tl + n;
|
|
2286
|
+
indices.push(bl, bl + 1, tl, tl, bl + 1, tl + 1);
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
return { vertices, uvs, indices };
|
|
2290
|
+
}
|
|
2260
2291
|
function meshPoints(mesh, box) {
|
|
2261
2292
|
const out = [];
|
|
2262
2293
|
for (let i = 0; i < mesh.vertices.length; i += 2) {
|
|
@@ -2380,10 +2411,13 @@ function browBindings(side, reach) {
|
|
|
2380
2411
|
];
|
|
2381
2412
|
}
|
|
2382
2413
|
var BROW_TILT = 15;
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2414
|
+
var mouthFrameOf = (box) => ({
|
|
2415
|
+
c: cx(box),
|
|
2416
|
+
half: bw(box) / 2,
|
|
2417
|
+
amp: 0.3 * bh(box)
|
|
2418
|
+
});
|
|
2419
|
+
function mouthForm(mesh, box, frame = mouthFrameOf(box)) {
|
|
2420
|
+
const { c, half, amp: a } = frame;
|
|
2387
2421
|
return localWarp(import_format3.StandardParameter.MouthForm, mesh, box, [-1, 0, 1], ([x], v) => {
|
|
2388
2422
|
if (v === 0) return [0, 0];
|
|
2389
2423
|
const u = (x - c) / half;
|
|
@@ -2401,8 +2435,8 @@ function mouthOpenGrow(mesh, box) {
|
|
|
2401
2435
|
([x, y], v) => v === 1 ? [(AMPLITUDE.mouthOpenWidth - 1) * (x - c), 0] : [0, box.y1 + (y - box.y1) * 0.2 - y]
|
|
2402
2436
|
);
|
|
2403
2437
|
}
|
|
2404
|
-
function mouthWiden(mesh, box) {
|
|
2405
|
-
const c =
|
|
2438
|
+
function mouthWiden(mesh, box, frame = mouthFrameOf(box)) {
|
|
2439
|
+
const c = frame.c;
|
|
2406
2440
|
return localWarp(import_format3.StandardParameter.MouthOpen, mesh, box, [0, 1], ([x], v) => [
|
|
2407
2441
|
v * (AMPLITUDE.mouthOpenWidth - 1) * (x - c),
|
|
2408
2442
|
0
|
|
@@ -3058,7 +3092,12 @@ function forearmPoseDeformer(role, arm) {
|
|
|
3058
3092
|
};
|
|
3059
3093
|
}
|
|
3060
3094
|
|
|
3095
|
+
// src/auto-rig/mouth.ts
|
|
3096
|
+
var import_format7 = require("@ikijs/format");
|
|
3097
|
+
|
|
3061
3098
|
// src/auto-rig/roles.ts
|
|
3099
|
+
var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
|
|
3100
|
+
var isLipRole = (role) => LIP_ROLES.includes(role);
|
|
3062
3101
|
var ROLE_TABLE = [
|
|
3063
3102
|
{ role: "hair_back", family: "hair_back" },
|
|
3064
3103
|
{ role: "body", family: "body" },
|
|
@@ -3068,8 +3107,14 @@ var ROLE_TABLE = [
|
|
|
3068
3107
|
{ role: "blush_L", family: "face" },
|
|
3069
3108
|
{ role: "blush_R", family: "face" },
|
|
3070
3109
|
{ role: "nose", family: "nose" },
|
|
3071
|
-
{ role: "mouth", family: "mouth"
|
|
3110
|
+
{ role: "mouth", family: "mouth" },
|
|
3072
3111
|
{ role: "mouth_open", family: "mouth" },
|
|
3112
|
+
// The lip set, back to front: the interior, the lower lip's skin, the upper
|
|
3113
|
+
// lip and its line over both. It folds open instead of crossfading
|
|
3114
|
+
// (`mouth.ts`), so it stands in for `mouth`, never beside it.
|
|
3115
|
+
{ role: "mouth_inner", family: "mouth" },
|
|
3116
|
+
{ role: "lip_lower", family: "mouth" },
|
|
3117
|
+
{ role: "lip_upper", family: "mouth" },
|
|
3073
3118
|
{ role: "eye_L", family: "eye_L", required: true },
|
|
3074
3119
|
{ role: "eye_R", family: "eye_R", required: true },
|
|
3075
3120
|
{ role: "iris_L", family: "eye_L" },
|
|
@@ -3127,6 +3172,29 @@ function checkArmsHaveBody(roles) {
|
|
|
3127
3172
|
);
|
|
3128
3173
|
}
|
|
3129
3174
|
}
|
|
3175
|
+
function checkMouth(roles) {
|
|
3176
|
+
const has = new Set(roles);
|
|
3177
|
+
const present = LIP_ROLES.filter((r) => has.has(r));
|
|
3178
|
+
if (present.length === 0) {
|
|
3179
|
+
if (!has.has("mouth")) {
|
|
3180
|
+
throw new Error(
|
|
3181
|
+
`auto-rig: missing required role mouth (or the lip set ${LIP_ROLES.join(", ")})`
|
|
3182
|
+
);
|
|
3183
|
+
}
|
|
3184
|
+
return;
|
|
3185
|
+
}
|
|
3186
|
+
if (present.length < LIP_ROLES.length) {
|
|
3187
|
+
throw new Error(
|
|
3188
|
+
`auto-rig: the lip set is partial: missing ${LIP_ROLES.filter((r) => !has.has(r)).join(", ")}`
|
|
3189
|
+
);
|
|
3190
|
+
}
|
|
3191
|
+
const mixed = ["mouth", "mouth_open"].filter((r) => has.has(r));
|
|
3192
|
+
if (mixed.length > 0) {
|
|
3193
|
+
throw new Error(
|
|
3194
|
+
`auto-rig: the lip set cannot be mixed with ${mixed.join(", ")} (the lip set replaces the mouth drawings; use one or the other)`
|
|
3195
|
+
);
|
|
3196
|
+
}
|
|
3197
|
+
}
|
|
3130
3198
|
function checkPosesHaveArms(roles) {
|
|
3131
3199
|
const has = new Set(roles);
|
|
3132
3200
|
for (const side of ["L", "R"]) {
|
|
@@ -3162,38 +3230,267 @@ function parseLayerRoles(fileNames) {
|
|
|
3162
3230
|
`auto-rig: missing required role${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`
|
|
3163
3231
|
);
|
|
3164
3232
|
}
|
|
3233
|
+
checkMouth(seen.keys());
|
|
3165
3234
|
checkArmsHaveBody(seen.keys());
|
|
3166
3235
|
checkPosesHaveArms(seen.keys());
|
|
3167
3236
|
return out;
|
|
3168
3237
|
}
|
|
3169
3238
|
|
|
3239
|
+
// src/auto-rig/mouth.ts
|
|
3240
|
+
var UPPER_SHARE = 0.4;
|
|
3241
|
+
var MOUTH_KNOT_PX = 4;
|
|
3242
|
+
var isLipSet = (byRole) => LIP_ROLES.every((r) => byRole.has(r));
|
|
3243
|
+
function columnRuns(layer) {
|
|
3244
|
+
const cols = /* @__PURE__ */ new Map();
|
|
3245
|
+
(layer.rowRuns ?? []).forEach((runs, k) => {
|
|
3246
|
+
for (let i = 0; i < runs.length; i += 2) {
|
|
3247
|
+
for (let c = runs[i]; c < runs[i + 1]; c++) {
|
|
3248
|
+
const list = cols.get(c) ?? [];
|
|
3249
|
+
const last = list[list.length - 1];
|
|
3250
|
+
if (last !== void 0 && last[1] === k) last[1] = k + 1;
|
|
3251
|
+
else list.push([k, k + 1]);
|
|
3252
|
+
cols.set(c, list);
|
|
3253
|
+
}
|
|
3254
|
+
}
|
|
3255
|
+
});
|
|
3256
|
+
return cols;
|
|
3257
|
+
}
|
|
3258
|
+
var edgesOf = (layer, run) => ({
|
|
3259
|
+
top: layer.canvasH / 2 - (layer.bbox.y + run[0]),
|
|
3260
|
+
bottom: layer.canvasH / 2 - (layer.bbox.y + run[1])
|
|
3261
|
+
});
|
|
3262
|
+
function mouthOpening(byRole) {
|
|
3263
|
+
const inner = byRole.get("mouth_inner");
|
|
3264
|
+
const upper = byRole.get("lip_upper");
|
|
3265
|
+
const lower = byRole.get("lip_lower");
|
|
3266
|
+
const innerCols = columnRuns(inner);
|
|
3267
|
+
if (innerCols.size === 0) {
|
|
3268
|
+
throw new LayerGeometryError(
|
|
3269
|
+
`auto-rig: layer "${inner.fileName}": mouth_inner has no opaque pixel (alpha \u2265 128) to read the opening off`
|
|
3270
|
+
);
|
|
3271
|
+
}
|
|
3272
|
+
const upperCols = columnRuns(upper);
|
|
3273
|
+
const lowerCols = columnRuns(lower);
|
|
3274
|
+
const cols = [...innerCols.keys()];
|
|
3275
|
+
const x0 = Math.min(...cols);
|
|
3276
|
+
const x1 = Math.max(...cols);
|
|
3277
|
+
const read = (col) => {
|
|
3278
|
+
const runs = innerCols.get(col);
|
|
3279
|
+
if (runs === void 0) return void 0;
|
|
3280
|
+
const T = edgesOf(inner, runs[0]).top;
|
|
3281
|
+
const Bb = edgesOf(inner, runs[runs.length - 1]).bottom;
|
|
3282
|
+
const line = [...upperCols.get(col) ?? []].reverse().map((r) => edgesOf(upper, r)).find((e) => e.top >= T);
|
|
3283
|
+
const Tu = line?.bottom ?? T;
|
|
3284
|
+
const lineTop = line?.top ?? T;
|
|
3285
|
+
const skin = lowerCols.get(col)?.[0];
|
|
3286
|
+
return {
|
|
3287
|
+
T,
|
|
3288
|
+
Bb,
|
|
3289
|
+
Tu,
|
|
3290
|
+
lineTop,
|
|
3291
|
+
lineH: lineTop - Tu,
|
|
3292
|
+
Bl: skin === void 0 ? Bb : edgesOf(lower, skin).top,
|
|
3293
|
+
H: T - Bb
|
|
3294
|
+
};
|
|
3295
|
+
};
|
|
3296
|
+
const reads = [];
|
|
3297
|
+
for (let c = x0; c <= x1; c++) {
|
|
3298
|
+
let r;
|
|
3299
|
+
for (let d = 0; (r = read(c - d) ?? read(c + d)) === void 0; d++) ;
|
|
3300
|
+
reads.push(r);
|
|
3301
|
+
}
|
|
3302
|
+
const m = (x0 + x1) / 2;
|
|
3303
|
+
let p = 0;
|
|
3304
|
+
let q = 0;
|
|
3305
|
+
if (reads.length >= 4) {
|
|
3306
|
+
let s11 = 0;
|
|
3307
|
+
let s12 = 0;
|
|
3308
|
+
let s22 = 0;
|
|
3309
|
+
let t1 = 0;
|
|
3310
|
+
let t2 = 0;
|
|
3311
|
+
reads.forEach((r, i) => {
|
|
3312
|
+
const x = x0 + i;
|
|
3313
|
+
const b1 = (x - x0) * (x - x1);
|
|
3314
|
+
const b2 = b1 * (x - m);
|
|
3315
|
+
const d = r.T - UPPER_SHARE * r.H - r.Tu;
|
|
3316
|
+
s11 += b1 * b1;
|
|
3317
|
+
s12 += b1 * b2;
|
|
3318
|
+
s22 += b2 * b2;
|
|
3319
|
+
t1 += b1 * d;
|
|
3320
|
+
t2 += b2 * d;
|
|
3321
|
+
});
|
|
3322
|
+
const det = s11 * s22 - s12 * s12;
|
|
3323
|
+
p = (t1 * s22 - t2 * s12) / det;
|
|
3324
|
+
q = (s11 * t2 - s12 * t1) / det;
|
|
3325
|
+
}
|
|
3326
|
+
const columns = reads.map((r, i) => {
|
|
3327
|
+
const x = x0 + i;
|
|
3328
|
+
return { ...r, seam: r.Tu + (x - x0) * (x - x1) * (p + q * (x - m)) };
|
|
3329
|
+
});
|
|
3330
|
+
const base = (col) => columns[Math.min(x1, Math.max(x0, col)) - x0];
|
|
3331
|
+
const centre = Math.round((x0 + x1) / 2);
|
|
3332
|
+
const w = base(centre).lineH;
|
|
3333
|
+
return {
|
|
3334
|
+
x0,
|
|
3335
|
+
x1,
|
|
3336
|
+
centre,
|
|
3337
|
+
canvasW: inner.canvasW,
|
|
3338
|
+
w,
|
|
3339
|
+
at(col) {
|
|
3340
|
+
const b = base(col);
|
|
3341
|
+
return { ...b, overlap: Math.max(0, Math.min(w, b.lineH)) };
|
|
3342
|
+
}
|
|
3343
|
+
};
|
|
3344
|
+
}
|
|
3345
|
+
function mouthFold(role, opening) {
|
|
3346
|
+
return (col, y, v) => {
|
|
3347
|
+
if (v === 1) return 0;
|
|
3348
|
+
if (role !== "mouth_inner" && (col < opening.x0 || col > opening.x1))
|
|
3349
|
+
return 0;
|
|
3350
|
+
const c = opening.at(col);
|
|
3351
|
+
const k = 1 - v;
|
|
3352
|
+
switch (role) {
|
|
3353
|
+
case "lip_upper":
|
|
3354
|
+
return (c.seam - c.Tu) * k;
|
|
3355
|
+
case "lip_lower":
|
|
3356
|
+
return (c.seam + c.overlap - c.Bl) * k;
|
|
3357
|
+
case "mouth_inner":
|
|
3358
|
+
return (c.seam + c.overlap - y) * k;
|
|
3359
|
+
}
|
|
3360
|
+
};
|
|
3361
|
+
}
|
|
3362
|
+
function columnOfKnot(knot, opening, role) {
|
|
3363
|
+
const boundary = Math.round(knot + opening.canvasW / 2);
|
|
3364
|
+
const col = boundary === opening.x1 + 1 ? opening.x1 : boundary;
|
|
3365
|
+
if (col >= opening.x0 && col <= opening.x1) return col;
|
|
3366
|
+
return role === "mouth_inner" ? Math.min(opening.x1, Math.max(opening.x0, col)) : void 0;
|
|
3367
|
+
}
|
|
3368
|
+
function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
3369
|
+
const fold = mouthFold(role, opening);
|
|
3370
|
+
return localWarp(import_format7.StandardParameter.MouthOpen, mesh, box, [0, 1], (p, v, i) => {
|
|
3371
|
+
const col = columnOfKnot(xs[i % xs.length], opening, role);
|
|
3372
|
+
return [0, col === void 0 ? 0 : fold(col, p[1], v)];
|
|
3373
|
+
});
|
|
3374
|
+
}
|
|
3375
|
+
function mouthRestShift(byRole) {
|
|
3376
|
+
const rig = buildMouthRig(byRole);
|
|
3377
|
+
const fields = /* @__PURE__ */ new Map();
|
|
3378
|
+
for (const role of ["lip_upper", "lip_lower"]) {
|
|
3379
|
+
const box = boxOfLayer(byRole.get(role));
|
|
3380
|
+
const { mesh, xs } = mouthMesh(box, rig.knots);
|
|
3381
|
+
const warp = mouthFoldWarp(role, mesh, xs, box, rig.opening);
|
|
3382
|
+
const offsets = warp.keyforms[0].offsets;
|
|
3383
|
+
fields.set(role, {
|
|
3384
|
+
box,
|
|
3385
|
+
xs,
|
|
3386
|
+
dy: xs.map((_, i) => offsets[2 * i + 1] * bh(box))
|
|
3387
|
+
});
|
|
3388
|
+
}
|
|
3389
|
+
const half = rig.opening.canvasW / 2;
|
|
3390
|
+
return (role, col) => {
|
|
3391
|
+
const { box, xs, dy } = fields.get(role);
|
|
3392
|
+
const x = col + 0.5 - half;
|
|
3393
|
+
if (x < box.x0 || x > box.x1) return 0;
|
|
3394
|
+
let k = 1;
|
|
3395
|
+
while (k < xs.length - 1 && xs[k] < x) k++;
|
|
3396
|
+
const t = (x - xs[k - 1]) / (xs[k] - xs[k - 1]);
|
|
3397
|
+
return dy[k - 1] + (dy[k] - dy[k - 1]) * t;
|
|
3398
|
+
};
|
|
3399
|
+
}
|
|
3400
|
+
function mouthKnots(union, opening) {
|
|
3401
|
+
const half = opening.canvasW / 2;
|
|
3402
|
+
const step = MOUTH_KNOT_PX;
|
|
3403
|
+
const xs = [union.x0];
|
|
3404
|
+
while (xs[xs.length - 1] + step < union.x1 - 0.3 * step)
|
|
3405
|
+
xs.push(xs[xs.length - 1] + step);
|
|
3406
|
+
xs.push(union.x1);
|
|
3407
|
+
const fixed = /* @__PURE__ */ new Set([union.x0, union.x1]);
|
|
3408
|
+
for (const x of [
|
|
3409
|
+
opening.x0 - half,
|
|
3410
|
+
opening.x1 - half,
|
|
3411
|
+
opening.x1 + 1 - half
|
|
3412
|
+
]) {
|
|
3413
|
+
fixed.add(x);
|
|
3414
|
+
if (xs.includes(x)) continue;
|
|
3415
|
+
const near = xs.findIndex((v) => Math.abs(v - x) < 0.3 * step);
|
|
3416
|
+
if (near >= 0 && !fixed.has(xs[near])) xs[near] = x;
|
|
3417
|
+
else xs.push(x);
|
|
3418
|
+
}
|
|
3419
|
+
xs.sort((p, q) => p - q);
|
|
3420
|
+
for (const x of xs) {
|
|
3421
|
+
if (!Number.isInteger(x + half)) {
|
|
3422
|
+
throw new Error(
|
|
3423
|
+
`auto-rig: mouth knot ${x} is not an integer canvas boundary`
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
}
|
|
3427
|
+
return xs;
|
|
3428
|
+
}
|
|
3429
|
+
function mouthMesh(box, knots) {
|
|
3430
|
+
const xs = [box.x0, ...knots.filter((k) => k > box.x0 && k < box.x1), box.x1];
|
|
3431
|
+
const rows = cellsFor(bh(box), MOUTH_KNOT_PX, 2, 40);
|
|
3432
|
+
const ys = Array.from(
|
|
3433
|
+
{ length: rows + 1 },
|
|
3434
|
+
(_, r) => box.y1 - bh(box) * r / rows
|
|
3435
|
+
);
|
|
3436
|
+
return { mesh: columnMesh(box, { xs, ys }), xs };
|
|
3437
|
+
}
|
|
3438
|
+
function buildMouthRig(byRole) {
|
|
3439
|
+
const opening = mouthOpening(byRole);
|
|
3440
|
+
const union = unionBoxes(LIP_ROLES.map((r) => boxOfLayer(byRole.get(r))));
|
|
3441
|
+
return {
|
|
3442
|
+
opening,
|
|
3443
|
+
frame: mouthFrameOf(union),
|
|
3444
|
+
knots: mouthKnots(union, opening),
|
|
3445
|
+
union
|
|
3446
|
+
};
|
|
3447
|
+
}
|
|
3448
|
+
function mouthAnchor(byRole) {
|
|
3449
|
+
const box = (role) => boxOfLayer(byRole.get(role));
|
|
3450
|
+
if (isLipSet(byRole)) {
|
|
3451
|
+
const opening = mouthOpening(byRole);
|
|
3452
|
+
return {
|
|
3453
|
+
at: {
|
|
3454
|
+
x: cx(box("mouth_inner")),
|
|
3455
|
+
y: opening.at(opening.centre).seam
|
|
3456
|
+
},
|
|
3457
|
+
box: unionBoxes(LIP_ROLES.map(box))
|
|
3458
|
+
};
|
|
3459
|
+
}
|
|
3460
|
+
const m = box("mouth");
|
|
3461
|
+
return {
|
|
3462
|
+
at: { x: cx(m), y: cy(m) },
|
|
3463
|
+
box: byRole.has("mouth_open") ? unionBoxes([m, box("mouth_open")]) : m
|
|
3464
|
+
};
|
|
3465
|
+
}
|
|
3466
|
+
|
|
3170
3467
|
// src/auto-rig/generate.ts
|
|
3171
|
-
var
|
|
3468
|
+
var import_format10 = require("@ikijs/format");
|
|
3172
3469
|
|
|
3173
3470
|
// src/auto-rig/animations.ts
|
|
3174
|
-
var
|
|
3471
|
+
var import_format8 = require("@ikijs/format");
|
|
3175
3472
|
function browsY(y) {
|
|
3176
3473
|
return [
|
|
3177
|
-
{ parameter:
|
|
3178
|
-
{ parameter:
|
|
3474
|
+
{ parameter: import_format8.StandardParameter.BrowLeftY, value: y, blend: "add" },
|
|
3475
|
+
{ parameter: import_format8.StandardParameter.BrowRightY, value: y, blend: "add" }
|
|
3179
3476
|
];
|
|
3180
3477
|
}
|
|
3181
3478
|
function browsTilt(innerDown) {
|
|
3182
3479
|
return [
|
|
3183
|
-
{ parameter:
|
|
3184
|
-
{ parameter:
|
|
3480
|
+
{ parameter: import_format8.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
3481
|
+
{ parameter: import_format8.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
3185
3482
|
];
|
|
3186
3483
|
}
|
|
3187
3484
|
function cheek(value) {
|
|
3188
|
-
return { parameter:
|
|
3485
|
+
return { parameter: import_format8.StandardParameter.Cheek, value, blend: "add" };
|
|
3189
3486
|
}
|
|
3190
|
-
var BROWS = [
|
|
3487
|
+
var BROWS = [import_format8.StandardParameter.BrowLeftY, import_format8.StandardParameter.BrowRightY, import_format8.StandardParameter.BrowLeftAngle, import_format8.StandardParameter.BrowRightAngle];
|
|
3191
3488
|
var EXPRESSIONS = [
|
|
3192
3489
|
{
|
|
3193
3490
|
id: "smile",
|
|
3194
3491
|
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
3195
3492
|
parameters: [
|
|
3196
|
-
{ parameter:
|
|
3493
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3197
3494
|
...browsY(0.25),
|
|
3198
3495
|
cheek(0.4)
|
|
3199
3496
|
]
|
|
@@ -3202,10 +3499,10 @@ var EXPRESSIONS = [
|
|
|
3202
3499
|
id: "laugh",
|
|
3203
3500
|
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
3204
3501
|
parameters: [
|
|
3205
|
-
{ parameter:
|
|
3206
|
-
{ parameter:
|
|
3207
|
-
{ parameter:
|
|
3208
|
-
{ parameter:
|
|
3502
|
+
{ parameter: import_format8.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
|
|
3503
|
+
{ parameter: import_format8.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
|
|
3504
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3505
|
+
{ parameter: import_format8.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
|
|
3209
3506
|
...browsY(0.4),
|
|
3210
3507
|
cheek(0.65)
|
|
3211
3508
|
]
|
|
@@ -3217,7 +3514,7 @@ var EXPRESSIONS = [
|
|
|
3217
3514
|
parameters: [
|
|
3218
3515
|
...browsY(-0.5),
|
|
3219
3516
|
...browsTilt(0.8),
|
|
3220
|
-
{ parameter:
|
|
3517
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: -0.9, blend: "add" }
|
|
3221
3518
|
]
|
|
3222
3519
|
},
|
|
3223
3520
|
{
|
|
@@ -3227,7 +3524,7 @@ var EXPRESSIONS = [
|
|
|
3227
3524
|
parameters: [
|
|
3228
3525
|
...browsY(0.35),
|
|
3229
3526
|
...browsTilt(-0.8),
|
|
3230
|
-
{ parameter:
|
|
3527
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: -0.8, blend: "add" }
|
|
3231
3528
|
]
|
|
3232
3529
|
},
|
|
3233
3530
|
{
|
|
@@ -3235,18 +3532,18 @@ var EXPRESSIONS = [
|
|
|
3235
3532
|
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
3236
3533
|
parameters: [
|
|
3237
3534
|
...browsY(0.8),
|
|
3238
|
-
{ parameter:
|
|
3535
|
+
{ parameter: import_format8.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
3239
3536
|
]
|
|
3240
3537
|
},
|
|
3241
3538
|
{
|
|
3242
3539
|
id: "shy",
|
|
3243
3540
|
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
3244
|
-
needs: [
|
|
3541
|
+
needs: [import_format8.StandardParameter.Cheek],
|
|
3245
3542
|
parameters: [
|
|
3246
3543
|
cheek(1),
|
|
3247
3544
|
// Gaze down.
|
|
3248
|
-
{ parameter:
|
|
3249
|
-
{ parameter:
|
|
3545
|
+
{ parameter: import_format8.StandardParameter.EyeballY, value: -0.65, blend: "add" },
|
|
3546
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: 0.4, blend: "add" },
|
|
3250
3547
|
...browsTilt(-0.25)
|
|
3251
3548
|
]
|
|
3252
3549
|
}
|
|
@@ -3266,7 +3563,7 @@ var HEAD_MOTIONS = {
|
|
|
3266
3563
|
duration: 1,
|
|
3267
3564
|
curves: [
|
|
3268
3565
|
{
|
|
3269
|
-
parameter:
|
|
3566
|
+
parameter: import_format8.StandardParameter.AngleY,
|
|
3270
3567
|
keys: [
|
|
3271
3568
|
[0, 0],
|
|
3272
3569
|
[0.3, -18],
|
|
@@ -3287,7 +3584,7 @@ var HEAD_MOTIONS = {
|
|
|
3287
3584
|
fadeIn: 0.15,
|
|
3288
3585
|
curves: [
|
|
3289
3586
|
{
|
|
3290
|
-
parameter:
|
|
3587
|
+
parameter: import_format8.StandardParameter.AngleX,
|
|
3291
3588
|
keys: [
|
|
3292
3589
|
[0, 0],
|
|
3293
3590
|
[0.2, -18],
|
|
@@ -3306,7 +3603,7 @@ var HEAD_MOTIONS = {
|
|
|
3306
3603
|
duration: 1.4,
|
|
3307
3604
|
curves: [
|
|
3308
3605
|
{
|
|
3309
|
-
parameter:
|
|
3606
|
+
parameter: import_format8.StandardParameter.AngleZ,
|
|
3310
3607
|
keys: [
|
|
3311
3608
|
[0, 0],
|
|
3312
3609
|
[0.35, 20],
|
|
@@ -3327,7 +3624,7 @@ var WAVE = {
|
|
|
3327
3624
|
fadeOut: 0.15,
|
|
3328
3625
|
curves: [
|
|
3329
3626
|
{
|
|
3330
|
-
parameter:
|
|
3627
|
+
parameter: import_format8.StandardParameter.ArmPoseRight,
|
|
3331
3628
|
interpolation: "linear",
|
|
3332
3629
|
keys: [
|
|
3333
3630
|
[0, 0],
|
|
@@ -3337,7 +3634,7 @@ var WAVE = {
|
|
|
3337
3634
|
]
|
|
3338
3635
|
},
|
|
3339
3636
|
{
|
|
3340
|
-
parameter:
|
|
3637
|
+
parameter: import_format8.StandardParameter.ArmRight,
|
|
3341
3638
|
keys: [
|
|
3342
3639
|
[0, 0],
|
|
3343
3640
|
[0.4, 10],
|
|
@@ -3346,7 +3643,7 @@ var WAVE = {
|
|
|
3346
3643
|
]
|
|
3347
3644
|
},
|
|
3348
3645
|
{
|
|
3349
|
-
parameter:
|
|
3646
|
+
parameter: import_format8.StandardParameter.ArmPoseAngleRight,
|
|
3350
3647
|
keys: [
|
|
3351
3648
|
[0, 0],
|
|
3352
3649
|
[0.5, 0],
|
|
@@ -3362,7 +3659,7 @@ var WAVE = {
|
|
|
3362
3659
|
]
|
|
3363
3660
|
};
|
|
3364
3661
|
function defaultMotions(declared) {
|
|
3365
|
-
const waves = [
|
|
3662
|
+
const waves = [import_format8.StandardParameter.ArmRight, import_format8.StandardParameter.ArmPoseRight, import_format8.StandardParameter.ArmPoseAngleRight].every(
|
|
3366
3663
|
(id) => declared.has(id)
|
|
3367
3664
|
);
|
|
3368
3665
|
return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
|
|
@@ -3471,6 +3768,14 @@ function checkLayers(layers, canvas) {
|
|
|
3471
3768
|
if (missing.length > 0) {
|
|
3472
3769
|
throw new Error(`auto-rig: missing required role(s) ${missing.join(", ")}`);
|
|
3473
3770
|
}
|
|
3771
|
+
checkMouth(seen);
|
|
3772
|
+
for (const l of layers) {
|
|
3773
|
+
if (l.rowRuns === void 0 && isLipRole(l.role)) {
|
|
3774
|
+
throw new Error(
|
|
3775
|
+
`auto-rig: layer "${l.fileName}": the lip set needs rowRuns (the fold reads the opening's rows off them; measure with createLayerSetMeasurer)`
|
|
3776
|
+
);
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3474
3779
|
checkArmsHaveBody(seen);
|
|
3475
3780
|
checkPosesHaveArms(seen);
|
|
3476
3781
|
}
|
|
@@ -3590,7 +3895,7 @@ function checkStrandEdges(strand, irises, hair) {
|
|
|
3590
3895
|
}
|
|
3591
3896
|
|
|
3592
3897
|
// src/auto-rig/context.ts
|
|
3593
|
-
var
|
|
3898
|
+
var import_format9 = require("@ikijs/format");
|
|
3594
3899
|
|
|
3595
3900
|
// src/auto-rig/head.ts
|
|
3596
3901
|
function outerEdge(runs, axisX, side) {
|
|
@@ -3670,7 +3975,7 @@ function buildHeadFrame(layers, opts) {
|
|
|
3670
3975
|
return ya === void 0 || yb === void 0 ? void 0 : (ya + yb) / 2;
|
|
3671
3976
|
};
|
|
3672
3977
|
const eyeY = pair("iris_L", "iris_R") ?? pair("eye_L", "eye_R");
|
|
3673
|
-
const mouthY =
|
|
3978
|
+
const mouthY = mouthAnchor(byRole).at.y;
|
|
3674
3979
|
const rowY = (r) => canvasH / 2 - (faceLayer.bbox.y + r + 0.5);
|
|
3675
3980
|
const raw = filledProfile(faceLayer.rowHalfWidths);
|
|
3676
3981
|
const profile = raw === null ? null : smoothProfile(raw);
|
|
@@ -4643,7 +4948,6 @@ function sideIrises(byRole) {
|
|
|
4643
4948
|
return pair === void 0 ? {} : { left: pair.left.box, right: pair.right.box };
|
|
4644
4949
|
}
|
|
4645
4950
|
function solveContext(frame, byRole, options, hasNose) {
|
|
4646
|
-
const box = (role) => boxOfLayer(byRole.get(role));
|
|
4647
4951
|
const row = (s, strand2, sideSign) => {
|
|
4648
4952
|
if (strand2 !== void 0) {
|
|
4649
4953
|
const lo = sideSign < 0 ? strand2.irisOuter : strand2.irisInner;
|
|
@@ -4692,8 +4996,7 @@ function solveContext(frame, byRole, options, hasNose) {
|
|
|
4692
4996
|
noseBox = nose.denseCore ? boxOfPixels(nose.denseCore, nose.canvasW, nose.canvasH) : boxOfLayer(nose);
|
|
4693
4997
|
noseAt = { x: cx(noseBox), y: cy(noseBox) };
|
|
4694
4998
|
}
|
|
4695
|
-
const
|
|
4696
|
-
const m = box("mouth");
|
|
4999
|
+
const mouth = mouthAnchor(byRole);
|
|
4697
5000
|
return {
|
|
4698
5001
|
frame,
|
|
4699
5002
|
left,
|
|
@@ -4705,8 +5008,8 @@ function solveContext(frame, byRole, options, hasNose) {
|
|
|
4705
5008
|
eyeBoxes: { left: eyes.left.box, right: eyes.right.box },
|
|
4706
5009
|
noseAt,
|
|
4707
5010
|
noseBox,
|
|
4708
|
-
mouthAt:
|
|
4709
|
-
mouthBox
|
|
5011
|
+
mouthAt: mouth.at,
|
|
5012
|
+
mouthBox: mouth.box
|
|
4710
5013
|
};
|
|
4711
5014
|
}
|
|
4712
5015
|
function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
@@ -4722,7 +5025,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
|
4722
5025
|
const family = roleSpec(role).family;
|
|
4723
5026
|
const part = parts.find((p) => p.id === role);
|
|
4724
5027
|
const g = grids.get(family);
|
|
4725
|
-
const warp = part?.warps?.find((w) => w.parameter ===
|
|
5028
|
+
const warp = part?.warps?.find((w) => w.parameter === import_format9.StandardParameter.AngleX);
|
|
4726
5029
|
if (part === void 0 || part.mesh === void 0 || !g && !warp) {
|
|
4727
5030
|
return x + field(role)(x, y, ax, 0)[0];
|
|
4728
5031
|
}
|
|
@@ -5519,6 +5822,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5519
5822
|
hh: frame.hh,
|
|
5520
5823
|
pivots: [...arms.values()].map((g) => g.shoulder)
|
|
5521
5824
|
}) : void 0;
|
|
5825
|
+
const lips = isLipSet(byRole) ? buildMouthRig(byRole) : void 0;
|
|
5522
5826
|
const parts = [];
|
|
5523
5827
|
const reach = /* @__PURE__ */ new Map();
|
|
5524
5828
|
let regionOf = [];
|
|
@@ -5550,7 +5854,8 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5550
5854
|
frame,
|
|
5551
5855
|
byRole,
|
|
5552
5856
|
style,
|
|
5553
|
-
landmarks.hairFrontGrid
|
|
5857
|
+
landmarks.hairFrontGrid,
|
|
5858
|
+
lips
|
|
5554
5859
|
);
|
|
5555
5860
|
if (built.region !== void 0) {
|
|
5556
5861
|
regionOf = built.region;
|
|
@@ -5607,7 +5912,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5607
5912
|
}
|
|
5608
5913
|
const parameters = declareParameters(new Set(byRole.keys()));
|
|
5609
5914
|
const model = {
|
|
5610
|
-
version:
|
|
5915
|
+
version: import_format10.IKI_FORMAT_VERSION,
|
|
5611
5916
|
name: "auto-rigged",
|
|
5612
5917
|
canvas: { width: canvas.width, height: canvas.height },
|
|
5613
5918
|
parameters,
|
|
@@ -5617,7 +5922,7 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5617
5922
|
expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
|
|
5618
5923
|
motions: defaultMotions(new Set(parameters.map((p) => p.id)))
|
|
5619
5924
|
};
|
|
5620
|
-
const valid = (0,
|
|
5925
|
+
const valid = (0, import_format10.parseIkiModel)(model);
|
|
5621
5926
|
if (hasNose && options.onTurnSolved) {
|
|
5622
5927
|
options.onTurnSolved(
|
|
5623
5928
|
turnReport(
|
|
@@ -5632,14 +5937,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
5632
5937
|
function turnWarps(mesh, b, fieldOf) {
|
|
5633
5938
|
return [
|
|
5634
5939
|
localWarp(
|
|
5635
|
-
|
|
5940
|
+
import_format10.StandardParameter.AngleX,
|
|
5636
5941
|
mesh,
|
|
5637
5942
|
b,
|
|
5638
5943
|
X_STOPS,
|
|
5639
5944
|
([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
|
|
5640
5945
|
),
|
|
5641
5946
|
localWarp(
|
|
5642
|
-
|
|
5947
|
+
import_format10.StandardParameter.AngleY,
|
|
5643
5948
|
mesh,
|
|
5644
5949
|
b,
|
|
5645
5950
|
Y_STOPS,
|
|
@@ -5652,7 +5957,7 @@ function plateFields(turn, region, head) {
|
|
|
5652
5957
|
const neck = neckField(turn);
|
|
5653
5958
|
return (i) => region[i] === "ear" ? ear : region[i] === "neck" ? neck : head;
|
|
5654
5959
|
}
|
|
5655
|
-
function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
5960
|
+
function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
5656
5961
|
const box = (role) => boxOfLayer(byRole.get(role));
|
|
5657
5962
|
const part = {
|
|
5658
5963
|
id: spec.role,
|
|
@@ -5667,6 +5972,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5667
5972
|
const bindings = [];
|
|
5668
5973
|
let extra = [0, 0];
|
|
5669
5974
|
let mesh;
|
|
5975
|
+
let knotXs;
|
|
5670
5976
|
let region;
|
|
5671
5977
|
let headStart = 0;
|
|
5672
5978
|
if (spec.role === "face") {
|
|
@@ -5677,6 +5983,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5677
5983
|
headStart = fm.headStart;
|
|
5678
5984
|
} else if (spec.role === "hair_front") {
|
|
5679
5985
|
mesh = columnMesh(b, frontGrid);
|
|
5986
|
+
} else if (lips !== void 0 && isLipRole(spec.role)) {
|
|
5987
|
+
({ mesh, xs: knotXs } = mouthMesh(b, lips.knots));
|
|
5680
5988
|
} else if (spec.family !== "body") {
|
|
5681
5989
|
const cells = MESH_CELLS[spec.role] ?? FEATURE_MESH_CELLS;
|
|
5682
5990
|
const px = Math.min(cells.px, meshScale(spec.role, frame));
|
|
@@ -5687,7 +5995,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5687
5995
|
}
|
|
5688
5996
|
if (mesh !== void 0) part.mesh = mesh;
|
|
5689
5997
|
const side = spec.role.endsWith("_L") ? "L" : "R";
|
|
5690
|
-
const eyeOpen = side === "L" ?
|
|
5998
|
+
const eyeOpen = side === "L" ? import_format10.StandardParameter.EyeOpenLeft : import_format10.StandardParameter.EyeOpenRight;
|
|
5691
5999
|
const hh = frame.hh;
|
|
5692
6000
|
switch (spec.role) {
|
|
5693
6001
|
case "face":
|
|
@@ -5702,7 +6010,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5702
6010
|
headOwnRoll(byRole.has("body"))
|
|
5703
6011
|
),
|
|
5704
6012
|
localWarp(
|
|
5705
|
-
|
|
6013
|
+
import_format10.StandardParameter.Breath,
|
|
5706
6014
|
mesh,
|
|
5707
6015
|
b,
|
|
5708
6016
|
[0, 1],
|
|
@@ -5749,8 +6057,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5749
6057
|
if (byRole.has("mouth_open")) {
|
|
5750
6058
|
warps.push(mouthWiden(mesh, b));
|
|
5751
6059
|
bindings.push(
|
|
5752
|
-
{ parameter:
|
|
5753
|
-
{ parameter:
|
|
6060
|
+
{ parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
|
|
6061
|
+
{ parameter: import_format10.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
|
|
5754
6062
|
);
|
|
5755
6063
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
5756
6064
|
} else {
|
|
@@ -5760,19 +6068,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5760
6068
|
);
|
|
5761
6069
|
bindings.push(
|
|
5762
6070
|
{
|
|
5763
|
-
parameter:
|
|
6071
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5764
6072
|
channel: "scaleY",
|
|
5765
6073
|
from: 0,
|
|
5766
6074
|
to: roundTo(grow, 0.01)
|
|
5767
6075
|
},
|
|
5768
6076
|
{
|
|
5769
|
-
parameter:
|
|
6077
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5770
6078
|
channel: "scaleX",
|
|
5771
6079
|
from: 0,
|
|
5772
6080
|
to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
|
|
5773
6081
|
},
|
|
5774
6082
|
{
|
|
5775
|
-
parameter:
|
|
6083
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5776
6084
|
channel: "translateY",
|
|
5777
6085
|
from: 0,
|
|
5778
6086
|
to: -roundTo(grow * bh(b) / 2, 0.01)
|
|
@@ -5784,17 +6092,26 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5784
6092
|
case "mouth_open":
|
|
5785
6093
|
warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
|
|
5786
6094
|
bindings.push({
|
|
5787
|
-
parameter:
|
|
6095
|
+
parameter: import_format10.StandardParameter.MouthOpen,
|
|
5788
6096
|
channel: "opacity",
|
|
5789
6097
|
from: 0,
|
|
5790
6098
|
to: 1
|
|
5791
6099
|
});
|
|
5792
6100
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
5793
6101
|
break;
|
|
6102
|
+
case "mouth_inner":
|
|
6103
|
+
case "lip_lower":
|
|
6104
|
+
case "lip_upper":
|
|
6105
|
+
warps.push(
|
|
6106
|
+
mouthFoldWarp(spec.role, mesh, knotXs, b, lips.opening),
|
|
6107
|
+
mouthForm(mesh, b, lips.frame),
|
|
6108
|
+
mouthWiden(mesh, b, lips.frame)
|
|
6109
|
+
);
|
|
6110
|
+
break;
|
|
5794
6111
|
case "blush_L":
|
|
5795
6112
|
case "blush_R":
|
|
5796
6113
|
bindings.push({
|
|
5797
|
-
parameter:
|
|
6114
|
+
parameter: import_format10.StandardParameter.Cheek,
|
|
5798
6115
|
channel: "opacity",
|
|
5799
6116
|
from: AMPLITUDE.blushRest,
|
|
5800
6117
|
to: 1
|
|
@@ -5805,8 +6122,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5805
6122
|
if (byRole.has("hair_front")) {
|
|
5806
6123
|
const amp = style.sway * AMPLITUDE.sway * hh;
|
|
5807
6124
|
warps.push(
|
|
5808
|
-
hairSway(
|
|
5809
|
-
hairSway(
|
|
6125
|
+
hairSway(import_format10.StandardParameter.HairSwayX, mesh, b, amp),
|
|
6126
|
+
hairSway(import_format10.StandardParameter.HairSwayZ, mesh, b, amp)
|
|
5810
6127
|
);
|
|
5811
6128
|
}
|
|
5812
6129
|
warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
|
|
@@ -5847,29 +6164,6 @@ function hairFrontGrid(b, frame, boxes) {
|
|
|
5847
6164
|
);
|
|
5848
6165
|
return { xs, ys };
|
|
5849
6166
|
}
|
|
5850
|
-
function columnMesh(b, grid) {
|
|
5851
|
-
const { xs } = grid;
|
|
5852
|
-
const rows = grid.ys.length - 1;
|
|
5853
|
-
const vertices = [];
|
|
5854
|
-
const uvs = [];
|
|
5855
|
-
for (let r = 0; r <= rows; r++) {
|
|
5856
|
-
for (const x of xs) {
|
|
5857
|
-
const u = (x - b.x0) / bw(b);
|
|
5858
|
-
vertices.push(roundTo(u - 0.5, 1e-5), roundTo(0.5 - r / rows, 1e-5));
|
|
5859
|
-
uvs.push(roundTo(u, 1e-5), roundTo(r / rows, 1e-5));
|
|
5860
|
-
}
|
|
5861
|
-
}
|
|
5862
|
-
const n = xs.length;
|
|
5863
|
-
const indices = [];
|
|
5864
|
-
for (let r = 0; r < rows; r++) {
|
|
5865
|
-
for (let c = 0; c + 1 < n; c++) {
|
|
5866
|
-
const tl = r * n + c;
|
|
5867
|
-
const bl = tl + n;
|
|
5868
|
-
indices.push(bl, bl + 1, tl, tl, bl + 1, tl + 1);
|
|
5869
|
-
}
|
|
5870
|
-
}
|
|
5871
|
-
return { vertices, uvs, indices };
|
|
5872
|
-
}
|
|
5873
6167
|
function meshScale(role, frame) {
|
|
5874
6168
|
switch (role) {
|
|
5875
6169
|
case "hair_front":
|
|
@@ -5895,9 +6189,9 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
|
5895
6189
|
pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
|
|
5896
6190
|
bindings: [
|
|
5897
6191
|
// AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
|
|
5898
|
-
{ parameter:
|
|
6192
|
+
{ parameter: import_format10.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
|
|
5899
6193
|
{
|
|
5900
|
-
parameter:
|
|
6194
|
+
parameter: import_format10.StandardParameter.Breath,
|
|
5901
6195
|
channel: "translateY",
|
|
5902
6196
|
from: 0,
|
|
5903
6197
|
to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
|
|
@@ -5915,8 +6209,8 @@ function deformers(frame, grids, bodyWarp, arms, byRole) {
|
|
|
5915
6209
|
points: g.lattice.points
|
|
5916
6210
|
},
|
|
5917
6211
|
warp2d: {
|
|
5918
|
-
parameter:
|
|
5919
|
-
parameterY:
|
|
6212
|
+
parameter: import_format10.StandardParameter.AngleX,
|
|
6213
|
+
parameterY: import_format10.StandardParameter.AngleY,
|
|
5920
6214
|
valuesX: X_STOPS,
|
|
5921
6215
|
valuesY: Y_STOPS,
|
|
5922
6216
|
keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
|
|
@@ -5929,16 +6223,16 @@ function hairPhysics() {
|
|
|
5929
6223
|
return [
|
|
5930
6224
|
{
|
|
5931
6225
|
id: "hairSway",
|
|
5932
|
-
input: { parameter:
|
|
5933
|
-
output: { parameter:
|
|
6226
|
+
input: { parameter: import_format10.StandardParameter.AngleX, weight: 1 },
|
|
6227
|
+
output: { parameter: import_format10.StandardParameter.HairSwayX, scale: 5 },
|
|
5934
6228
|
mass: 1,
|
|
5935
6229
|
stiffness: 30,
|
|
5936
6230
|
damping: 3
|
|
5937
6231
|
},
|
|
5938
6232
|
{
|
|
5939
6233
|
id: "hairTilt",
|
|
5940
|
-
input: { parameter:
|
|
5941
|
-
output: { parameter:
|
|
6234
|
+
input: { parameter: import_format10.StandardParameter.AngleZ, weight: 1 },
|
|
6235
|
+
output: { parameter: import_format10.StandardParameter.HairSwayZ, scale: 8 },
|
|
5942
6236
|
mass: 1,
|
|
5943
6237
|
stiffness: 25,
|
|
5944
6238
|
damping: 2.5
|
|
@@ -5948,13 +6242,13 @@ function hairPhysics() {
|
|
|
5948
6242
|
function declareParameters(roles) {
|
|
5949
6243
|
const out = [];
|
|
5950
6244
|
const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
|
|
5951
|
-
add(
|
|
5952
|
-
add(
|
|
5953
|
-
add(
|
|
6245
|
+
add(import_format10.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
|
|
6246
|
+
add(import_format10.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
|
|
6247
|
+
add(import_format10.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
|
|
5954
6248
|
if (roles.has("body")) {
|
|
5955
|
-
add(
|
|
5956
|
-
add(
|
|
5957
|
-
add(
|
|
6249
|
+
add(import_format10.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
|
|
6250
|
+
add(import_format10.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
|
|
6251
|
+
add(import_format10.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
|
|
5958
6252
|
}
|
|
5959
6253
|
const addPose = (side, pose, angle) => {
|
|
5960
6254
|
add(pose, `Arm Pose ${side}`, 0, 1, 0);
|
|
@@ -5963,40 +6257,40 @@ function declareParameters(roles) {
|
|
|
5963
6257
|
const [armMin, armMax] = ARM_RANGE;
|
|
5964
6258
|
const [elbowMin, elbowMax] = ELBOW_RANGE;
|
|
5965
6259
|
if (roles.has("arm_L")) {
|
|
5966
|
-
add(
|
|
5967
|
-
add(
|
|
6260
|
+
add(import_format10.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
|
|
6261
|
+
add(import_format10.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
|
|
5968
6262
|
if (roles.has("forearm_pose_L"))
|
|
5969
|
-
addPose("L",
|
|
6263
|
+
addPose("L", import_format10.StandardParameter.ArmPoseLeft, import_format10.StandardParameter.ArmPoseAngleLeft);
|
|
5970
6264
|
}
|
|
5971
6265
|
if (roles.has("arm_R")) {
|
|
5972
|
-
add(
|
|
5973
|
-
add(
|
|
6266
|
+
add(import_format10.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
|
|
6267
|
+
add(import_format10.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
|
|
5974
6268
|
if (roles.has("forearm_pose_R"))
|
|
5975
|
-
addPose("R",
|
|
6269
|
+
addPose("R", import_format10.StandardParameter.ArmPoseRight, import_format10.StandardParameter.ArmPoseAngleRight);
|
|
5976
6270
|
}
|
|
5977
|
-
add(
|
|
5978
|
-
add(
|
|
6271
|
+
add(import_format10.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
|
|
6272
|
+
add(import_format10.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
|
|
5979
6273
|
if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
|
|
5980
|
-
add(
|
|
5981
|
-
add(
|
|
6274
|
+
add(import_format10.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
|
|
6275
|
+
add(import_format10.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
|
|
5982
6276
|
}
|
|
5983
|
-
add(
|
|
5984
|
-
add(
|
|
6277
|
+
add(import_format10.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
|
|
6278
|
+
add(import_format10.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
|
|
5985
6279
|
if (roles.has("brow_L")) {
|
|
5986
|
-
add(
|
|
5987
|
-
add(
|
|
6280
|
+
add(import_format10.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
|
|
6281
|
+
add(import_format10.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
|
|
5988
6282
|
}
|
|
5989
6283
|
if (roles.has("brow_R")) {
|
|
5990
|
-
add(
|
|
5991
|
-
add(
|
|
6284
|
+
add(import_format10.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
|
|
6285
|
+
add(import_format10.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
|
|
5992
6286
|
}
|
|
5993
6287
|
if (roles.has("blush_L") || roles.has("blush_R")) {
|
|
5994
|
-
add(
|
|
6288
|
+
add(import_format10.StandardParameter.Cheek, "Cheek", 0, 1, 0);
|
|
5995
6289
|
}
|
|
5996
|
-
add(
|
|
6290
|
+
add(import_format10.StandardParameter.Breath, "Breath", 0, 1, 0);
|
|
5997
6291
|
if (roles.has("hair_front")) {
|
|
5998
|
-
add(
|
|
5999
|
-
add(
|
|
6292
|
+
add(import_format10.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
6293
|
+
add(import_format10.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
6000
6294
|
}
|
|
6001
6295
|
return out;
|
|
6002
6296
|
}
|
|
@@ -6015,6 +6309,7 @@ function declareParameters(roles) {
|
|
|
6015
6309
|
DeletePhysicsRig,
|
|
6016
6310
|
EditorDocument,
|
|
6017
6311
|
HEAD_BAND,
|
|
6312
|
+
LIP_ROLES,
|
|
6018
6313
|
LayerGeometryError,
|
|
6019
6314
|
SPECK_CORE_FRACTION,
|
|
6020
6315
|
SetDeformerBindings,
|
|
@@ -6036,6 +6331,7 @@ function declareParameters(roles) {
|
|
|
6036
6331
|
UV_INSET_PX,
|
|
6037
6332
|
armGeometry,
|
|
6038
6333
|
captureBindingEndpoint,
|
|
6334
|
+
columnRuns,
|
|
6039
6335
|
computeGridOffsets,
|
|
6040
6336
|
createDefaultMatrixDeformer,
|
|
6041
6337
|
createDefaultPart,
|
|
@@ -6050,6 +6346,9 @@ function declareParameters(roles) {
|
|
|
6050
6346
|
headHalfOf,
|
|
6051
6347
|
interpolateGridOffsets,
|
|
6052
6348
|
isSpeckCore,
|
|
6349
|
+
mouthFold,
|
|
6350
|
+
mouthOpening,
|
|
6351
|
+
mouthRestShift,
|
|
6053
6352
|
packAtlas,
|
|
6054
6353
|
parseLayerRoles,
|
|
6055
6354
|
partIdsOfRole,
|