@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/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
- function mouthForm(mesh, box) {
2384
- const a = 0.3 * bh(box);
2385
- const c = cx(box);
2386
- const half = bw(box) / 2;
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 = cx(box);
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", required: true },
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 import_format9 = require("@ikijs/format");
3468
+ var import_format10 = require("@ikijs/format");
3172
3469
 
3173
3470
  // src/auto-rig/animations.ts
3174
- var import_format7 = require("@ikijs/format");
3471
+ var import_format8 = require("@ikijs/format");
3175
3472
  function browsY(y) {
3176
3473
  return [
3177
- { parameter: import_format7.StandardParameter.BrowLeftY, value: y, blend: "add" },
3178
- { parameter: import_format7.StandardParameter.BrowRightY, value: y, blend: "add" }
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: import_format7.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
3184
- { parameter: import_format7.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
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: import_format7.StandardParameter.Cheek, value, blend: "add" };
3485
+ return { parameter: import_format8.StandardParameter.Cheek, value, blend: "add" };
3189
3486
  }
3190
- var BROWS = [import_format7.StandardParameter.BrowLeftY, import_format7.StandardParameter.BrowRightY, import_format7.StandardParameter.BrowLeftAngle, import_format7.StandardParameter.BrowRightAngle];
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: import_format7.StandardParameter.MouthForm, value: 1, blend: "add" },
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: import_format7.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
3206
- { parameter: import_format7.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
3207
- { parameter: import_format7.StandardParameter.MouthForm, value: 1, blend: "add" },
3208
- { parameter: import_format7.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
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: import_format7.StandardParameter.MouthForm, value: -0.9, blend: "add" }
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: import_format7.StandardParameter.MouthForm, value: -0.8, blend: "add" }
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: import_format7.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
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: [import_format7.StandardParameter.Cheek],
3541
+ needs: [import_format8.StandardParameter.Cheek],
3245
3542
  parameters: [
3246
3543
  cheek(1),
3247
3544
  // Gaze down.
3248
- { parameter: import_format7.StandardParameter.EyeballY, value: -0.65, blend: "add" },
3249
- { parameter: import_format7.StandardParameter.MouthForm, value: 0.4, blend: "add" },
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: import_format7.StandardParameter.AngleY,
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: import_format7.StandardParameter.AngleX,
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: import_format7.StandardParameter.AngleZ,
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: import_format7.StandardParameter.ArmPoseRight,
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: import_format7.StandardParameter.ArmRight,
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: import_format7.StandardParameter.ArmPoseAngleRight,
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 = [import_format7.StandardParameter.ArmRight, import_format7.StandardParameter.ArmPoseRight, import_format7.StandardParameter.ArmPoseAngleRight].every(
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 import_format8 = require("@ikijs/format");
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 = centreY("mouth");
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 mouthBox = byRole.has("mouth_open") ? unionBoxes([box("mouth"), box("mouth_open")]) : box("mouth");
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: { x: cx(m), y: cy(m) },
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 === import_format8.StandardParameter.AngleX);
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: import_format9.IKI_FORMAT_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, import_format9.parseIkiModel)(model);
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
- import_format9.StandardParameter.AngleX,
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
- import_format9.StandardParameter.AngleY,
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" ? import_format9.StandardParameter.EyeOpenLeft : import_format9.StandardParameter.EyeOpenRight;
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
- import_format9.StandardParameter.Breath,
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: import_format9.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
5753
- { parameter: import_format9.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
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: import_format9.StandardParameter.MouthOpen,
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: import_format9.StandardParameter.MouthOpen,
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: import_format9.StandardParameter.MouthOpen,
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: import_format9.StandardParameter.MouthOpen,
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: import_format9.StandardParameter.Cheek,
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(import_format9.StandardParameter.HairSwayX, mesh, b, amp),
5809
- hairSway(import_format9.StandardParameter.HairSwayZ, mesh, b, amp)
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: import_format9.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
6192
+ { parameter: import_format10.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
5899
6193
  {
5900
- parameter: import_format9.StandardParameter.Breath,
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: import_format9.StandardParameter.AngleX,
5919
- parameterY: import_format9.StandardParameter.AngleY,
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: import_format9.StandardParameter.AngleX, weight: 1 },
5933
- output: { parameter: import_format9.StandardParameter.HairSwayX, scale: 5 },
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: import_format9.StandardParameter.AngleZ, weight: 1 },
5941
- output: { parameter: import_format9.StandardParameter.HairSwayZ, scale: 8 },
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(import_format9.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
5952
- add(import_format9.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
5953
- add(import_format9.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
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(import_format9.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
5956
- add(import_format9.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
5957
- add(import_format9.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
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(import_format9.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
5967
- add(import_format9.StandardParameter.ElbowLeft, "Elbow L", elbowMin, elbowMax, 0);
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", import_format9.StandardParameter.ArmPoseLeft, import_format9.StandardParameter.ArmPoseAngleLeft);
6263
+ addPose("L", import_format10.StandardParameter.ArmPoseLeft, import_format10.StandardParameter.ArmPoseAngleLeft);
5970
6264
  }
5971
6265
  if (roles.has("arm_R")) {
5972
- add(import_format9.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
5973
- add(import_format9.StandardParameter.ElbowRight, "Elbow R", elbowMin, elbowMax, 0);
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", import_format9.StandardParameter.ArmPoseRight, import_format9.StandardParameter.ArmPoseAngleRight);
6269
+ addPose("R", import_format10.StandardParameter.ArmPoseRight, import_format10.StandardParameter.ArmPoseAngleRight);
5976
6270
  }
5977
- add(import_format9.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
5978
- add(import_format9.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
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(import_format9.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
5981
- add(import_format9.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
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(import_format9.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
5984
- add(import_format9.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
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(import_format9.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
5987
- add(import_format9.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
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(import_format9.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
5991
- add(import_format9.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
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(import_format9.StandardParameter.Cheek, "Cheek", 0, 1, 0);
6288
+ add(import_format10.StandardParameter.Cheek, "Cheek", 0, 1, 0);
5995
6289
  }
5996
- add(import_format9.StandardParameter.Breath, "Breath", 0, 1, 0);
6290
+ add(import_format10.StandardParameter.Breath, "Breath", 0, 1, 0);
5997
6291
  if (roles.has("hair_front")) {
5998
- add(import_format9.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
5999
- add(import_format9.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
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,