@lovelace_lol/embody 0.0.1

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.
Files changed (98) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +2411 -0
  3. package/dist/authoring/boneAuthoring.d.ts +132 -0
  4. package/dist/camera/annotationCameraAngles.d.ts +28 -0
  5. package/dist/camera/cameraRelativeGaze.d.ts +15 -0
  6. package/dist/camera/rustAnnotationCameraCore.d.ts +136 -0
  7. package/dist/cljs.cjs +362 -0
  8. package/dist/cljs.cjs.map +1 -0
  9. package/dist/cljs.d.ts +5 -0
  10. package/dist/cljs.js +359 -0
  11. package/dist/cljs.js.map +1 -0
  12. package/dist/core/TsClipCompiler.d.ts +51 -0
  13. package/dist/core/TsRuntimeCore.d.ts +40 -0
  14. package/dist/core/WasmRuntimeCore.d.ts +55 -0
  15. package/dist/core/compositeAxis.d.ts +4 -0
  16. package/dist/core/contracts/abi.d.ts +70 -0
  17. package/dist/core/contracts/clip.d.ts +44 -0
  18. package/dist/core/contracts/frame.d.ts +27 -0
  19. package/dist/core/contracts/host.d.ts +28 -0
  20. package/dist/core/contracts/ids.d.ts +15 -0
  21. package/dist/core/contracts/index.d.ts +10 -0
  22. package/dist/core/contracts/model.d.ts +31 -0
  23. package/dist/core/contracts/primitives.d.ts +17 -0
  24. package/dist/core/contracts/skeletonFit.d.ts +41 -0
  25. package/dist/core/index.d.ts +8 -0
  26. package/dist/core/types.d.ts +472 -0
  27. package/dist/core.cjs +1065 -0
  28. package/dist/core.cjs.map +1 -0
  29. package/dist/core.js +1040 -0
  30. package/dist/core.js.map +1 -0
  31. package/dist/engines/three/Embody.d.ts +373 -0
  32. package/dist/engines/three/ThreeAnimationRuntime.d.ts +203 -0
  33. package/dist/engines/three/ThreeClipAdapter.d.ts +24 -0
  34. package/dist/engines/three/ThreeFrameApplier.d.ts +68 -0
  35. package/dist/engines/three/ThreeModelInspector.d.ts +54 -0
  36. package/dist/engines/three/bakedClipPartitioning.d.ts +16 -0
  37. package/dist/engines/three/balanceUtils.d.ts +3 -0
  38. package/dist/engines/three/characterHost.d.ts +80 -0
  39. package/dist/engines/three/characterScene.d.ts +98 -0
  40. package/dist/engines/three/hair/HairPhysicsController.d.ts +124 -0
  41. package/dist/engines/three/index.d.ts +20 -0
  42. package/dist/engines/three/modelLoader.d.ts +61 -0
  43. package/dist/engines/three/profileTestScene.d.ts +52 -0
  44. package/dist/engines/three/types.d.ts +20 -0
  45. package/dist/hair/appearance.d.ts +29 -0
  46. package/dist/index.cjs +15431 -0
  47. package/dist/index.cjs.map +1 -0
  48. package/dist/index.d.ts +100 -0
  49. package/dist/index.js +15141 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/interfaces/Animation.d.ts +315 -0
  52. package/dist/interfaces/EmbodyRuntime.d.ts +90 -0
  53. package/dist/interfaces/Hair.d.ts +181 -0
  54. package/dist/interfaces/HairPhysics.d.ts +90 -0
  55. package/dist/interfaces/index.d.ts +11 -0
  56. package/dist/mappings/extendPresetWithProfile.d.ts +17 -0
  57. package/dist/mappings/types.d.ts +309 -0
  58. package/dist/mappings/visemeSystem.d.ts +26 -0
  59. package/dist/physics/HairPhysics.d.ts +86 -0
  60. package/dist/physics/RustHairPhysics.d.ts +18 -0
  61. package/dist/physics/index.d.ts +6 -0
  62. package/dist/preset.cjs +2000 -0
  63. package/dist/preset.cjs.map +1 -0
  64. package/dist/preset.js +1997 -0
  65. package/dist/preset.js.map +1 -0
  66. package/dist/presets/bettaFish.d.ts +328 -0
  67. package/dist/presets/cc4.d.ts +145 -0
  68. package/dist/presets/index.d.ts +27 -0
  69. package/dist/profiles/resolveProfileConfig.d.ts +36 -0
  70. package/dist/profiles/types.d.ts +118 -0
  71. package/dist/regions/normalizeRegionTree.d.ts +6 -0
  72. package/dist/regions/regionMapping.d.ts +17 -0
  73. package/dist/regions/types.d.ts +122 -0
  74. package/dist/skeletonTemplates/humanoidSkeletonTemplates.d.ts +32 -0
  75. package/dist/test/setupWasm.d.ts +1 -0
  76. package/dist/three.cjs +10115 -0
  77. package/dist/three.cjs.map +1 -0
  78. package/dist/three.js +10086 -0
  79. package/dist/three.js.map +1 -0
  80. package/dist/validation/analyzeModel.d.ts +68 -0
  81. package/dist/validation/extractModelData.d.ts +90 -0
  82. package/dist/validation/generateMappingCorrections.d.ts +32 -0
  83. package/dist/validation/geometryHelpers.d.ts +66 -0
  84. package/dist/validation/types.d.ts +10 -0
  85. package/dist/validation/validateMappings.d.ts +77 -0
  86. package/dist/wasm/LICENSE +21 -0
  87. package/dist/wasm/README.md +72 -0
  88. package/dist/wasm/embody_wasm.d.ts +531 -0
  89. package/dist/wasm/embody_wasm.js +1635 -0
  90. package/dist/wasm/embody_wasm_bg.wasm +0 -0
  91. package/dist/wasm/embody_wasm_bg.wasm.d.ts +107 -0
  92. package/dist/wasm.cjs +302 -0
  93. package/dist/wasm.cjs.map +1 -0
  94. package/dist/wasm.d.ts +43 -0
  95. package/dist/wasm.js +268 -0
  96. package/dist/wasm.js.map +1 -0
  97. package/dist/wasmTypes.d.ts +121 -0
  98. package/package.json +99 -0
