@ikijs/editor 0.16.1 → 0.18.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 +23 -17
- package/dist/index.d.mts +101 -18
- package/dist/index.d.ts +101 -18
- package/dist/index.js +1127 -622
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1128 -622
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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
|
+
LayerGeometryError: () => LayerGeometryError,
|
|
36
37
|
SPECK_CORE_FRACTION: () => SPECK_CORE_FRACTION,
|
|
37
38
|
SetDeformerBindings: () => SetDeformerBindings,
|
|
38
39
|
SetDeformerParent: () => SetDeformerParent,
|
|
@@ -51,6 +52,7 @@ __export(index_exports, {
|
|
|
51
52
|
SetPhysicsRig: () => SetPhysicsRig,
|
|
52
53
|
TurnTargetError: () => TurnTargetError,
|
|
53
54
|
UV_INSET_PX: () => UV_INSET_PX,
|
|
55
|
+
armGeometry: () => armGeometry,
|
|
54
56
|
captureBindingEndpoint: () => captureBindingEndpoint,
|
|
55
57
|
computeGridOffsets: () => computeGridOffsets,
|
|
56
58
|
createDefaultMatrixDeformer: () => createDefaultMatrixDeformer,
|
|
@@ -67,6 +69,7 @@ __export(index_exports, {
|
|
|
67
69
|
isSpeckCore: () => isSpeckCore,
|
|
68
70
|
packAtlas: () => packAtlas,
|
|
69
71
|
parseLayerRoles: () => parseLayerRoles,
|
|
72
|
+
partIdsOfRole: () => partIdsOfRole,
|
|
70
73
|
upsertGridKeyform: () => upsertGridKeyform,
|
|
71
74
|
uvRectFor: () => uvRectFor,
|
|
72
75
|
validateDeformerDelete: () => validateDeformerDelete,
|
|
@@ -1632,6 +1635,14 @@ function facePlateHalfOf(layers) {
|
|
|
1632
1635
|
return face.cropW / 2;
|
|
1633
1636
|
}
|
|
1634
1637
|
var STRAND_MIN_RUN_FRACTION = 0.5;
|
|
1638
|
+
var ROW_RUN_ROLES = /* @__PURE__ */ new Set([
|
|
1639
|
+
"face",
|
|
1640
|
+
"hair_front",
|
|
1641
|
+
"hair_back",
|
|
1642
|
+
"body",
|
|
1643
|
+
"arm_L",
|
|
1644
|
+
"arm_R"
|
|
1645
|
+
]);
|
|
1635
1646
|
function createLayerSetMeasurer(canvas) {
|
|
1636
1647
|
for (const field of ["width", "height"]) {
|
|
1637
1648
|
const value = canvas[field];
|
|
@@ -1661,7 +1672,7 @@ function createLayerSetMeasurer(canvas) {
|
|
|
1661
1672
|
if (bbox === null) return null;
|
|
1662
1673
|
const rowLeft = new Int32Array(canvasH).fill(canvasW);
|
|
1663
1674
|
const rowRight = new Int32Array(canvasH).fill(-1);
|
|
1664
|
-
const rowRuns = role
|
|
1675
|
+
const rowRuns = ROW_RUN_ROLES.has(role) ? [] : void 0;
|
|
1665
1676
|
for (let y = 0; y < canvasH; y++) {
|
|
1666
1677
|
let runs;
|
|
1667
1678
|
if (rowRuns !== void 0 && y >= bbox.y && y < bbox.y + bbox.h) {
|
|
@@ -2003,7 +2014,13 @@ var PROFILE_VALUES = {
|
|
|
2003
2014
|
brow: 0.15,
|
|
2004
2015
|
breath: 0.03,
|
|
2005
2016
|
sway: 0.08,
|
|
2006
|
-
blushRest: 0.4
|
|
2017
|
+
blushRest: 0.4,
|
|
2018
|
+
bodySlide: 0.1,
|
|
2019
|
+
bodyNarrow: 0.05,
|
|
2020
|
+
bodyBow: 0.06,
|
|
2021
|
+
bodyRoll: 4,
|
|
2022
|
+
bodyFollowX: 0.3,
|
|
2023
|
+
bodyFollowZ: 0.3
|
|
2007
2024
|
};
|
|
2008
2025
|
function deriveProfile(v) {
|
|
2009
2026
|
const farLead = v.lead * (1 - v.fore / 2);
|
|
@@ -2097,9 +2114,32 @@ function deriveProfile(v) {
|
|
|
2097
2114
|
* slide, to the next 0.05. */
|
|
2098
2115
|
hiddenNeck: Math.ceil(30 * (v.slide + v.lead) - 1e-9) / 20
|
|
2099
2116
|
};
|
|
2100
|
-
|
|
2117
|
+
const body = {
|
|
2118
|
+
/** BodyAngleX: the upper body's slide, hh. */
|
|
2119
|
+
slide: v.bodySlide,
|
|
2120
|
+
/** BodyAngleX: how much narrower the upper body gets, either way. */
|
|
2121
|
+
narrow: v.bodyNarrow,
|
|
2122
|
+
/** BodyAngleY: the upper body's rise (+) or bow (−), hh. */
|
|
2123
|
+
bow: v.bodyBow,
|
|
2124
|
+
/** BodyAngleZ: the upper body's roll about the hips, degrees. */
|
|
2125
|
+
rollDeg: v.bodyRoll,
|
|
2126
|
+
/** AngleX: the body's follow, as a share of BodyAngleX's motion. */
|
|
2127
|
+
followX: v.bodyFollowX,
|
|
2128
|
+
/** AngleZ: the body's follow, as a share of its roll. */
|
|
2129
|
+
followZ: v.bodyFollowZ,
|
|
2130
|
+
/** The roll the follow gives the body at AngleZ ±30, degrees: β, the
|
|
2131
|
+
* share of `ROLL_DEG` the head's own roll leaves to the body. */
|
|
2132
|
+
followRoll: v.bodyFollowZ * v.bodyRoll
|
|
2133
|
+
};
|
|
2134
|
+
return {
|
|
2135
|
+
TURN: turn,
|
|
2136
|
+
NOD: nod,
|
|
2137
|
+
ROLL_DEG: v.rollDeg,
|
|
2138
|
+
AMPLITUDE: amplitude,
|
|
2139
|
+
BODY: body
|
|
2140
|
+
};
|
|
2101
2141
|
}
|
|
2102
|
-
var { TURN, NOD, ROLL_DEG, AMPLITUDE } = deriveProfile(PROFILE_VALUES);
|
|
2142
|
+
var { TURN, NOD, ROLL_DEG, AMPLITUDE, BODY } = deriveProfile(PROFILE_VALUES);
|
|
2103
2143
|
var STYLE_KNOBS = [
|
|
2104
2144
|
"turn",
|
|
2105
2145
|
"featureLead",
|
|
@@ -2129,236 +2169,23 @@ var TurnTargetError = class extends Error {
|
|
|
2129
2169
|
this.name = "TurnTargetError";
|
|
2130
2170
|
}
|
|
2131
2171
|
};
|
|
2172
|
+
var LayerGeometryError = class extends Error {
|
|
2173
|
+
constructor(message) {
|
|
2174
|
+
super(message);
|
|
2175
|
+
this.name = "LayerGeometryError";
|
|
2176
|
+
}
|
|
2177
|
+
};
|
|
2132
2178
|
var DEFAULT_TURN_TARGETS = Object.freeze({
|
|
2133
2179
|
eyeShift: round2((TURN.eyeFar + TURN.eyeNear) / 2 - TURN.hairBack),
|
|
2134
2180
|
farEyeRatio: round2(TURN.eyeFarScale / TURN.eyeNearScale),
|
|
2135
2181
|
silhouetteRatio: 1
|
|
2136
2182
|
});
|
|
2137
2183
|
|
|
2138
|
-
// src/auto-rig/
|
|
2139
|
-
var
|
|
2140
|
-
{ role: "hair_back", family: "hair_back" },
|
|
2141
|
-
{ role: "body", family: "body" },
|
|
2142
|
-
{ role: "face", family: "face", required: true },
|
|
2143
|
-
{ role: "blush_L", family: "face" },
|
|
2144
|
-
{ role: "blush_R", family: "face" },
|
|
2145
|
-
{ role: "nose", family: "nose" },
|
|
2146
|
-
{ role: "mouth", family: "mouth", required: true },
|
|
2147
|
-
{ role: "mouth_open", family: "mouth" },
|
|
2148
|
-
{ role: "eye_L", family: "eye_L", required: true },
|
|
2149
|
-
{ role: "eye_R", family: "eye_R", required: true },
|
|
2150
|
-
{ role: "iris_L", family: "eye_L" },
|
|
2151
|
-
{ role: "iris_R", family: "eye_R" },
|
|
2152
|
-
{ role: "pupil_L", family: "eye_L" },
|
|
2153
|
-
{ role: "pupil_R", family: "eye_R" },
|
|
2154
|
-
{ role: "highlight_L", family: "eye_L" },
|
|
2155
|
-
{ role: "highlight_R", family: "eye_R" },
|
|
2156
|
-
{ role: "lash_lower_L", family: "eye_L" },
|
|
2157
|
-
{ role: "lash_lower_R", family: "eye_R" },
|
|
2158
|
-
{ role: "lash_L", family: "eye_L" },
|
|
2159
|
-
{ role: "lash_R", family: "eye_R" },
|
|
2160
|
-
{ role: "brow_L", family: "brow_L" },
|
|
2161
|
-
{ role: "brow_R", family: "brow_R" },
|
|
2162
|
-
{ role: "hair_front", family: "hair_front" }
|
|
2163
|
-
];
|
|
2164
|
-
var REQUIRED_ROLES = ROLE_TABLE.filter(
|
|
2165
|
-
(r) => r.required
|
|
2166
|
-
).map((r) => r.role);
|
|
2167
|
-
var SPEC_BY_ROLE = new Map(ROLE_TABLE.map((r, i) => [r.role, { ...r, i }]));
|
|
2168
|
-
function roleSpec(role) {
|
|
2169
|
-
const spec = SPEC_BY_ROLE.get(role);
|
|
2170
|
-
if (spec === void 0) throw new Error(`auto-rig: unknown role "${role}"`);
|
|
2171
|
-
return spec;
|
|
2172
|
-
}
|
|
2173
|
-
var ALIASES = {
|
|
2174
|
-
eyebrow_L: "brow_L",
|
|
2175
|
-
eyebrow_R: "brow_R",
|
|
2176
|
-
eye_white_L: "eye_L",
|
|
2177
|
-
eye_white_R: "eye_R"
|
|
2178
|
-
};
|
|
2179
|
-
function roleOf(fileName) {
|
|
2180
|
-
const role = fileName.replace(/\.[^.]+$/, "").toLowerCase().replace(/[-\s]+/g, "_").replace(/_([lr])$/, (_, s) => `_${s.toUpperCase()}`);
|
|
2181
|
-
return ALIASES[role] ?? role;
|
|
2182
|
-
}
|
|
2183
|
-
function parseLayerRoles(fileNames) {
|
|
2184
|
-
const seen = /* @__PURE__ */ new Map();
|
|
2185
|
-
const out = [];
|
|
2186
|
-
for (const fileName of fileNames) {
|
|
2187
|
-
const role = roleOf(fileName);
|
|
2188
|
-
if (!SPEC_BY_ROLE.has(role)) {
|
|
2189
|
-
throw new Error(
|
|
2190
|
-
`auto-rig: "${fileName}" names no known role (expected one of ${ROLE_TABLE.map((r) => r.role).join(", ")})`
|
|
2191
|
-
);
|
|
2192
|
-
}
|
|
2193
|
-
const prior = seen.get(role);
|
|
2194
|
-
if (prior !== void 0) {
|
|
2195
|
-
throw new Error(
|
|
2196
|
-
`auto-rig: role "${role}" is named twice ("${prior}" and "${fileName}")`
|
|
2197
|
-
);
|
|
2198
|
-
}
|
|
2199
|
-
seen.set(role, fileName);
|
|
2200
|
-
out.push({ role, fileName });
|
|
2201
|
-
}
|
|
2202
|
-
const missing = REQUIRED_ROLES.filter((r) => !seen.has(r));
|
|
2203
|
-
if (missing.length > 0) {
|
|
2204
|
-
throw new Error(
|
|
2205
|
-
`auto-rig: missing required role${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`
|
|
2206
|
-
);
|
|
2207
|
-
}
|
|
2208
|
-
return out;
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
// src/auto-rig/generate.ts
|
|
2212
|
-
var import_format6 = require("@ikijs/format");
|
|
2184
|
+
// src/auto-rig/arms.ts
|
|
2185
|
+
var import_format5 = require("@ikijs/format");
|
|
2213
2186
|
|
|
2214
|
-
// src/auto-rig/
|
|
2215
|
-
var
|
|
2216
|
-
function browsY(y) {
|
|
2217
|
-
return [
|
|
2218
|
-
{ parameter: import_format3.StandardParameter.BrowLeftY, value: y, blend: "add" },
|
|
2219
|
-
{ parameter: import_format3.StandardParameter.BrowRightY, value: y, blend: "add" }
|
|
2220
|
-
];
|
|
2221
|
-
}
|
|
2222
|
-
function browsTilt(innerDown) {
|
|
2223
|
-
return [
|
|
2224
|
-
{ parameter: import_format3.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
2225
|
-
{ parameter: import_format3.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
2226
|
-
];
|
|
2227
|
-
}
|
|
2228
|
-
function cheek(value) {
|
|
2229
|
-
return { parameter: import_format3.StandardParameter.Cheek, value, blend: "add" };
|
|
2230
|
-
}
|
|
2231
|
-
var BROWS = [import_format3.StandardParameter.BrowLeftY, import_format3.StandardParameter.BrowRightY, import_format3.StandardParameter.BrowLeftAngle, import_format3.StandardParameter.BrowRightAngle];
|
|
2232
|
-
var EXPRESSIONS = [
|
|
2233
|
-
{
|
|
2234
|
-
id: "smile",
|
|
2235
|
-
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
2236
|
-
parameters: [
|
|
2237
|
-
{ parameter: import_format3.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
2238
|
-
...browsY(0.25),
|
|
2239
|
-
cheek(0.4)
|
|
2240
|
-
]
|
|
2241
|
-
},
|
|
2242
|
-
{
|
|
2243
|
-
id: "laugh",
|
|
2244
|
-
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
2245
|
-
parameters: [
|
|
2246
|
-
{ parameter: import_format3.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
|
|
2247
|
-
{ parameter: import_format3.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
|
|
2248
|
-
{ parameter: import_format3.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
2249
|
-
{ parameter: import_format3.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
|
|
2250
|
-
...browsY(0.4),
|
|
2251
|
-
cheek(0.65)
|
|
2252
|
-
]
|
|
2253
|
-
},
|
|
2254
|
-
{
|
|
2255
|
-
id: "angry",
|
|
2256
|
-
description: "Angry or annoyed, frowning. For irritation, frustration, indignation or scolding.",
|
|
2257
|
-
needs: BROWS,
|
|
2258
|
-
parameters: [
|
|
2259
|
-
...browsY(-0.5),
|
|
2260
|
-
...browsTilt(0.8),
|
|
2261
|
-
{ parameter: import_format3.StandardParameter.MouthForm, value: -0.9, blend: "add" }
|
|
2262
|
-
]
|
|
2263
|
-
},
|
|
2264
|
-
{
|
|
2265
|
-
id: "sad",
|
|
2266
|
-
description: "Sad or disappointed, downcast. For sorrow, regret, apology or sympathy.",
|
|
2267
|
-
needs: BROWS,
|
|
2268
|
-
parameters: [
|
|
2269
|
-
...browsY(0.35),
|
|
2270
|
-
...browsTilt(-0.8),
|
|
2271
|
-
{ parameter: import_format3.StandardParameter.MouthForm, value: -0.8, blend: "add" }
|
|
2272
|
-
]
|
|
2273
|
-
},
|
|
2274
|
-
{
|
|
2275
|
-
id: "surprised",
|
|
2276
|
-
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
2277
|
-
parameters: [
|
|
2278
|
-
...browsY(0.8),
|
|
2279
|
-
{ parameter: import_format3.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
2280
|
-
]
|
|
2281
|
-
},
|
|
2282
|
-
{
|
|
2283
|
-
id: "shy",
|
|
2284
|
-
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
2285
|
-
needs: [import_format3.StandardParameter.Cheek],
|
|
2286
|
-
parameters: [
|
|
2287
|
-
cheek(1),
|
|
2288
|
-
// Gaze down.
|
|
2289
|
-
{ parameter: import_format3.StandardParameter.EyeballY, value: -0.65, blend: "add" },
|
|
2290
|
-
{ parameter: import_format3.StandardParameter.MouthForm, value: 0.4, blend: "add" },
|
|
2291
|
-
...browsTilt(-0.25)
|
|
2292
|
-
]
|
|
2293
|
-
}
|
|
2294
|
-
];
|
|
2295
|
-
function defaultExpressions(declared) {
|
|
2296
|
-
return EXPRESSIONS.filter(
|
|
2297
|
-
(e) => e.needs === void 0 || e.needs.some((id) => declared.has(id))
|
|
2298
|
-
).map(({ needs: _needs, ...e }) => ({
|
|
2299
|
-
...e,
|
|
2300
|
-
parameters: e.parameters.filter((t) => declared.has(t.parameter))
|
|
2301
|
-
}));
|
|
2302
|
-
}
|
|
2303
|
-
var DEFAULT_MOTIONS = {
|
|
2304
|
-
Nod: [
|
|
2305
|
-
{
|
|
2306
|
-
description: "Nods yes: agreement, acknowledgement, understanding.",
|
|
2307
|
-
duration: 1,
|
|
2308
|
-
curves: [
|
|
2309
|
-
{
|
|
2310
|
-
parameter: import_format3.StandardParameter.AngleY,
|
|
2311
|
-
keys: [
|
|
2312
|
-
[0, 0],
|
|
2313
|
-
[0.3, -18],
|
|
2314
|
-
[0.6, 4],
|
|
2315
|
-
[0.8, -1],
|
|
2316
|
-
[1, 0]
|
|
2317
|
-
]
|
|
2318
|
-
}
|
|
2319
|
-
]
|
|
2320
|
-
}
|
|
2321
|
-
],
|
|
2322
|
-
Shake: [
|
|
2323
|
-
{
|
|
2324
|
-
description: "Shakes the head no: disagreement, refusal, disbelief.",
|
|
2325
|
-
duration: 1.2,
|
|
2326
|
-
// The first swing peaks at 0.2 s, which the default fade would still
|
|
2327
|
-
// be damping.
|
|
2328
|
-
fadeIn: 0.15,
|
|
2329
|
-
curves: [
|
|
2330
|
-
{
|
|
2331
|
-
parameter: import_format3.StandardParameter.AngleX,
|
|
2332
|
-
keys: [
|
|
2333
|
-
[0, 0],
|
|
2334
|
-
[0.2, -18],
|
|
2335
|
-
[0.45, 18],
|
|
2336
|
-
[0.7, -16],
|
|
2337
|
-
[0.95, 10],
|
|
2338
|
-
[1.2, 0]
|
|
2339
|
-
]
|
|
2340
|
-
}
|
|
2341
|
-
]
|
|
2342
|
-
}
|
|
2343
|
-
],
|
|
2344
|
-
Tilt: [
|
|
2345
|
-
{
|
|
2346
|
-
description: "Tilts the head to one side and back: curiosity, puzzlement, a question.",
|
|
2347
|
-
duration: 1.4,
|
|
2348
|
-
curves: [
|
|
2349
|
-
{
|
|
2350
|
-
parameter: import_format3.StandardParameter.AngleZ,
|
|
2351
|
-
keys: [
|
|
2352
|
-
[0, 0],
|
|
2353
|
-
[0.35, 20],
|
|
2354
|
-
[1, 20],
|
|
2355
|
-
[1.4, 0]
|
|
2356
|
-
]
|
|
2357
|
-
}
|
|
2358
|
-
]
|
|
2359
|
-
}
|
|
2360
|
-
]
|
|
2361
|
-
};
|
|
2187
|
+
// src/auto-rig/body.ts
|
|
2188
|
+
var import_format4 = require("@ikijs/format");
|
|
2362
2189
|
|
|
2363
2190
|
// src/auto-rig/layout.ts
|
|
2364
2191
|
function boxOfLayer(layer) {
|
|
@@ -2433,49 +2260,941 @@ function meshPoints(mesh, box) {
|
|
|
2433
2260
|
return out;
|
|
2434
2261
|
}
|
|
2435
2262
|
|
|
2436
|
-
// src/auto-rig/
|
|
2437
|
-
var
|
|
2438
|
-
var
|
|
2439
|
-
var
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2263
|
+
// src/auto-rig/drivers.ts
|
|
2264
|
+
var import_format3 = require("@ikijs/format");
|
|
2265
|
+
var SWAY_RANGE = 20;
|
|
2266
|
+
var SWAY_CURVE = 1.4;
|
|
2267
|
+
var HAIR_HANG = 0.35;
|
|
2268
|
+
var Z_STOPS = [-30, 0, 30];
|
|
2269
|
+
function localWarp(parameter, mesh, box, values, f) {
|
|
2270
|
+
const pts = meshPoints(mesh, box);
|
|
2271
|
+
const w = bw(box);
|
|
2272
|
+
const h = bh(box);
|
|
2273
|
+
return {
|
|
2274
|
+
parameter,
|
|
2275
|
+
keyforms: values.map((value) => ({
|
|
2276
|
+
value,
|
|
2277
|
+
offsets: pts.flatMap((p, i) => {
|
|
2278
|
+
const [dx, dy] = f(p, value, i);
|
|
2279
|
+
return [roundTo(dx / w, 1e-4), roundTo(dy / h, 1e-4)];
|
|
2280
|
+
})
|
|
2281
|
+
}))
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
2284
|
+
var rollOf = (v, deg = ROLL_DEG) => -v / 30 * deg * DEG;
|
|
2285
|
+
function motionReach(warps, mesh, box, extra = [0, 0]) {
|
|
2286
|
+
const out = {
|
|
2287
|
+
left: extra[0],
|
|
2288
|
+
right: extra[0],
|
|
2289
|
+
bottom: extra[1],
|
|
2290
|
+
top: extra[1]
|
|
2291
|
+
};
|
|
2292
|
+
if (mesh === void 0) return out;
|
|
2293
|
+
const pts = meshPoints(mesh, box);
|
|
2294
|
+
for (const warp of warps ?? []) {
|
|
2295
|
+
let l = 0;
|
|
2296
|
+
let r = 0;
|
|
2297
|
+
let b = 0;
|
|
2298
|
+
let t = 0;
|
|
2299
|
+
for (const k of warp.keyforms) {
|
|
2300
|
+
pts.forEach(([x, y], i) => {
|
|
2301
|
+
const px = x + k.offsets[2 * i] * bw(box);
|
|
2302
|
+
const py = y + k.offsets[2 * i + 1] * bh(box);
|
|
2303
|
+
l = Math.max(l, box.x0 - px);
|
|
2304
|
+
r = Math.max(r, px - box.x1);
|
|
2305
|
+
b = Math.max(b, box.y0 - py);
|
|
2306
|
+
t = Math.max(t, py - box.y1);
|
|
2307
|
+
});
|
|
2308
|
+
}
|
|
2309
|
+
out.left += l;
|
|
2310
|
+
out.right += r;
|
|
2311
|
+
out.bottom += b;
|
|
2312
|
+
out.top += t;
|
|
2313
|
+
}
|
|
2314
|
+
return out;
|
|
2315
|
+
}
|
|
2316
|
+
function blinkFold(param, mesh, box, travel, eye = box) {
|
|
2317
|
+
const yc = eye.y1 - travel * bh(eye);
|
|
2318
|
+
return localWarp(
|
|
2319
|
+
param,
|
|
2320
|
+
mesh,
|
|
2321
|
+
box,
|
|
2322
|
+
[0, 1],
|
|
2323
|
+
([, y], v) => v === 1 ? [0, 0] : [0, yc - y]
|
|
2324
|
+
);
|
|
2325
|
+
}
|
|
2326
|
+
function lashFold(param, mesh, lash, eye, travel) {
|
|
2327
|
+
const yc = eye.y1 - travel * bh(eye) - 0.04 * bh(eye);
|
|
2328
|
+
const c = cx(lash);
|
|
2329
|
+
const half = bw(lash) / 2;
|
|
2330
|
+
const sag = 0.3 * bh(lash);
|
|
2331
|
+
return localWarp(param, mesh, lash, [0, 1], ([x, y], v) => {
|
|
2332
|
+
if (v === 1) return [0, 0];
|
|
2333
|
+
const u = (x - c) / half;
|
|
2334
|
+
return [0, yc + (y - lash.y0) * 0.5 - sag * (1 - u * u) - y];
|
|
2335
|
+
});
|
|
2336
|
+
}
|
|
2337
|
+
function gazeBindings(irisW, share = 1) {
|
|
2338
|
+
const gx = AMPLITUDE.gazeX * irisW * share;
|
|
2339
|
+
const gy = AMPLITUDE.gazeY * irisW * share;
|
|
2340
|
+
return [
|
|
2341
|
+
{
|
|
2342
|
+
parameter: import_format3.StandardParameter.EyeballX,
|
|
2343
|
+
channel: "translateX",
|
|
2344
|
+
from: -round1(gx),
|
|
2345
|
+
to: round1(gx)
|
|
2346
|
+
},
|
|
2347
|
+
{
|
|
2348
|
+
parameter: import_format3.StandardParameter.EyeballY,
|
|
2349
|
+
channel: "translateY",
|
|
2350
|
+
from: -round1(gy),
|
|
2351
|
+
to: round1(gy)
|
|
2352
|
+
}
|
|
2353
|
+
];
|
|
2354
|
+
}
|
|
2355
|
+
function gazeReach(irisW, share = 1) {
|
|
2356
|
+
return [AMPLITUDE.gazeX * irisW * share, AMPLITUDE.gazeY * irisW * share];
|
|
2357
|
+
}
|
|
2358
|
+
function browReach(hh) {
|
|
2359
|
+
return AMPLITUDE.brow * hh;
|
|
2360
|
+
}
|
|
2361
|
+
function browBindings(side, reach) {
|
|
2362
|
+
const y = side === "L" ? import_format3.StandardParameter.BrowLeftY : import_format3.StandardParameter.BrowRightY;
|
|
2363
|
+
const a = side === "L" ? import_format3.StandardParameter.BrowLeftAngle : import_format3.StandardParameter.BrowRightAngle;
|
|
2364
|
+
return [
|
|
2365
|
+
{
|
|
2366
|
+
parameter: y,
|
|
2367
|
+
channel: "translateY",
|
|
2368
|
+
from: -round1(reach),
|
|
2369
|
+
to: round1(reach)
|
|
2370
|
+
},
|
|
2371
|
+
{ parameter: a, channel: "rotate", from: -BROW_TILT, to: BROW_TILT }
|
|
2372
|
+
];
|
|
2373
|
+
}
|
|
2374
|
+
var BROW_TILT = 15;
|
|
2375
|
+
function mouthForm(mesh, box) {
|
|
2376
|
+
const a = 0.3 * bh(box);
|
|
2377
|
+
const c = cx(box);
|
|
2378
|
+
const half = bw(box) / 2;
|
|
2379
|
+
return localWarp(import_format3.StandardParameter.MouthForm, mesh, box, [-1, 0, 1], ([x], v) => {
|
|
2380
|
+
if (v === 0) return [0, 0];
|
|
2381
|
+
const u = (x - c) / half;
|
|
2382
|
+
const widen = v > 0 ? 0.05 : 0.03;
|
|
2383
|
+
return [v * widen * (x - c), v * a * (u * u - 0.3)];
|
|
2384
|
+
});
|
|
2385
|
+
}
|
|
2386
|
+
function mouthOpenGrow(mesh, box) {
|
|
2387
|
+
const c = cx(box);
|
|
2388
|
+
return localWarp(
|
|
2389
|
+
import_format3.StandardParameter.MouthOpen,
|
|
2390
|
+
mesh,
|
|
2391
|
+
box,
|
|
2392
|
+
[0, 1],
|
|
2393
|
+
([x, y], v) => v === 1 ? [(AMPLITUDE.mouthOpenWidth - 1) * (x - c), 0] : [0, box.y1 + (y - box.y1) * 0.2 - y]
|
|
2394
|
+
);
|
|
2395
|
+
}
|
|
2396
|
+
function mouthWiden(mesh, box) {
|
|
2397
|
+
const c = cx(box);
|
|
2398
|
+
return localWarp(import_format3.StandardParameter.MouthOpen, mesh, box, [0, 1], ([x], v) => [
|
|
2399
|
+
v * (AMPLITUDE.mouthOpenWidth - 1) * (x - c),
|
|
2400
|
+
0
|
|
2401
|
+
]);
|
|
2402
|
+
}
|
|
2403
|
+
function hairSway(param, mesh, box, amp) {
|
|
2404
|
+
return localWarp(
|
|
2405
|
+
param,
|
|
2406
|
+
mesh,
|
|
2407
|
+
box,
|
|
2408
|
+
[-SWAY_RANGE, 0, SWAY_RANGE],
|
|
2409
|
+
([, y], v) => {
|
|
2410
|
+
const t = Math.max(0, (box.y1 - y) / bh(box));
|
|
2411
|
+
return [v / SWAY_RANGE * amp * t ** SWAY_CURVE, 0];
|
|
2412
|
+
}
|
|
2413
|
+
);
|
|
2414
|
+
}
|
|
2415
|
+
function tiltHang(mesh, box, pivot, weight, deg = ROLL_DEG) {
|
|
2416
|
+
return localWarp(import_format3.StandardParameter.AngleZ, mesh, box, Z_STOPS, ([x, y], v, i) => {
|
|
2417
|
+
const g = weight(y, i);
|
|
2418
|
+
if (g === 0 || v === 0) return [0, 0];
|
|
2419
|
+
const a = -g * rollOf(v, deg);
|
|
2420
|
+
const px = x - pivot[0];
|
|
2421
|
+
const py = y - pivot[1];
|
|
2422
|
+
return [
|
|
2423
|
+
pivot[0] + px * Math.cos(a) - py * Math.sin(a) - x,
|
|
2424
|
+
pivot[1] + px * Math.sin(a) + py * Math.cos(a) - y
|
|
2425
|
+
];
|
|
2426
|
+
});
|
|
2427
|
+
}
|
|
2428
|
+
function hairHangWeight(pivotY, bottom) {
|
|
2429
|
+
if (bottom >= pivotY) return () => 0;
|
|
2430
|
+
return (y) => HAIR_HANG * smoothstep(pivotY, bottom, y);
|
|
2431
|
+
}
|
|
2432
|
+
var round1 = (v) => roundTo(v, 0.1);
|
|
2433
|
+
|
|
2434
|
+
// src/auto-rig/grid.ts
|
|
2435
|
+
var X_STOPS = [-30, 0, 30];
|
|
2436
|
+
var Y_STOPS = [-30, 0, 30];
|
|
2437
|
+
function lattice(box, cell, min, max) {
|
|
2438
|
+
const cols = cellsFor(box.x1 - box.x0, cell, min, max);
|
|
2439
|
+
const rows = cellsFor(box.y1 - box.y0, cell, min, max);
|
|
2440
|
+
const points = [];
|
|
2441
|
+
for (let r = 0; r <= rows; r++) {
|
|
2442
|
+
const y = box.y1 - (box.y1 - box.y0) * r / rows;
|
|
2443
|
+
for (let c = 0; c <= cols; c++) {
|
|
2444
|
+
points.push(
|
|
2445
|
+
roundTo(box.x0 + (box.x1 - box.x0) * c / cols, 0.01),
|
|
2446
|
+
roundTo(y, 0.01)
|
|
2447
|
+
);
|
|
2448
|
+
}
|
|
2449
|
+
}
|
|
2450
|
+
return { box, cols, rows, points };
|
|
2451
|
+
}
|
|
2452
|
+
function bake(l, field) {
|
|
2453
|
+
const out = [];
|
|
2454
|
+
for (const ay of Y_STOPS) {
|
|
2455
|
+
for (const ax of X_STOPS) {
|
|
2456
|
+
const offsets = [];
|
|
2457
|
+
for (let k = 0; k < l.points.length; k += 2) {
|
|
2458
|
+
if (ax === 0 && ay === 0) {
|
|
2459
|
+
offsets.push(0, 0);
|
|
2460
|
+
continue;
|
|
2461
|
+
}
|
|
2462
|
+
const [dx, dy] = field(l.points[k], l.points[k + 1], ax, ay);
|
|
2463
|
+
offsets.push(roundTo(dx, 0.1), roundTo(dy, 0.1));
|
|
2464
|
+
}
|
|
2465
|
+
out.push(offsets);
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
return out;
|
|
2469
|
+
}
|
|
2470
|
+
function keyformAt(keyforms, ax) {
|
|
2471
|
+
const j = Y_STOPS.indexOf(0);
|
|
2472
|
+
const row = X_STOPS.map((_, i) => keyforms[j * X_STOPS.length + i]);
|
|
2473
|
+
return blendStops(X_STOPS, row, ax);
|
|
2474
|
+
}
|
|
2475
|
+
function blendStops(values, offsets, v) {
|
|
2476
|
+
const x = clamp(v, values[0], values[values.length - 1]);
|
|
2477
|
+
let i = 0;
|
|
2478
|
+
while (i < values.length - 2 && x > values[i + 1]) i++;
|
|
2479
|
+
const t = (x - values[i]) / (values[i + 1] - values[i]);
|
|
2480
|
+
return offsets[i].map((o, k) => o + (offsets[i + 1][k] - o) * t);
|
|
2481
|
+
}
|
|
2482
|
+
function land(l, offsets, x, y) {
|
|
2483
|
+
const { cols, rows, points } = l;
|
|
2484
|
+
const stride = cols + 1;
|
|
2485
|
+
let col = cols - 1;
|
|
2486
|
+
for (let c = 0; c < cols; c++) {
|
|
2487
|
+
if (x < points[(c + 1) * 2]) {
|
|
2488
|
+
col = c;
|
|
2489
|
+
break;
|
|
2490
|
+
}
|
|
2491
|
+
}
|
|
2492
|
+
let row = rows - 1;
|
|
2493
|
+
for (let r = 0; r < rows; r++) {
|
|
2494
|
+
if (y > points[(r + 1) * stride * 2 + 1]) {
|
|
2495
|
+
row = r;
|
|
2496
|
+
break;
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
const x0 = points[col * 2];
|
|
2500
|
+
const x1 = points[(col + 1) * 2];
|
|
2501
|
+
const yT = points[row * stride * 2 + 1];
|
|
2502
|
+
const yB = points[(row + 1) * stride * 2 + 1];
|
|
2503
|
+
const s = clamp((x - x0) / (x1 - x0), 0, 1);
|
|
2504
|
+
const t = clamp((yT - y) / (yT - yB), 0, 1);
|
|
2505
|
+
const at = (r, c) => {
|
|
2506
|
+
const k = (r * stride + c) * 2;
|
|
2507
|
+
return [points[k] + offsets[k], points[k + 1] + offsets[k + 1]];
|
|
2508
|
+
};
|
|
2509
|
+
const p00 = at(row, col);
|
|
2510
|
+
const p10 = at(row, col + 1);
|
|
2511
|
+
const p01 = at(row + 1, col);
|
|
2512
|
+
const p11 = at(row + 1, col + 1);
|
|
2513
|
+
const topX = p00[0] + (p10[0] - p00[0]) * s;
|
|
2514
|
+
const topY = p00[1] + (p10[1] - p00[1]) * s;
|
|
2515
|
+
const botX = p01[0] + (p11[0] - p01[0]) * s;
|
|
2516
|
+
const botY = p01[1] + (p11[1] - p01[1]) * s;
|
|
2517
|
+
return [topX + (botX - topX) * t, topY + (botY - topY) * t];
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
// src/auto-rig/body.ts
|
|
2521
|
+
var BODY_WARP_ID = "bodyWarp";
|
|
2522
|
+
var BODY_GRID_PAD = 6;
|
|
2523
|
+
var CHEST_DROP = 0.8;
|
|
2524
|
+
var HIP_BAND = [0.35, 0.75];
|
|
2525
|
+
var LEG_MIN = 0.1;
|
|
2526
|
+
var HIP_FRACTION = 0.5;
|
|
2527
|
+
var BODY_MESH_PX = 48;
|
|
2528
|
+
var BODY_STOPS = [-10, 0, 10];
|
|
2529
|
+
var FOLD_MARGIN = 0.01;
|
|
2530
|
+
function hipLine(layer) {
|
|
2531
|
+
const { bbox, canvasH, rowRuns } = layer;
|
|
2532
|
+
const top = canvasH / 2 - bbox.y;
|
|
2533
|
+
if (rowRuns !== void 0) {
|
|
2534
|
+
const legMin = LEG_MIN * bbox.w;
|
|
2535
|
+
const last = Math.floor(HIP_BAND[1] * bbox.h);
|
|
2536
|
+
for (let k = Math.ceil(HIP_BAND[0] * bbox.h); k <= last; k++) {
|
|
2537
|
+
const runs = rowRuns[k];
|
|
2538
|
+
let legs = 0;
|
|
2539
|
+
for (let i = 0; i < runs.length; i += 2) {
|
|
2540
|
+
if (runs[i + 1] - runs[i] >= legMin) legs++;
|
|
2541
|
+
}
|
|
2542
|
+
if (legs >= 2) return roundTo(top - k, 0.01);
|
|
2543
|
+
}
|
|
2544
|
+
}
|
|
2545
|
+
if (bbox.y + bbox.h === canvasH) return roundTo(top - bbox.h, 0.01);
|
|
2546
|
+
return roundTo(top - HIP_FRACTION * bbox.h, 0.01);
|
|
2547
|
+
}
|
|
2548
|
+
function bodyLattice(body, chin, hh, pivots, anchor) {
|
|
2549
|
+
const b = boxOfLayer(body);
|
|
2550
|
+
const g = (v) => Math.round(roundTo(v, 0.01) * 100);
|
|
2551
|
+
const pad = BODY_GRID_PAD * 100;
|
|
2552
|
+
const all = [chin, ...pivots];
|
|
2553
|
+
const a = g(anchor);
|
|
2554
|
+
const p = Math.min(...all.map((q) => g(q.y)));
|
|
2555
|
+
if (p - a < 2) {
|
|
2556
|
+
throw new LayerGeometryError(
|
|
2557
|
+
`auto-rig: layer "${body.fileName}": its hips (y ${anchor}) leave no row line strictly between them and the lowest of the head's and shoulders' pivots (y ${p / 100}), so the body warp cannot plant the legs below the pivots`
|
|
2558
|
+
);
|
|
2559
|
+
}
|
|
2560
|
+
let full = Math.min(g(chin.y - CHEST_DROP * hh), p - pad);
|
|
2561
|
+
if (full - a < pad) {
|
|
2562
|
+
full = Math.min(Math.max(Math.round((a + p) / 2), a + 1), p - 1);
|
|
2563
|
+
}
|
|
2564
|
+
const top = Math.max(g(b.y1), ...all.map((q) => g(q.y))) + pad;
|
|
2565
|
+
const bottom = Math.min(a, g(b.y0)) - pad;
|
|
2566
|
+
const split = (from, to, cells) => {
|
|
2567
|
+
const n = Math.max(1, Math.min(cells, from - to));
|
|
2568
|
+
const out = [from];
|
|
2569
|
+
for (let i = 1; i < n; i++) {
|
|
2570
|
+
out.push(Math.round(from - (from - to) * i / n));
|
|
2571
|
+
}
|
|
2572
|
+
out.push(to);
|
|
2573
|
+
return out;
|
|
2574
|
+
};
|
|
2575
|
+
const bandA = split(top, full, cellsFor((top - full) / 100, 96, 1, 8));
|
|
2576
|
+
const ramp = split(full, a, cellsFor((full - a) / 100, 96, 2, 8));
|
|
2577
|
+
const rows = [...bandA, ...ramp.slice(1, -1)];
|
|
2578
|
+
const ys = [...rows.map((y) => y / 100), anchor, bottom / 100];
|
|
2579
|
+
const weights = [...rows.map((y) => smoothstep(a, full, y)), 0, 0];
|
|
2580
|
+
const x0 = Math.min(b.x0, ...all.map((q) => q.x)) - BODY_GRID_PAD;
|
|
2581
|
+
const x1 = Math.max(b.x1, ...all.map((q) => q.x)) + BODY_GRID_PAD;
|
|
2582
|
+
const cols = cellsFor(x1 - x0, 96, 2, 8);
|
|
2583
|
+
const xs = [];
|
|
2584
|
+
for (let c = 0; c <= cols; c++) {
|
|
2585
|
+
xs.push(roundTo(x0 + (x1 - x0) * c / cols, 0.01));
|
|
2586
|
+
}
|
|
2587
|
+
return { xs, ys, weights, yFull: full / 100 };
|
|
2588
|
+
}
|
|
2589
|
+
function bodyMesh(b, rows) {
|
|
2590
|
+
const lines = [b.y1, ...rows.filter((y) => y > b.y0 && y < b.y1), b.y0];
|
|
2591
|
+
const ys = [lines[0]];
|
|
2592
|
+
for (let i = 1; i < lines.length; i++) {
|
|
2593
|
+
const [from, to] = [lines[i - 1], lines[i]];
|
|
2594
|
+
const n = Math.ceil((from - to) / BODY_MESH_PX);
|
|
2595
|
+
for (let k = 1; k < n; k++) ys.push(from - (from - to) * k / n);
|
|
2596
|
+
ys.push(to);
|
|
2597
|
+
}
|
|
2598
|
+
const cols = cellsFor(bw(b), BODY_MESH_PX, 2, 16);
|
|
2599
|
+
const vertices = [];
|
|
2600
|
+
const uvs = [];
|
|
2601
|
+
for (const y of ys) {
|
|
2602
|
+
const vy = roundTo((y - cy(b)) / bh(b), 1e-5);
|
|
2603
|
+
for (let c = 0; c <= cols; c++) {
|
|
2604
|
+
const vx = roundTo(c / cols - 0.5, 1e-5);
|
|
2605
|
+
vertices.push(vx, vy);
|
|
2606
|
+
uvs.push(roundTo(vx + 0.5, 1e-5), roundTo(0.5 - vy, 1e-5));
|
|
2607
|
+
}
|
|
2608
|
+
}
|
|
2609
|
+
const indices = [];
|
|
2610
|
+
for (let r = 0; r + 1 < ys.length; r++) {
|
|
2611
|
+
for (let c = 0; c < cols; c++) {
|
|
2612
|
+
const tl = r * (cols + 1) + c;
|
|
2613
|
+
const tr = tl + 1;
|
|
2614
|
+
const bl = tl + cols + 1;
|
|
2615
|
+
const br = bl + 1;
|
|
2616
|
+
indices.push(bl, br, tl, tl, br, tr);
|
|
2617
|
+
}
|
|
2618
|
+
}
|
|
2619
|
+
return { vertices, uvs, indices };
|
|
2620
|
+
}
|
|
2621
|
+
function buildBodyWarp({
|
|
2622
|
+
body,
|
|
2623
|
+
chin,
|
|
2624
|
+
hh,
|
|
2625
|
+
pivots
|
|
2626
|
+
}) {
|
|
2627
|
+
const b = boxOfLayer(body);
|
|
2628
|
+
const anchor = hipLine(body);
|
|
2629
|
+
const { xs, ys, weights } = bodyLattice(body, chin, hh, pivots, anchor);
|
|
2630
|
+
const hx = cx(b);
|
|
2631
|
+
const turn = (x, t) => t * BODY.slide * hh - Math.abs(t) * BODY.narrow * (x - hx);
|
|
2632
|
+
const roll = (x, y, deg) => {
|
|
2633
|
+
const c = Math.cos(deg * DEG) - 1;
|
|
2634
|
+
const s = Math.sin(deg * DEG);
|
|
2635
|
+
const [dx, dy] = [x - hx, y - anchor];
|
|
2636
|
+
return [c * dx - s * dy, s * dx + c * dy];
|
|
2637
|
+
};
|
|
2638
|
+
const warp = (parameter, values, field) => {
|
|
2639
|
+
const extreme = Math.max(...values.map(Math.abs));
|
|
2640
|
+
return {
|
|
2641
|
+
parameter,
|
|
2642
|
+
keyforms: values.map((value) => ({
|
|
2643
|
+
value,
|
|
2644
|
+
offsets: ys.flatMap(
|
|
2645
|
+
(y, r) => xs.flatMap((x) => {
|
|
2646
|
+
const [dx, dy] = field(x, y, value / extreme);
|
|
2647
|
+
return [
|
|
2648
|
+
roundTo(weights[r] * dx, 0.01),
|
|
2649
|
+
roundTo(weights[r] * dy, 0.01)
|
|
2650
|
+
];
|
|
2651
|
+
})
|
|
2652
|
+
)
|
|
2653
|
+
}))
|
|
2654
|
+
};
|
|
2655
|
+
};
|
|
2656
|
+
const deformer = {
|
|
2657
|
+
kind: "warp",
|
|
2658
|
+
id: BODY_WARP_ID,
|
|
2659
|
+
grid: {
|
|
2660
|
+
cols: xs.length - 1,
|
|
2661
|
+
rows: ys.length - 1,
|
|
2662
|
+
points: ys.flatMap((y) => xs.flatMap((x) => [x, y]))
|
|
2663
|
+
},
|
|
2664
|
+
warps: [
|
|
2665
|
+
warp(import_format4.StandardParameter.BodyAngleX, BODY_STOPS, (x, _y, t) => [turn(x, t), 0]),
|
|
2666
|
+
warp(import_format4.StandardParameter.BodyAngleY, BODY_STOPS, (_x, _y, t) => [0, t * BODY.bow * hh]),
|
|
2667
|
+
warp(
|
|
2668
|
+
import_format4.StandardParameter.BodyAngleZ,
|
|
2669
|
+
BODY_STOPS,
|
|
2670
|
+
(x, y, t) => roll(x, y, -t * BODY.rollDeg)
|
|
2671
|
+
),
|
|
2672
|
+
warp(import_format4.StandardParameter.AngleX, X_STOPS, (x, _y, t) => [BODY.followX * turn(x, t), 0]),
|
|
2673
|
+
warp(import_format4.StandardParameter.AngleZ, Z_STOPS, (x, y, t) => roll(x, y, -t * BODY.followRoll)),
|
|
2674
|
+
warp(import_format4.StandardParameter.Breath, [0, 1], (_x, _y, t) => [0, t * AMPLITUDE.breathBody * hh])
|
|
2675
|
+
]
|
|
2676
|
+
};
|
|
2677
|
+
refuseFold(body, deformer, anchor, [chin, ...pivots]);
|
|
2678
|
+
return { deformer, mesh: bodyMesh(b, ys) };
|
|
2679
|
+
}
|
|
2680
|
+
function refuseFold(body, d, anchor, pivots) {
|
|
2681
|
+
const { cols, rows, points } = d.grid;
|
|
2682
|
+
const warps = d.warps ?? [];
|
|
2683
|
+
const stride = cols + 1;
|
|
2684
|
+
for (let r = 0; r < rows; r++) {
|
|
2685
|
+
for (let c = 0; c < cols; c++) {
|
|
2686
|
+
const tl = r * stride + c;
|
|
2687
|
+
const [tr, bl, br] = [tl + 1, tl + stride, tl + stride + 1];
|
|
2688
|
+
const corners = [
|
|
2689
|
+
[bl, br, tl],
|
|
2690
|
+
[br, tr, bl],
|
|
2691
|
+
[tr, tl, br],
|
|
2692
|
+
[tl, bl, tr]
|
|
2693
|
+
];
|
|
2694
|
+
for (const [p, q, s] of corners) {
|
|
2695
|
+
const fold = leastArea(points, warps, p, q, s);
|
|
2696
|
+
if (fold.least >= FOLD_MARGIN * fold.rest) continue;
|
|
2697
|
+
const lowest = roundTo(Math.min(...pivots.map((v) => v.y)), 0.01);
|
|
2698
|
+
const pose = warps.map((w, i) => `${w.parameter} ${w.keyforms[fold.stops[i]].value}`).join(", ");
|
|
2699
|
+
throw new LayerGeometryError(
|
|
2700
|
+
`auto-rig: layer "${body.fileName}": the body is too short between its hips (y ${anchor}) and the lowest of the head's and shoulders' pivots (y ${lowest}) for its motion: its body warp's cell from x ${points[tl * 2]} to ${points[tr * 2]}, y ${points[tl * 2 + 1]} to ${points[bl * 2 + 1]}, would fold at or near ${pose}`
|
|
2701
|
+
);
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
}
|
|
2706
|
+
function leastArea(points, warps, p, q, s) {
|
|
2707
|
+
const moves = warps.map(
|
|
2708
|
+
(w) => w.keyforms.map(({ offsets: o }) => [
|
|
2709
|
+
o[q * 2] - o[p * 2],
|
|
2710
|
+
o[q * 2 + 1] - o[p * 2 + 1],
|
|
2711
|
+
o[s * 2] - o[p * 2],
|
|
2712
|
+
o[s * 2 + 1] - o[p * 2 + 1]
|
|
2713
|
+
])
|
|
2714
|
+
);
|
|
2715
|
+
const cross = (e) => e[0] * e[3] - e[1] * e[2];
|
|
2716
|
+
const edges = [
|
|
2717
|
+
points[q * 2] - points[p * 2],
|
|
2718
|
+
points[q * 2 + 1] - points[p * 2 + 1],
|
|
2719
|
+
points[s * 2] - points[p * 2],
|
|
2720
|
+
points[s * 2 + 1] - points[p * 2 + 1]
|
|
2721
|
+
];
|
|
2722
|
+
let least = Infinity;
|
|
2723
|
+
let stops = [];
|
|
2724
|
+
const at = [];
|
|
2725
|
+
const walk = (w, ax, ay, bx, by) => {
|
|
2726
|
+
if (w === moves.length) {
|
|
2727
|
+
const area = ax * by - ay * bx;
|
|
2728
|
+
if (area < least) [least, stops] = [area, [...at]];
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2731
|
+
moves[w].forEach(([mx, my, nx, ny], k) => {
|
|
2732
|
+
at[w] = k;
|
|
2733
|
+
walk(w + 1, ax + mx, ay + my, bx + nx, by + ny);
|
|
2734
|
+
});
|
|
2735
|
+
};
|
|
2736
|
+
walk(0, edges[0], edges[1], edges[2], edges[3]);
|
|
2737
|
+
for (const m of moves) {
|
|
2738
|
+
let most = 0;
|
|
2739
|
+
for (let k = 1; k < m.length; k++) {
|
|
2740
|
+
most = Math.max(most, cross(m[k].map((v, i) => v - m[k - 1][i])));
|
|
2741
|
+
}
|
|
2742
|
+
least -= most / 4;
|
|
2743
|
+
}
|
|
2744
|
+
return { rest: cross(edges), least, stops };
|
|
2745
|
+
}
|
|
2746
|
+
function headOwnRoll(hasBody) {
|
|
2747
|
+
return hasBody ? ROLL_DEG - BODY.followRoll : ROLL_DEG;
|
|
2748
|
+
}
|
|
2749
|
+
function headOwnBreath(hasBody) {
|
|
2750
|
+
return hasBody ? AMPLITUDE.breathHead - AMPLITUDE.breathBody : AMPLITUDE.breathHead;
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
// src/auto-rig/arms.ts
|
|
2754
|
+
var ELBOW_AT = 0.42;
|
|
2755
|
+
var CAP_ROWS = [0.1, 0.3];
|
|
2756
|
+
var CAP_SEGMENTS = 12;
|
|
2757
|
+
var ARM_RANGE = [-30, 150];
|
|
2758
|
+
var ARM_PARAMS = {
|
|
2759
|
+
arm_L: { arm: import_format5.StandardParameter.ArmLeft, elbow: import_format5.StandardParameter.ElbowLeft },
|
|
2760
|
+
arm_R: { arm: import_format5.StandardParameter.ArmRight, elbow: import_format5.StandardParameter.ElbowRight }
|
|
2761
|
+
};
|
|
2762
|
+
var ARM_IDS = {
|
|
2763
|
+
arm_L: {
|
|
2764
|
+
forearm: "forearm_L",
|
|
2765
|
+
armDeformer: "armDeformer_L",
|
|
2766
|
+
forearmDeformer: "forearmDeformer_L"
|
|
2767
|
+
},
|
|
2768
|
+
arm_R: {
|
|
2769
|
+
forearm: "forearm_R",
|
|
2770
|
+
armDeformer: "armDeformer_R",
|
|
2771
|
+
forearmDeformer: "forearmDeformer_R"
|
|
2772
|
+
}
|
|
2773
|
+
};
|
|
2774
|
+
function armGeometry(layer, bodyAxisX) {
|
|
2775
|
+
const { bbox, canvasW, canvasH, rowRuns } = layer;
|
|
2776
|
+
const b = boxOfLayer(layer);
|
|
2777
|
+
const span = (k) => {
|
|
2778
|
+
const runs = rowRuns?.[k] ?? [];
|
|
2779
|
+
if (runs.length === 0) return [bbox.x, bbox.x + bbox.w];
|
|
2780
|
+
let [s2, e2] = [runs[0], runs[1]];
|
|
2781
|
+
for (let i = 2; i < runs.length; i += 2) {
|
|
2782
|
+
if (runs[i + 1] - runs[i] > e2 - s2) [s2, e2] = [runs[i], runs[i + 1]];
|
|
2783
|
+
}
|
|
2784
|
+
return [s2, e2];
|
|
2785
|
+
};
|
|
2786
|
+
const centreX = (k) => {
|
|
2787
|
+
const [s2, e2] = span(k);
|
|
2788
|
+
return (s2 + e2) / 2 - canvasW / 2;
|
|
2789
|
+
};
|
|
2790
|
+
const rowY = (k) => canvasH / 2 - (bbox.y + k + 0.5);
|
|
2791
|
+
const painted = rowRuns?.flatMap((runs, k) => runs.length > 0 ? [k] : []) ?? [];
|
|
2792
|
+
const first = painted[0] ?? 0;
|
|
2793
|
+
const last = painted.at(-1) ?? bbox.h - 1;
|
|
2794
|
+
const lo = Math.ceil(CAP_ROWS[0] * bbox.h);
|
|
2795
|
+
const hi = Math.max(lo, Math.floor(CAP_ROWS[1] * bbox.h));
|
|
2796
|
+
const widths = [];
|
|
2797
|
+
for (let k = lo; k <= hi; k++) {
|
|
2798
|
+
const [s2, e2] = span(k);
|
|
2799
|
+
widths.push(e2 - s2);
|
|
2800
|
+
}
|
|
2801
|
+
widths.sort((p, q) => p - q);
|
|
2802
|
+
const ru = widths[widths.length >> 1] / 2;
|
|
2803
|
+
const ks = first + ru;
|
|
2804
|
+
const shoulder = {
|
|
2805
|
+
x: roundTo(centreX(Math.round(ks)), 0.01),
|
|
2806
|
+
y: roundTo(rowY(ks), 0.01)
|
|
2807
|
+
};
|
|
2808
|
+
const ke = Math.round(ks + ELBOW_AT * (last - ks));
|
|
2809
|
+
if (ke <= ks) {
|
|
2810
|
+
throw new LayerGeometryError(
|
|
2811
|
+
`auto-rig: layer "${layer.fileName}": the arm is too short for its width to place a shoulder above an elbow (draw the arm hanging, shoulder at the top)`
|
|
2812
|
+
);
|
|
2813
|
+
}
|
|
2814
|
+
const elbow = { x: roundTo(centreX(ke), 0.01), y: roundTo(rowY(ke), 0.01) };
|
|
2815
|
+
const [s, e] = span(ke);
|
|
2816
|
+
const capRadius = Math.min(
|
|
2817
|
+
(e - s) / 2 + 1,
|
|
2818
|
+
elbow.x - b.x0,
|
|
2819
|
+
b.x1 - elbow.x,
|
|
2820
|
+
b.y1 - elbow.y
|
|
2821
|
+
);
|
|
2822
|
+
return {
|
|
2823
|
+
shoulder,
|
|
2824
|
+
shoulderRadius: ru,
|
|
2825
|
+
elbow,
|
|
2826
|
+
capRadius,
|
|
2827
|
+
side: shoulder.x > bodyAxisX ? 1 : -1
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
function meshOf(vertices, indices) {
|
|
2831
|
+
const uvs = vertices.map(
|
|
2832
|
+
(v, i) => roundTo(i % 2 === 0 ? v + 0.5 : 0.5 - v, 1e-5)
|
|
2833
|
+
);
|
|
2834
|
+
return { vertices, uvs, indices };
|
|
2835
|
+
}
|
|
2836
|
+
function armParts(role, layer, g, order) {
|
|
2837
|
+
const b = boxOfLayer(layer);
|
|
2838
|
+
const lx = (x) => roundTo((x - cx(b)) / bw(b), 1e-5);
|
|
2839
|
+
const ly = (y) => roundTo((y - cy(b)) / bh(b), 1e-5);
|
|
2840
|
+
const seam = ly(g.elbow.y);
|
|
2841
|
+
const band = (top, bottom) => [
|
|
2842
|
+
-0.5,
|
|
2843
|
+
top,
|
|
2844
|
+
0.5,
|
|
2845
|
+
top,
|
|
2846
|
+
-0.5,
|
|
2847
|
+
bottom,
|
|
2848
|
+
0.5,
|
|
2849
|
+
bottom
|
|
2850
|
+
];
|
|
2851
|
+
const quad = [2, 3, 0, 0, 3, 1];
|
|
2852
|
+
const cap = [lx(g.elbow.x), seam];
|
|
2853
|
+
const fan = [];
|
|
2854
|
+
for (let i = 0; i <= CAP_SEGMENTS; i++) {
|
|
2855
|
+
const a = Math.PI * i / CAP_SEGMENTS;
|
|
2856
|
+
cap.push(
|
|
2857
|
+
lx(g.elbow.x + g.capRadius * Math.cos(a)),
|
|
2858
|
+
ly(g.elbow.y + g.capRadius * Math.sin(a))
|
|
2859
|
+
);
|
|
2860
|
+
if (i > 0) fan.push(4, 4 + i, 5 + i);
|
|
2861
|
+
}
|
|
2862
|
+
const part = (id, deformer, at, mesh) => ({
|
|
2863
|
+
id,
|
|
2864
|
+
color: [1, 1, 1, 1],
|
|
2865
|
+
width: bw(b),
|
|
2866
|
+
height: bh(b),
|
|
2867
|
+
transform: { x: cx(b), y: cy(b) },
|
|
2868
|
+
order: at,
|
|
2869
|
+
deformer,
|
|
2870
|
+
mesh
|
|
2871
|
+
});
|
|
2872
|
+
const ids = ARM_IDS[role];
|
|
2873
|
+
return [
|
|
2874
|
+
part(role, ids.armDeformer, order, meshOf(band(0.5, seam), quad)),
|
|
2875
|
+
part(
|
|
2876
|
+
ids.forearm,
|
|
2877
|
+
ids.forearmDeformer,
|
|
2878
|
+
order + 1,
|
|
2879
|
+
meshOf([...band(seam, -0.5), ...cap], [...quad, ...fan])
|
|
2880
|
+
)
|
|
2881
|
+
];
|
|
2882
|
+
}
|
|
2883
|
+
function armDeformers(role, g) {
|
|
2884
|
+
const ids = ARM_IDS[role];
|
|
2885
|
+
const params = ARM_PARAMS[role];
|
|
2886
|
+
const rotate = (parameter) => ({
|
|
2887
|
+
parameter,
|
|
2888
|
+
channel: "rotate",
|
|
2889
|
+
from: g.side * ARM_RANGE[0],
|
|
2890
|
+
to: g.side * ARM_RANGE[1]
|
|
2891
|
+
});
|
|
2892
|
+
return [
|
|
2893
|
+
{
|
|
2894
|
+
id: ids.armDeformer,
|
|
2895
|
+
parent: BODY_WARP_ID,
|
|
2896
|
+
pivot: { ...g.shoulder },
|
|
2897
|
+
bindings: [rotate(params.arm)]
|
|
2898
|
+
},
|
|
2899
|
+
{
|
|
2900
|
+
id: ids.forearmDeformer,
|
|
2901
|
+
parent: ids.armDeformer,
|
|
2902
|
+
pivot: { ...g.elbow },
|
|
2903
|
+
bindings: [rotate(params.elbow)]
|
|
2904
|
+
}
|
|
2905
|
+
];
|
|
2906
|
+
}
|
|
2907
|
+
|
|
2908
|
+
// src/auto-rig/roles.ts
|
|
2909
|
+
var ROLE_TABLE = [
|
|
2910
|
+
{ role: "hair_back", family: "hair_back" },
|
|
2911
|
+
{ role: "body", family: "body" },
|
|
2912
|
+
{ role: "arm_L", family: "arm", parts: ["arm_L", "forearm_L"] },
|
|
2913
|
+
{ role: "arm_R", family: "arm", parts: ["arm_R", "forearm_R"] },
|
|
2914
|
+
{ role: "face", family: "face", required: true },
|
|
2915
|
+
{ role: "blush_L", family: "face" },
|
|
2916
|
+
{ role: "blush_R", family: "face" },
|
|
2917
|
+
{ role: "nose", family: "nose" },
|
|
2918
|
+
{ role: "mouth", family: "mouth", required: true },
|
|
2919
|
+
{ role: "mouth_open", family: "mouth" },
|
|
2920
|
+
{ role: "eye_L", family: "eye_L", required: true },
|
|
2921
|
+
{ role: "eye_R", family: "eye_R", required: true },
|
|
2922
|
+
{ role: "iris_L", family: "eye_L" },
|
|
2923
|
+
{ role: "iris_R", family: "eye_R" },
|
|
2924
|
+
{ role: "pupil_L", family: "eye_L" },
|
|
2925
|
+
{ role: "pupil_R", family: "eye_R" },
|
|
2926
|
+
{ role: "highlight_L", family: "eye_L" },
|
|
2927
|
+
{ role: "highlight_R", family: "eye_R" },
|
|
2928
|
+
{ role: "lash_lower_L", family: "eye_L" },
|
|
2929
|
+
{ role: "lash_lower_R", family: "eye_R" },
|
|
2930
|
+
{ role: "lash_L", family: "eye_L" },
|
|
2931
|
+
{ role: "lash_R", family: "eye_R" },
|
|
2932
|
+
{ role: "brow_L", family: "brow_L" },
|
|
2933
|
+
{ role: "brow_R", family: "brow_R" },
|
|
2934
|
+
{ role: "hair_front", family: "hair_front" }
|
|
2935
|
+
];
|
|
2936
|
+
var REQUIRED_ROLES = ROLE_TABLE.filter(
|
|
2937
|
+
(r) => r.required
|
|
2938
|
+
).map((r) => r.role);
|
|
2939
|
+
var SPEC_BY_ROLE = new Map(ROLE_TABLE.map((r, i) => [r.role, { ...r, i }]));
|
|
2940
|
+
function roleSpec(role) {
|
|
2941
|
+
const spec = SPEC_BY_ROLE.get(role);
|
|
2942
|
+
if (spec === void 0) throw new Error(`auto-rig: unknown role "${role}"`);
|
|
2943
|
+
return spec;
|
|
2944
|
+
}
|
|
2945
|
+
function partIdsOfRole(role) {
|
|
2946
|
+
return roleSpec(role).parts ?? [role];
|
|
2947
|
+
}
|
|
2948
|
+
var ROLE_BY_PART = new Map(
|
|
2949
|
+
ROLE_TABLE.flatMap((r) => (r.parts ?? []).map((p) => [p, r.role]))
|
|
2950
|
+
);
|
|
2951
|
+
function roleOfPart(partId) {
|
|
2952
|
+
return ROLE_BY_PART.get(partId) ?? partId;
|
|
2953
|
+
}
|
|
2954
|
+
var ALIASES = {
|
|
2955
|
+
eyebrow_L: "brow_L",
|
|
2956
|
+
eyebrow_R: "brow_R",
|
|
2957
|
+
eye_white_L: "eye_L",
|
|
2958
|
+
eye_white_R: "eye_R"
|
|
2959
|
+
};
|
|
2960
|
+
function roleOf(fileName) {
|
|
2961
|
+
const role = fileName.replace(/\.[^.]+$/, "").toLowerCase().replace(/[-\s]+/g, "_").replace(/_([lr])$/, (_, s) => `_${s.toUpperCase()}`);
|
|
2962
|
+
return ALIASES[role] ?? role;
|
|
2963
|
+
}
|
|
2964
|
+
function checkArmsHaveBody(roles) {
|
|
2965
|
+
const has = new Set(roles);
|
|
2966
|
+
if (has.has("body")) return;
|
|
2967
|
+
const arm = ROLE_TABLE.find((r) => r.family === "arm" && has.has(r.role));
|
|
2968
|
+
if (arm !== void 0) {
|
|
2969
|
+
throw new Error(
|
|
2970
|
+
`auto-rig: ${arm.role} needs a body layer (an arm hangs from the body's shoulder)`
|
|
2971
|
+
);
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
function parseLayerRoles(fileNames) {
|
|
2975
|
+
const seen = /* @__PURE__ */ new Map();
|
|
2976
|
+
const out = [];
|
|
2977
|
+
for (const fileName of fileNames) {
|
|
2978
|
+
const role = roleOf(fileName);
|
|
2979
|
+
if (!SPEC_BY_ROLE.has(role)) {
|
|
2980
|
+
throw new Error(
|
|
2981
|
+
`auto-rig: "${fileName}" names no known role (expected one of ${ROLE_TABLE.map((r) => r.role).join(", ")})`
|
|
2982
|
+
);
|
|
2983
|
+
}
|
|
2984
|
+
const prior = seen.get(role);
|
|
2985
|
+
if (prior !== void 0) {
|
|
2986
|
+
throw new Error(
|
|
2987
|
+
`auto-rig: role "${role}" is named twice ("${prior}" and "${fileName}")`
|
|
2988
|
+
);
|
|
2989
|
+
}
|
|
2990
|
+
seen.set(role, fileName);
|
|
2991
|
+
out.push({ role, fileName });
|
|
2992
|
+
}
|
|
2993
|
+
const missing = REQUIRED_ROLES.filter((r) => !seen.has(r));
|
|
2994
|
+
if (missing.length > 0) {
|
|
2995
|
+
throw new Error(
|
|
2996
|
+
`auto-rig: missing required role${missing.length > 1 ? "s" : ""} ${missing.join(", ")}`
|
|
2997
|
+
);
|
|
2998
|
+
}
|
|
2999
|
+
checkArmsHaveBody(seen.keys());
|
|
3000
|
+
return out;
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
// src/auto-rig/generate.ts
|
|
3004
|
+
var import_format8 = require("@ikijs/format");
|
|
3005
|
+
|
|
3006
|
+
// src/auto-rig/animations.ts
|
|
3007
|
+
var import_format6 = require("@ikijs/format");
|
|
3008
|
+
function browsY(y) {
|
|
3009
|
+
return [
|
|
3010
|
+
{ parameter: import_format6.StandardParameter.BrowLeftY, value: y, blend: "add" },
|
|
3011
|
+
{ parameter: import_format6.StandardParameter.BrowRightY, value: y, blend: "add" }
|
|
3012
|
+
];
|
|
3013
|
+
}
|
|
3014
|
+
function browsTilt(innerDown) {
|
|
3015
|
+
return [
|
|
3016
|
+
{ parameter: import_format6.StandardParameter.BrowLeftAngle, value: innerDown, blend: "add" },
|
|
3017
|
+
{ parameter: import_format6.StandardParameter.BrowRightAngle, value: -innerDown, blend: "add" }
|
|
3018
|
+
];
|
|
3019
|
+
}
|
|
3020
|
+
function cheek(value) {
|
|
3021
|
+
return { parameter: import_format6.StandardParameter.Cheek, value, blend: "add" };
|
|
3022
|
+
}
|
|
3023
|
+
var BROWS = [import_format6.StandardParameter.BrowLeftY, import_format6.StandardParameter.BrowRightY, import_format6.StandardParameter.BrowLeftAngle, import_format6.StandardParameter.BrowRightAngle];
|
|
3024
|
+
var EXPRESSIONS = [
|
|
3025
|
+
{
|
|
3026
|
+
id: "smile",
|
|
3027
|
+
description: "Smiling and pleased: warm, friendly, content. For greetings, thanks, agreement and gentle happiness.",
|
|
3028
|
+
parameters: [
|
|
3029
|
+
{ parameter: import_format6.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3030
|
+
...browsY(0.25),
|
|
3031
|
+
cheek(0.4)
|
|
3032
|
+
]
|
|
3033
|
+
},
|
|
3034
|
+
{
|
|
3035
|
+
id: "laugh",
|
|
3036
|
+
description: "Laughing, eyes shut and mouth open: delighted, amused. For jokes, playfulness and big joy.",
|
|
3037
|
+
parameters: [
|
|
3038
|
+
{ parameter: import_format6.StandardParameter.EyeOpenLeft, value: 0, blend: "multiply" },
|
|
3039
|
+
{ parameter: import_format6.StandardParameter.EyeOpenRight, value: 0, blend: "multiply" },
|
|
3040
|
+
{ parameter: import_format6.StandardParameter.MouthForm, value: 1, blend: "add" },
|
|
3041
|
+
{ parameter: import_format6.StandardParameter.MouthOpen, value: 0.8, blend: "overwrite" },
|
|
3042
|
+
...browsY(0.4),
|
|
3043
|
+
cheek(0.65)
|
|
3044
|
+
]
|
|
3045
|
+
},
|
|
3046
|
+
{
|
|
3047
|
+
id: "angry",
|
|
3048
|
+
description: "Angry or annoyed, frowning. For irritation, frustration, indignation or scolding.",
|
|
3049
|
+
needs: BROWS,
|
|
3050
|
+
parameters: [
|
|
3051
|
+
...browsY(-0.5),
|
|
3052
|
+
...browsTilt(0.8),
|
|
3053
|
+
{ parameter: import_format6.StandardParameter.MouthForm, value: -0.9, blend: "add" }
|
|
3054
|
+
]
|
|
3055
|
+
},
|
|
3056
|
+
{
|
|
3057
|
+
id: "sad",
|
|
3058
|
+
description: "Sad or disappointed, downcast. For sorrow, regret, apology or sympathy.",
|
|
3059
|
+
needs: BROWS,
|
|
3060
|
+
parameters: [
|
|
3061
|
+
...browsY(0.35),
|
|
3062
|
+
...browsTilt(-0.8),
|
|
3063
|
+
{ parameter: import_format6.StandardParameter.MouthForm, value: -0.8, blend: "add" }
|
|
3064
|
+
]
|
|
3065
|
+
},
|
|
3066
|
+
{
|
|
3067
|
+
id: "surprised",
|
|
3068
|
+
description: "Surprised, mouth dropped open: startled, amazed. For shock, sudden news or disbelief.",
|
|
3069
|
+
parameters: [
|
|
3070
|
+
...browsY(0.8),
|
|
3071
|
+
{ parameter: import_format6.StandardParameter.MouthOpen, value: 0.9, blend: "overwrite" }
|
|
3072
|
+
]
|
|
3073
|
+
},
|
|
3074
|
+
{
|
|
3075
|
+
id: "shy",
|
|
3076
|
+
description: "Shy or embarrassed: bashful, flustered. For being praised, teased or caught off guard.",
|
|
3077
|
+
needs: [import_format6.StandardParameter.Cheek],
|
|
3078
|
+
parameters: [
|
|
3079
|
+
cheek(1),
|
|
3080
|
+
// Gaze down.
|
|
3081
|
+
{ parameter: import_format6.StandardParameter.EyeballY, value: -0.65, blend: "add" },
|
|
3082
|
+
{ parameter: import_format6.StandardParameter.MouthForm, value: 0.4, blend: "add" },
|
|
3083
|
+
...browsTilt(-0.25)
|
|
3084
|
+
]
|
|
3085
|
+
}
|
|
3086
|
+
];
|
|
3087
|
+
function defaultExpressions(declared) {
|
|
3088
|
+
return EXPRESSIONS.filter(
|
|
3089
|
+
(e) => e.needs === void 0 || e.needs.some((id) => declared.has(id))
|
|
3090
|
+
).map(({ needs: _needs, ...e }) => ({
|
|
3091
|
+
...e,
|
|
3092
|
+
parameters: e.parameters.filter((t) => declared.has(t.parameter))
|
|
3093
|
+
}));
|
|
3094
|
+
}
|
|
3095
|
+
var DEFAULT_MOTIONS = {
|
|
3096
|
+
Nod: [
|
|
3097
|
+
{
|
|
3098
|
+
description: "Nods yes: agreement, acknowledgement, understanding.",
|
|
3099
|
+
duration: 1,
|
|
3100
|
+
curves: [
|
|
3101
|
+
{
|
|
3102
|
+
parameter: import_format6.StandardParameter.AngleY,
|
|
3103
|
+
keys: [
|
|
3104
|
+
[0, 0],
|
|
3105
|
+
[0.3, -18],
|
|
3106
|
+
[0.6, 4],
|
|
3107
|
+
[0.8, -1],
|
|
3108
|
+
[1, 0]
|
|
3109
|
+
]
|
|
3110
|
+
}
|
|
3111
|
+
]
|
|
3112
|
+
}
|
|
3113
|
+
],
|
|
3114
|
+
Shake: [
|
|
3115
|
+
{
|
|
3116
|
+
description: "Shakes the head no: disagreement, refusal, disbelief.",
|
|
3117
|
+
duration: 1.2,
|
|
3118
|
+
// The first swing peaks at 0.2 s, which the default fade would still
|
|
3119
|
+
// be damping.
|
|
3120
|
+
fadeIn: 0.15,
|
|
3121
|
+
curves: [
|
|
3122
|
+
{
|
|
3123
|
+
parameter: import_format6.StandardParameter.AngleX,
|
|
3124
|
+
keys: [
|
|
3125
|
+
[0, 0],
|
|
3126
|
+
[0.2, -18],
|
|
3127
|
+
[0.45, 18],
|
|
3128
|
+
[0.7, -16],
|
|
3129
|
+
[0.95, 10],
|
|
3130
|
+
[1.2, 0]
|
|
3131
|
+
]
|
|
3132
|
+
}
|
|
3133
|
+
]
|
|
3134
|
+
}
|
|
3135
|
+
],
|
|
3136
|
+
Tilt: [
|
|
3137
|
+
{
|
|
3138
|
+
description: "Tilts the head to one side and back: curiosity, puzzlement, a question.",
|
|
3139
|
+
duration: 1.4,
|
|
3140
|
+
curves: [
|
|
3141
|
+
{
|
|
3142
|
+
parameter: import_format6.StandardParameter.AngleZ,
|
|
3143
|
+
keys: [
|
|
3144
|
+
[0, 0],
|
|
3145
|
+
[0.35, 20],
|
|
3146
|
+
[1, 20],
|
|
3147
|
+
[1.4, 0]
|
|
3148
|
+
]
|
|
3149
|
+
}
|
|
3150
|
+
]
|
|
3151
|
+
}
|
|
3152
|
+
]
|
|
3153
|
+
};
|
|
3154
|
+
|
|
3155
|
+
// src/auto-rig/checks.ts
|
|
3156
|
+
var finite = (v) => typeof v === "number" && Number.isFinite(v);
|
|
3157
|
+
var isObject = (v) => typeof v === "object" && v !== null;
|
|
3158
|
+
var MIN_FACE_PX = 24;
|
|
3159
|
+
function checkLayers(layers, canvas) {
|
|
3160
|
+
if (!Array.isArray(layers) || layers.length === 0) {
|
|
3161
|
+
throw new Error(
|
|
3162
|
+
"auto-rig: generateIkiFromLayerSet: layers must be a non-empty array"
|
|
3163
|
+
);
|
|
3164
|
+
}
|
|
3165
|
+
if (!(finite(canvas?.width) && canvas.width > 0)) {
|
|
3166
|
+
throw new Error("auto-rig: canvas.width must be a positive number");
|
|
3167
|
+
}
|
|
3168
|
+
if (!(finite(canvas?.height) && canvas.height > 0)) {
|
|
3169
|
+
throw new Error("auto-rig: canvas.height must be a positive number");
|
|
3170
|
+
}
|
|
3171
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3172
|
+
for (const l of layers) {
|
|
3173
|
+
roleSpec(l.role);
|
|
3174
|
+
if (seen.has(l.role)) {
|
|
3175
|
+
throw new Error(`auto-rig: role "${l.role}" appears twice`);
|
|
3176
|
+
}
|
|
3177
|
+
seen.add(l.role);
|
|
3178
|
+
const at = `auto-rig: layer "${l.fileName}"`;
|
|
3179
|
+
const { x, y, w, h } = l.bbox ?? {};
|
|
3180
|
+
if (![x, y, w, h].every(finite) || w <= 0 || h <= 0 || x < 0 || y < 0 || x + w > canvas.width || y + h > canvas.height) {
|
|
3181
|
+
throw new Error(
|
|
3182
|
+
`${at} has a bbox outside its ${canvas.width}x${canvas.height} canvas`
|
|
3183
|
+
);
|
|
3184
|
+
}
|
|
3185
|
+
if (l.canvasW !== canvas.width || l.canvasH !== canvas.height) {
|
|
3186
|
+
throw new Error(
|
|
3187
|
+
`${at} was measured on a ${l.canvasW}x${l.canvasH} canvas, not ${canvas.width}x${canvas.height}`
|
|
3188
|
+
);
|
|
3189
|
+
}
|
|
3190
|
+
if (l.cropW !== w || l.cropH !== h) {
|
|
3191
|
+
throw new Error(
|
|
3192
|
+
`${at}: crop ${l.cropW}x${l.cropH} is not its bbox ${w}x${h}`
|
|
3193
|
+
);
|
|
3194
|
+
}
|
|
3195
|
+
if (l.role === "face" && (w < MIN_FACE_PX || h < MIN_FACE_PX)) {
|
|
3196
|
+
throw new Error(
|
|
3197
|
+
`${at}: the face is ${w}x${h} px; the face plate needs at least ${MIN_FACE_PX} px each way to be meshed`
|
|
2479
3198
|
);
|
|
2480
3199
|
}
|
|
2481
3200
|
if (l.rowHalfWidths !== void 0) {
|
|
@@ -2536,6 +3255,7 @@ function checkLayers(layers, canvas) {
|
|
|
2536
3255
|
if (missing.length > 0) {
|
|
2537
3256
|
throw new Error(`auto-rig: missing required role(s) ${missing.join(", ")}`);
|
|
2538
3257
|
}
|
|
3258
|
+
checkArmsHaveBody(seen);
|
|
2539
3259
|
}
|
|
2540
3260
|
function checkHeadEdges(edges, byRole) {
|
|
2541
3261
|
if (edges === void 0) return;
|
|
@@ -2616,131 +3336,45 @@ function checkStrandEdges(strand, irises, hair) {
|
|
|
2616
3336
|
for (const [k, x] of [
|
|
2617
3337
|
["irisOuter", outer],
|
|
2618
3338
|
["irisInner", inner]
|
|
2619
|
-
]) {
|
|
2620
|
-
if (x < iris.x0 || x > iris.x1) {
|
|
2621
|
-
throw new TurnTargetError(`${at}.${k} ${x} is off its iris's crop`);
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
if (sign * (outer - inner) <= 0) {
|
|
2625
|
-
throw new TurnTargetError(
|
|
2626
|
-
`${at}.irisOuter ${outer} is not strictly outward of irisInner ${inner}`
|
|
2627
|
-
);
|
|
2628
|
-
}
|
|
2629
|
-
for (const [k, x] of [
|
|
2630
|
-
["runOuter", runOuter],
|
|
2631
|
-
["runFace", runFace]
|
|
2632
|
-
]) {
|
|
2633
|
-
if (x !== null && (x < hair.x0 || x > hair.x1)) {
|
|
2634
|
-
throw new TurnTargetError(`${at}.${k} ${x} is off hair_front's crop`);
|
|
2635
|
-
}
|
|
2636
|
-
}
|
|
2637
|
-
if (sign * (runOuter - cx(iris)) <= 0) {
|
|
2638
|
-
throw new TurnTargetError(
|
|
2639
|
-
`${at}.runOuter ${runOuter} is not strictly outward of its iris's centre`
|
|
2640
|
-
);
|
|
2641
|
-
}
|
|
2642
|
-
if (runFace !== null && sign * (runOuter - runFace) <= 0) {
|
|
2643
|
-
throw new TurnTargetError(
|
|
2644
|
-
`${at}.runOuter ${runOuter} is not strictly outward of runFace ${runFace}`
|
|
2645
|
-
);
|
|
2646
|
-
}
|
|
2647
|
-
if (runFace !== null && sign * (runFace - cx(other)) <= 0) {
|
|
2648
|
-
throw new TurnTargetError(
|
|
2649
|
-
`${at}.runFace ${runFace} reaches the other iris's centre; a run that does is a fringe, runFace null`
|
|
2650
|
-
);
|
|
2651
|
-
}
|
|
2652
|
-
}
|
|
2653
|
-
}
|
|
2654
|
-
|
|
2655
|
-
// src/auto-rig/context.ts
|
|
2656
|
-
var import_format4 = require("@ikijs/format");
|
|
2657
|
-
|
|
2658
|
-
// src/auto-rig/grid.ts
|
|
2659
|
-
var X_STOPS = [-30, 0, 30];
|
|
2660
|
-
var Y_STOPS = [-30, 0, 30];
|
|
2661
|
-
function lattice(box, cell, min, max) {
|
|
2662
|
-
const cols = cellsFor(box.x1 - box.x0, cell, min, max);
|
|
2663
|
-
const rows = cellsFor(box.y1 - box.y0, cell, min, max);
|
|
2664
|
-
const points = [];
|
|
2665
|
-
for (let r = 0; r <= rows; r++) {
|
|
2666
|
-
const y = box.y1 - (box.y1 - box.y0) * r / rows;
|
|
2667
|
-
for (let c = 0; c <= cols; c++) {
|
|
2668
|
-
points.push(
|
|
2669
|
-
roundTo(box.x0 + (box.x1 - box.x0) * c / cols, 0.01),
|
|
2670
|
-
roundTo(y, 0.01)
|
|
2671
|
-
);
|
|
2672
|
-
}
|
|
2673
|
-
}
|
|
2674
|
-
return { box, cols, rows, points };
|
|
2675
|
-
}
|
|
2676
|
-
function bake(l, field) {
|
|
2677
|
-
const out = [];
|
|
2678
|
-
for (const ay of Y_STOPS) {
|
|
2679
|
-
for (const ax of X_STOPS) {
|
|
2680
|
-
const offsets = [];
|
|
2681
|
-
for (let k = 0; k < l.points.length; k += 2) {
|
|
2682
|
-
if (ax === 0 && ay === 0) {
|
|
2683
|
-
offsets.push(0, 0);
|
|
2684
|
-
continue;
|
|
2685
|
-
}
|
|
2686
|
-
const [dx, dy] = field(l.points[k], l.points[k + 1], ax, ay);
|
|
2687
|
-
offsets.push(roundTo(dx, 0.1), roundTo(dy, 0.1));
|
|
3339
|
+
]) {
|
|
3340
|
+
if (x < iris.x0 || x > iris.x1) {
|
|
3341
|
+
throw new TurnTargetError(`${at}.${k} ${x} is off its iris's crop`);
|
|
2688
3342
|
}
|
|
2689
|
-
out.push(offsets);
|
|
2690
3343
|
}
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
}
|
|
2694
|
-
|
|
2695
|
-
const j = Y_STOPS.indexOf(0);
|
|
2696
|
-
const row = X_STOPS.map((_, i) => keyforms[j * X_STOPS.length + i]);
|
|
2697
|
-
return blendStops(X_STOPS, row, ax);
|
|
2698
|
-
}
|
|
2699
|
-
function blendStops(values, offsets, v) {
|
|
2700
|
-
const x = clamp(v, values[0], values[values.length - 1]);
|
|
2701
|
-
let i = 0;
|
|
2702
|
-
while (i < values.length - 2 && x > values[i + 1]) i++;
|
|
2703
|
-
const t = (x - values[i]) / (values[i + 1] - values[i]);
|
|
2704
|
-
return offsets[i].map((o, k) => o + (offsets[i + 1][k] - o) * t);
|
|
2705
|
-
}
|
|
2706
|
-
function land(l, offsets, x, y) {
|
|
2707
|
-
const { cols, rows, points } = l;
|
|
2708
|
-
const stride = cols + 1;
|
|
2709
|
-
let col = cols - 1;
|
|
2710
|
-
for (let c = 0; c < cols; c++) {
|
|
2711
|
-
if (x < points[(c + 1) * 2]) {
|
|
2712
|
-
col = c;
|
|
2713
|
-
break;
|
|
3344
|
+
if (sign * (outer - inner) <= 0) {
|
|
3345
|
+
throw new TurnTargetError(
|
|
3346
|
+
`${at}.irisOuter ${outer} is not strictly outward of irisInner ${inner}`
|
|
3347
|
+
);
|
|
2714
3348
|
}
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
3349
|
+
for (const [k, x] of [
|
|
3350
|
+
["runOuter", runOuter],
|
|
3351
|
+
["runFace", runFace]
|
|
3352
|
+
]) {
|
|
3353
|
+
if (x !== null && (x < hair.x0 || x > hair.x1)) {
|
|
3354
|
+
throw new TurnTargetError(`${at}.${k} ${x} is off hair_front's crop`);
|
|
3355
|
+
}
|
|
3356
|
+
}
|
|
3357
|
+
if (sign * (runOuter - cx(iris)) <= 0) {
|
|
3358
|
+
throw new TurnTargetError(
|
|
3359
|
+
`${at}.runOuter ${runOuter} is not strictly outward of its iris's centre`
|
|
3360
|
+
);
|
|
3361
|
+
}
|
|
3362
|
+
if (runFace !== null && sign * (runOuter - runFace) <= 0) {
|
|
3363
|
+
throw new TurnTargetError(
|
|
3364
|
+
`${at}.runOuter ${runOuter} is not strictly outward of runFace ${runFace}`
|
|
3365
|
+
);
|
|
3366
|
+
}
|
|
3367
|
+
if (runFace !== null && sign * (runFace - cx(other)) <= 0) {
|
|
3368
|
+
throw new TurnTargetError(
|
|
3369
|
+
`${at}.runFace ${runFace} reaches the other iris's centre; a run that does is a fringe, runFace null`
|
|
3370
|
+
);
|
|
2721
3371
|
}
|
|
2722
3372
|
}
|
|
2723
|
-
const x0 = points[col * 2];
|
|
2724
|
-
const x1 = points[(col + 1) * 2];
|
|
2725
|
-
const yT = points[row * stride * 2 + 1];
|
|
2726
|
-
const yB = points[(row + 1) * stride * 2 + 1];
|
|
2727
|
-
const s = clamp((x - x0) / (x1 - x0), 0, 1);
|
|
2728
|
-
const t = clamp((yT - y) / (yT - yB), 0, 1);
|
|
2729
|
-
const at = (r, c) => {
|
|
2730
|
-
const k = (r * stride + c) * 2;
|
|
2731
|
-
return [points[k] + offsets[k], points[k + 1] + offsets[k + 1]];
|
|
2732
|
-
};
|
|
2733
|
-
const p00 = at(row, col);
|
|
2734
|
-
const p10 = at(row, col + 1);
|
|
2735
|
-
const p01 = at(row + 1, col);
|
|
2736
|
-
const p11 = at(row + 1, col + 1);
|
|
2737
|
-
const topX = p00[0] + (p10[0] - p00[0]) * s;
|
|
2738
|
-
const topY = p00[1] + (p10[1] - p00[1]) * s;
|
|
2739
|
-
const botX = p01[0] + (p11[0] - p01[0]) * s;
|
|
2740
|
-
const botY = p01[1] + (p11[1] - p01[1]) * s;
|
|
2741
|
-
return [topX + (botX - topX) * t, topY + (botY - topY) * t];
|
|
2742
3373
|
}
|
|
2743
3374
|
|
|
3375
|
+
// src/auto-rig/context.ts
|
|
3376
|
+
var import_format7 = require("@ikijs/format");
|
|
3377
|
+
|
|
2744
3378
|
// src/auto-rig/head.ts
|
|
2745
3379
|
function outerEdge(runs, axisX, side) {
|
|
2746
3380
|
if (runs.length === 0) return void 0;
|
|
@@ -3770,6 +4404,7 @@ function familyField(m, family) {
|
|
|
3770
4404
|
case "hair_back":
|
|
3771
4405
|
return hairField(m, m.hairBack, NOD.hairBack);
|
|
3772
4406
|
case "body":
|
|
4407
|
+
case "arm":
|
|
3773
4408
|
return () => [0, 0];
|
|
3774
4409
|
}
|
|
3775
4410
|
}
|
|
@@ -3869,7 +4504,7 @@ function renderedLander(turn, parts, grids, from = /* @__PURE__ */ new Map()) {
|
|
|
3869
4504
|
const family = roleSpec(role).family;
|
|
3870
4505
|
const part = parts.find((p) => p.id === role);
|
|
3871
4506
|
const g = grids.get(family);
|
|
3872
|
-
const warp = part?.warps?.find((w) => w.parameter ===
|
|
4507
|
+
const warp = part?.warps?.find((w) => w.parameter === import_format7.StandardParameter.AngleX);
|
|
3873
4508
|
if (part === void 0 || part.mesh === void 0 || !g && !warp) {
|
|
3874
4509
|
return x + field(role)(x, y, ax, 0)[0];
|
|
3875
4510
|
}
|
|
@@ -4578,177 +5213,6 @@ function buildTurn(frame, landmarks, q) {
|
|
|
4578
5213
|
};
|
|
4579
5214
|
}
|
|
4580
5215
|
|
|
4581
|
-
// src/auto-rig/drivers.ts
|
|
4582
|
-
var import_format5 = require("@ikijs/format");
|
|
4583
|
-
var SWAY_RANGE = 20;
|
|
4584
|
-
var SWAY_CURVE = 1.4;
|
|
4585
|
-
var HAIR_HANG = 0.35;
|
|
4586
|
-
var Z_STOPS = [-30, 0, 30];
|
|
4587
|
-
function localWarp(parameter, mesh, box, values, f) {
|
|
4588
|
-
const pts = meshPoints(mesh, box);
|
|
4589
|
-
const w = bw(box);
|
|
4590
|
-
const h = bh(box);
|
|
4591
|
-
return {
|
|
4592
|
-
parameter,
|
|
4593
|
-
keyforms: values.map((value) => ({
|
|
4594
|
-
value,
|
|
4595
|
-
offsets: pts.flatMap((p, i) => {
|
|
4596
|
-
const [dx, dy] = f(p, value, i);
|
|
4597
|
-
return [roundTo(dx / w, 1e-4), roundTo(dy / h, 1e-4)];
|
|
4598
|
-
})
|
|
4599
|
-
}))
|
|
4600
|
-
};
|
|
4601
|
-
}
|
|
4602
|
-
var rollOf = (v) => -v / 30 * ROLL_DEG * DEG;
|
|
4603
|
-
function motionReach(warps, mesh, box, extra = [0, 0]) {
|
|
4604
|
-
const out = {
|
|
4605
|
-
left: extra[0],
|
|
4606
|
-
right: extra[0],
|
|
4607
|
-
bottom: extra[1],
|
|
4608
|
-
top: extra[1]
|
|
4609
|
-
};
|
|
4610
|
-
if (mesh === void 0) return out;
|
|
4611
|
-
const pts = meshPoints(mesh, box);
|
|
4612
|
-
for (const warp of warps ?? []) {
|
|
4613
|
-
let l = 0;
|
|
4614
|
-
let r = 0;
|
|
4615
|
-
let b = 0;
|
|
4616
|
-
let t = 0;
|
|
4617
|
-
for (const k of warp.keyforms) {
|
|
4618
|
-
pts.forEach(([x, y], i) => {
|
|
4619
|
-
const px = x + k.offsets[2 * i] * bw(box);
|
|
4620
|
-
const py = y + k.offsets[2 * i + 1] * bh(box);
|
|
4621
|
-
l = Math.max(l, box.x0 - px);
|
|
4622
|
-
r = Math.max(r, px - box.x1);
|
|
4623
|
-
b = Math.max(b, box.y0 - py);
|
|
4624
|
-
t = Math.max(t, py - box.y1);
|
|
4625
|
-
});
|
|
4626
|
-
}
|
|
4627
|
-
out.left += l;
|
|
4628
|
-
out.right += r;
|
|
4629
|
-
out.bottom += b;
|
|
4630
|
-
out.top += t;
|
|
4631
|
-
}
|
|
4632
|
-
return out;
|
|
4633
|
-
}
|
|
4634
|
-
function blinkFold(param, mesh, box, travel, eye = box) {
|
|
4635
|
-
const yc = eye.y1 - travel * bh(eye);
|
|
4636
|
-
return localWarp(
|
|
4637
|
-
param,
|
|
4638
|
-
mesh,
|
|
4639
|
-
box,
|
|
4640
|
-
[0, 1],
|
|
4641
|
-
([, y], v) => v === 1 ? [0, 0] : [0, yc - y]
|
|
4642
|
-
);
|
|
4643
|
-
}
|
|
4644
|
-
function lashFold(param, mesh, lash, eye, travel) {
|
|
4645
|
-
const yc = eye.y1 - travel * bh(eye) - 0.04 * bh(eye);
|
|
4646
|
-
const c = cx(lash);
|
|
4647
|
-
const half = bw(lash) / 2;
|
|
4648
|
-
const sag = 0.3 * bh(lash);
|
|
4649
|
-
return localWarp(param, mesh, lash, [0, 1], ([x, y], v) => {
|
|
4650
|
-
if (v === 1) return [0, 0];
|
|
4651
|
-
const u = (x - c) / half;
|
|
4652
|
-
return [0, yc + (y - lash.y0) * 0.5 - sag * (1 - u * u) - y];
|
|
4653
|
-
});
|
|
4654
|
-
}
|
|
4655
|
-
function gazeBindings(irisW, share = 1) {
|
|
4656
|
-
const gx = AMPLITUDE.gazeX * irisW * share;
|
|
4657
|
-
const gy = AMPLITUDE.gazeY * irisW * share;
|
|
4658
|
-
return [
|
|
4659
|
-
{
|
|
4660
|
-
parameter: import_format5.StandardParameter.EyeballX,
|
|
4661
|
-
channel: "translateX",
|
|
4662
|
-
from: -round1(gx),
|
|
4663
|
-
to: round1(gx)
|
|
4664
|
-
},
|
|
4665
|
-
{
|
|
4666
|
-
parameter: import_format5.StandardParameter.EyeballY,
|
|
4667
|
-
channel: "translateY",
|
|
4668
|
-
from: -round1(gy),
|
|
4669
|
-
to: round1(gy)
|
|
4670
|
-
}
|
|
4671
|
-
];
|
|
4672
|
-
}
|
|
4673
|
-
function gazeReach(irisW, share = 1) {
|
|
4674
|
-
return [AMPLITUDE.gazeX * irisW * share, AMPLITUDE.gazeY * irisW * share];
|
|
4675
|
-
}
|
|
4676
|
-
function browReach(hh) {
|
|
4677
|
-
return AMPLITUDE.brow * hh;
|
|
4678
|
-
}
|
|
4679
|
-
function browBindings(side, reach) {
|
|
4680
|
-
const y = side === "L" ? import_format5.StandardParameter.BrowLeftY : import_format5.StandardParameter.BrowRightY;
|
|
4681
|
-
const a = side === "L" ? import_format5.StandardParameter.BrowLeftAngle : import_format5.StandardParameter.BrowRightAngle;
|
|
4682
|
-
return [
|
|
4683
|
-
{
|
|
4684
|
-
parameter: y,
|
|
4685
|
-
channel: "translateY",
|
|
4686
|
-
from: -round1(reach),
|
|
4687
|
-
to: round1(reach)
|
|
4688
|
-
},
|
|
4689
|
-
{ parameter: a, channel: "rotate", from: -BROW_TILT, to: BROW_TILT }
|
|
4690
|
-
];
|
|
4691
|
-
}
|
|
4692
|
-
var BROW_TILT = 15;
|
|
4693
|
-
function mouthForm(mesh, box) {
|
|
4694
|
-
const a = 0.3 * bh(box);
|
|
4695
|
-
const c = cx(box);
|
|
4696
|
-
const half = bw(box) / 2;
|
|
4697
|
-
return localWarp(import_format5.StandardParameter.MouthForm, mesh, box, [-1, 0, 1], ([x], v) => {
|
|
4698
|
-
if (v === 0) return [0, 0];
|
|
4699
|
-
const u = (x - c) / half;
|
|
4700
|
-
const widen = v > 0 ? 0.05 : 0.03;
|
|
4701
|
-
return [v * widen * (x - c), v * a * (u * u - 0.3)];
|
|
4702
|
-
});
|
|
4703
|
-
}
|
|
4704
|
-
function mouthOpenGrow(mesh, box) {
|
|
4705
|
-
const c = cx(box);
|
|
4706
|
-
return localWarp(
|
|
4707
|
-
import_format5.StandardParameter.MouthOpen,
|
|
4708
|
-
mesh,
|
|
4709
|
-
box,
|
|
4710
|
-
[0, 1],
|
|
4711
|
-
([x, y], v) => v === 1 ? [(AMPLITUDE.mouthOpenWidth - 1) * (x - c), 0] : [0, box.y1 + (y - box.y1) * 0.2 - y]
|
|
4712
|
-
);
|
|
4713
|
-
}
|
|
4714
|
-
function mouthWiden(mesh, box) {
|
|
4715
|
-
const c = cx(box);
|
|
4716
|
-
return localWarp(import_format5.StandardParameter.MouthOpen, mesh, box, [0, 1], ([x], v) => [
|
|
4717
|
-
v * (AMPLITUDE.mouthOpenWidth - 1) * (x - c),
|
|
4718
|
-
0
|
|
4719
|
-
]);
|
|
4720
|
-
}
|
|
4721
|
-
function hairSway(param, mesh, box, amp) {
|
|
4722
|
-
return localWarp(
|
|
4723
|
-
param,
|
|
4724
|
-
mesh,
|
|
4725
|
-
box,
|
|
4726
|
-
[-SWAY_RANGE, 0, SWAY_RANGE],
|
|
4727
|
-
([, y], v) => {
|
|
4728
|
-
const t = Math.max(0, (box.y1 - y) / bh(box));
|
|
4729
|
-
return [v / SWAY_RANGE * amp * t ** SWAY_CURVE, 0];
|
|
4730
|
-
}
|
|
4731
|
-
);
|
|
4732
|
-
}
|
|
4733
|
-
function tiltHang(mesh, box, pivot, weight) {
|
|
4734
|
-
return localWarp(import_format5.StandardParameter.AngleZ, mesh, box, Z_STOPS, ([x, y], v, i) => {
|
|
4735
|
-
const g = weight(y, i);
|
|
4736
|
-
if (g === 0 || v === 0) return [0, 0];
|
|
4737
|
-
const a = -g * rollOf(v);
|
|
4738
|
-
const px = x - pivot[0];
|
|
4739
|
-
const py = y - pivot[1];
|
|
4740
|
-
return [
|
|
4741
|
-
pivot[0] + px * Math.cos(a) - py * Math.sin(a) - x,
|
|
4742
|
-
pivot[1] + px * Math.sin(a) + py * Math.cos(a) - y
|
|
4743
|
-
];
|
|
4744
|
-
});
|
|
4745
|
-
}
|
|
4746
|
-
function hairHangWeight(pivotY, bottom) {
|
|
4747
|
-
if (bottom >= pivotY) return () => 0;
|
|
4748
|
-
return (y) => HAIR_HANG * smoothstep(pivotY, bottom, y);
|
|
4749
|
-
}
|
|
4750
|
-
var round1 = (v) => roundTo(v, 0.1);
|
|
4751
|
-
|
|
4752
5216
|
// src/auto-rig/generate.ts
|
|
4753
5217
|
var MESH_CELLS = {
|
|
4754
5218
|
face: { px: 28, min: 4, max: 24 },
|
|
@@ -4775,6 +5239,7 @@ var WARP_ID = {
|
|
|
4775
5239
|
mouth: "mouthWarp"
|
|
4776
5240
|
};
|
|
4777
5241
|
var isGridFamily = (f) => f in WARP_ID;
|
|
5242
|
+
var isArm = (spec) => spec.family === "arm";
|
|
4778
5243
|
var GRID_PAD = 6;
|
|
4779
5244
|
function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
4780
5245
|
checkLayers(layers, canvas);
|
|
@@ -4823,6 +5288,18 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
4823
5288
|
chinSlide: chinSlide(fit.model)
|
|
4824
5289
|
});
|
|
4825
5290
|
const turn = { ...fit.model, frame };
|
|
5291
|
+
const arms = /* @__PURE__ */ new Map();
|
|
5292
|
+
for (const spec of ROLE_TABLE) {
|
|
5293
|
+
const layer = byRole.get(spec.role);
|
|
5294
|
+
if (layer === void 0 || !isArm(spec)) continue;
|
|
5295
|
+
arms.set(spec.role, armGeometry(layer, cx(box("body"))));
|
|
5296
|
+
}
|
|
5297
|
+
const bodyWarp = has("body") ? buildBodyWarp({
|
|
5298
|
+
body: byRole.get("body"),
|
|
5299
|
+
chin: { x: frame.axisX, y: frame.chinY },
|
|
5300
|
+
hh: frame.hh,
|
|
5301
|
+
pivots: [...arms.values()].map((g) => g.shoulder)
|
|
5302
|
+
}) : void 0;
|
|
4826
5303
|
const parts = [];
|
|
4827
5304
|
const reach = /* @__PURE__ */ new Map();
|
|
4828
5305
|
let regionOf = [];
|
|
@@ -4830,6 +5307,12 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
4830
5307
|
for (const spec of ROLE_TABLE) {
|
|
4831
5308
|
const layer = byRole.get(spec.role);
|
|
4832
5309
|
if (layer === void 0) continue;
|
|
5310
|
+
if (isArm(spec)) {
|
|
5311
|
+
parts.push(
|
|
5312
|
+
...armParts(spec.role, layer, arms.get(spec.role), parts.length)
|
|
5313
|
+
);
|
|
5314
|
+
continue;
|
|
5315
|
+
}
|
|
4833
5316
|
const built = buildPart(
|
|
4834
5317
|
spec,
|
|
4835
5318
|
boxOfLayer(layer),
|
|
@@ -4848,7 +5331,9 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
4848
5331
|
}
|
|
4849
5332
|
const grids = /* @__PURE__ */ new Map();
|
|
4850
5333
|
for (const family of Object.keys(WARP_ID)) {
|
|
4851
|
-
const members = parts.filter(
|
|
5334
|
+
const members = parts.filter(
|
|
5335
|
+
(p) => roleSpec(roleOfPart(p.id)).family === family
|
|
5336
|
+
);
|
|
4852
5337
|
if (members.length === 0) continue;
|
|
4853
5338
|
const area = unionBoxes(
|
|
4854
5339
|
members.map((p) => {
|
|
@@ -4870,13 +5355,19 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
4870
5355
|
});
|
|
4871
5356
|
}
|
|
4872
5357
|
for (const part of parts) {
|
|
4873
|
-
const family = roleSpec(part.id).family;
|
|
5358
|
+
const family = roleSpec(roleOfPart(part.id)).family;
|
|
4874
5359
|
if (isGridFamily(family)) {
|
|
4875
5360
|
part.deformer = WARP_ID[family];
|
|
4876
5361
|
continue;
|
|
4877
5362
|
}
|
|
4878
|
-
|
|
4879
|
-
|
|
5363
|
+
if (family === "body") {
|
|
5364
|
+
part.deformer = BODY_WARP_ID;
|
|
5365
|
+
part.mesh = bodyWarp.mesh;
|
|
5366
|
+
continue;
|
|
5367
|
+
}
|
|
5368
|
+
if (family === "arm") continue;
|
|
5369
|
+
part.deformer = "headDeformer";
|
|
5370
|
+
if (part.mesh === void 0) continue;
|
|
4880
5371
|
const own = familyField(turn, family);
|
|
4881
5372
|
const fieldOf = part.id === "face" ? plateFields(turn, regionOf, own) : () => own;
|
|
4882
5373
|
part.warps = [
|
|
@@ -4886,17 +5377,17 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
4886
5377
|
}
|
|
4887
5378
|
const parameters = declareParameters(new Set(byRole.keys()));
|
|
4888
5379
|
const model = {
|
|
4889
|
-
version:
|
|
5380
|
+
version: import_format8.IKI_FORMAT_VERSION,
|
|
4890
5381
|
name: "auto-rigged",
|
|
4891
5382
|
canvas: { width: canvas.width, height: canvas.height },
|
|
4892
5383
|
parameters,
|
|
4893
5384
|
parts,
|
|
4894
|
-
deformers: deformers(frame, grids,
|
|
5385
|
+
deformers: deformers(frame, grids, bodyWarp?.deformer, arms),
|
|
4895
5386
|
...has("hair_front") ? { physics: hairPhysics() } : {},
|
|
4896
5387
|
expressions: defaultExpressions(new Set(parameters.map((p) => p.id))),
|
|
4897
5388
|
motions: DEFAULT_MOTIONS
|
|
4898
5389
|
};
|
|
4899
|
-
const valid = (0,
|
|
5390
|
+
const valid = (0, import_format8.parseIkiModel)(model);
|
|
4900
5391
|
if (hasNose && options.onTurnSolved) {
|
|
4901
5392
|
options.onTurnSolved(
|
|
4902
5393
|
turnReport(
|
|
@@ -4911,14 +5402,14 @@ function generateIkiFromLayerSet(layers, canvas, options = {}) {
|
|
|
4911
5402
|
function turnWarps(mesh, b, fieldOf) {
|
|
4912
5403
|
return [
|
|
4913
5404
|
localWarp(
|
|
4914
|
-
|
|
5405
|
+
import_format8.StandardParameter.AngleX,
|
|
4915
5406
|
mesh,
|
|
4916
5407
|
b,
|
|
4917
5408
|
X_STOPS,
|
|
4918
5409
|
([x, y], v, i) => v === 0 ? [0, 0] : [fieldOf(i)(x, y, v, 0)[0], 0]
|
|
4919
5410
|
),
|
|
4920
5411
|
localWarp(
|
|
4921
|
-
|
|
5412
|
+
import_format8.StandardParameter.AngleY,
|
|
4922
5413
|
mesh,
|
|
4923
5414
|
b,
|
|
4924
5415
|
Y_STOPS,
|
|
@@ -4966,16 +5457,22 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
4966
5457
|
}
|
|
4967
5458
|
if (mesh !== void 0) part.mesh = mesh;
|
|
4968
5459
|
const side = spec.role.endsWith("_L") ? "L" : "R";
|
|
4969
|
-
const eyeOpen = side === "L" ?
|
|
5460
|
+
const eyeOpen = side === "L" ? import_format8.StandardParameter.EyeOpenLeft : import_format8.StandardParameter.EyeOpenRight;
|
|
4970
5461
|
const hh = frame.hh;
|
|
4971
5462
|
switch (spec.role) {
|
|
4972
5463
|
case "face":
|
|
4973
5464
|
if (region?.some((r) => r === "neck")) {
|
|
4974
5465
|
const isNeck = region.map((r) => r === "neck");
|
|
4975
5466
|
warps.push(
|
|
4976
|
-
tiltHang(
|
|
5467
|
+
tiltHang(
|
|
5468
|
+
mesh,
|
|
5469
|
+
b,
|
|
5470
|
+
pivot,
|
|
5471
|
+
(_y, i) => isNeck[i] ? 1 : 0,
|
|
5472
|
+
headOwnRoll(byRole.has("body"))
|
|
5473
|
+
),
|
|
4977
5474
|
localWarp(
|
|
4978
|
-
|
|
5475
|
+
import_format8.StandardParameter.Breath,
|
|
4979
5476
|
mesh,
|
|
4980
5477
|
b,
|
|
4981
5478
|
[0, 1],
|
|
@@ -5022,8 +5519,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5022
5519
|
if (byRole.has("mouth_open")) {
|
|
5023
5520
|
warps.push(mouthWiden(mesh, b));
|
|
5024
5521
|
bindings.push(
|
|
5025
|
-
{ parameter:
|
|
5026
|
-
{ parameter:
|
|
5522
|
+
{ parameter: import_format8.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 },
|
|
5523
|
+
{ parameter: import_format8.StandardParameter.MouthOpen, channel: "opacity", from: 1, to: 0 }
|
|
5027
5524
|
);
|
|
5028
5525
|
extra = [(AMPLITUDE.mouthOpenWidth - 1) * (bw(b) / 2), 0];
|
|
5029
5526
|
} else {
|
|
@@ -5033,19 +5530,19 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5033
5530
|
);
|
|
5034
5531
|
bindings.push(
|
|
5035
5532
|
{
|
|
5036
|
-
parameter:
|
|
5533
|
+
parameter: import_format8.StandardParameter.MouthOpen,
|
|
5037
5534
|
channel: "scaleY",
|
|
5038
5535
|
from: 0,
|
|
5039
5536
|
to: roundTo(grow, 0.01)
|
|
5040
5537
|
},
|
|
5041
5538
|
{
|
|
5042
|
-
parameter:
|
|
5539
|
+
parameter: import_format8.StandardParameter.MouthOpen,
|
|
5043
5540
|
channel: "scaleX",
|
|
5044
5541
|
from: 0,
|
|
5045
5542
|
to: roundTo(AMPLITUDE.mouthOpenWidth - 1, 0.01)
|
|
5046
5543
|
},
|
|
5047
5544
|
{
|
|
5048
|
-
parameter:
|
|
5545
|
+
parameter: import_format8.StandardParameter.MouthOpen,
|
|
5049
5546
|
channel: "translateY",
|
|
5050
5547
|
from: 0,
|
|
5051
5548
|
to: -roundTo(grow * bh(b) / 2, 0.01)
|
|
@@ -5057,7 +5554,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5057
5554
|
case "mouth_open":
|
|
5058
5555
|
warps.push(mouthForm(mesh, b), mouthOpenGrow(mesh, b));
|
|
5059
5556
|
bindings.push({
|
|
5060
|
-
parameter:
|
|
5557
|
+
parameter: import_format8.StandardParameter.MouthOpen,
|
|
5061
5558
|
channel: "opacity",
|
|
5062
5559
|
from: 0,
|
|
5063
5560
|
to: 1
|
|
@@ -5067,7 +5564,7 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5067
5564
|
case "blush_L":
|
|
5068
5565
|
case "blush_R":
|
|
5069
5566
|
bindings.push({
|
|
5070
|
-
parameter:
|
|
5567
|
+
parameter: import_format8.StandardParameter.Cheek,
|
|
5071
5568
|
channel: "opacity",
|
|
5072
5569
|
from: AMPLITUDE.blushRest,
|
|
5073
5570
|
to: 1
|
|
@@ -5078,8 +5575,8 @@ function buildPart(spec, b, order, frame, byRole, style, frontGrid) {
|
|
|
5078
5575
|
if (byRole.has("hair_front")) {
|
|
5079
5576
|
const amp = style.sway * AMPLITUDE.sway * hh;
|
|
5080
5577
|
warps.push(
|
|
5081
|
-
hairSway(
|
|
5082
|
-
hairSway(
|
|
5578
|
+
hairSway(import_format8.StandardParameter.HairSwayX, mesh, b, amp),
|
|
5579
|
+
hairSway(import_format8.StandardParameter.HairSwayZ, mesh, b, amp)
|
|
5083
5580
|
);
|
|
5084
5581
|
}
|
|
5085
5582
|
warps.push(tiltHang(mesh, b, pivot, hairHangWeight(frame.chinY, b.y0)));
|
|
@@ -5152,33 +5649,24 @@ function meshScale(role, frame) {
|
|
|
5152
5649
|
return Infinity;
|
|
5153
5650
|
}
|
|
5154
5651
|
}
|
|
5155
|
-
function deformers(frame, grids,
|
|
5652
|
+
function deformers(frame, grids, bodyWarp, arms) {
|
|
5156
5653
|
const out = [];
|
|
5157
|
-
if (
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
bindings: [
|
|
5162
|
-
{
|
|
5163
|
-
parameter: import_format6.StandardParameter.Breath,
|
|
5164
|
-
channel: "translateY",
|
|
5165
|
-
from: 0,
|
|
5166
|
-
to: roundTo(AMPLITUDE.breathBody * frame.hh, 0.01)
|
|
5167
|
-
}
|
|
5168
|
-
]
|
|
5169
|
-
});
|
|
5170
|
-
}
|
|
5654
|
+
if (bodyWarp !== void 0) out.push(bodyWarp);
|
|
5655
|
+
for (const [role, g] of arms) out.push(...armDeformers(role, g));
|
|
5656
|
+
const hasBody = bodyWarp !== void 0;
|
|
5657
|
+
const roll = headOwnRoll(hasBody);
|
|
5171
5658
|
out.push({
|
|
5172
5659
|
id: "headDeformer",
|
|
5660
|
+
...hasBody ? { parent: BODY_WARP_ID } : {},
|
|
5173
5661
|
pivot: { x: roundTo(frame.axisX, 0.01), y: roundTo(frame.chinY, 0.01) },
|
|
5174
5662
|
bindings: [
|
|
5175
5663
|
// AngleZ is clockwise-positive (Live2D's); a rotation is CCW-positive.
|
|
5176
|
-
{ parameter:
|
|
5664
|
+
{ parameter: import_format8.StandardParameter.AngleZ, channel: "rotate", from: roll, to: -roll },
|
|
5177
5665
|
{
|
|
5178
|
-
parameter:
|
|
5666
|
+
parameter: import_format8.StandardParameter.Breath,
|
|
5179
5667
|
channel: "translateY",
|
|
5180
5668
|
from: 0,
|
|
5181
|
-
to: roundTo(
|
|
5669
|
+
to: roundTo(headOwnBreath(hasBody) * frame.hh, 0.01)
|
|
5182
5670
|
}
|
|
5183
5671
|
]
|
|
5184
5672
|
});
|
|
@@ -5193,8 +5681,8 @@ function deformers(frame, grids, body) {
|
|
|
5193
5681
|
points: g.lattice.points
|
|
5194
5682
|
},
|
|
5195
5683
|
warp2d: {
|
|
5196
|
-
parameter:
|
|
5197
|
-
parameterY:
|
|
5684
|
+
parameter: import_format8.StandardParameter.AngleX,
|
|
5685
|
+
parameterY: import_format8.StandardParameter.AngleY,
|
|
5198
5686
|
valuesX: X_STOPS,
|
|
5199
5687
|
valuesY: Y_STOPS,
|
|
5200
5688
|
keyforms2d: g.keyforms.map((offsets) => ({ offsets }))
|
|
@@ -5207,16 +5695,16 @@ function hairPhysics() {
|
|
|
5207
5695
|
return [
|
|
5208
5696
|
{
|
|
5209
5697
|
id: "hairSway",
|
|
5210
|
-
input: { parameter:
|
|
5211
|
-
output: { parameter:
|
|
5698
|
+
input: { parameter: import_format8.StandardParameter.AngleX, weight: 1 },
|
|
5699
|
+
output: { parameter: import_format8.StandardParameter.HairSwayX, scale: 5 },
|
|
5212
5700
|
mass: 1,
|
|
5213
5701
|
stiffness: 30,
|
|
5214
5702
|
damping: 3
|
|
5215
5703
|
},
|
|
5216
5704
|
{
|
|
5217
5705
|
id: "hairTilt",
|
|
5218
|
-
input: { parameter:
|
|
5219
|
-
output: { parameter:
|
|
5706
|
+
input: { parameter: import_format8.StandardParameter.AngleZ, weight: 1 },
|
|
5707
|
+
output: { parameter: import_format8.StandardParameter.HairSwayZ, scale: 8 },
|
|
5220
5708
|
mass: 1,
|
|
5221
5709
|
stiffness: 25,
|
|
5222
5710
|
damping: 2.5
|
|
@@ -5226,32 +5714,46 @@ function hairPhysics() {
|
|
|
5226
5714
|
function declareParameters(roles) {
|
|
5227
5715
|
const out = [];
|
|
5228
5716
|
const add = (id, name, min, max, def) => out.push({ id, name, min, max, default: def });
|
|
5229
|
-
add(
|
|
5230
|
-
add(
|
|
5231
|
-
add(
|
|
5232
|
-
|
|
5233
|
-
|
|
5717
|
+
add(import_format8.StandardParameter.AngleX, "Head Angle", -30, 30, 0);
|
|
5718
|
+
add(import_format8.StandardParameter.AngleY, "Head Angle Y", -30, 30, 0);
|
|
5719
|
+
add(import_format8.StandardParameter.AngleZ, "Head Angle Z", -30, 30, 0);
|
|
5720
|
+
if (roles.has("body")) {
|
|
5721
|
+
add(import_format8.StandardParameter.BodyAngleX, "Body Angle X", -10, 10, 0);
|
|
5722
|
+
add(import_format8.StandardParameter.BodyAngleY, "Body Angle Y", -10, 10, 0);
|
|
5723
|
+
add(import_format8.StandardParameter.BodyAngleZ, "Body Angle Z", -10, 10, 0);
|
|
5724
|
+
}
|
|
5725
|
+
const [armMin, armMax] = ARM_RANGE;
|
|
5726
|
+
if (roles.has("arm_L")) {
|
|
5727
|
+
add(import_format8.StandardParameter.ArmLeft, "Arm L", armMin, armMax, 0);
|
|
5728
|
+
add(import_format8.StandardParameter.ElbowLeft, "Elbow L", armMin, armMax, 0);
|
|
5729
|
+
}
|
|
5730
|
+
if (roles.has("arm_R")) {
|
|
5731
|
+
add(import_format8.StandardParameter.ArmRight, "Arm R", armMin, armMax, 0);
|
|
5732
|
+
add(import_format8.StandardParameter.ElbowRight, "Elbow R", armMin, armMax, 0);
|
|
5733
|
+
}
|
|
5734
|
+
add(import_format8.StandardParameter.EyeOpenLeft, "Eye L", 0, 1, 1);
|
|
5735
|
+
add(import_format8.StandardParameter.EyeOpenRight, "Eye R", 0, 1, 1);
|
|
5234
5736
|
if ([...roles].some((r) => /^(iris|pupil|highlight)_/.test(r))) {
|
|
5235
|
-
add(
|
|
5236
|
-
add(
|
|
5737
|
+
add(import_format8.StandardParameter.EyeballX, "Gaze X", -1, 1, 0);
|
|
5738
|
+
add(import_format8.StandardParameter.EyeballY, "Gaze Y", -1, 1, 0);
|
|
5237
5739
|
}
|
|
5238
|
-
add(
|
|
5239
|
-
add(
|
|
5740
|
+
add(import_format8.StandardParameter.MouthOpen, "Mouth Open", 0, 1, 0);
|
|
5741
|
+
add(import_format8.StandardParameter.MouthForm, "Mouth Form", -1, 1, 0);
|
|
5240
5742
|
if (roles.has("brow_L")) {
|
|
5241
|
-
add(
|
|
5242
|
-
add(
|
|
5743
|
+
add(import_format8.StandardParameter.BrowLeftY, "Brow L Y", -1, 1, 0);
|
|
5744
|
+
add(import_format8.StandardParameter.BrowLeftAngle, "Brow L Angle", -1, 1, 0);
|
|
5243
5745
|
}
|
|
5244
5746
|
if (roles.has("brow_R")) {
|
|
5245
|
-
add(
|
|
5246
|
-
add(
|
|
5747
|
+
add(import_format8.StandardParameter.BrowRightY, "Brow R Y", -1, 1, 0);
|
|
5748
|
+
add(import_format8.StandardParameter.BrowRightAngle, "Brow R Angle", -1, 1, 0);
|
|
5247
5749
|
}
|
|
5248
5750
|
if (roles.has("blush_L") || roles.has("blush_R")) {
|
|
5249
|
-
add(
|
|
5751
|
+
add(import_format8.StandardParameter.Cheek, "Cheek", 0, 1, 0);
|
|
5250
5752
|
}
|
|
5251
|
-
add(
|
|
5753
|
+
add(import_format8.StandardParameter.Breath, "Breath", 0, 1, 0);
|
|
5252
5754
|
if (roles.has("hair_front")) {
|
|
5253
|
-
add(
|
|
5254
|
-
add(
|
|
5755
|
+
add(import_format8.StandardParameter.HairSwayX, "Hair Sway X", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
5756
|
+
add(import_format8.StandardParameter.HairSwayZ, "Hair Sway Z", -SWAY_RANGE, SWAY_RANGE, 0);
|
|
5255
5757
|
}
|
|
5256
5758
|
return out;
|
|
5257
5759
|
}
|
|
@@ -5270,6 +5772,7 @@ function declareParameters(roles) {
|
|
|
5270
5772
|
DeletePhysicsRig,
|
|
5271
5773
|
EditorDocument,
|
|
5272
5774
|
HEAD_BAND,
|
|
5775
|
+
LayerGeometryError,
|
|
5273
5776
|
SPECK_CORE_FRACTION,
|
|
5274
5777
|
SetDeformerBindings,
|
|
5275
5778
|
SetDeformerParent,
|
|
@@ -5288,6 +5791,7 @@ function declareParameters(roles) {
|
|
|
5288
5791
|
SetPhysicsRig,
|
|
5289
5792
|
TurnTargetError,
|
|
5290
5793
|
UV_INSET_PX,
|
|
5794
|
+
armGeometry,
|
|
5291
5795
|
captureBindingEndpoint,
|
|
5292
5796
|
computeGridOffsets,
|
|
5293
5797
|
createDefaultMatrixDeformer,
|
|
@@ -5304,6 +5808,7 @@ function declareParameters(roles) {
|
|
|
5304
5808
|
isSpeckCore,
|
|
5305
5809
|
packAtlas,
|
|
5306
5810
|
parseLayerRoles,
|
|
5811
|
+
partIdsOfRole,
|
|
5307
5812
|
upsertGridKeyform,
|
|
5308
5813
|
uvRectFor,
|
|
5309
5814
|
validateDeformerDelete,
|