@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.mjs
CHANGED
|
@@ -1761,7 +1761,10 @@ function captureBindingEndpoint(channel, restValue, posedValue) {
|
|
|
1761
1761
|
// src/atlas.ts
|
|
1762
1762
|
var ATLAS_PADDING = 2;
|
|
1763
1763
|
var UV_INSET_PX = 0.5;
|
|
1764
|
-
function packAtlas(sources, padding = ATLAS_PADDING) {
|
|
1764
|
+
function packAtlas(sources, padding = ATLAS_PADDING, maxSide) {
|
|
1765
|
+
if (maxSide !== void 0 && !(isFinite(maxSide) && maxSide > 0)) {
|
|
1766
|
+
throw new Error(`packAtlas: invalid maxSide ${maxSide}`);
|
|
1767
|
+
}
|
|
1765
1768
|
for (const src of sources) {
|
|
1766
1769
|
if (!isFinite(src.width) || src.width <= 0) {
|
|
1767
1770
|
throw new Error(
|
|
@@ -1782,42 +1785,50 @@ function packAtlas(sources, padding = ATLAS_PADDING) {
|
|
|
1782
1785
|
(sum, s) => sum + (s.width + padding) * (s.height + padding),
|
|
1783
1786
|
0
|
|
1784
1787
|
);
|
|
1785
|
-
const
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1788
|
+
const widest = Math.max(...sorted.map((s) => s.width + padding));
|
|
1789
|
+
const firstWidth = Math.max(Math.ceil(Math.sqrt(totalArea)), widest);
|
|
1790
|
+
const shelve = (targetWidth) => {
|
|
1791
|
+
const placements = [];
|
|
1792
|
+
let shelfX = 0;
|
|
1793
|
+
let shelfY = 0;
|
|
1794
|
+
let shelfHeight = 0;
|
|
1795
|
+
for (const src of sorted) {
|
|
1796
|
+
const paddedW = src.width + padding;
|
|
1797
|
+
const paddedH = src.height + padding;
|
|
1798
|
+
if (shelfX > 0 && shelfX + paddedW > targetWidth) {
|
|
1799
|
+
shelfY += shelfHeight;
|
|
1800
|
+
shelfX = 0;
|
|
1801
|
+
shelfHeight = 0;
|
|
1802
|
+
}
|
|
1803
|
+
placements.push({
|
|
1804
|
+
id: src.id,
|
|
1805
|
+
x: shelfX,
|
|
1806
|
+
y: shelfY,
|
|
1807
|
+
width: src.width,
|
|
1808
|
+
height: src.height
|
|
1809
|
+
});
|
|
1810
|
+
shelfX += paddedW;
|
|
1811
|
+
if (paddedH > shelfHeight) shelfHeight = paddedH;
|
|
1812
|
+
}
|
|
1813
|
+
let pageWidth = 0;
|
|
1814
|
+
let pageHeight = 0;
|
|
1815
|
+
for (const p of placements) {
|
|
1816
|
+
const right = p.x + p.width + padding;
|
|
1817
|
+
const bottom = p.y + p.height + padding;
|
|
1818
|
+
if (right > pageWidth) pageWidth = right;
|
|
1819
|
+
if (bottom > pageHeight) pageHeight = bottom;
|
|
1820
|
+
}
|
|
1821
|
+
return { pageWidth, pageHeight, placements };
|
|
1822
|
+
};
|
|
1823
|
+
const first = shelve(firstWidth);
|
|
1824
|
+
if (maxSide === void 0 || first.pageHeight <= maxSide || first.pageWidth > maxSide) {
|
|
1825
|
+
return { ...first, padding };
|
|
1811
1826
|
}
|
|
1812
|
-
let
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
const right = p.x + p.width + padding;
|
|
1816
|
-
const bottom = p.y + p.height + padding;
|
|
1817
|
-
if (right > pageWidth) pageWidth = right;
|
|
1818
|
-
if (bottom > pageHeight) pageHeight = bottom;
|
|
1827
|
+
for (let w = widest; w <= maxSide; w++) {
|
|
1828
|
+
const layout = shelve(w);
|
|
1829
|
+
if (layout.pageHeight <= maxSide) return { ...layout, padding };
|
|
1819
1830
|
}
|
|
1820
|
-
return {
|
|
1831
|
+
return { ...first, padding };
|
|
1821
1832
|
}
|
|
1822
1833
|
function uvRectFor(placement, page, insetPx = UV_INSET_PX) {
|
|
1823
1834
|
const px = placement.x + insetPx;
|
|
@@ -2926,14 +2937,14 @@ function armParts(role, layer, g, order, poseSwitch) {
|
|
|
2926
2937
|
},
|
|
2927
2938
|
true
|
|
2928
2939
|
),
|
|
2929
|
-
part(role, ids.armDeformer, order + 1, meshOf(band(0.5, seam), quad)),
|
|
2930
2940
|
part(
|
|
2931
2941
|
ids.forearm,
|
|
2932
2942
|
ids.forearmDeformer,
|
|
2933
|
-
order +
|
|
2943
|
+
order + 1,
|
|
2934
2944
|
meshOf(band(seam, -0.5), quad),
|
|
2935
2945
|
true
|
|
2936
|
-
)
|
|
2946
|
+
),
|
|
2947
|
+
part(role, ids.armDeformer, order + 2, meshOf(band(0.5, seam), quad))
|
|
2937
2948
|
];
|
|
2938
2949
|
}
|
|
2939
2950
|
var rotateBinding = (parameter, range, sign) => ({
|
|
@@ -3026,21 +3037,26 @@ import {
|
|
|
3026
3037
|
// src/auto-rig/roles.ts
|
|
3027
3038
|
var LIP_ROLES = ["mouth_inner", "lip_lower", "lip_upper"];
|
|
3028
3039
|
var isLipRole = (role) => LIP_ROLES.includes(role);
|
|
3040
|
+
var LIP_INSIDE_ROLES = ["mouth_tongue", "mouth_teeth"];
|
|
3041
|
+
var isLipInsideRole = (role) => LIP_INSIDE_ROLES.includes(role);
|
|
3029
3042
|
var ROLE_TABLE = [
|
|
3030
3043
|
{ role: "hair_back", family: "hair_back" },
|
|
3031
3044
|
{ role: "body", family: "body" },
|
|
3032
|
-
{ role: "arm_L", family: "arm", parts: ["elbow_L", "
|
|
3033
|
-
{ role: "arm_R", family: "arm", parts: ["elbow_R", "
|
|
3045
|
+
{ role: "arm_L", family: "arm", parts: ["elbow_L", "forearm_L", "arm_L"] },
|
|
3046
|
+
{ role: "arm_R", family: "arm", parts: ["elbow_R", "forearm_R", "arm_R"] },
|
|
3034
3047
|
{ role: "face", family: "face", required: true },
|
|
3035
3048
|
{ role: "blush_L", family: "face" },
|
|
3036
3049
|
{ role: "blush_R", family: "face" },
|
|
3037
3050
|
{ role: "nose", family: "nose" },
|
|
3038
3051
|
{ role: "mouth", family: "mouth" },
|
|
3039
3052
|
{ role: "mouth_open", family: "mouth" },
|
|
3040
|
-
// The lip set, back to front: the interior, the
|
|
3041
|
-
// lip and its line over both. It
|
|
3042
|
-
// (`mouth.ts`), so it stands in for
|
|
3053
|
+
// The lip set, back to front: the interior, the tongue and the teeth clipped
|
|
3054
|
+
// to it, the lower lip's skin, the upper lip and its line over both. It
|
|
3055
|
+
// folds open instead of crossfading (`mouth.ts`), so it stands in for
|
|
3056
|
+
// `mouth`, never beside it.
|
|
3043
3057
|
{ role: "mouth_inner", family: "mouth" },
|
|
3058
|
+
{ role: "mouth_tongue", family: "mouth" },
|
|
3059
|
+
{ role: "mouth_teeth", family: "mouth" },
|
|
3044
3060
|
{ role: "lip_lower", family: "mouth" },
|
|
3045
3061
|
{ role: "lip_upper", family: "mouth" },
|
|
3046
3062
|
{ role: "eye_L", family: "eye_L", required: true },
|
|
@@ -3103,6 +3119,12 @@ function checkArmsHaveBody(roles) {
|
|
|
3103
3119
|
function checkMouth(roles) {
|
|
3104
3120
|
const has = new Set(roles);
|
|
3105
3121
|
const present = LIP_ROLES.filter((r) => has.has(r));
|
|
3122
|
+
const inside = LIP_INSIDE_ROLES.filter((r) => has.has(r));
|
|
3123
|
+
if (inside.length > 0 && present.length === 0) {
|
|
3124
|
+
throw new Error(
|
|
3125
|
+
`auto-rig: ${inside[0]} needs the lip set ${LIP_ROLES.join(", ")} (it is cut from the interior and clipped to it)`
|
|
3126
|
+
);
|
|
3127
|
+
}
|
|
3106
3128
|
if (present.length === 0) {
|
|
3107
3129
|
if (!has.has("mouth")) {
|
|
3108
3130
|
throw new Error(
|
|
@@ -3166,6 +3188,12 @@ function parseLayerRoles(fileNames) {
|
|
|
3166
3188
|
|
|
3167
3189
|
// src/auto-rig/mouth.ts
|
|
3168
3190
|
var UPPER_SHARE = 0.4;
|
|
3191
|
+
var CLOSED_SAG_MAX = 0.1;
|
|
3192
|
+
var UPPER_THIN = 0.7;
|
|
3193
|
+
var END_TAPER = 0.35;
|
|
3194
|
+
var END_TAPER_FROM = 0.7;
|
|
3195
|
+
var FLICK_PULL = 0.5;
|
|
3196
|
+
var FLICK_SQUASH = 0.25;
|
|
3169
3197
|
var MOUTH_KNOT_PX = 4;
|
|
3170
3198
|
var isLipSet = (byRole) => LIP_ROLES.every((r) => byRole.has(r));
|
|
3171
3199
|
function columnRuns(layer) {
|
|
@@ -3227,10 +3255,15 @@ function mouthOpening(byRole) {
|
|
|
3227
3255
|
for (let d = 0; (r = read(c - d) ?? read(c + d)) === void 0; d++) ;
|
|
3228
3256
|
reads.push(r);
|
|
3229
3257
|
}
|
|
3258
|
+
const n = reads.length;
|
|
3230
3259
|
const m = (x0 + x1) / 2;
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3260
|
+
const centre = Math.round(m);
|
|
3261
|
+
const smooth = reads.map((_, i) => {
|
|
3262
|
+
const near = reads.slice(Math.max(0, i - 3), i + 4);
|
|
3263
|
+
return near.reduce((s, r) => s + r.Tu, 0) / near.length;
|
|
3264
|
+
});
|
|
3265
|
+
let sag = 0;
|
|
3266
|
+
if (n >= 4) {
|
|
3234
3267
|
let s11 = 0;
|
|
3235
3268
|
let s12 = 0;
|
|
3236
3269
|
let s22 = 0;
|
|
@@ -3248,16 +3281,32 @@ function mouthOpening(byRole) {
|
|
|
3248
3281
|
t2 += b2 * d;
|
|
3249
3282
|
});
|
|
3250
3283
|
const det = s11 * s22 - s12 * s12;
|
|
3251
|
-
p = (t1 * s22 - t2 * s12) / det;
|
|
3252
|
-
q = (s11 * t2 - s12 * t1) / det;
|
|
3284
|
+
const p = (t1 * s22 - t2 * s12) / det;
|
|
3285
|
+
const q = (s11 * t2 - s12 * t1) / det;
|
|
3286
|
+
const c = centre - x0;
|
|
3287
|
+
const D = c * (centre - x1) * (p + q * (centre - m));
|
|
3288
|
+
const arc = (reads[0].Tu + reads[n - 1].Tu) / 2 - reads[c].Tu;
|
|
3289
|
+
const t = c / (n - 1);
|
|
3290
|
+
const k = 4 * t * (1 - t);
|
|
3291
|
+
const offset = arc - (smooth[0] + (smooth[n - 1] - smooth[0]) * t - smooth[c]);
|
|
3292
|
+
sag = Math.min(arc - D, CLOSED_SAG_MAX * n - Math.max(0, offset) / k);
|
|
3253
3293
|
}
|
|
3254
3294
|
const columns = reads.map((r, i) => {
|
|
3255
|
-
|
|
3256
|
-
|
|
3295
|
+
if (n < 4) return { ...r, seam: r.Tu };
|
|
3296
|
+
const t = i / (x1 - x0);
|
|
3297
|
+
const parabola = smooth[0] + (smooth[n - 1] - smooth[0]) * t - sag * 4 * t * (1 - t);
|
|
3298
|
+
return { ...r, seam: r.Tu + parabola - smooth[i] };
|
|
3257
3299
|
});
|
|
3258
|
-
const
|
|
3259
|
-
const
|
|
3300
|
+
const clamp2 = (col) => Math.min(x1, Math.max(x0, col));
|
|
3301
|
+
const base = (col) => columns[clamp2(col) - x0];
|
|
3260
3302
|
const w = base(centre).lineH;
|
|
3303
|
+
const from = END_TAPER_FROM * ((x1 - x0 + 1) / 2);
|
|
3304
|
+
const squash = (col) => {
|
|
3305
|
+
const d = Math.abs(col - centre);
|
|
3306
|
+
const edge = col < centre ? centre - upper.bbox.x : upper.bbox.x + upper.bbox.w - 1 - centre;
|
|
3307
|
+
const t = d <= from ? 0 : d >= edge ? 1 : (d - from) / (edge - from);
|
|
3308
|
+
return 1 - (1 - END_TAPER) * t;
|
|
3309
|
+
};
|
|
3261
3310
|
return {
|
|
3262
3311
|
x0,
|
|
3263
3312
|
x1,
|
|
@@ -3266,39 +3315,80 @@ function mouthOpening(byRole) {
|
|
|
3266
3315
|
w,
|
|
3267
3316
|
at(col) {
|
|
3268
3317
|
const b = base(col);
|
|
3269
|
-
|
|
3318
|
+
const s = squash(clamp2(col));
|
|
3319
|
+
return {
|
|
3320
|
+
...b,
|
|
3321
|
+
overlap: UPPER_THIN * (b.lineH * (1 - s) / 2 + Math.min(w, b.lineH) * s)
|
|
3322
|
+
};
|
|
3323
|
+
},
|
|
3324
|
+
line(col) {
|
|
3325
|
+
if (col >= x0 && col <= x1) {
|
|
3326
|
+
const b = base(col);
|
|
3327
|
+
return { bottom: b.Tu, top: b.lineTop, squash: squash(col) };
|
|
3328
|
+
}
|
|
3329
|
+
const runs = upperCols.get(col);
|
|
3330
|
+
if (runs === void 0) return void 0;
|
|
3331
|
+
return {
|
|
3332
|
+
bottom: edgesOf(upper, runs[runs.length - 1]).bottom,
|
|
3333
|
+
top: edgesOf(upper, runs[0]).top,
|
|
3334
|
+
squash: FLICK_SQUASH
|
|
3335
|
+
};
|
|
3270
3336
|
}
|
|
3271
3337
|
};
|
|
3272
3338
|
}
|
|
3273
3339
|
function mouthFold(role, opening) {
|
|
3340
|
+
const { x0, x1 } = opening;
|
|
3341
|
+
const stackThin = (c, y) => y > c.Tu ? c.Tu + UPPER_THIN * (y - c.Tu) : y;
|
|
3274
3342
|
return (col, y, v) => {
|
|
3275
|
-
if (v === 1) return 0;
|
|
3276
|
-
if (role !== "mouth_inner" && (col < opening.x0 || col > opening.x1))
|
|
3277
|
-
return 0;
|
|
3278
|
-
const c = opening.at(col);
|
|
3279
3343
|
const k = 1 - v;
|
|
3344
|
+
const inside = col >= x0 && col <= x1;
|
|
3280
3345
|
switch (role) {
|
|
3281
|
-
case "
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
return (c.seam + c.overlap - c.Bl) * k;
|
|
3285
|
-
|
|
3286
|
-
|
|
3346
|
+
case "lip_lower": {
|
|
3347
|
+
if (!inside) return [0, 0];
|
|
3348
|
+
const c = opening.at(col);
|
|
3349
|
+
return [0, (c.seam + c.overlap - c.Bl) * k];
|
|
3350
|
+
}
|
|
3351
|
+
case "mouth_inner": {
|
|
3352
|
+
const c = opening.at(col);
|
|
3353
|
+
const S = c.seam + c.overlap;
|
|
3354
|
+
return [0, S + v * (stackThin(c, y) - S) - y];
|
|
3355
|
+
}
|
|
3356
|
+
case "mouth_teeth": {
|
|
3357
|
+
const c = opening.at(col);
|
|
3358
|
+
return [0, stackThin(c, y) - y + (c.seam - c.Tu) * k];
|
|
3359
|
+
}
|
|
3360
|
+
case "mouth_tongue": {
|
|
3361
|
+
const c = opening.at(col);
|
|
3362
|
+
return [0, (c.seam + c.overlap - c.Bb) * k];
|
|
3363
|
+
}
|
|
3364
|
+
case "lip_upper": {
|
|
3365
|
+
let c = col;
|
|
3366
|
+
let line = opening.line(c);
|
|
3367
|
+
while (line === void 0)
|
|
3368
|
+
line = opening.line(c += col < x0 ? 1 : -1);
|
|
3369
|
+
const thin = line.bottom + UPPER_THIN * (y - line.bottom);
|
|
3370
|
+
const mid = line.bottom + UPPER_THIN * (line.top - line.bottom) / 2;
|
|
3371
|
+
const travel = inside ? opening.at(col).seam - line.bottom : 0;
|
|
3372
|
+
const shut = mid + line.squash * (thin - mid) + travel;
|
|
3373
|
+
const pull = inside ? 0 : (1 - FLICK_PULL) * ((col < x0 ? x0 : x1 + 1) - col);
|
|
3374
|
+
return [pull * k, thin - y + (shut - thin) * k];
|
|
3375
|
+
}
|
|
3287
3376
|
}
|
|
3288
3377
|
};
|
|
3289
3378
|
}
|
|
3290
|
-
function columnOfKnot(knot, opening
|
|
3379
|
+
function columnOfKnot(knot, opening) {
|
|
3291
3380
|
const boundary = Math.round(knot + opening.canvasW / 2);
|
|
3292
|
-
|
|
3293
|
-
if (col >= opening.x0 && col <= opening.x1) return col;
|
|
3294
|
-
return role === "mouth_inner" ? Math.min(opening.x1, Math.max(opening.x0, col)) : void 0;
|
|
3381
|
+
return boundary === opening.x1 + 1 ? opening.x1 : boundary;
|
|
3295
3382
|
}
|
|
3296
3383
|
function mouthFoldWarp(role, mesh, xs, box, opening) {
|
|
3297
3384
|
const fold = mouthFold(role, opening);
|
|
3298
|
-
return localWarp(
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3385
|
+
return localWarp(
|
|
3386
|
+
P5.MouthOpen,
|
|
3387
|
+
mesh,
|
|
3388
|
+
box,
|
|
3389
|
+
[0, 1],
|
|
3390
|
+
(p, v, i) => fold(columnOfKnot(xs[i % xs.length], opening), p[1], v)
|
|
3391
|
+
);
|
|
3302
3392
|
}
|
|
3303
3393
|
function mouthRestShift(byRole) {
|
|
3304
3394
|
const rig = buildMouthRig(byRole);
|
|
@@ -3311,18 +3401,31 @@ function mouthRestShift(byRole) {
|
|
|
3311
3401
|
fields.set(role, {
|
|
3312
3402
|
box,
|
|
3313
3403
|
xs,
|
|
3314
|
-
|
|
3404
|
+
rows: offsets.length / 2 / xs.length - 1,
|
|
3405
|
+
dy: offsets.filter((_, i) => i % 2 === 1).map((o) => o * bh(box))
|
|
3315
3406
|
});
|
|
3316
3407
|
}
|
|
3317
|
-
const
|
|
3318
|
-
return (role, col) => {
|
|
3319
|
-
const { box, xs, dy } = fields.get(role);
|
|
3320
|
-
const x = col + 0.5 -
|
|
3321
|
-
|
|
3408
|
+
const { canvasW, canvasH } = byRole.get("lip_upper");
|
|
3409
|
+
return (role, col, row) => {
|
|
3410
|
+
const { box, xs, rows, dy } = fields.get(role);
|
|
3411
|
+
const x = col + 0.5 - canvasW / 2;
|
|
3412
|
+
const y = canvasH / 2 - (row + 0.5);
|
|
3413
|
+
if (x < box.x0 || x > box.x1 || y < box.y0 || y > box.y1) return void 0;
|
|
3322
3414
|
let k = 1;
|
|
3323
3415
|
while (k < xs.length - 1 && xs[k] < x) k++;
|
|
3324
|
-
const
|
|
3325
|
-
|
|
3416
|
+
const down = (box.y1 - y) / bh(box) * rows;
|
|
3417
|
+
const r = Math.min(rows - 1, Math.floor(down));
|
|
3418
|
+
const fx = (x - xs[k - 1]) / (xs[k] - xs[k - 1]);
|
|
3419
|
+
const fy = down - r;
|
|
3420
|
+
const at = (i, j) => dy[i * xs.length + j];
|
|
3421
|
+
const tl = at(r, k - 1);
|
|
3422
|
+
const br = at(r + 1, k);
|
|
3423
|
+
if (fx <= fy) {
|
|
3424
|
+
const bl = at(r + 1, k - 1);
|
|
3425
|
+
return tl + fy * (bl - tl) + fx * (br - bl);
|
|
3426
|
+
}
|
|
3427
|
+
const tr = at(r, k);
|
|
3428
|
+
return tl + fx * (tr - tl) + fy * (br - tr);
|
|
3326
3429
|
};
|
|
3327
3430
|
}
|
|
3328
3431
|
function mouthKnots(union, opening) {
|
|
@@ -5917,7 +6020,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
5917
6020
|
headStart = fm.headStart;
|
|
5918
6021
|
} else if (spec.role === "hair_front") {
|
|
5919
6022
|
mesh = columnMesh(b, frontGrid);
|
|
5920
|
-
} else if (lips !== void 0 && isLipRole(spec.role)) {
|
|
6023
|
+
} else if (lips !== void 0 && (isLipRole(spec.role) || isLipInsideRole(spec.role))) {
|
|
5921
6024
|
({ mesh, xs: knotXs } = mouthMesh(b, lips.knots));
|
|
5922
6025
|
} else if (spec.family !== "body") {
|
|
5923
6026
|
const cells = MESH_CELLS[spec.role] ?? FEATURE_MESH_CELLS;
|
|
@@ -6034,6 +6137,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6034
6137
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
6035
6138
|
break;
|
|
6036
6139
|
case "mouth_inner":
|
|
6140
|
+
case "mouth_tongue":
|
|
6141
|
+
case "mouth_teeth":
|
|
6037
6142
|
case "lip_lower":
|
|
6038
6143
|
case "lip_upper":
|
|
6039
6144
|
warps.push(
|
|
@@ -6041,6 +6146,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid, lips) {
|
|
|
6041
6146
|
mouthForm(mesh, b, lips.frame),
|
|
6042
6147
|
mouthWiden(mesh, b, lips.frame)
|
|
6043
6148
|
);
|
|
6149
|
+
if (isLipInsideRole(spec.role)) part.clip = { masks: ["mouth_inner"] };
|
|
6044
6150
|
break;
|
|
6045
6151
|
case "blush_L":
|
|
6046
6152
|
case "blush_R":
|
|
@@ -6242,6 +6348,7 @@ export {
|
|
|
6242
6348
|
DeletePhysicsRig,
|
|
6243
6349
|
EditorDocument,
|
|
6244
6350
|
HEAD_BAND,
|
|
6351
|
+
LIP_INSIDE_ROLES,
|
|
6245
6352
|
LIP_ROLES,
|
|
6246
6353
|
LayerGeometryError,
|
|
6247
6354
|
SPECK_CORE_FRACTION,
|
|
@@ -6278,6 +6385,7 @@ export {
|
|
|
6278
6385
|
generateIkiFromLayerSet,
|
|
6279
6386
|
headHalfOf,
|
|
6280
6387
|
interpolateGridOffsets,
|
|
6388
|
+
isLipInsideRole,
|
|
6281
6389
|
isSpeckCore,
|
|
6282
6390
|
mouthFold,
|
|
6283
6391
|
mouthOpening,
|