@lovelace_lol/embody 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/WasmRuntimeCore.d.ts +8 -0
- package/dist/core/index.d.ts +0 -2
- package/dist/core.cjs +27 -512
- package/dist/core.cjs.map +1 -1
- package/dist/core.js +28 -512
- package/dist/core.js.map +1 -1
- package/dist/engines/three/Embody.d.ts +1 -2
- package/dist/index.cjs +164 -502
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +0 -2
- package/dist/index.js +166 -503
- package/dist/index.js.map +1 -1
- package/dist/three.cjs +162 -499
- package/dist/three.cjs.map +1 -1
- package/dist/three.js +163 -500
- package/dist/three.js.map +1 -1
- package/dist/wasm/embody_wasm.d.ts +3 -3
- package/dist/wasm/embody_wasm.js +3 -3
- package/dist/wasm/embody_wasm_bg.wasm +0 -0
- package/package.json +1 -1
- package/dist/core/TsRuntimeCore.d.ts +0 -40
package/dist/core.js
CHANGED
|
@@ -185,526 +185,18 @@ function numericArrayToNumbers(values) {
|
|
|
185
185
|
return Array.from(values);
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
// src/core/compositeAxis.ts
|
|
189
|
-
function toAUList(value) {
|
|
190
|
-
if (value === void 0) return [];
|
|
191
|
-
return Array.isArray(value) ? value : [value];
|
|
192
|
-
}
|
|
193
|
-
function findBoneBindingForNode(auToBones, auId, nodeKey) {
|
|
194
|
-
return auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
|
|
195
|
-
}
|
|
196
|
-
function getCompositeAxisValue(axisConfig, getValue) {
|
|
197
|
-
if (!axisConfig) return 0;
|
|
198
|
-
const negativeAUs = toAUList(axisConfig.negative);
|
|
199
|
-
const positiveAUs = toAUList(axisConfig.positive);
|
|
200
|
-
if (negativeAUs.length > 0 && positiveAUs.length > 0) {
|
|
201
|
-
const negativeValue = Math.max(...negativeAUs.map(getValue), 0);
|
|
202
|
-
const positiveValue = Math.max(...positiveAUs.map(getValue), 0);
|
|
203
|
-
return positiveValue - negativeValue;
|
|
204
|
-
}
|
|
205
|
-
if (axisConfig.aus.length > 1) {
|
|
206
|
-
return Math.max(...axisConfig.aus.map(getValue), 0);
|
|
207
|
-
}
|
|
208
|
-
return getValue(axisConfig.aus[0]);
|
|
209
|
-
}
|
|
210
|
-
function getCompositeAxisBinding(nodeKey, axisConfig, direction, getValue, auToBones) {
|
|
211
|
-
if (!axisConfig) return null;
|
|
212
|
-
const directionalAUs = direction < 0 ? toAUList(axisConfig.negative) : toAUList(axisConfig.positive);
|
|
213
|
-
const candidates = directionalAUs.length > 0 ? directionalAUs : axisConfig.aus;
|
|
214
|
-
const ranked = [...candidates].sort((a, b) => getValue(b) - getValue(a));
|
|
215
|
-
for (const auId of ranked) {
|
|
216
|
-
const binding = findBoneBindingForNode(auToBones, auId, nodeKey);
|
|
217
|
-
if (binding) return binding;
|
|
218
|
-
}
|
|
219
|
-
return null;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// src/mappings/visemeSystem.ts
|
|
223
|
-
function hasOwn(value, key) {
|
|
224
|
-
return Boolean(value && Object.prototype.hasOwnProperty.call(value, key));
|
|
225
|
-
}
|
|
226
|
-
function toSlotId(label, index) {
|
|
227
|
-
const normalized = label.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
228
|
-
return normalized || `viseme-${index}`;
|
|
229
|
-
}
|
|
230
|
-
function normalizeBindingWeight(weight) {
|
|
231
|
-
return Number.isFinite(weight) ? Math.max(0, weight ?? 1) : 1;
|
|
232
|
-
}
|
|
233
|
-
function getProfileVisemeSlots(profile) {
|
|
234
|
-
if (profile.visemeSlots && profile.visemeSlots.length > 0) {
|
|
235
|
-
return [...profile.visemeSlots].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
236
|
-
}
|
|
237
|
-
return (profile.visemeKeys || []).map((key, index) => {
|
|
238
|
-
const label = typeof key === "string" && key ? key : `Viseme ${index}`;
|
|
239
|
-
return {
|
|
240
|
-
id: toSlotId(label, index),
|
|
241
|
-
label,
|
|
242
|
-
order: index,
|
|
243
|
-
defaultJawAmount: profile.visemeJawAmounts?.[index]
|
|
244
|
-
};
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
function getVisemeSlotIndex(profile, slotId) {
|
|
248
|
-
return getProfileVisemeSlots(profile).findIndex((slot) => slot.id === slotId);
|
|
249
|
-
}
|
|
250
|
-
function getVisemeBindingTargets(profile, visemeIndex) {
|
|
251
|
-
const slots = getProfileVisemeSlots(profile);
|
|
252
|
-
const slot = slots[visemeIndex];
|
|
253
|
-
const binding = slot ? profile.visemeBindings?.[slot.id] : void 0;
|
|
254
|
-
const boundTargets = binding?.targets?.filter((target) => target.morph !== void 0 && target.morph !== "").map((target) => ({
|
|
255
|
-
morph: target.morph,
|
|
256
|
-
weight: normalizeBindingWeight(target.weight)
|
|
257
|
-
}));
|
|
258
|
-
if (boundTargets && boundTargets.length > 0) {
|
|
259
|
-
return boundTargets;
|
|
260
|
-
}
|
|
261
|
-
if (binding?.morph !== void 0 && binding.morph !== "") {
|
|
262
|
-
return [{ morph: binding.morph, weight: 1 }];
|
|
263
|
-
}
|
|
264
|
-
const legacyTarget = profile.visemeKeys?.[visemeIndex];
|
|
265
|
-
return legacyTarget !== void 0 && legacyTarget !== "" ? [{ morph: legacyTarget, weight: 1 }] : [];
|
|
266
|
-
}
|
|
267
|
-
function getVisemeJawAmounts(profile) {
|
|
268
|
-
const slots = getProfileVisemeSlots(profile);
|
|
269
|
-
if (slots.length === 0) return profile.visemeJawAmounts ? [...profile.visemeJawAmounts] : void 0;
|
|
270
|
-
return slots.map((slot, index) => slot.defaultJawAmount ?? profile.visemeJawAmounts?.[index] ?? 0);
|
|
271
|
-
}
|
|
272
|
-
function resolveVisemeMeshCategory(profile) {
|
|
273
|
-
const morphToMesh = profile.morphToMesh || {};
|
|
274
|
-
if (profile.visemeMeshCategory) return profile.visemeMeshCategory;
|
|
275
|
-
if (hasOwn(morphToMesh, "viseme")) return "viseme";
|
|
276
|
-
return "face";
|
|
277
|
-
}
|
|
278
|
-
function getMeshNamesForVisemeProfile(profile) {
|
|
279
|
-
const morphToMesh = profile.morphToMesh || {};
|
|
280
|
-
const category = resolveVisemeMeshCategory(profile);
|
|
281
|
-
if (hasOwn(morphToMesh, category)) {
|
|
282
|
-
return Array.isArray(morphToMesh[category]) ? [...morphToMesh[category]] : [];
|
|
283
|
-
}
|
|
284
|
-
return profile.visemeMeshCategory ? [] : [...morphToMesh.face || []];
|
|
285
|
-
}
|
|
286
|
-
function getMeshNamesForAUProfile(profile, auId) {
|
|
287
|
-
const morphToMesh = profile.morphToMesh || {};
|
|
288
|
-
const facePart = profile.auInfo?.[String(auId)]?.facePart;
|
|
289
|
-
const category = facePart ? profile.auFacePartToMeshCategory?.[facePart] : void 0;
|
|
290
|
-
if (category) return Array.isArray(morphToMesh[category]) ? [...morphToMesh[category]] : [];
|
|
291
|
-
return [...morphToMesh.face || []];
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// src/core/TsRuntimeCore.ts
|
|
295
|
-
var identityQuat = { x: 0, y: 0, z: 0, w: 1 };
|
|
296
|
-
function isRotationBoneBinding(binding) {
|
|
297
|
-
return binding?.channel === "rx" || binding?.channel === "ry" || binding?.channel === "rz";
|
|
298
|
-
}
|
|
299
|
-
var TsRuntimeCore = class {
|
|
300
|
-
constructor(options) {
|
|
301
|
-
__publicField(this, "profile");
|
|
302
|
-
__publicField(this, "model");
|
|
303
|
-
__publicField(this, "auValues", {});
|
|
304
|
-
__publicField(this, "auBalances", {});
|
|
305
|
-
__publicField(this, "visemeValues");
|
|
306
|
-
__publicField(this, "visemeJawScales");
|
|
307
|
-
__publicField(this, "mixWeights");
|
|
308
|
-
this.profile = options.profile;
|
|
309
|
-
this.model = options.model;
|
|
310
|
-
this.visemeValues = new Array(getProfileVisemeSlots(this.profile).length).fill(0);
|
|
311
|
-
this.visemeJawScales = new Array(this.visemeValues.length).fill(1);
|
|
312
|
-
this.mixWeights = { ...this.profile.auMixDefaults };
|
|
313
|
-
}
|
|
314
|
-
setProfile(profile) {
|
|
315
|
-
this.profile = profile;
|
|
316
|
-
this.visemeValues = new Array(getProfileVisemeSlots(this.profile).length).fill(0);
|
|
317
|
-
this.visemeJawScales = new Array(this.visemeValues.length).fill(1);
|
|
318
|
-
this.mixWeights = { ...this.profile.auMixDefaults };
|
|
319
|
-
}
|
|
320
|
-
setModelDescriptor(model) {
|
|
321
|
-
this.model = model;
|
|
322
|
-
}
|
|
323
|
-
setAU(id, value, balance) {
|
|
324
|
-
if (value < 0 && this.profile.continuumPairs) {
|
|
325
|
-
const pairInfo = this.profile.continuumPairs[id];
|
|
326
|
-
if (pairInfo) {
|
|
327
|
-
const negAU = pairInfo.isNegative ? id : pairInfo.pairId;
|
|
328
|
-
const posAU = pairInfo.isNegative ? pairInfo.pairId : id;
|
|
329
|
-
this.setContinuum(negAU, posAU, pairInfo.isNegative ? -value : value, balance);
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
this.auValues[id] = clamp01(value);
|
|
334
|
-
if (balance !== void 0) {
|
|
335
|
-
this.auBalances[id] = clampBalance(balance);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
getAU(id) {
|
|
339
|
-
return this.auValues[id] ?? 0;
|
|
340
|
-
}
|
|
341
|
-
setContinuum(negAU, posAU, continuumValue, balance) {
|
|
342
|
-
const value = Math.max(-1, Math.min(1, continuumValue));
|
|
343
|
-
if (value < 0) {
|
|
344
|
-
this.setAU(posAU, 0, balance);
|
|
345
|
-
this.setAU(negAU, Math.abs(value), balance);
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
if (value > 0) {
|
|
349
|
-
this.setAU(negAU, 0, balance);
|
|
350
|
-
this.setAU(posAU, value, balance);
|
|
351
|
-
return;
|
|
352
|
-
}
|
|
353
|
-
this.setAU(negAU, 0, balance);
|
|
354
|
-
this.setAU(posAU, 0, balance);
|
|
355
|
-
}
|
|
356
|
-
setViseme(visemeIndex, value, jawScale = 1) {
|
|
357
|
-
if (visemeIndex < 0 || visemeIndex >= this.visemeValues.length) return;
|
|
358
|
-
this.visemeValues[visemeIndex] = clamp01(value);
|
|
359
|
-
this.visemeJawScales[visemeIndex] = jawScale;
|
|
360
|
-
}
|
|
361
|
-
setVisemeById(slotId, value, jawScale = 1) {
|
|
362
|
-
const index = getVisemeSlotIndex(this.profile, slotId);
|
|
363
|
-
if (index < 0) return;
|
|
364
|
-
this.setViseme(index, value, jawScale);
|
|
365
|
-
}
|
|
366
|
-
setAUMixWeight(id, weight) {
|
|
367
|
-
this.mixWeights[id] = clamp01(weight);
|
|
368
|
-
}
|
|
369
|
-
evaluateFrameDelta(deltaSeconds) {
|
|
370
|
-
const morphWrites = /* @__PURE__ */ new Map();
|
|
371
|
-
this.collectAUWrites(morphWrites);
|
|
372
|
-
this.collectVisemeWrites(morphWrites);
|
|
373
|
-
return {
|
|
374
|
-
deltaSeconds,
|
|
375
|
-
morphTargets: Array.from(morphWrites.values()),
|
|
376
|
-
bones: this.collectBoneWrites()
|
|
377
|
-
};
|
|
378
|
-
}
|
|
379
|
-
collectAUWrites(morphWrites) {
|
|
380
|
-
for (const [auIdText, entry] of Object.entries(this.profile.auToMorphs || {})) {
|
|
381
|
-
const auId = Number(auIdText);
|
|
382
|
-
if (Number.isNaN(auId) || !entry) continue;
|
|
383
|
-
const mixWeight = this.isMixedAU(auId) ? this.getAUMixWeight(auId) : 1;
|
|
384
|
-
const base = clamp01(this.auValues[auId] ?? 0) * mixWeight;
|
|
385
|
-
const { left, right } = computeSideValues(base, this.auBalances[auId]);
|
|
386
|
-
const meshNames = getMeshNamesForAUProfile(this.profile, auId);
|
|
387
|
-
for (const morph of entry.left || []) {
|
|
388
|
-
this.addMorphWrites(morphWrites, morph, meshNames, left);
|
|
389
|
-
}
|
|
390
|
-
for (const morph of entry.right || []) {
|
|
391
|
-
this.addMorphWrites(morphWrites, morph, meshNames, right);
|
|
392
|
-
}
|
|
393
|
-
for (const morph of entry.center || []) {
|
|
394
|
-
this.addMorphWrites(morphWrites, morph, meshNames, base);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
collectVisemeWrites(morphWrites) {
|
|
399
|
-
const meshNames = getMeshNamesForVisemeProfile(this.profile);
|
|
400
|
-
const localMax = /* @__PURE__ */ new Map();
|
|
401
|
-
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
402
|
-
const value = clamp01(this.visemeValues[index] ?? 0);
|
|
403
|
-
for (const bindingTarget of getVisemeBindingTargets(this.profile, index)) {
|
|
404
|
-
const weighted = clamp01(value * bindingTarget.weight);
|
|
405
|
-
for (const resolved of this.resolveMorphTargets(bindingTarget.morph, meshNames)) {
|
|
406
|
-
const key = morphWriteKey(resolved.mesh.id, resolved.morphTarget.id);
|
|
407
|
-
const existing = localMax.get(key);
|
|
408
|
-
if (!existing || weighted > existing.value) {
|
|
409
|
-
localMax.set(key, {
|
|
410
|
-
meshId: resolved.mesh.id,
|
|
411
|
-
morphTargetId: resolved.morphTarget.id,
|
|
412
|
-
value: weighted,
|
|
413
|
-
mode: "absolute"
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
}
|
|
419
|
-
for (const [key, write] of localMax) {
|
|
420
|
-
const existing = morphWrites.get(key);
|
|
421
|
-
if (!existing || write.value > existing.value) {
|
|
422
|
-
morphWrites.set(key, write);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
}
|
|
426
|
-
collectBoneWrites() {
|
|
427
|
-
const writes = [];
|
|
428
|
-
const rotations = this.computeRotationState();
|
|
429
|
-
for (const composite of this.profile.compositeRotations || []) {
|
|
430
|
-
const bone = this.findBoneDescriptor(composite.node);
|
|
431
|
-
if (!bone) continue;
|
|
432
|
-
const transform = this.evaluateCompositeTransform(bone, composite, rotations[composite.node]);
|
|
433
|
-
if (transform) {
|
|
434
|
-
writes.push({
|
|
435
|
-
boneId: bone.id,
|
|
436
|
-
transform,
|
|
437
|
-
mode: "absolute",
|
|
438
|
-
space: "local"
|
|
439
|
-
});
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
for (const [nodeKey, translation] of Object.entries(this.computeTranslations())) {
|
|
443
|
-
const bone = this.findBoneDescriptor(nodeKey);
|
|
444
|
-
if (!bone) continue;
|
|
445
|
-
const position = addVec3(bone.restTransform?.position, translation);
|
|
446
|
-
upsertBoneWrite(writes, bone.id, { position });
|
|
447
|
-
}
|
|
448
|
-
const jawAmount = this.getActiveVisemeJawAmount();
|
|
449
|
-
const jawBinding = this.findAutoVisemeJawBinding();
|
|
450
|
-
const jawBone = jawBinding ? this.findBoneDescriptor(jawBinding.node) : void 0;
|
|
451
|
-
if (jawBone && jawBinding?.maxDegrees && jawBinding.channel) {
|
|
452
|
-
const current = writes.find((write) => write.boneId === jawBone.id);
|
|
453
|
-
if (jawAmount > 1e-6 || !current?.transform.rotation) {
|
|
454
|
-
const rest = jawBone.restTransform?.rotation ?? identityQuat;
|
|
455
|
-
const rotation = jawAmount > 1e-6 ? multiplyQuat(
|
|
456
|
-
rest,
|
|
457
|
-
quatFromChannel(jawBinding.channel, deg2rad(jawBinding.maxDegrees) * jawAmount * jawBinding.scale)
|
|
458
|
-
) : rest;
|
|
459
|
-
upsertBoneWrite(writes, jawBone.id, { rotation });
|
|
460
|
-
}
|
|
461
|
-
}
|
|
462
|
-
return writes;
|
|
463
|
-
}
|
|
464
|
-
computeRotationState() {
|
|
465
|
-
const rotations = {};
|
|
466
|
-
for (const composite of this.profile.compositeRotations || []) {
|
|
467
|
-
rotations[composite.node] = {
|
|
468
|
-
pitch: this.getCompositeAxisValueForNode(composite.node, composite.pitch),
|
|
469
|
-
yaw: this.getCompositeAxisValueForNode(composite.node, composite.yaw),
|
|
470
|
-
roll: this.getCompositeAxisValueForNode(composite.node, composite.roll)
|
|
471
|
-
};
|
|
472
|
-
}
|
|
473
|
-
return rotations;
|
|
474
|
-
}
|
|
475
|
-
evaluateCompositeTransform(bone, composite, state) {
|
|
476
|
-
if (!state) return null;
|
|
477
|
-
let rotation = bone.restTransform?.rotation ?? identityQuat;
|
|
478
|
-
const applyAxis = (axisConfig, direction) => {
|
|
479
|
-
if (!axisConfig || Math.abs(direction) <= 1e-6) return;
|
|
480
|
-
const binding = getCompositeAxisBinding(
|
|
481
|
-
composite.node,
|
|
482
|
-
axisConfig,
|
|
483
|
-
direction,
|
|
484
|
-
(auId) => this.getEffectiveBoneAUValue(auId, composite.node),
|
|
485
|
-
this.profile.auToBones
|
|
486
|
-
);
|
|
487
|
-
if (!binding?.maxDegrees || !binding.channel) return;
|
|
488
|
-
rotation = multiplyQuat(
|
|
489
|
-
rotation,
|
|
490
|
-
quatFromChannel(binding.channel, deg2rad(binding.maxDegrees) * Math.abs(direction) * binding.scale)
|
|
491
|
-
);
|
|
492
|
-
};
|
|
493
|
-
applyAxis(composite.yaw, state.yaw);
|
|
494
|
-
applyAxis(composite.pitch, state.pitch);
|
|
495
|
-
applyAxis(composite.roll, state.roll);
|
|
496
|
-
return { rotation };
|
|
497
|
-
}
|
|
498
|
-
computeTranslations() {
|
|
499
|
-
const translations = {};
|
|
500
|
-
for (const [auIdText, bindings] of Object.entries(this.profile.auToBones || {})) {
|
|
501
|
-
const auId = Number(auIdText);
|
|
502
|
-
if (Number.isNaN(auId)) continue;
|
|
503
|
-
const value = clamp01(this.auValues[auId] ?? 0);
|
|
504
|
-
for (const binding of bindings || []) {
|
|
505
|
-
if (binding.channel !== "tx" && binding.channel !== "ty" && binding.channel !== "tz") continue;
|
|
506
|
-
if (binding.maxUnits === void 0) continue;
|
|
507
|
-
const current = translations[binding.node] ?? { x: 0, y: 0, z: 0 };
|
|
508
|
-
translations[binding.node] = current;
|
|
509
|
-
if (value <= 1e-6) continue;
|
|
510
|
-
const offset = clampSigned(value * binding.scale) * binding.maxUnits;
|
|
511
|
-
translations[binding.node] = {
|
|
512
|
-
x: binding.channel === "tx" ? offset : current.x,
|
|
513
|
-
y: binding.channel === "ty" ? offset : current.y,
|
|
514
|
-
z: binding.channel === "tz" ? offset : current.z
|
|
515
|
-
};
|
|
516
|
-
}
|
|
517
|
-
}
|
|
518
|
-
return translations;
|
|
519
|
-
}
|
|
520
|
-
addMorphWrites(morphWrites, morph, meshNames, value) {
|
|
521
|
-
for (const resolved of this.resolveMorphTargets(morph, meshNames)) {
|
|
522
|
-
const key = morphWriteKey(resolved.mesh.id, resolved.morphTarget.id);
|
|
523
|
-
const existing = morphWrites.get(key);
|
|
524
|
-
if (existing && existing.value >= value) continue;
|
|
525
|
-
morphWrites.set(key, {
|
|
526
|
-
meshId: resolved.mesh.id,
|
|
527
|
-
morphTargetId: resolved.morphTarget.id,
|
|
528
|
-
value,
|
|
529
|
-
mode: "absolute"
|
|
530
|
-
});
|
|
531
|
-
}
|
|
532
|
-
}
|
|
533
|
-
resolveMorphTargets(morph, meshNames) {
|
|
534
|
-
const result = [];
|
|
535
|
-
for (const meshName of meshNames) {
|
|
536
|
-
const mesh = this.model.meshes.find((candidate) => candidate.name === meshName);
|
|
537
|
-
if (!mesh) continue;
|
|
538
|
-
const morphTargets = mesh.morphTargetIds.map((id) => this.model.morphTargets.find((candidate) => candidate.id === id)).filter((target2) => !!target2);
|
|
539
|
-
const target = typeof morph === "number" ? morphTargets.find((candidate) => candidate.hostIndex === morph) : resolveMorphTargetByName(morphTargets, morph, this.profile);
|
|
540
|
-
if (target) {
|
|
541
|
-
result.push({ mesh, morphTarget: target });
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
return result;
|
|
545
|
-
}
|
|
546
|
-
findBoneDescriptor(nodeKey) {
|
|
547
|
-
const configuredName = this.profile.boneNodes[nodeKey] || nodeKey;
|
|
548
|
-
const prefix = this.profile.bonePrefix ?? "";
|
|
549
|
-
const suffix = this.profile.boneSuffix ?? "";
|
|
550
|
-
const prefixedName = prefix && !configuredName.startsWith(prefix) ? `${prefix}${configuredName}` : configuredName;
|
|
551
|
-
const fullName = suffix && !prefixedName.endsWith(suffix) ? `${prefixedName}${suffix}` : prefixedName;
|
|
552
|
-
const candidates = /* @__PURE__ */ new Set([nodeKey, configuredName, fullName]);
|
|
553
|
-
return this.model.bones.find((bone) => candidates.has(bone.name));
|
|
554
|
-
}
|
|
555
|
-
findAutoVisemeJawBinding() {
|
|
556
|
-
const candidates = [
|
|
557
|
-
this.profile.auToBones[103]?.find(isRotationBoneBinding),
|
|
558
|
-
this.profile.auToBones[26]?.find(isRotationBoneBinding)
|
|
559
|
-
].filter((binding) => !!binding);
|
|
560
|
-
return candidates.find((binding) => !!this.findBoneDescriptor(binding.node)) ?? candidates[0] ?? null;
|
|
561
|
-
}
|
|
562
|
-
getActiveVisemeJawAmount() {
|
|
563
|
-
const jawAmounts = getVisemeJawAmounts(this.profile) ?? [];
|
|
564
|
-
let jawAmount = 0;
|
|
565
|
-
for (let index = 0; index < this.visemeValues.length; index += 1) {
|
|
566
|
-
const value = clamp01(this.visemeValues[index] ?? 0);
|
|
567
|
-
if (value <= 1e-6) continue;
|
|
568
|
-
const jawScale = this.visemeJawScales[index] ?? 1;
|
|
569
|
-
if (Math.abs(jawScale) <= 1e-6) continue;
|
|
570
|
-
jawAmount = Math.max(jawAmount, (jawAmounts[index] ?? 0) * value * jawScale);
|
|
571
|
-
}
|
|
572
|
-
return jawAmount;
|
|
573
|
-
}
|
|
574
|
-
getCompositeAxisValueForNode(nodeKey, axisConfig) {
|
|
575
|
-
return getCompositeAxisValue(axisConfig, (auId) => this.getEffectiveBoneAUValue(auId, nodeKey));
|
|
576
|
-
}
|
|
577
|
-
getEffectiveBoneAUValue(auId, nodeKey) {
|
|
578
|
-
const rawValue = clamp01(this.auValues[auId] ?? 0);
|
|
579
|
-
if (rawValue <= 1e-6) return 0;
|
|
580
|
-
const binding = this.profile.auToBones[auId]?.find((candidate) => candidate.node === nodeKey) ?? null;
|
|
581
|
-
if (!binding?.side) return rawValue;
|
|
582
|
-
return rawValue * getSideScale(this.auBalances[auId] ?? 0, binding.side);
|
|
583
|
-
}
|
|
584
|
-
getAUMixWeight(auId) {
|
|
585
|
-
return this.mixWeights[auId] ?? this.profile.auMixDefaults?.[auId] ?? 1;
|
|
586
|
-
}
|
|
587
|
-
isMixedAU(auId) {
|
|
588
|
-
const morphs = this.profile.auToMorphs[auId];
|
|
589
|
-
const hasMorphs = !!(morphs?.left?.length || morphs?.right?.length || morphs?.center?.length);
|
|
590
|
-
return !!(hasMorphs && this.profile.auToBones[auId]?.length);
|
|
591
|
-
}
|
|
592
|
-
};
|
|
593
|
-
function resolveMorphTargetByName(targets, key, profile) {
|
|
594
|
-
const prefix = profile.morphPrefix || "";
|
|
595
|
-
const suffix = profile.morphSuffix || "";
|
|
596
|
-
const fullName = prefix + key + suffix;
|
|
597
|
-
const exact = targets.find((target) => target.name === fullName);
|
|
598
|
-
if (exact) return exact;
|
|
599
|
-
const suffixRegex = profile.suffixPattern ? new RegExp(profile.suffixPattern) : null;
|
|
600
|
-
if (!suffixRegex) return void 0;
|
|
601
|
-
return targets.find((target) => {
|
|
602
|
-
if (!target.name.startsWith(fullName)) return false;
|
|
603
|
-
const candidateSuffix = target.name.slice(fullName.length);
|
|
604
|
-
return candidateSuffix === "" || suffixRegex.test(candidateSuffix);
|
|
605
|
-
});
|
|
606
|
-
}
|
|
607
|
-
function computeSideValues(base, balance) {
|
|
608
|
-
const b = clampBalance(balance ?? 0);
|
|
609
|
-
if (b === 0) return { left: base, right: base };
|
|
610
|
-
if (b < 0) return { left: base, right: base * (1 + b) };
|
|
611
|
-
return { left: base * (1 - b), right: base };
|
|
612
|
-
}
|
|
613
|
-
function getSideScale(balance, side) {
|
|
614
|
-
if (side !== "left" && side !== "right") return 1;
|
|
615
|
-
const b = clampBalance(balance);
|
|
616
|
-
if (side === "left") return b > 0 ? 1 - b : 1;
|
|
617
|
-
return b < 0 ? 1 + b : 1;
|
|
618
|
-
}
|
|
619
|
-
function addVec3(base, offset) {
|
|
620
|
-
return {
|
|
621
|
-
x: (base?.x ?? 0) + offset.x,
|
|
622
|
-
y: (base?.y ?? 0) + offset.y,
|
|
623
|
-
z: (base?.z ?? 0) + offset.z
|
|
624
|
-
};
|
|
625
|
-
}
|
|
626
|
-
function upsertBoneWrite(writes, boneId, transform) {
|
|
627
|
-
const existingIndex = writes.findIndex((write) => write.boneId === boneId);
|
|
628
|
-
if (existingIndex >= 0) {
|
|
629
|
-
const existing = writes[existingIndex];
|
|
630
|
-
writes[existingIndex] = {
|
|
631
|
-
...existing,
|
|
632
|
-
transform: {
|
|
633
|
-
...existing.transform,
|
|
634
|
-
...transform
|
|
635
|
-
}
|
|
636
|
-
};
|
|
637
|
-
return;
|
|
638
|
-
}
|
|
639
|
-
writes.push({
|
|
640
|
-
boneId,
|
|
641
|
-
transform,
|
|
642
|
-
mode: "absolute",
|
|
643
|
-
space: "local"
|
|
644
|
-
});
|
|
645
|
-
}
|
|
646
|
-
function quatFromChannel(channel, radians) {
|
|
647
|
-
if (channel === "rx") return quatFromAxis({ x: 1, y: 0, z: 0 }, radians);
|
|
648
|
-
if (channel === "ry") return quatFromAxis({ x: 0, y: 1, z: 0 }, radians);
|
|
649
|
-
return quatFromAxis({ x: 0, y: 0, z: 1 }, radians);
|
|
650
|
-
}
|
|
651
|
-
function quatFromAxis(axis, radians) {
|
|
652
|
-
const half = radians / 2;
|
|
653
|
-
const s = Math.sin(half);
|
|
654
|
-
return normalizeQuat({
|
|
655
|
-
x: axis.x * s,
|
|
656
|
-
y: axis.y * s,
|
|
657
|
-
z: axis.z * s,
|
|
658
|
-
w: Math.cos(half)
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
function multiplyQuat(a, b) {
|
|
662
|
-
return normalizeQuat({
|
|
663
|
-
x: a.w * b.x + a.x * b.w + a.y * b.z - a.z * b.y,
|
|
664
|
-
y: a.w * b.y - a.x * b.z + a.y * b.w + a.z * b.x,
|
|
665
|
-
z: a.w * b.z + a.x * b.y - a.y * b.x + a.z * b.w,
|
|
666
|
-
w: a.w * b.w - a.x * b.x - a.y * b.y - a.z * b.z
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
|
-
function normalizeQuat(q) {
|
|
670
|
-
const len = Math.hypot(q.x, q.y, q.z, q.w);
|
|
671
|
-
if (len <= 1e-12) return identityQuat;
|
|
672
|
-
return {
|
|
673
|
-
x: q.x / len,
|
|
674
|
-
y: q.y / len,
|
|
675
|
-
z: q.z / len,
|
|
676
|
-
w: q.w / len
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
function deg2rad(degrees) {
|
|
680
|
-
return degrees * Math.PI / 180;
|
|
681
|
-
}
|
|
682
|
-
function clamp01(value) {
|
|
683
|
-
if (!Number.isFinite(value)) return 0;
|
|
684
|
-
return value < 0 ? 0 : value > 1 ? 1 : value;
|
|
685
|
-
}
|
|
686
|
-
function clampSigned(value) {
|
|
687
|
-
if (!Number.isFinite(value)) return 0;
|
|
688
|
-
return value < -1 ? -1 : value > 1 ? 1 : value;
|
|
689
|
-
}
|
|
690
|
-
function clampBalance(value) {
|
|
691
|
-
if (!Number.isFinite(value)) return 0;
|
|
692
|
-
return Math.max(-1, Math.min(1, value));
|
|
693
|
-
}
|
|
694
|
-
function morphWriteKey(meshId, morphTargetId) {
|
|
695
|
-
return `${meshId}:${morphTargetId}`;
|
|
696
|
-
}
|
|
697
|
-
|
|
698
188
|
// src/core/WasmRuntimeCore.ts
|
|
699
189
|
var WasmRuntimeCore = class {
|
|
700
190
|
constructor(options) {
|
|
701
191
|
__publicField(this, "wasm");
|
|
702
192
|
__publicField(this, "core");
|
|
193
|
+
__publicField(this, "allowEmptyBindings");
|
|
703
194
|
__publicField(this, "profile");
|
|
704
195
|
__publicField(this, "model");
|
|
705
196
|
this.wasm = options.wasm;
|
|
706
197
|
this.profile = options.profile;
|
|
707
198
|
this.model = options.model;
|
|
199
|
+
this.allowEmptyBindings = options.allowEmptyBindings ?? false;
|
|
708
200
|
const RuntimeCtor = options.wasm.RuntimeCore;
|
|
709
201
|
if (!RuntimeCtor) {
|
|
710
202
|
throw new Error("Embody Wasm module does not export RuntimeCore");
|
|
@@ -723,6 +215,24 @@ var WasmRuntimeCore = class {
|
|
|
723
215
|
setAU(id, value, balance = 0) {
|
|
724
216
|
this.core.set_au(id >>> 0, value, balance);
|
|
725
217
|
}
|
|
218
|
+
setContinuum(negativeAU, positiveAU, value, balance = 0) {
|
|
219
|
+
this.core.set_continuum(negativeAU >>> 0, positiveAU >>> 0, value, balance);
|
|
220
|
+
}
|
|
221
|
+
transitionAU(id, to, durationMs, balance = Number.NaN) {
|
|
222
|
+
this.core.transition_au(id >>> 0, to, durationMs, balance);
|
|
223
|
+
}
|
|
224
|
+
transitionViseme(visemeIndex, to, durationMs, jawScale = 1) {
|
|
225
|
+
this.core.transition_viseme(visemeIndex >>> 0, to, durationMs, jawScale);
|
|
226
|
+
}
|
|
227
|
+
update(deltaSeconds) {
|
|
228
|
+
return this.core.update(deltaSeconds);
|
|
229
|
+
}
|
|
230
|
+
activeTransitionCount() {
|
|
231
|
+
return this.core.active_transition_count();
|
|
232
|
+
}
|
|
233
|
+
clearTransitions() {
|
|
234
|
+
this.core.clear_transitions();
|
|
235
|
+
}
|
|
726
236
|
getAU(id) {
|
|
727
237
|
return this.core.get_au(id >>> 0);
|
|
728
238
|
}
|
|
@@ -758,7 +268,13 @@ var WasmRuntimeCore = class {
|
|
|
758
268
|
};
|
|
759
269
|
}
|
|
760
270
|
reloadBindings() {
|
|
761
|
-
|
|
271
|
+
const profileJson = JSON.stringify(this.profile);
|
|
272
|
+
const modelJson = JSON.stringify(this.model);
|
|
273
|
+
if (this.allowEmptyBindings) {
|
|
274
|
+
this.core.configure(profileJson, modelJson);
|
|
275
|
+
} else {
|
|
276
|
+
this.core.configure_with_profile(profileJson, modelJson);
|
|
277
|
+
}
|
|
762
278
|
}
|
|
763
279
|
};
|
|
764
280
|
function unpackBoneFrameDelta(values) {
|
|
@@ -802,6 +318,6 @@ function unpackMorphFrameDelta(values) {
|
|
|
802
318
|
return writes;
|
|
803
319
|
}
|
|
804
320
|
|
|
805
|
-
export { MESH_PROPORTIONS_FIELDS, MESH_PROPORTIONS_STRIDE, TEMPLATE_SKELETON_FIT_METADATA_KIND, TEMPLATE_SKELETON_FIT_METADATA_VERSION, TEMPLATE_SKELETON_FIT_SOLUTION_FIELDS, TEMPLATE_SKELETON_FIT_SOLUTION_STRIDE, TEMPLATE_SKELETON_FIT_STATUSES, TEMPLATE_SKELETON_FIT_TRANSFORM_FIELDS, TEMPLATE_SKELETON_FIT_TRANSFORM_STRIDE, TEMPLATE_SKELETON_FIT_VERTICAL_ANCHORS, TEMPLATE_SKELETON_FIT_VERTICAL_AXES, TsClipCompiler,
|
|
321
|
+
export { MESH_PROPORTIONS_FIELDS, MESH_PROPORTIONS_STRIDE, TEMPLATE_SKELETON_FIT_METADATA_KIND, TEMPLATE_SKELETON_FIT_METADATA_VERSION, TEMPLATE_SKELETON_FIT_SOLUTION_FIELDS, TEMPLATE_SKELETON_FIT_SOLUTION_STRIDE, TEMPLATE_SKELETON_FIT_STATUSES, TEMPLATE_SKELETON_FIT_TRANSFORM_FIELDS, TEMPLATE_SKELETON_FIT_TRANSFORM_STRIDE, TEMPLATE_SKELETON_FIT_VERTICAL_ANCHORS, TEMPLATE_SKELETON_FIT_VERTICAL_AXES, TsClipCompiler, WasmRuntimeCore, composeTemplateSkeletonFitTransform, isTemplateSkeletonFitStatus, numericArrayToNumbers, unpackBoneFrameDelta, unpackMorphFrameDelta, validateTemplateSkeletonFitMetadata };
|
|
806
322
|
//# sourceMappingURL=core.js.map
|
|
807
323
|
//# sourceMappingURL=core.js.map
|