package/dist/core.js ADDED
@@ -0,0 +1,1040 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+
5
+ // src/core/contracts/abi.ts
6
+ var PACKED_MORPH_FRAME_DELTA_STRIDE = 4;
7
+ var PACKED_BONE_FRAME_DELTA_STRIDE = 9;
8
+ var PACKED_BONE_FLAG_HAS_POSITION = 1;
9
+ var PACKED_BONE_FLAG_HAS_ROTATION = 2;
10
+ var TEMPLATE_SKELETON_FIT_TRANSFORM_STRIDE = 4;
11
+ var MESH_PROPORTIONS_STRIDE = 16;
12
+ var TEMPLATE_SKELETON_FIT_SOLUTION_STRIDE = 10;
13
+ var TEMPLATE_SKELETON_FIT_TRANSFORM_FIELDS = [
14
+ "scale",
15
+ "translationX",
16
+ "translationY",
17
+ "translationZ"
18
+ ];
19
+ var MESH_PROPORTIONS_FIELDS = [
20
+ "minX",
21
+ "minY",
22
+ "minZ",
23
+ "maxX",
24
+ "maxY",
25
+ "maxZ",
26
+ "centerX",
27
+ "centerY",
28
+ "centerZ",
29
+ "spanX",
30
+ "spanY",
31
+ "spanZ",
32
+ "verticalHeight",
33
+ "crossAxisWideSpan",
34
+ "crossAxisNarrowSpan",
35
+ "humanoidLikelihood"
36
+ ];
37
+ var TEMPLATE_SKELETON_FIT_SOLUTION_FIELDS = [
38
+ "scale",
39
+ "translationX",
40
+ "translationY",
41
+ "translationZ",
42
+ "confidence",
43
+ "meshHeight",
44
+ "templateHeight",
45
+ "meshCrossAxisWideSpan",
46
+ "templateCrossAxisWideSpan",
47
+ "status"
48
+ ];
49
+
50
+ // src/core/contracts/skeletonFit.ts
51
+ var TEMPLATE_SKELETON_FIT_METADATA_KIND = "template-skeleton-fit";
52
+ var TEMPLATE_SKELETON_FIT_METADATA_VERSION = 1;
53
+ var TEMPLATE_SKELETON_FIT_STATUSES = [
54
+ "unfitted",
55
+ "estimated",
56
+ "manual-adjusted",
57
+ "invalid"
58
+ ];
59
+ var TEMPLATE_SKELETON_FIT_VERTICAL_AXES = ["x", "y", "z"];
60
+ var TEMPLATE_SKELETON_FIT_VERTICAL_ANCHORS = ["min", "center", "max"];
61
+ function isTemplateSkeletonFitStatus(value) {
62
+ return typeof value === "string" && TEMPLATE_SKELETON_FIT_STATUSES.includes(value);
63
+ }
64
+ function composeTemplateSkeletonFitTransform(fit, manualAdjustment = {}) {
65
+ return {
66
+ scale: positiveFiniteOr(fit.scale, 1) * positiveFiniteOr(manualAdjustment.scale ?? 1, 1),
67
+ translation: {
68
+ x: finiteOr(fit.translation.x, 0) + finiteOr(manualAdjustment.translation?.x ?? 0, 0),
69
+ y: finiteOr(fit.translation.y, 0) + finiteOr(manualAdjustment.translation?.y ?? 0, 0),
70
+ z: finiteOr(fit.translation.z, 0) + finiteOr(manualAdjustment.translation?.z ?? 0, 0)
71
+ }
72
+ };
73
+ }
74
+ function validateTemplateSkeletonFitMetadata(metadata) {
75
+ const errors = [];
76
+ if (!isRecord(metadata)) {
77
+ return { valid: false, errors: ["metadata must be an object"] };
78
+ }
79
+ if (metadata.kind !== TEMPLATE_SKELETON_FIT_METADATA_KIND) {
80
+ errors.push(`kind must be "${TEMPLATE_SKELETON_FIT_METADATA_KIND}"`);
81
+ }
82
+ if (metadata.version !== TEMPLATE_SKELETON_FIT_METADATA_VERSION) {
83
+ errors.push(`version must be ${TEMPLATE_SKELETON_FIT_METADATA_VERSION}`);
84
+ }
85
+ if (!isNonEmptyString(metadata.templateId)) {
86
+ errors.push("templateId must be a non-empty string");
87
+ }
88
+ if (!isNonEmptyString(metadata.sourceCharacterId)) {
89
+ errors.push("sourceCharacterId must be a non-empty string");
90
+ }
91
+ if (!TEMPLATE_SKELETON_FIT_VERTICAL_AXES.includes(metadata.verticalAxis)) {
92
+ errors.push(`verticalAxis must be one of: ${TEMPLATE_SKELETON_FIT_VERTICAL_AXES.join(", ")}`);
93
+ }
94
+ if (!TEMPLATE_SKELETON_FIT_VERTICAL_ANCHORS.includes(metadata.verticalAnchor)) {
95
+ errors.push(`verticalAnchor must be one of: ${TEMPLATE_SKELETON_FIT_VERTICAL_ANCHORS.join(", ")}`);
96
+ }
97
+ validateFitMetrics(metadata.fit, errors);
98
+ validateManualAdjustment(metadata.manualAdjustment, errors);
99
+ return { valid: errors.length === 0, errors };
100
+ }
101
+ function validateFitMetrics(value, errors) {
102
+ if (!isRecord(value)) {
103
+ errors.push("fit must be an object");
104
+ return;
105
+ }
106
+ if (!isPositiveFiniteNumber(value.scale)) {
107
+ errors.push("fit.scale must be a positive finite number");
108
+ }
109
+ validateVec3(value.translation, "fit.translation", errors);
110
+ if (!isFiniteNumber(value.confidence) || value.confidence < 0 || value.confidence > 1) {
111
+ errors.push("fit.confidence must be a finite number between 0 and 1");
112
+ }
113
+ if (!isPositiveFiniteNumber(value.meshHeight)) {
114
+ errors.push("fit.meshHeight must be a positive finite number");
115
+ }
116
+ if (!isPositiveFiniteNumber(value.templateHeight)) {
117
+ errors.push("fit.templateHeight must be a positive finite number");
118
+ }
119
+ validateVec3(value.crossAxisSpans, "fit.crossAxisSpans", errors, { min: 0 });
120
+ if (!isTemplateSkeletonFitStatus(value.status)) {
121
+ errors.push(`fit.status must be one of: ${TEMPLATE_SKELETON_FIT_STATUSES.join(", ")}`);
122
+ }
123
+ }
124
+ function validateManualAdjustment(value, errors) {
125
+ if (value == null) {
126
+ return;
127
+ }
128
+ if (!isRecord(value)) {
129
+ errors.push("manualAdjustment must be an object");
130
+ return;
131
+ }
132
+ if (value.scale != null && !isPositiveFiniteNumber(value.scale)) {
133
+ errors.push("manualAdjustment.scale must be a positive finite number");
134
+ }
135
+ if (value.translation != null) {
136
+ validateVec3(value.translation, "manualAdjustment.translation", errors);
137
+ }
138
+ }
139
+ function validateVec3(value, field, errors, options = {}) {
140
+ if (!isRecord(value)) {
141
+ errors.push(`${field} must be an object with finite x, y, and z values`);
142
+ return;
143
+ }
144
+ for (const axis of ["x", "y", "z"]) {
145
+ const axisValue = value[axis];
146
+ if (!isFiniteNumber(axisValue)) {
147
+ errors.push(`${field}.${axis} must be a finite number`);
148
+ } else if (options.min != null && axisValue < options.min) {
149
+ errors.push(`${field}.${axis} must be a finite number >= ${options.min}`);
150
+ }
151
+ }
152
+ }
153
+ function isRecord(value) {
154
+ return typeof value === "object" && value != null && !Array.isArray(value);
155
+ }
156
+ function isNonEmptyString(value) {
157
+ return typeof value === "string" && value.trim().length > 0;
158
+ }
159
+ function isFiniteNumber(value) {
160
+ return typeof value === "number" && Number.isFinite(value);
161
+ }
162
+ function isPositiveFiniteNumber(value) {
163
+ return isFiniteNumber(value) && value > 0;
164
+ }
165
+ function finiteOr(value, fallback) {
166
+ return Number.isFinite(value) ? value : fallback;
167
+ }
168
+ function positiveFiniteOr(value, fallback) {
169
+ return Number.isFinite(value) && value > 0 ? value : fallback;
170
+ }
171
+
172
+ // src/core/TsClipCompiler.ts
173
+ var TsClipCompiler = class {
174
+ constructor(core) {
175
+ this.core = core;
176
+ }
177
+ compile(input) {
178
+ return JSON.parse(this.core.compile_clip(JSON.stringify(input)));
179
+ }
180
+ compileCurves(input) {
181
+ return JSON.parse(this.core.compile_clip_curves(JSON.stringify(input)));
182
+ }
183
+ };
184
+ function numericArrayToNumbers(values) {
185
+ return Array.from(values);
186
+ }
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
+ // src/core/WasmRuntimeCore.ts
699
+ var AU_MORPH_BINDING_STRIDE = 5;
700
+ var VISEME_MORPH_BINDING_STRIDE = 4;
701
+ var AXIS_GROUP_NEGATIVE = 0;
702
+ var AXIS_GROUP_POSITIVE = 1;
703
+ var AXIS_GROUP_PLAIN = 2;
704
+ var BONE_SIDE_NONE = 0;
705
+ var BONE_SIDE_LEFT = 1;
706
+ var BONE_SIDE_RIGHT = 2;
707
+ var ROTATION_CHANNELS = { rx: 0, ry: 1, rz: 2 };
708
+ var TRANSLATION_CHANNELS = { tx: 0, ty: 1, tz: 2 };
709
+ var WasmRuntimeCore = class {
710
+ constructor(options) {
711
+ __publicField(this, "wasm");
712
+ __publicField(this, "core");
713
+ __publicField(this, "profile");
714
+ __publicField(this, "model");
715
+ this.wasm = options.wasm;
716
+ this.profile = options.profile;
717
+ this.model = options.model;
718
+ const RuntimeCtor = options.wasm.RuntimeCore;
719
+ if (!RuntimeCtor) {
720
+ throw new Error("Embody Wasm module does not export RuntimeCore");
721
+ }
722
+ this.core = new RuntimeCtor(getProfileVisemeSlots(options.profile).length);
723
+ this.reloadBindings();
724
+ }
725
+ setProfile(profile) {
726
+ this.profile = profile;
727
+ this.core.set_viseme_slot_count(getProfileVisemeSlots(profile).length);
728
+ this.reloadBindings();
729
+ }
730
+ setModelDescriptor(model) {
731
+ this.model = model;
732
+ this.reloadBindings();
733
+ }
734
+ setAU(id, value, balance = 0) {
735
+ this.core.set_au(id >>> 0, value, balance);
736
+ }
737
+ getAU(id) {
738
+ return this.core.get_au(id >>> 0);
739
+ }
740
+ setAUMixWeight(id, weight) {
741
+ this.core.set_au_mix_weight(id >>> 0, weight);
742
+ }
743
+ setViseme(visemeIndex, value, jawScale) {
744
+ this.core.set_viseme(visemeIndex >>> 0, value);
745
+ if (jawScale !== void 0) {
746
+ this.core.set_viseme_jaw_scale(visemeIndex >>> 0, jawScale);
747
+ }
748
+ }
749
+ clear() {
750
+ this.core.clear();
751
+ }
752
+ evaluatePackedMorphFrameDelta() {
753
+ return this.core.evaluate_morph_frame_delta();
754
+ }
755
+ evaluatePackedBoneFrameDelta() {
756
+ return this.core.evaluate_bone_frame_delta();
757
+ }
758
+ evaluateMorphFrameDelta(deltaSeconds) {
759
+ return {
760
+ deltaSeconds,
761
+ morphTargets: unpackMorphFrameDelta(this.evaluatePackedMorphFrameDelta())
762
+ };
763
+ }
764
+ evaluateFrameDelta(deltaSeconds) {
765
+ return {
766
+ deltaSeconds,
767
+ morphTargets: unpackMorphFrameDelta(this.evaluatePackedMorphFrameDelta()),
768
+ bones: unpackBoneFrameDelta(this.evaluatePackedBoneFrameDelta())
769
+ };
770
+ }
771
+ reloadBindings() {
772
+ const { auBindings, mixedAus, visemeBindings } = compileMorphBindings(this.profile, this.model);
773
+ this.core.load_au_morph_bindings(auBindings);
774
+ this.core.load_viseme_morph_bindings(visemeBindings);
775
+ this.core.set_mixed_aus(mixedAus);
776
+ const boneBindings = compileBoneBindings(this.profile, this.model);
777
+ this.core.load_bone_rest_transforms(boneBindings.restTransforms);
778
+ this.core.load_composite_axes(boneBindings.compositeAxes);
779
+ this.core.load_bone_translations(boneBindings.translations);
780
+ this.core.load_jaw_binding(boneBindings.jawBinding);
781
+ this.core.load_viseme_jaw_amounts(boneBindings.visemeJawAmounts);
782
+ }
783
+ };
784
+ function compileMorphBindings(profile, model) {
785
+ const auRows = [];
786
+ const mixed = [];
787
+ for (const [auIdText, entry] of Object.entries(profile.auToMorphs || {})) {
788
+ const auId = Number(auIdText);
789
+ if (Number.isNaN(auId) || !entry) continue;
790
+ const meshNames = getMeshNamesForAUProfile(profile, auId);
791
+ const hasMorphs = !!(entry.left?.length || entry.right?.length || entry.center?.length);
792
+ if (hasMorphs && profile.auToBones?.[auId]?.length) {
793
+ mixed.push(auId);
794
+ }
795
+ pushAuSide(auRows, auId, 0, entry.left || [], meshNames, model, profile);
796
+ pushAuSide(auRows, auId, 1, entry.right || [], meshNames, model, profile);
797
+ pushAuSide(auRows, auId, 2, entry.center || [], meshNames, model, profile);
798
+ }
799
+ const visemeRows = [];
800
+ const visemeMeshNames = getMeshNamesForVisemeProfile(profile);
801
+ const slotCount = getProfileVisemeSlots(profile).length;
802
+ for (let index = 0; index < slotCount; index += 1) {
803
+ for (const bindingTarget of getVisemeBindingTargets(profile, index)) {
804
+ for (const resolved of resolveMorphTargets(model, profile, bindingTarget.morph, visemeMeshNames)) {
805
+ visemeRows.push(
806
+ index,
807
+ Number(resolved.mesh.id),
808
+ Number(resolved.morphTarget.id),
809
+ bindingTarget.weight
810
+ );
811
+ }
812
+ }
813
+ }
814
+ return {
815
+ auBindings: Float32Array.from(auRows),
816
+ visemeBindings: Float32Array.from(visemeRows),
817
+ mixedAus: Uint32Array.from(mixed)
818
+ };
819
+ }
820
+ function compileBoneBindings(profile, model) {
821
+ const referencedBones = /* @__PURE__ */ new Map();
822
+ const findBone = (nodeKey) => {
823
+ const bone = findBoneDescriptor(profile, model, nodeKey);
824
+ if (bone) referencedBones.set(bone.id, bone);
825
+ return bone;
826
+ };
827
+ const boneSideForAU = (auId, nodeKey) => {
828
+ const side = profile.auToBones?.[auId]?.find((candidate) => candidate.node === nodeKey)?.side;
829
+ if (side === "left") return BONE_SIDE_LEFT;
830
+ if (side === "right") return BONE_SIDE_RIGHT;
831
+ return BONE_SIDE_NONE;
832
+ };
833
+ const compositeRows = [];
834
+ for (const composite of profile.compositeRotations || []) {
835
+ const bone = findBone(composite.node);
836
+ if (!bone) continue;
837
+ const axes = [
838
+ { axis: 1, config: composite.yaw },
839
+ { axis: 0, config: composite.pitch },
840
+ { axis: 2, config: composite.roll }
841
+ ];
842
+ for (const { axis, config } of axes) {
843
+ if (!config) continue;
844
+ const negativeAUs = toAUList(config.negative);
845
+ const positiveAUs = toAUList(config.positive);
846
+ const hasDirectional = negativeAUs.length > 0 && positiveAUs.length > 0;
847
+ const valueRows = [];
848
+ if (hasDirectional) {
849
+ for (const auId of negativeAUs) {
850
+ valueRows.push([auId, AXIS_GROUP_NEGATIVE, boneSideForAU(auId, composite.node)]);
851
+ }
852
+ for (const auId of positiveAUs) {
853
+ valueRows.push([auId, AXIS_GROUP_POSITIVE, boneSideForAU(auId, composite.node)]);
854
+ }
855
+ } else {
856
+ for (const auId of config.aus) {
857
+ valueRows.push([auId, AXIS_GROUP_PLAIN, boneSideForAU(auId, composite.node)]);
858
+ }
859
+ }
860
+ const bindingRows = [];
861
+ const pushBindingRows = (auIds, group) => {
862
+ for (const auId of auIds) {
863
+ const binding = profile.auToBones?.[auId]?.find(
864
+ (candidate) => candidate.node === composite.node
865
+ );
866
+ if (!binding?.maxDegrees) continue;
867
+ const channel = ROTATION_CHANNELS[binding.channel];
868
+ if (channel === void 0) continue;
869
+ bindingRows.push([
870
+ auId,
871
+ group,
872
+ boneSideForAU(auId, composite.node),
873
+ channel,
874
+ binding.scale,
875
+ binding.maxDegrees
876
+ ]);
877
+ }
878
+ };
879
+ pushBindingRows(negativeAUs, AXIS_GROUP_NEGATIVE);
880
+ pushBindingRows(positiveAUs, AXIS_GROUP_POSITIVE);
881
+ pushBindingRows(config.aus, AXIS_GROUP_PLAIN);
882
+ compositeRows.push(
883
+ Number(bone.id),
884
+ axis,
885
+ hasDirectional ? 1 : 0,
886
+ valueRows.length,
887
+ bindingRows.length,
888
+ 0,
889
+ 0,
890
+ 0
891
+ );
892
+ for (const row of valueRows) compositeRows.push(...row);
893
+ for (const row of bindingRows) compositeRows.push(...row);
894
+ }
895
+ }
896
+ const translationRows = [];
897
+ for (const [auIdText, bindings] of Object.entries(profile.auToBones || {})) {
898
+ const auId = Number(auIdText);
899
+ if (Number.isNaN(auId)) continue;
900
+ for (const binding of bindings || []) {
901
+ const channel = TRANSLATION_CHANNELS[binding.channel];
902
+ if (channel === void 0 || binding.maxUnits === void 0) continue;
903
+ const bone = findBone(binding.node);
904
+ if (!bone) continue;
905
+ translationRows.push(auId, Number(bone.id), channel, binding.scale, binding.maxUnits);
906
+ }
907
+ }
908
+ const jawRow = [];
909
+ const jawBinding = findAutoVisemeJawBinding(profile, model);
910
+ if (jawBinding?.maxDegrees) {
911
+ const bone = findBone(jawBinding.node);
912
+ const channel = ROTATION_CHANNELS[jawBinding.channel];
913
+ if (bone && channel !== void 0) {
914
+ jawRow.push(Number(bone.id), channel, jawBinding.scale, jawBinding.maxDegrees);
915
+ }
916
+ }
917
+ const restRows = [];
918
+ for (const bone of referencedBones.values()) {
919
+ const position = bone.restTransform?.position;
920
+ const rotation = bone.restTransform?.rotation;
921
+ restRows.push(
922
+ Number(bone.id),
923
+ position?.x ?? 0,
924
+ position?.y ?? 0,
925
+ position?.z ?? 0,
926
+ rotation?.x ?? 0,
927
+ rotation?.y ?? 0,
928
+ rotation?.z ?? 0,
929
+ rotation?.w ?? 1
930
+ );
931
+ }
932
+ return {
933
+ restTransforms: Float32Array.from(restRows),
934
+ compositeAxes: Float32Array.from(compositeRows),
935
+ translations: Float32Array.from(translationRows),
936
+ jawBinding: Float32Array.from(jawRow),
937
+ visemeJawAmounts: Float32Array.from(getVisemeJawAmounts(profile) ?? [])
938
+ };
939
+ }
940
+ function findBoneDescriptor(profile, model, nodeKey) {
941
+ const configuredName = profile.boneNodes?.[nodeKey] || nodeKey;
942
+ const prefix = profile.bonePrefix ?? "";
943
+ const suffix = profile.boneSuffix ?? "";
944
+ const prefixedName = prefix && !configuredName.startsWith(prefix) ? `${prefix}${configuredName}` : configuredName;
945
+ const fullName = suffix && !prefixedName.endsWith(suffix) ? `${prefixedName}${suffix}` : prefixedName;
946
+ const candidates = /* @__PURE__ */ new Set([nodeKey, configuredName, fullName]);
947
+ return model.bones.find((bone) => candidates.has(bone.name));
948
+ }
949
+ function findAutoVisemeJawBinding(profile, model) {
950
+ const isRotation = (binding) => binding?.channel === "rx" || binding?.channel === "ry" || binding?.channel === "rz";
951
+ const candidates = [
952
+ profile.auToBones?.[103]?.find(isRotation),
953
+ profile.auToBones?.[26]?.find(isRotation)
954
+ ].filter((binding) => !!binding);
955
+ return candidates.find((binding) => !!findBoneDescriptor(profile, model, binding.node)) ?? candidates[0] ?? null;
956
+ }
957
+ function unpackBoneFrameDelta(values) {
958
+ const stride = PACKED_BONE_FRAME_DELTA_STRIDE;
959
+ const writes = [];
960
+ for (let index = 0; index + stride <= values.length; index += stride) {
961
+ const flags = values[index + 8] ?? 0;
962
+ const hasPosition = (flags & PACKED_BONE_FLAG_HAS_POSITION) !== 0;
963
+ const hasRotation = (flags & PACKED_BONE_FLAG_HAS_ROTATION) !== 0;
964
+ if (!hasPosition && !hasRotation) continue;
965
+ writes.push({
966
+ boneId: values[index],
967
+ transform: {
968
+ ...hasPosition ? { position: { x: values[index + 1], y: values[index + 2], z: values[index + 3] } } : {},
969
+ ...hasRotation ? {
970
+ rotation: {
971
+ x: values[index + 4],
972
+ y: values[index + 5],
973
+ z: values[index + 6],
974
+ w: values[index + 7]
975
+ }
976
+ } : {}
977
+ },
978
+ mode: "absolute",
979
+ space: "local"
980
+ });
981
+ }
982
+ return writes;
983
+ }
984
+ function unpackMorphFrameDelta(values) {
985
+ const stride = PACKED_MORPH_FRAME_DELTA_STRIDE;
986
+ const writes = [];
987
+ for (let index = 0; index + stride <= values.length; index += stride) {
988
+ writes.push({
989
+ meshId: values[index],
990
+ morphTargetId: values[index + 1],
991
+ value: values[index + 2] ?? 0,
992
+ mode: (values[index + 3] ?? 0) === 1 ? "additive" : "absolute"
993
+ });
994
+ }
995
+ return writes;
996
+ }
997
+ function pushAuSide(rows, auId, side, morphs, meshNames, model, profile) {
998
+ for (const morph of morphs) {
999
+ for (const resolved of resolveMorphTargets(model, profile, morph, meshNames)) {
1000
+ rows.push(
1001
+ auId,
1002
+ side,
1003
+ Number(resolved.mesh.id),
1004
+ Number(resolved.morphTarget.id),
1005
+ 1
1006
+ );
1007
+ }
1008
+ }
1009
+ }
1010
+ function resolveMorphTargets(model, profile, morph, meshNames) {
1011
+ const result = [];
1012
+ for (const meshName of meshNames) {
1013
+ const mesh = model.meshes.find((candidate) => candidate.name === meshName);
1014
+ if (!mesh) continue;
1015
+ const morphTargets = mesh.morphTargetIds.map((id) => model.morphTargets.find((candidate) => candidate.id === id)).filter((target2) => !!target2);
1016
+ const target = typeof morph === "number" ? morphTargets.find((candidate) => candidate.hostIndex === morph) : resolveMorphTargetByName2(morphTargets, morph, profile);
1017
+ if (target) {
1018
+ result.push({ mesh, morphTarget: target });
1019
+ }
1020
+ }
1021
+ return result;
1022
+ }
1023
+ function resolveMorphTargetByName2(targets, key, profile) {
1024
+ const prefix = profile.morphPrefix || "";
1025
+ const suffix = profile.morphSuffix || "";
1026
+ const fullName = prefix + key + suffix;
1027
+ const exact = targets.find((target) => target.name === fullName);
1028
+ if (exact) return exact;
1029
+ const suffixRegex = profile.suffixPattern ? new RegExp(profile.suffixPattern) : null;
1030
+ if (!suffixRegex) return void 0;
1031
+ return targets.find((target) => {
1032
+ if (!target.name.startsWith(fullName)) return false;
1033
+ const candidateSuffix = target.name.slice(fullName.length);
1034
+ return candidateSuffix === "" || suffixRegex.test(candidateSuffix);
1035
+ });
1036
+ }
1037
+
1038
+ export { AU_MORPH_BINDING_STRIDE, 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, TsRuntimeCore, VISEME_MORPH_BINDING_STRIDE, WasmRuntimeCore, compileBoneBindings, compileMorphBindings, composeTemplateSkeletonFitTransform, isTemplateSkeletonFitStatus, numericArrayToNumbers, unpackBoneFrameDelta, unpackMorphFrameDelta, validateTemplateSkeletonFitMetadata };
1039
+ //# sourceMappingURL=core.js.map
1040
+ //# sourceMappingURL=core.js.map