@ikijs/editor 0.21.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 +13 -13
- package/dist/index.d.mts +122 -1
- package/dist/index.d.ts +122 -1
- package/dist/index.js +266 -198
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +263 -200
- 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,
|
|
@@ -3087,6 +3092,9 @@ function forearmPoseDeformer(role, arm) {
|
|
|
3087
3092
|
};
|
|
3088
3093
|
}
|
|
3089
3094
|
|
|
3095
|
+
// src/auto-rig/mouth.ts
|
|
3096
|
+
var import_format7 = require("@ikijs/format");
|
|
3097
|
+
|
|
3090
3098
|
// src/auto-rig/roles.ts
|
|
3091
3099
|
var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
|
|
3092
3100
|
var isLipRole = (role) => LIP_ROLES.includes(role);
|
|
@@ -3228,33 +3236,261 @@ function parseLayerRoles(fileNames) {
|
|
|
3228
3236
|
return out;
|
|
3229
3237
|
}
|
|
3230
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
|
+
|
|
3231
3467
|
// src/auto-rig/generate.ts
|
|
3232
3468
|
var import_format10 = require("@ikijs/format");
|
|
3233
3469
|
|
|
3234
3470
|
// src/auto-rig/animations.ts
|
|
3235
|
-
var
|
|
3471
|
+
var import_format8 = require("@ikijs/format");
|
|
3236
3472
|
function browsY(y) {
|
|
3237
3473
|
return [
|
|
3238
|
-
{ parameter:
|
|
3239
|
-
{ parameter:
|
|
3474
|
+
{ parameter: import_format8.StandardParameter.BrowLeftY, value: y, blend: "add" },
|
|
3475
|
+
{ parameter: import_format8.StandardParameter.BrowRightY, value: y, blend: "add" }
|
|
3240
3476
|
];
|
|
3241
3477
|
}
|
|
3242
3478
|
function browsTilt(innerDown) {
|
|
3243
3479
|
return [
|
|
3244
|
-
{ parameter:
|
|
3245
|
-
{ parameter:
|
|
3480
|
+
{ parameter: import_format8.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
3481
|
+
{ parameter: import_format8.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
3246
3482
|
];
|
|
3247
3483
|
}
|
|
3248
3484
|
function cheek(value) {
|
|
3249
|
-
return { parameter:
|
|
3485
|
+
return { parameter: import_format8.StandardParameter.Cheek, value, blend: "add" };
|
|
3250
3486
|
}
|
|
3251
|
-
var BROWS = [
|
|
3487
|
+
var BROWS = [import_format8.StandardParameter.BrowLeftY, import_format8.StandardParameter.BrowRightY, import_format8.StandardParameter.BrowLeftAngle, import_format8.StandardParameter.BrowRightAngle];
|
|
3252
3488
|
var EXPRESSIONS = [
|
|
3253
3489
|
{
|
|
3254
3490
|
id: "smile",
|
|
3255
3491
|
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
3256
3492
|
parameters: [
|
|
3257
|
-
{ parameter:
|
|
3493
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3258
3494
|
...browsY(0.25),
|
|
3259
3495
|
cheek(0.4)
|
|
3260
3496
|
]
|
|
@@ -3263,10 +3499,10 @@ var EXPRESSIONS = [
|
|
|
3263
3499
|
id: "laugh",
|
|
3264
3500
|
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
3265
3501
|
parameters: [
|
|
3266
|
-
{ parameter:
|
|
3267
|
-
{ parameter:
|
|
3268
|
-
{ parameter:
|
|
3269
|
-
{ 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" },
|
|
3270
3506
|
...browsY(0.4),
|
|
3271
3507
|
cheek(0.65)
|
|
3272
3508
|
]
|
|
@@ -3278,7 +3514,7 @@ var EXPRESSIONS = [
|
|
|
3278
3514
|
parameters: [
|
|
3279
3515
|
...browsY(-0.5),
|
|
3280
3516
|
...browsTilt(0.8),
|
|
3281
|
-
{ parameter:
|
|
3517
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: -0.9, blend: "add" }
|
|
3282
3518
|
]
|
|
3283
3519
|
},
|
|
3284
3520
|
{
|
|
@@ -3288,7 +3524,7 @@ var EXPRESSIONS = [
|
|
|
3288
3524
|
parameters: [
|
|
3289
3525
|
...browsY(0.35),
|
|
3290
3526
|
...browsTilt(-0.8),
|
|
3291
|
-
{ parameter:
|
|
3527
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: -0.8, blend: "add" }
|
|
3292
3528
|
]
|
|
3293
3529
|
},
|
|
3294
3530
|
{
|
|
@@ -3296,18 +3532,18 @@ var EXPRESSIONS = [
|
|
|
3296
3532
|
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
3297
3533
|
parameters: [
|
|
3298
3534
|
...browsY(0.8),
|
|
3299
|
-
{ parameter:
|
|
3535
|
+
{ parameter: import_format8.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
3300
3536
|
]
|
|
3301
3537
|
},
|
|
3302
3538
|
{
|
|
3303
3539
|
id: "shy",
|
|
3304
3540
|
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
3305
|
-
needs: [
|
|
3541
|
+
needs: [import_format8.StandardParameter.Cheek],
|
|
3306
3542
|
parameters: [
|
|
3307
3543
|
cheek(1),
|
|
3308
3544
|
// Gaze down.
|
|
3309
|
-
{ parameter:
|
|
3310
|
-
{ parameter:
|
|
3545
|
+
{ parameter: import_format8.StandardParameter.EyeballY, value: -0.65, blend: "add" },
|
|
3546
|
+
{ parameter: import_format8.StandardParameter.MouthForm, value: 0.4, blend: "add" },
|
|
3311
3547
|
...browsTilt(-0.25)
|
|
3312
3548
|
]
|
|
3313
3549
|
}
|
|
@@ -3327,7 +3563,7 @@ var HEAD_MOTIONS = {
|
|
|
3327
3563
|
duration: 1,
|
|
3328
3564
|
curves: [
|
|
3329
3565
|
{
|
|
3330
|
-
parameter:
|
|
3566
|
+
parameter: import_format8.StandardParameter.AngleY,
|
|
3331
3567
|
keys: [
|
|
3332
3568
|
[0, 0],
|
|
3333
3569
|
[0.3, -18],
|
|
@@ -3348,7 +3584,7 @@ var HEAD_MOTIONS = {
|
|
|
3348
3584
|
fadeIn: 0.15,
|
|
3349
3585
|
curves: [
|
|
3350
3586
|
{
|
|
3351
|
-
parameter:
|
|
3587
|
+
parameter: import_format8.StandardParameter.AngleX,
|
|
3352
3588
|
keys: [
|
|
3353
3589
|
[0, 0],
|
|
3354
3590
|
[0.2, -18],
|
|
@@ -3367,7 +3603,7 @@ var HEAD_MOTIONS = {
|
|
|
3367
3603
|
duration: 1.4,
|
|
3368
3604
|
curves: [
|
|
3369
3605
|
{
|
|
3370
|
-
parameter:
|
|
3606
|
+
parameter: import_format8.StandardParameter.AngleZ,
|
|
3371
3607
|
keys: [
|
|
3372
3608
|
[0, 0],
|
|
3373
3609
|
[0.35, 20],
|
|
@@ -3388,7 +3624,7 @@ var WAVE = {
|
|
|
3388
3624
|
fadeOut: 0.15,
|
|
3389
3625
|
curves: [
|
|
3390
3626
|
{
|
|
3391
|
-
parameter:
|
|
3627
|
+
parameter: import_format8.StandardParameter.ArmPoseRight,
|
|
3392
3628
|
interpolation: "linear",
|
|
3393
3629
|
keys: [
|
|
3394
3630
|
[0, 0],
|
|
@@ -3398,7 +3634,7 @@ var WAVE = {
|
|
|
3398
3634
|
]
|
|
3399
3635
|
},
|
|
3400
3636
|
{
|
|
3401
|
-
parameter:
|
|
3637
|
+
parameter: import_format8.StandardParameter.ArmRight,
|
|
3402
3638
|
keys: [
|
|
3403
3639
|
[0, 0],
|
|
3404
3640
|
[0.4, 10],
|
|
@@ -3407,7 +3643,7 @@ var WAVE = {
|
|
|
3407
3643
|
]
|
|
3408
3644
|
},
|
|
3409
3645
|
{
|
|
3410
|
-
parameter:
|
|
3646
|
+
parameter: import_format8.StandardParameter.ArmPoseAngleRight,
|
|
3411
3647
|
keys: [
|
|
3412
3648
|
[0, 0],
|
|
3413
3649
|
[0.5, 0],
|
|
@@ -3423,7 +3659,7 @@ var WAVE = {
|
|
|
3423
3659
|
]
|
|
3424
3660
|
};
|
|
3425
3661
|
function defaultMotions(declared) {
|
|
3426
|
-
const waves = [
|
|
3662
|
+
const waves = [import_format8.StandardParameter.ArmRight, import_format8.StandardParameter.ArmPoseRight, import_format8.StandardParameter.ArmPoseAngleRight].every(
|
|
3427
3663
|
(id) => declared.has(id)
|
|
3428
3664
|
);
|
|
3429
3665
|
return waves ? { ...HEAD_MOTIONS, ...WAVE } : HEAD_MOTIONS;
|
|
@@ -3661,179 +3897,6 @@ function checkStrandEdges(strand, irises, hair) {
|
|
|
3661
3897
|
// src/auto-rig/context.ts
|
|
3662
3898
|
var import_format9 = require("@ikijs/format");
|
|
3663
3899
|
|
|
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
|
-
}
|
|
3836
|
-
|
|
3837
3900
|
// src/auto-rig/head.ts
|
|
3838
3901
|
function outerEdge(runs, axisX, side) {
|
|
3839
3902
|
if (runs.length === 0) return void 0;
|
|
@@ -6246,6 +6309,7 @@ function declareParameters(roles) {
|
|
|
6246
6309
|
DeletePhysicsRig,
|
|
6247
6310
|
EditorDocument,
|
|
6248
6311
|
HEAD_BAND,
|
|
6312
|
+
LIP_ROLES,
|
|
6249
6313
|
LayerGeometryError,
|
|
6250
6314
|
SPECK_CORE_FRACTION,
|
|
6251
6315
|
SetDeformerBindings,
|
|
@@ -6267,6 +6331,7 @@ function declareParameters(roles) {
|
|
|
6267
6331
|
UV_INSET_PX,
|
|
6268
6332
|
armGeometry,
|
|
6269
6333
|
captureBindingEndpoint,
|
|
6334
|
+
columnRuns,
|
|
6270
6335
|
computeGridOffsets,
|
|
6271
6336
|
createDefaultMatrixDeformer,
|
|
6272
6337
|
createDefaultPart,
|
|
@@ -6281,6 +6346,9 @@ function declareParameters(roles) {
|
|
|
6281
6346
|
headHalfOf,
|
|
6282
6347
|
interpolateGridOffsets,
|
|
6283
6348
|
isSpeckCore,
|
|
6349
|
+
mouthFold,
|
|
6350
|
+
mouthOpening,
|
|
6351
|
+
mouthRestShift,
|
|
6284
6352
|
packAtlas,
|
|
6285
6353
|
parseLayerRoles,
|
|
6286
6354
|
partIdsOfRole,
|