@ikijs/editor 0.22.0 → 0.24.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 +1 -1
- package/dist/index.d.mts +127 -55
- package/dist/index.d.ts +127 -55
- package/dist/index.js +190 -80
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +188 -80
- 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_INSIDE_ROLES: () => LIP_INSIDE_ROLES,
|
|
36
37
|
LIP_ROLES: () => LIP_ROLES,
|
|
37
38
|
LayerGeometryError: () => LayerGeometryError,
|
|
38
39
|
SPECK_CORE_FRACTION: () => SPECK_CORE_FRACTION,
|
|
@@ -69,6 +70,7 @@ __export(index_exports, {
|
|
|
69
70
|
generateIkiFromLayerSet: () => generateIkiFromLayerSet,
|
|
70
71
|
headHalfOf: () => headHalfOf,
|
|
71
72
|
interpolateGridOffsets: () => interpolateGridOffsets,
|
|
73
|
+
isLipInsideRole: () => isLipInsideRole,
|
|
72
74
|
isSpeckCore: () => isSpeckCore,
|
|
73
75
|
mouthFold: () => mouthFold,
|
|
74
76
|
mouthOpening: () => mouthOpening,
|
|
@@ -1843,7 +1845,10 @@ function captureBindingEndpoint(channel, restValue, posedValue) {
|
|
|
1843
1845
|
// src/atlas.ts
|
|
1844
1846
|
var ATLAS_PADDING = 2;
|
|
1845
1847
|
var UV_INSET_PX = 0.5;
|
|
1846
|
-
function packAtlas(sources, padding = ATLAS_PADDING) {
|
|
1848
|
+
function packAtlas(sources, padding = ATLAS_PADDING, maxSide) {
|
|
1849
|
+
if (maxSide !== void 0 && !(isFinite(maxSide) && maxSide > 0)) {
|
|
1850
|
+
throw new Error(`packAtlas: invalid maxSide ${maxSide}`);
|
|
1851
|
+
}
|
|
1847
1852
|
for (const src of sources) {
|
|
1848
1853
|
if (!isFinite(src.width) || src.width <= 0) {
|
|
1849
1854
|
throw new Error(
|
|
@@ -1864,42 +1869,50 @@ function packAtlas(sources, padding = ATLAS_PADDING) {
|
|
|
1864
1869
|
(sum, s) => sum + (s.width + padding) * (s.height + padding),
|
|
1865
1870
|
0
|
|
1866
1871
|
);
|
|
1867
|
-
const
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1872
|
+
const widest = Math.max(...sorted.map((s) => s.width + padding));
|
|
1873
|
+
const firstWidth = Math.max(Math.ceil(Math.sqrt(totalArea)), widest);
|
|
1874
|
+
const shelve = (targetWidth) => {
|
|
1875
|
+
const placements = [];
|
|
1876
|
+
let shelfX = 0;
|
|
1877
|
+
let shelfY = 0;
|
|
1878
|
+
let shelfHeight = 0;
|
|
1879
|
+
for (const src of sorted) {
|
|
1880
|
+
const paddedW = src.width + padding;
|
|
1881
|
+
const paddedH = src.height + padding;
|
|
1882
|
+
if (shelfX > 0 && shelfX + paddedW > targetWidth) {
|
|
1883
|
+
shelfY += shelfHeight;
|
|
1884
|
+
shelfX = 0;
|
|
1885
|
+
shelfHeight = 0;
|
|
1886
|
+
}
|
|
1887
|
+
placements.push({
|
|
1888
|
+
id: src.id,
|
|
1889
|
+
x: shelfX,
|
|
1890
|
+
y: shelfY,
|
|
1891
|
+
width: src.width,
|
|
1892
|
+
height: src.height
|
|
1893
|
+
});
|
|
1894
|
+
shelfX += paddedW;
|
|
1895
|
+
if (paddedH > shelfHeight) shelfHeight = paddedH;
|
|
1896
|
+
}
|
|
1897
|
+
let pageWidth = 0;
|
|
1898
|
+
let pageHeight = 0;
|
|
1899
|
+
for (const p of placements) {
|
|
1900
|
+
const right = p.x + p.width + padding;
|
|
1901
|
+
const bottom = p.y + p.height + padding;
|
|
1902
|
+
if (right > pageWidth) pageWidth = right;
|
|
1903
|
+
if (bottom > pageHeight) pageHeight = bottom;
|
|
1904
|
+
}
|
|
1905
|
+
return { pageWidth, pageHeight, placements };
|
|
1906
|
+
};
|
|
1907
|
+
const first = shelve(firstWidth);
|
|
1908
|
+
if (maxSide === void 0 || first.pageHeight <= maxSide || first.pageWidth > maxSide) {
|
|
1909
|
+
return { ...first, padding };
|
|
1893
1910
|
}
|
|
1894
|
-
let
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
const right = p.x + p.width + padding;
|
|
1898
|
-
const bottom = p.y + p.height + padding;
|
|
1899
|
-
if (right > pageWidth) pageWidth = right;
|
|
1900
|
-
if (bottom > pageHeight) pageHeight = bottom;
|
|
1911
|
+
for (let w = widest; w <= maxSide; w++) {
|
|
1912
|
+
const layout = shelve(w);
|
|
1913
|
+
if (layout.pageHeight <= maxSide) return { ...layout, padding };
|
|
1901
1914
|
}
|
|
1902
|
-
return {
|
|
1915
|
+
return { ...first, padding };
|
|
1903
1916
|
}
|
|
1904
1917
|
function uvRectFor(placement, page, insetPx = UV_INSET_PX) {
|
|
1905
1918
|
const px = placement.x + insetPx;
|
|
@@ -3002,14 +3015,14 @@ function armParts(role, layer, g, order, poseSwitch) {
|
|
|
3002
3015
|
},
|
|
3003
3016
|
true
|
|
3004
3017
|
),
|
|
3005
|
-
part(role, ids.armDeformer, order + 1, meshOf(band(0.5, seam), quad)),
|
|
3006
3018
|
part(
|
|
3007
3019
|
ids.forearm,
|
|
3008
3020
|
ids.forearmDeformer,
|
|
3009
|
-
order +
|
|
3021
|
+
order + 1,
|
|
3010
3022
|
meshOf(band(seam, -0.5), quad),
|
|
3011
3023
|
true
|
|
3012
|
-
)
|
|
3024
|
+
),
|
|
3025
|
+
part(role, ids.armDeformer, order + 2, meshOf(band(0.5, seam), quad))
|
|
3013
3026
|
];
|
|
3014
3027
|
}
|
|
3015
3028
|
var rotateBinding = (parameter, range, sign) => ({
|
|
@@ -3098,21 +3111,26 @@ var import_format7 = require("@ikijs/format");
|
|
|
3098
3111
|
// src/auto-rig/roles.ts
|
|
3099
3112
|
var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
|
|
3100
3113
|
var isLipRole = (role) => LIP_ROLES.includes(role);
|
|
3114
|
+
var LIP_INSIDE_ROLES = ["mouth_tongue", "mouth_teeth"];
|
|
3115
|
+
var isLipInsideRole = (role) => LIP_INSIDE_ROLES.includes(role);
|
|
3101
3116
|
var ROLE_TABLE = [
|
|
3102
3117
|
{ role: "hair_back", family: "hair_back" },
|
|
3103
3118
|
{ role: "body", family: "body" },
|
|
3104
|
-
{ role: "arm_L", family: "arm", parts: ["elbow_L", "
|
|
3105
|
-
{ role: "arm_R", family: "arm", parts: ["elbow_R", "
|
|
3119
|
+
{ role: "arm_L", family: "arm", parts: ["elbow_L", "forearm_L", "arm_L"] },
|
|
3120
|
+
{ role: "arm_R", family: "arm", parts: ["elbow_R", "forearm_R", "arm_R"] },
|
|
3106
3121
|
{ role: "face", family: "face", required: true },
|
|
3107
3122
|
{ role: "blush_L", family: "face" },
|
|
3108
3123
|
{ role: "blush_R", family: "face" },
|
|
3109
3124
|
{ role: "nose", family: "nose" },
|
|
3110
3125
|
{ role: "mouth", family: "mouth" },
|
|
3111
3126
|
{ role: "mouth_open", family: "mouth" },
|
|
3112
|
-
// The lip set, back to front: the interior, the
|
|
3113
|
-
// lip and its line over both. It
|
|
3114
|
-
// (`mouth.ts`), so it stands in for
|
|
3127
|
+
// The lip set, back to front: the interior, the tongue and the teeth clipped
|
|
3128
|
+
// to it, the lower lip's skin, the upper lip and its line over both. It
|
|
3129
|
+
// folds open instead of crossfading (`mouth.ts`), so it stands in for
|
|
3130
|
+
// `mouth`, never beside it.
|
|
3115
3131
|
{ role: "mouth_inner", family: "mouth" },
|
|
3132
|
+
{ role: "mouth_tongue", family: "mouth" },
|
|
3133
|
+
{ role: "mouth_teeth", family: "mouth" },
|
|
3116
3134
|
{ role: "lip_lower", family: "mouth" },
|
|
3117
3135
|
{ role: "lip_upper", family: "mouth" },
|
|
3118
3136
|
{ role: "eye_L", family: "eye_L", required: true },
|
|
@@ -3175,6 +3193,12 @@ function checkArmsHaveBody(roles) {
|
|
|
3175
3193
|
function checkMouth(roles) {
|
|
3176
3194
|
const has = new Set(roles);
|
|
3177
3195
|
const present = LIP_ROLES.filter((r) => has.has(r));
|
|
3196
|
+
const inside = LIP_INSIDE_ROLES.filter((r) => has.has(r));
|
|
3197
|
+
if (inside.length > 0 && present.length === 0) {
|
|
3198
|
+
throw new Error(
|
|
3199
|
+
`auto-rig: ${inside[0]} needs the lip set ${LIP_ROLES.join(", ")} (it is cut from the interior and clipped to it)`
|
|
3200
|
+
);
|
|
3201
|
+
}
|
|
3178
3202
|
if (present.length === 0) {
|
|
3179
3203
|
if (!has.has("mouth")) {
|
|
3180
3204
|
throw new Error(
|
|
@@ -3238,6 +3262,12 @@ function parseLayerRoles(fileNames) {
|
|
|
3238
3262
|
|
|
3239
3263
|
// src/auto-rig/mouth.ts
|
|
3240
3264
|
var UPPER_SHARE = 0.4;
|
|
3265
|
+
var CLOSED_SAG_MAX = 0.1;
|
|
3266
|
+
var UPPER_THIN = 0.7;
|
|
3267
|
+
var END_TAPER = 0.35;
|
|
3268
|
+
var END_TAPER_FROM = 0.7;
|
|
3269
|
+
var FLICK_PULL = 0.5;
|
|
3270
|
+
var FLICK_SQUASH = 0.25;
|
|
3241
3271
|
var MOUTH_KNOT_PX = 4;
|
|
3242
3272
|
var isLipSet = (byRole) => LIP_ROLES.every((r) => byRole.has(r));
|
|
3243
3273
|
function columnRuns(layer) {
|
|
@@ -3299,10 +3329,15 @@ function mouthOpening(byRole) {
|
|
|
3299
3329
|
for (let d = 0; (r = read(c - d) ?? read(c + d)) === void 0; d++) ;
|
|
3300
3330
|
reads.push(r);
|
|
3301
3331
|
}
|
|
3332
|
+
const n = reads.length;
|
|
3302
3333
|
const m = (x0 + x1) / 2;
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3334
|
+
const centre = Math.round(m);
|
|
3335
|
+
const smooth = reads.map((_, i) => {
|
|
3336
|
+
const near = reads.slice(Math.max(0, i - 3), i + 4);
|
|
3337
|
+
return near.reduce((s, r) => s + r.Tu, 0) / near.length;
|
|
3338
|
+
});
|
|
3339
|
+
let sag = 0;
|
|
3340
|
+
if (n >= 4) {
|
|
3306
3341
|
let s11 = 0;
|
|
3307
3342
|
let s12 = 0;
|
|
3308
3343
|
let s22 = 0;
|
|
@@ -3320,16 +3355,32 @@ function mouthOpening(byRole) {
|
|
|
3320
3355
|
t2 += b2 * d;
|
|
3321
3356
|
});
|
|
3322
3357
|
const det = s11 * s22 - s12 * s12;
|
|
3323
|
-
p = (t1 * s22 - t2 * s12) / det;
|
|
3324
|
-
q = (s11 * t2 - s12 * t1) / det;
|
|
3358
|
+
const p = (t1 * s22 - t2 * s12) / det;
|
|
3359
|
+
const q = (s11 * t2 - s12 * t1) / det;
|
|
3360
|
+
const c = centre - x0;
|
|
3361
|
+
const D = c * (centre - x1) * (p + q * (centre - m));
|
|
3362
|
+
const arc = (reads[0].Tu + reads[n - 1].Tu) / 2 - reads[c].Tu;
|
|
3363
|
+
const t = c / (n - 1);
|
|
3364
|
+
const k = 4 * t * (1 - t);
|
|
3365
|
+
const offset = arc - (smooth[0] + (smooth[n - 1] - smooth[0]) * t - smooth[c]);
|
|
3366
|
+
sag = Math.min(arc - D, CLOSED_SAG_MAX * n - Math.max(0, offset) / k);
|
|
3325
3367
|
}
|
|
3326
3368
|
const columns = reads.map((r, i) => {
|
|
3327
|
-
|
|
3328
|
-
|
|
3369
|
+
if (n < 4) return { ...r, seam: r.Tu };
|
|
3370
|
+
const t = i / (x1 - x0);
|
|
3371
|
+
const parabola = smooth[0] + (smooth[n - 1] - smooth[0]) * t - sag * 4 * t * (1 - t);
|
|
3372
|
+
return { ...r, seam: r.Tu + parabola - smooth[i] };
|
|
3329
3373
|
});
|
|
3330
|
-
const
|
|
3331
|
-
const
|
|
3374
|
+
const clamp2 = (col) => Math.min(x1, Math.max(x0, col));
|
|
3375
|
+
const base = (col) => columns[clamp2(col) - x0];
|
|
3332
3376
|
const w = base(centre).lineH;
|
|
3377
|
+
const from = END_TAPER_FROM * ((x1 - x0 + 1) / 2);
|
|
3378
|
+
const squash = (col) => {
|
|
3379
|
+
const d = Math.abs(col - centre);
|
|
3380
|
+
const edge = col < centre ? centre - upper.bbox.x : upper.bbox.x + upper.bbox.w - 1 - centre;
|
|
3381
|
+
const t = d <= from ? 0 : d >= edge ? 1 : (d - from) / (edge - from);
|
|
3382
|
+
return 1 - (1 - END_TAPER) * t;
|
|
3383
|
+
};
|
|
3333
3384
|
return {
|
|
3334
3385
|
x0,
|
|
3335
3386
|
x1,
|
|
@@ -3338,39 +3389,80 @@ function mouthOpening(byRole) {
|
|
|
3338
3389
|
w,
|
|
3339
3390
|
at(col) {
|
|
3340
3391
|
const b = base(col);
|
|
3341
|
-
|
|
3392
|
+
const s = squash(clamp2(col));
|
|
3393
|
+
return {
|
|
3394
|
+
...b,
|
|
3395
|
+
overlap: UPPER_THIN * (b.lineH * (1 - s) / 2 + Math.min(w, b.lineH) * s)
|
|
3396
|
+
};
|
|
3397
|
+
},
|
|
3398
|
+
line(col) {
|
|
3399
|
+
if (col >= x0 && col <= x1) {
|
|
3400
|
+
const b = base(col);
|
|
3401
|
+
return { bottom: b.Tu, top: b.lineTop, squash: squash(col) };
|
|
3402
|
+
}
|
|
3403
|
+
const runs = upperCols.get(col);
|
|
3404
|
+
if (runs === void 0) return void 0;
|
|
3405
|
+
return {
|
|
3406
|
+
bottom: edgesOf(upper, runs[runs.length - 1]).bottom,
|
|
3407
|
+
top: edgesOf(upper, runs[0]).top,
|
|
3408
|
+
squash: FLICK_SQUASH
|
|
3409
|
+
};
|
|
3342
3410
|
}
|
|
3343
3411
|
};
|
|
3344
3412
|
}
|
|
3345
3413
|
function mouthFold(role, opening) {
|
|
3414
|
+
const { x0, x1 } = opening;
|
|
3415
|
+
const stackThin = (c, y) => y > c.Tu ? c.Tu + UPPER_THIN * (y - c.Tu) : y;
|
|
3346
3416
|
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
3417
|
const k = 1 - v;
|
|
3418
|
+
const inside = col >= x0 && col <= x1;
|
|
3352
3419
|
switch (role) {
|
|
3353
|
-
case "
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
return (c.seam + c.overlap - c.Bl) * k;
|
|
3357
|
-
|
|
3358
|
-
|
|
3420
|
+
case "lip_lower": {
|
|
3421
|
+
if (!inside) return [0, 0];
|
|
3422
|
+
const c = opening.at(col);
|
|
3423
|
+
return [0, (c.seam + c.overlap - c.Bl) * k];
|
|
3424
|
+
}
|
|
3425
|
+
case "mouth_inner": {
|
|
3426
|
+
const c = opening.at(col);
|
|
3427
|
+
const S = c.seam + c.overlap;
|
|
3428
|
+
return [0, S + v * (stackThin(c, y) - S) - y];
|
|
3429
|
+
}
|
|
3430
|
+
case "mouth_teeth": {
|
|
3431
|
+
const c = opening.at(col);
|
|
3432
|
+
return [0, stackThin(c, y) - y + (c.seam - c.Tu) * k];
|
|
3433
|
+
}
|
|
3434
|
+
case "mouth_tongue": {
|
|
3435
|
+
const c = opening.at(col);
|
|
3436
|
+
return [0, (c.seam + c.overlap - c.Bb) * k];
|
|
3437
|
+
}
|
|
3438
|
+
case "lip_upper": {
|
|
3439
|
+
let c = col;
|
|
3440
|
+
let line = opening.line(c);
|
|
3441
|
+
while (line === void 0)
|
|
3442
|
+
line = opening.line(c += col < x0 ? 1 : -1);
|
|
3443
|
+
const thin = line.bottom + UPPER_THIN * (y - line.bottom);
|
|
3444
|
+
const mid = line.bottom + UPPER_THIN * (line.top - line.bottom) / 2;
|
|
3445
|
+
const travel = inside ? opening.at(col).seam - line.bottom : 0;
|
|
3446
|
+
const shut = mid + line.squash * (thin - mid) + travel;
|
|
3447
|
+
const pull = inside ? 0 : (1 - FLICK_PULL) * ((col < x0 ? x0 : x1 + 1) - col);
|
|
3448
|
+
return [pull * k, thin - y + (shut - thin) * k];
|
|
3449
|
+
}
|
|
3359
3450
|
}
|
|
3360
3451
|
};
|
|
3361
3452
|
}
|
|
3362
|
-
function columnOfKnot(knot, opening
|
|
3453
|
+
function columnOfKnot(knot, opening) {
|
|
3363
3454
|
const boundary = Math.round(knot + opening.canvasW / 2);
|
|
3364
|
-
|
|
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;
|
|
3455
|
+
return boundary === opening.x1 + 1 ? opening.x1 : boundary;
|
|
3367
3456
|
}
|
|
3368
3457
|
function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
3369
3458
|
const fold = mouthFold(role, opening);
|
|
3370
|
-
return localWarp(
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3459
|
+
return localWarp(
|
|
3460
|
+
import_format7.StandardParameter.MouthOpen,
|
|
3461
|
+
mesh,
|
|
3462
|
+
box,
|
|
3463
|
+
[0, 1],
|
|
3464
|
+
(p, v, i) => fold(columnOfKnot(xs[i % xs.length], opening), p[1], v)
|
|
3465
|
+
);
|
|
3374
3466
|
}
|
|
3375
3467
|
function mouthRestShift(byRole) {
|
|
3376
3468
|
const rig = buildMouthRig(byRole);
|
|
@@ -3383,18 +3475,31 @@ function mouthRestShift(byRole) {
|
|
|
3383
3475
|
fields.set(role, {
|
|
3384
3476
|
box,
|
|
3385
3477
|
xs,
|
|
3386
|
-
|
|
3478
|
+
rows: offsets.length / 2 / xs.length - 1,
|
|
3479
|
+
dy: offsets.filter((_, i) => i % 2 === 1).map((o) => o * bh(box))
|
|
3387
3480
|
});
|
|
3388
3481
|
}
|
|
3389
|
-
const
|
|
3390
|
-
return (role, col) => {
|
|
3391
|
-
const { box, xs, dy } = fields.get(role);
|
|
3392
|
-
const x = col + 0.5 -
|
|
3393
|
-
|
|
3482
|
+
const { canvasW, canvasH } = byRole.get("lip_upper");
|
|
3483
|
+
return (role, col, row) => {
|
|
3484
|
+
const { box, xs, rows, dy } = fields.get(role);
|
|
3485
|
+
const x = col + 0.5 - canvasW / 2;
|
|
3486
|
+
const y = canvasH / 2 - (row + 0.5);
|
|
3487
|
+
if (x < box.x0 || x > box.x1 || y < box.y0 || y > box.y1) return void 0;
|
|
3394
3488
|
let k = 1;
|
|
3395
3489
|
while (k < xs.length - 1 && xs[k] < x) k++;
|
|
3396
|
-
const
|
|
3397
|
-
|
|
3490
|
+
const down = (box.y1 - y) / bh(box) * rows;
|
|
3491
|
+
const r = Math.min(rows - 1, Math.floor(down));
|
|
3492
|
+
const fx = (x - xs[k - 1]) / (xs[k] - xs[k - 1]);
|
|
3493
|
+
const fy = down - r;
|
|
3494
|
+
const at = (i, j) => dy[i * xs.length + j];
|
|
3495
|
+
const tl = at(r, k - 1);
|
|
3496
|
+
const br = at(r + 1, k);
|
|
3497
|
+
if (fx <= fy) {
|
|
3498
|
+
const bl = at(r + 1, k - 1);
|
|
3499
|
+
return tl + fy * (bl - tl) + fx * (br - bl);
|
|
3500
|
+
}
|
|
3501
|
+
const tr = at(r, k);
|
|
3502
|
+
return tl + fx * (tr - tl) + fy * (br - tr);
|
|
3398
3503
|
};
|
|
3399
3504
|
}
|
|
3400
3505
|
function mouthKnots(union, opening) {
|
|
@@ -5983,7 +6088,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
5983
6088
|
headStart = fm.headStart;
|
|
5984
6089
|
} else if (spec.role === "hair_front") {
|
|
5985
6090
|
mesh = columnMesh(b, frontGrid);
|
|
5986
|
-
} else if (lips !== void 0 && isLipRole(spec.role)) {
|
|
6091
|
+
} else if (lips !== void 0 && (isLipRole(spec.role) || isLipInsideRole(spec.role))) {
|
|
5987
6092
|
({ mesh, xs: knotXs } = mouthMesh(b, lips.knots));
|
|
5988
6093
|
} else if (spec.family !== "body") {
|
|
5989
6094
|
const cells = MESH_CELLS[spec.role] ?? FEATURE_MESH_CELLS;
|
|
@@ -6100,6 +6205,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6100
6205
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
6101
6206
|
break;
|
|
6102
6207
|
case "mouth_inner":
|
|
6208
|
+
case "mouth_tongue":
|
|
6209
|
+
case "mouth_teeth":
|
|
6103
6210
|
case "lip_lower":
|
|
6104
6211
|
case "lip_upper":
|
|
6105
6212
|
warps.push(
|
|
@@ -6107,6 +6214,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6107
6214
|
mouthForm(mesh, b, lips.frame),
|
|
6108
6215
|
mouthWiden(mesh, b, lips.frame)
|
|
6109
6216
|
);
|
|
6217
|
+
if (isLipInsideRole(spec.role)) part.clip = { masks: ["mouth_inner"] };
|
|
6110
6218
|
break;
|
|
6111
6219
|
case "blush_L":
|
|
6112
6220
|
case "blush_R":
|
|
@@ -6309,6 +6417,7 @@ function declareParameters(roles) {
|
|
|
6309
6417
|
DeletePhysicsRig,
|
|
6310
6418
|
EditorDocument,
|
|
6311
6419
|
HEAD_BAND,
|
|
6420
|
+
LIP_INSIDE_ROLES,
|
|
6312
6421
|
LIP_ROLES,
|
|
6313
6422
|
LayerGeometryError,
|
|
6314
6423
|
SPECK_CORE_FRACTION,
|
|
@@ -6345,6 +6454,7 @@ function declareParameters(roles) {
|
|
|
6345
6454
|
generateIkiFromLayerSet,
|
|
6346
6455
|
headHalfOf,
|
|
6347
6456
|
interpolateGridOffsets,
|
|
6457
|
+
isLipInsideRole,
|
|
6348
6458
|
isSpeckCore,
|
|
6349
6459
|
mouthFold,
|
|
6350
6460
|
mouthOpening,
|