@babylonjs/loaders 9.11.0 → 9.12.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 (77) hide show
  1. package/FBX/fbxFileLoader.d.ts +194 -0
  2. package/FBX/fbxFileLoader.js +2440 -0
  3. package/FBX/fbxFileLoader.js.map +1 -0
  4. package/FBX/fbxFileLoader.metadata.d.ts +11 -0
  5. package/FBX/fbxFileLoader.metadata.js +11 -0
  6. package/FBX/fbxFileLoader.metadata.js.map +1 -0
  7. package/FBX/index.d.ts +3 -0
  8. package/FBX/index.js +3 -0
  9. package/FBX/index.js.map +1 -0
  10. package/FBX/interpreter/animation.d.ts +122 -0
  11. package/FBX/interpreter/animation.js +648 -0
  12. package/FBX/interpreter/animation.js.map +1 -0
  13. package/FBX/interpreter/blendShapes.d.ts +44 -0
  14. package/FBX/interpreter/blendShapes.js +192 -0
  15. package/FBX/interpreter/blendShapes.js.map +1 -0
  16. package/FBX/interpreter/connections.d.ts +95 -0
  17. package/FBX/interpreter/connections.js +233 -0
  18. package/FBX/interpreter/connections.js.map +1 -0
  19. package/FBX/interpreter/fbxInterpreter.d.ts +149 -0
  20. package/FBX/interpreter/fbxInterpreter.js +496 -0
  21. package/FBX/interpreter/fbxInterpreter.js.map +1 -0
  22. package/FBX/interpreter/geometry.d.ts +55 -0
  23. package/FBX/interpreter/geometry.js +573 -0
  24. package/FBX/interpreter/geometry.js.map +1 -0
  25. package/FBX/interpreter/materials.d.ts +50 -0
  26. package/FBX/interpreter/materials.js +144 -0
  27. package/FBX/interpreter/materials.js.map +1 -0
  28. package/FBX/interpreter/propertyTemplates.d.ts +22 -0
  29. package/FBX/interpreter/propertyTemplates.js +125 -0
  30. package/FBX/interpreter/propertyTemplates.js.map +1 -0
  31. package/FBX/interpreter/rig.d.ts +20 -0
  32. package/FBX/interpreter/rig.js +259 -0
  33. package/FBX/interpreter/rig.js.map +1 -0
  34. package/FBX/interpreter/sceneDiagnostics.d.ts +14 -0
  35. package/FBX/interpreter/sceneDiagnostics.js +55 -0
  36. package/FBX/interpreter/sceneDiagnostics.js.map +1 -0
  37. package/FBX/interpreter/skeleton.d.ts +93 -0
  38. package/FBX/interpreter/skeleton.js +515 -0
  39. package/FBX/interpreter/skeleton.js.map +1 -0
  40. package/FBX/interpreter/transform.d.ts +21 -0
  41. package/FBX/interpreter/transform.js +92 -0
  42. package/FBX/interpreter/transform.js.map +1 -0
  43. package/FBX/parsers/fbxAsciiParser.d.ts +5 -0
  44. package/FBX/parsers/fbxAsciiParser.js +330 -0
  45. package/FBX/parsers/fbxAsciiParser.js.map +1 -0
  46. package/FBX/parsers/fbxBinaryParser.d.ts +6 -0
  47. package/FBX/parsers/fbxBinaryParser.js +255 -0
  48. package/FBX/parsers/fbxBinaryParser.js.map +1 -0
  49. package/FBX/parsers/zlibInflate.d.ts +7 -0
  50. package/FBX/parsers/zlibInflate.js +350 -0
  51. package/FBX/parsers/zlibInflate.js.map +1 -0
  52. package/FBX/types/fbxTypes.d.ts +54 -0
  53. package/FBX/types/fbxTypes.js +66 -0
  54. package/FBX/types/fbxTypes.js.map +1 -0
  55. package/SPLAT/gaussianSplattingStream.d.ts +341 -0
  56. package/SPLAT/gaussianSplattingStream.js +976 -0
  57. package/SPLAT/gaussianSplattingStream.js.map +1 -0
  58. package/SPLAT/gaussianSplattingWorkBuffer.d.ts +51 -0
  59. package/SPLAT/gaussianSplattingWorkBuffer.js +159 -0
  60. package/SPLAT/gaussianSplattingWorkBuffer.js.map +1 -0
  61. package/SPLAT/gaussianSplattingWorkBufferShaders.d.ts +25 -0
  62. package/SPLAT/gaussianSplattingWorkBufferShaders.js +255 -0
  63. package/SPLAT/gaussianSplattingWorkBufferShaders.js.map +1 -0
  64. package/SPLAT/index.d.ts +1 -0
  65. package/SPLAT/index.js +1 -0
  66. package/SPLAT/index.js.map +1 -1
  67. package/SPLAT/sog.js +18 -16
  68. package/SPLAT/sog.js.map +1 -1
  69. package/SPLAT/splatFileLoader.d.ts +8 -0
  70. package/SPLAT/splatFileLoader.js +49 -0
  71. package/SPLAT/splatFileLoader.js.map +1 -1
  72. package/dynamic.js +9 -0
  73. package/dynamic.js.map +1 -1
  74. package/index.d.ts +1 -0
  75. package/index.js +1 -0
  76. package/index.js.map +1 -1
  77. package/package.json +3 -3
@@ -0,0 +1,515 @@
1
+ /* eslint-disable @typescript-eslint/naming-convention, jsdoc/require-param, jsdoc/require-returns */
2
+ import { findChildByName, getPropertyValue, cleanFBXName } from "../types/fbxTypes.js";
3
+ import { getChildren } from "./connections.js";
4
+ const MAX_BONE_INFLUENCES = 8;
5
+ /**
6
+ * Extract all skin deformers from the FBX scene.
7
+ * Returns skin data including bone hierarchy and vertex weights.
8
+ */
9
+ export function extractSkins(objectMap) {
10
+ const skins = [];
11
+ for (const [id, node] of Array.from(objectMap.objects)) {
12
+ if (node.name === "Deformer" && getPropertyValue(node, 2) === "Skin") {
13
+ const skin = extractSkin(id, node, objectMap);
14
+ if (skin) {
15
+ skins.push(skin);
16
+ }
17
+ }
18
+ }
19
+ return skins;
20
+ }
21
+ function extractSkin(skinId, _skinNode, objectMap) {
22
+ // Find the geometry this skin is attached to
23
+ // Skin is a child of the geometry in FBX connection graph
24
+ const skinParent = objectMap.parentOf.get(skinId);
25
+ if (!skinParent) {
26
+ return null;
27
+ }
28
+ const geometryId = skinParent.id;
29
+ const geometryNode = objectMap.objects.get(geometryId);
30
+ if (!geometryNode || geometryNode.name !== "Geometry") {
31
+ return null;
32
+ }
33
+ const modelParent = objectMap.parentOf.get(geometryId);
34
+ const modelParentNode = modelParent ? objectMap.objects.get(modelParent.id) : undefined;
35
+ const meshModelId = modelParentNode?.name === "Model" ? modelParent.id : undefined;
36
+ // Find all clusters (children of this skin)
37
+ const clusterEntries = getChildren(objectMap, skinId, "Deformer");
38
+ if (clusterEntries.length === 0) {
39
+ return null;
40
+ }
41
+ // For each cluster, find the connected bone Model
42
+ // Connection graph: BoneModel → Cluster (bone is child of cluster)
43
+ const boneModelMap = new Map();
44
+ for (const { id: clusterId, node: clusterNode } of clusterEntries) {
45
+ const subType = getPropertyValue(clusterNode, 2);
46
+ if (subType !== "Cluster") {
47
+ continue;
48
+ }
49
+ // The bone Model is a child of the Cluster
50
+ const boneChildren = getChildren(objectMap, clusterId, "Model");
51
+ if (boneChildren.length > 0) {
52
+ boneModelMap.set(boneChildren[0].id, { clusterId, clusterNode });
53
+ }
54
+ }
55
+ // Build bone hierarchy from Model parent-child relationships. Include
56
+ // skeleton-like ancestors even when they are not weighted clusters; some
57
+ // rigs (for example 3ds Max Biped) animate a non-cluster root above the
58
+ // clustered bones.
59
+ const bindPoseMatrices = extractBindPoseMatrices(geometryId, objectMap);
60
+ const skinDiagnostics = [];
61
+ const bones = buildBoneHierarchy(boneModelMap, bindPoseMatrices, objectMap, skinDiagnostics);
62
+ if (bones.length === 0) {
63
+ return null;
64
+ }
65
+ // Extract per-vertex weights from clusters
66
+ const { boneIndices, boneWeights } = extractVertexWeights(bones, boneModelMap, objectMap);
67
+ return {
68
+ id: skinId,
69
+ geometryId,
70
+ meshBindPoseMatrix: meshModelId !== undefined ? (bindPoseMatrices.get(meshModelId) ?? null) : null,
71
+ bones,
72
+ boneIndices,
73
+ boneWeights,
74
+ diagnostics: skinDiagnostics,
75
+ };
76
+ }
77
+ /**
78
+ * Build a flat ordered bone list with parent indices from the FBX Model hierarchy.
79
+ */
80
+ function buildBoneHierarchy(boneModelMap, bindPoseMatrices, objectMap, skinDiagnostics) {
81
+ const bones = [];
82
+ const visited = new Set();
83
+ const skeletonModelIds = collectSkeletonModelIds(boneModelMap, objectMap);
84
+ const parentByModelId = buildSkeletonParentMap(skeletonModelIds, objectMap);
85
+ const childrenByModelId = buildSkeletonChildrenMap(skeletonModelIds, parentByModelId);
86
+ const rootBoneIds = Array.from(skeletonModelIds).filter((modelId) => !parentByModelId.has(modelId));
87
+ // BFS to build ordered list
88
+ const queue = rootBoneIds.map((id) => ({
89
+ modelId: id,
90
+ parentIndex: -1,
91
+ }));
92
+ while (queue.length > 0) {
93
+ const { modelId, parentIndex } = queue.shift();
94
+ if (visited.has(modelId)) {
95
+ continue;
96
+ }
97
+ visited.add(modelId);
98
+ const modelNode = objectMap.objects.get(modelId);
99
+ if (!modelNode) {
100
+ continue;
101
+ }
102
+ const boneIndex = bones.length;
103
+ const clusterInfo = boneModelMap.get(modelId);
104
+ const transform = extractBoneTransform(modelNode);
105
+ const { bindPoseMatrix, transformLinkMatrix, transformAssociateModelMatrix, clusterMode } = clusterInfo
106
+ ? extractClusterMatrices(clusterInfo.clusterNode)
107
+ : { bindPoseMatrix: null, transformLinkMatrix: null, transformAssociateModelMatrix: null, clusterMode: "Unknown" };
108
+ const diagnostics = createBoneDiagnostics(modelId, cleanFBXName(getPropertyValue(modelNode, 1) ?? `Bone${boneIndex}`), clusterInfo !== undefined, clusterMode, bindPoseMatrix, transformLinkMatrix, transformAssociateModelMatrix, bindPoseMatrices.get(modelId) ?? null);
109
+ skinDiagnostics.push(...diagnostics);
110
+ bones.push({
111
+ modelId,
112
+ name: cleanFBXName(getPropertyValue(modelNode, 1) ?? `Bone${boneIndex}`),
113
+ index: boneIndex,
114
+ parentIndex,
115
+ isCluster: clusterInfo !== undefined,
116
+ translation: transform.translation,
117
+ rotation: transform.rotation,
118
+ preRotation: transform.preRotation,
119
+ postRotation: transform.postRotation,
120
+ rotationPivot: transform.rotationPivot,
121
+ scalingPivot: transform.scalingPivot,
122
+ rotationOffset: transform.rotationOffset,
123
+ scalingOffset: transform.scalingOffset,
124
+ scale: transform.scale,
125
+ rotationOrder: transform.rotationOrder,
126
+ inheritType: transform.inheritType,
127
+ clusterMode,
128
+ bindPoseMatrix,
129
+ transformLinkMatrix,
130
+ transformAssociateModelMatrix,
131
+ modelBindPoseMatrix: bindPoseMatrices.get(modelId) ?? null,
132
+ diagnostics,
133
+ });
134
+ for (const childId of childrenByModelId.get(modelId) ?? []) {
135
+ if (!visited.has(childId)) {
136
+ queue.push({ modelId: childId, parentIndex: boneIndex });
137
+ }
138
+ }
139
+ }
140
+ return bones;
141
+ }
142
+ function extractBindPoseMatrices(geometryId, objectMap) {
143
+ const modelParent = objectMap.parentOf.get(geometryId);
144
+ const modelParentNode = modelParent ? objectMap.objects.get(modelParent.id) : undefined;
145
+ const modelId = modelParentNode?.name === "Model" ? modelParent.id : undefined;
146
+ if (modelId === undefined) {
147
+ return new Map();
148
+ }
149
+ for (const [, poseNode] of Array.from(objectMap.objects)) {
150
+ if (poseNode.name !== "Pose" || getPropertyValue(poseNode, 2) !== "BindPose") {
151
+ continue;
152
+ }
153
+ const matrices = new Map();
154
+ for (const poseChild of poseNode.children) {
155
+ if (poseChild.name !== "PoseNode") {
156
+ continue;
157
+ }
158
+ const nodeChild = findChildByName(poseChild, "Node");
159
+ const matrixChild = findChildByName(poseChild, "Matrix");
160
+ const nodeId = nodeChild?.properties[0]?.value;
161
+ const matrixValue = matrixChild?.properties[0]?.value;
162
+ if (typeof nodeId !== "number") {
163
+ continue;
164
+ }
165
+ const matrix = toFloat64Array(matrixValue);
166
+ if (matrix?.length === 16) {
167
+ matrices.set(nodeId, matrix);
168
+ }
169
+ }
170
+ if (matrices.has(modelId)) {
171
+ return matrices;
172
+ }
173
+ }
174
+ return new Map();
175
+ }
176
+ function buildSkeletonChildrenMap(skeletonModelIds, parentByModelId) {
177
+ const childrenByModelId = new Map();
178
+ for (const modelId of Array.from(skeletonModelIds)) {
179
+ const parentId = parentByModelId.get(modelId);
180
+ if (parentId === undefined) {
181
+ continue;
182
+ }
183
+ if (!childrenByModelId.has(parentId)) {
184
+ childrenByModelId.set(parentId, []);
185
+ }
186
+ childrenByModelId.get(parentId).push(modelId);
187
+ }
188
+ return childrenByModelId;
189
+ }
190
+ function collectSkeletonModelIds(boneModelMap, objectMap) {
191
+ const skeletonModelIds = new Set(Array.from(boneModelMap.keys()));
192
+ for (const modelId of Array.from(boneModelMap.keys())) {
193
+ let parentId = findModelParentId(modelId, objectMap);
194
+ while (parentId !== undefined) {
195
+ const parentNode = objectMap.objects.get(parentId);
196
+ if (!parentNode || parentNode.name !== "Model") {
197
+ break;
198
+ }
199
+ skeletonModelIds.add(parentId);
200
+ parentId = findModelParentId(parentId, objectMap);
201
+ }
202
+ }
203
+ return skeletonModelIds;
204
+ }
205
+ function buildSkeletonParentMap(skeletonModelIds, objectMap) {
206
+ const parentByModelId = new Map();
207
+ for (const modelId of Array.from(skeletonModelIds)) {
208
+ let parentId = findModelParentId(modelId, objectMap);
209
+ while (parentId !== undefined) {
210
+ if (skeletonModelIds.has(parentId)) {
211
+ parentByModelId.set(modelId, parentId);
212
+ break;
213
+ }
214
+ parentId = findModelParentId(parentId, objectMap);
215
+ }
216
+ }
217
+ return parentByModelId;
218
+ }
219
+ function findModelParentId(modelId, objectMap) {
220
+ const parentConnection = objectMap.connections.find((conn) => conn.type === "OO" && conn.childId === modelId && objectMap.objects.get(conn.parentId)?.name === "Model");
221
+ return parentConnection?.parentId;
222
+ }
223
+ export function isSkeletonModel(modelNode) {
224
+ const subType = getPropertyValue(modelNode, 2);
225
+ return subType === "Root" || subType === "LimbNode";
226
+ }
227
+ export function extractBoneTransform(modelNode) {
228
+ const translation = [0, 0, 0];
229
+ const rotation = [0, 0, 0];
230
+ const preRotation = [0, 0, 0];
231
+ const postRotation = [0, 0, 0];
232
+ const rotationPivot = [0, 0, 0];
233
+ const scalingPivot = [0, 0, 0];
234
+ const rotationOffset = [0, 0, 0];
235
+ const scalingOffset = [0, 0, 0];
236
+ const scale = [1, 1, 1];
237
+ let rotationOrder = 0;
238
+ let inheritType = 1;
239
+ const props70 = findChildByName(modelNode, "Properties70");
240
+ if (!props70) {
241
+ return { translation, rotation, preRotation, postRotation, rotationPivot, scalingPivot, rotationOffset, scalingOffset, scale, rotationOrder, inheritType };
242
+ }
243
+ for (const p of props70.children) {
244
+ if (p.name !== "P") {
245
+ continue;
246
+ }
247
+ const propName = getPropertyValue(p, 0);
248
+ if (!propName) {
249
+ continue;
250
+ }
251
+ switch (propName) {
252
+ case "Lcl Translation":
253
+ translation[0] = toNumber(p.properties[4]?.value) ?? 0;
254
+ translation[1] = toNumber(p.properties[5]?.value) ?? 0;
255
+ translation[2] = toNumber(p.properties[6]?.value) ?? 0;
256
+ break;
257
+ case "Lcl Rotation":
258
+ rotation[0] = toNumber(p.properties[4]?.value) ?? 0;
259
+ rotation[1] = toNumber(p.properties[5]?.value) ?? 0;
260
+ rotation[2] = toNumber(p.properties[6]?.value) ?? 0;
261
+ break;
262
+ case "PreRotation":
263
+ preRotation[0] = toNumber(p.properties[4]?.value) ?? 0;
264
+ preRotation[1] = toNumber(p.properties[5]?.value) ?? 0;
265
+ preRotation[2] = toNumber(p.properties[6]?.value) ?? 0;
266
+ break;
267
+ case "PostRotation":
268
+ postRotation[0] = toNumber(p.properties[4]?.value) ?? 0;
269
+ postRotation[1] = toNumber(p.properties[5]?.value) ?? 0;
270
+ postRotation[2] = toNumber(p.properties[6]?.value) ?? 0;
271
+ break;
272
+ case "RotationPivot":
273
+ rotationPivot[0] = toNumber(p.properties[4]?.value) ?? 0;
274
+ rotationPivot[1] = toNumber(p.properties[5]?.value) ?? 0;
275
+ rotationPivot[2] = toNumber(p.properties[6]?.value) ?? 0;
276
+ break;
277
+ case "ScalingPivot":
278
+ scalingPivot[0] = toNumber(p.properties[4]?.value) ?? 0;
279
+ scalingPivot[1] = toNumber(p.properties[5]?.value) ?? 0;
280
+ scalingPivot[2] = toNumber(p.properties[6]?.value) ?? 0;
281
+ break;
282
+ case "RotationOffset":
283
+ rotationOffset[0] = toNumber(p.properties[4]?.value) ?? 0;
284
+ rotationOffset[1] = toNumber(p.properties[5]?.value) ?? 0;
285
+ rotationOffset[2] = toNumber(p.properties[6]?.value) ?? 0;
286
+ break;
287
+ case "ScalingOffset":
288
+ scalingOffset[0] = toNumber(p.properties[4]?.value) ?? 0;
289
+ scalingOffset[1] = toNumber(p.properties[5]?.value) ?? 0;
290
+ scalingOffset[2] = toNumber(p.properties[6]?.value) ?? 0;
291
+ break;
292
+ case "Lcl Scaling":
293
+ scale[0] = toNumber(p.properties[4]?.value) ?? 1;
294
+ scale[1] = toNumber(p.properties[5]?.value) ?? 1;
295
+ scale[2] = toNumber(p.properties[6]?.value) ?? 1;
296
+ break;
297
+ case "RotationOrder":
298
+ rotationOrder = toNumber(p.properties[4]?.value) ?? 0;
299
+ break;
300
+ case "InheritType":
301
+ inheritType = toNumber(p.properties[4]?.value) ?? 1;
302
+ break;
303
+ }
304
+ }
305
+ return { translation, rotation, preRotation, postRotation, rotationPivot, scalingPivot, rotationOffset, scalingOffset, scale, rotationOrder, inheritType };
306
+ }
307
+ function extractClusterMatrices(clusterNode) {
308
+ let bindPoseMatrix = null;
309
+ let transformLinkMatrix = null;
310
+ let transformAssociateModelMatrix = null;
311
+ let clusterMode = "Normalize";
312
+ const transformNode = findChildByName(clusterNode, "Transform");
313
+ if (transformNode && transformNode.properties[0]) {
314
+ const val = transformNode.properties[0].value;
315
+ if (val instanceof Float64Array && val.length === 16) {
316
+ bindPoseMatrix = val;
317
+ }
318
+ else if (val instanceof Float32Array && val.length === 16) {
319
+ bindPoseMatrix = new Float64Array(val);
320
+ }
321
+ }
322
+ const transformLinkNode = findChildByName(clusterNode, "TransformLink");
323
+ if (transformLinkNode && transformLinkNode.properties[0]) {
324
+ const val = transformLinkNode.properties[0].value;
325
+ if (val instanceof Float64Array && val.length === 16) {
326
+ transformLinkMatrix = val;
327
+ }
328
+ else if (val instanceof Float32Array && val.length === 16) {
329
+ transformLinkMatrix = new Float64Array(val);
330
+ }
331
+ }
332
+ const transformAssociateModelNode = findChildByName(clusterNode, "TransformAssociateModel");
333
+ if (transformAssociateModelNode && transformAssociateModelNode.properties[0]) {
334
+ const val = transformAssociateModelNode.properties[0].value;
335
+ if (val instanceof Float64Array && val.length === 16) {
336
+ transformAssociateModelMatrix = val;
337
+ }
338
+ else if (val instanceof Float32Array && val.length === 16) {
339
+ transformAssociateModelMatrix = new Float64Array(val);
340
+ }
341
+ }
342
+ const modeNode = findChildByName(clusterNode, "Mode");
343
+ const mode = modeNode ? getPropertyValue(modeNode, 0) : undefined;
344
+ if (mode === "Normalize" || mode === "Additive" || mode === "TotalOne") {
345
+ clusterMode = mode;
346
+ }
347
+ else if (mode) {
348
+ clusterMode = "Unknown";
349
+ }
350
+ return { bindPoseMatrix, transformLinkMatrix, transformAssociateModelMatrix, clusterMode };
351
+ }
352
+ function createBoneDiagnostics(modelId, boneName, isCluster, clusterMode, bindPoseMatrix, transformLinkMatrix, transformAssociateModelMatrix, modelBindPoseMatrix) {
353
+ if (!isCluster) {
354
+ return [];
355
+ }
356
+ const diagnostics = [];
357
+ if (clusterMode === "Additive" || clusterMode === "TotalOne") {
358
+ diagnostics.push({
359
+ type: "cluster-mode-runtime-unsupported",
360
+ message: `Cluster mode '${clusterMode}' is preserved but not applied by Babylon linear blend skinning.`,
361
+ boneModelId: modelId,
362
+ boneName,
363
+ clusterMode,
364
+ });
365
+ }
366
+ if (!bindPoseMatrix) {
367
+ diagnostics.push({
368
+ type: "missing-cluster-transform",
369
+ message: "Cluster is missing Transform matrix; falling back to rest/bind-pose data.",
370
+ boneModelId: modelId,
371
+ boneName,
372
+ clusterMode,
373
+ });
374
+ }
375
+ if (!transformLinkMatrix) {
376
+ diagnostics.push({
377
+ type: "missing-cluster-transform-link",
378
+ message: "Cluster is missing TransformLink matrix; falling back to model bind pose or rest transform.",
379
+ boneModelId: modelId,
380
+ boneName,
381
+ clusterMode,
382
+ });
383
+ }
384
+ if (!modelBindPoseMatrix) {
385
+ diagnostics.push({
386
+ type: "missing-bind-pose-matrix",
387
+ message: "No BindPose matrix was found for this bone model.",
388
+ boneModelId: modelId,
389
+ boneName,
390
+ clusterMode,
391
+ });
392
+ }
393
+ if (transformAssociateModelMatrix) {
394
+ diagnostics.push({
395
+ type: "associate-model-present",
396
+ message: "TransformAssociateModel is preserved for future associate-model skinning semantics.",
397
+ boneModelId: modelId,
398
+ boneName,
399
+ clusterMode,
400
+ });
401
+ }
402
+ return diagnostics;
403
+ }
404
+ /**
405
+ * Extract per-vertex bone indices and weights from cluster data.
406
+ * Returns arrays indexed by control point index.
407
+ */
408
+ function extractVertexWeights(bones, boneModelMap, objectMap) {
409
+ // We need to find the max vertex index to size our arrays
410
+ let maxVertexIndex = 0;
411
+ // First pass: find max vertex index
412
+ for (const bone of bones) {
413
+ const clusterInfo = boneModelMap.get(bone.modelId);
414
+ if (!clusterInfo) {
415
+ continue;
416
+ }
417
+ const indexesNode = findChildByName(clusterInfo.clusterNode, "Indexes");
418
+ if (!indexesNode) {
419
+ continue;
420
+ }
421
+ const indexes = toInt32Array(indexesNode.properties[0]?.value);
422
+ if (!indexes) {
423
+ continue;
424
+ }
425
+ for (let i = 0; i < indexes.length; i++) {
426
+ if (indexes[i] > maxVertexIndex) {
427
+ maxVertexIndex = indexes[i];
428
+ }
429
+ }
430
+ }
431
+ // Initialize arrays
432
+ const vertexCount = maxVertexIndex + 1;
433
+ const boneIndices = new Array(vertexCount);
434
+ const boneWeights = new Array(vertexCount);
435
+ for (let i = 0; i < vertexCount; i++) {
436
+ boneIndices[i] = [];
437
+ boneWeights[i] = [];
438
+ }
439
+ // Second pass: collect influences
440
+ for (const bone of bones) {
441
+ const clusterInfo = boneModelMap.get(bone.modelId);
442
+ if (!clusterInfo) {
443
+ continue;
444
+ }
445
+ const indexesNode = findChildByName(clusterInfo.clusterNode, "Indexes");
446
+ const weightsNode = findChildByName(clusterInfo.clusterNode, "Weights");
447
+ if (!indexesNode || !weightsNode) {
448
+ continue;
449
+ }
450
+ const indexes = toInt32Array(indexesNode.properties[0]?.value);
451
+ const weights = toFloat64Array(weightsNode.properties[0]?.value);
452
+ if (!indexes || !weights) {
453
+ continue;
454
+ }
455
+ for (let i = 0; i < indexes.length; i++) {
456
+ const vertIdx = indexes[i];
457
+ boneIndices[vertIdx].push(bone.index);
458
+ boneWeights[vertIdx].push(weights[i]);
459
+ }
460
+ }
461
+ // Sort by weight descending and cap to Babylon's primary + extra influence buffers.
462
+ for (let i = 0; i < vertexCount; i++) {
463
+ if (boneIndices[i].length === 0) {
464
+ continue;
465
+ }
466
+ const pairs = boneIndices[i].map((bi, idx) => ({
467
+ index: bi,
468
+ weight: boneWeights[i][idx],
469
+ }));
470
+ pairs.sort((a, b) => b.weight - a.weight);
471
+ const cappedPairs = pairs.slice(0, MAX_BONE_INFLUENCES);
472
+ boneIndices[i] = cappedPairs.map((p) => p.index);
473
+ boneWeights[i] = cappedPairs.map((p) => p.weight);
474
+ }
475
+ // Normalize weights to sum to 1.0
476
+ for (let i = 0; i < vertexCount; i++) {
477
+ const sum = boneWeights[i].reduce((a, b) => a + b, 0);
478
+ if (sum > 0) {
479
+ for (let j = 0; j < boneWeights[i].length; j++) {
480
+ boneWeights[i][j] /= sum;
481
+ }
482
+ }
483
+ }
484
+ return { boneIndices, boneWeights };
485
+ }
486
+ // ── Utilities ──────────────────────────────────────────────────────────────────
487
+ function toNumber(value) {
488
+ if (typeof value === "number") {
489
+ return value;
490
+ }
491
+ return undefined;
492
+ }
493
+ function toInt32Array(value) {
494
+ if (value instanceof Int32Array) {
495
+ return value;
496
+ }
497
+ if (value instanceof Float64Array) {
498
+ const result = new Int32Array(value.length);
499
+ for (let i = 0; i < value.length; i++) {
500
+ result[i] = Math.round(value[i]);
501
+ }
502
+ return result;
503
+ }
504
+ return null;
505
+ }
506
+ function toFloat64Array(value) {
507
+ if (value instanceof Float64Array) {
508
+ return value;
509
+ }
510
+ if (value instanceof Float32Array) {
511
+ return new Float64Array(value);
512
+ }
513
+ return null;
514
+ }
515
+ //# sourceMappingURL=skeleton.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skeleton.js","sourceRoot":"","sources":["../../../../../dev/loaders/src/FBX/interpreter/skeleton.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/naming-convention, jsdoc/require-param, jsdoc/require-returns */\r\nimport { type FBXNode, findChildByName, getPropertyValue, cleanFBXName } from \"../types/fbxTypes\";\r\n\r\nimport { type FBXObjectMap, getChildren } from \"./connections\";\r\n\r\nconst MAX_BONE_INFLUENCES = 8;\r\n\r\nexport type FBXClusterMode = \"Normalize\" | \"Additive\" | \"TotalOne\" | \"Unknown\";\r\n\r\nexport interface FBXSkinDiagnostic {\r\n type: \"cluster-mode-runtime-unsupported\" | \"missing-cluster-transform\" | \"missing-cluster-transform-link\" | \"missing-bind-pose-matrix\" | \"associate-model-present\";\r\n message: string;\r\n boneModelId?: number;\r\n boneName?: string;\r\n clusterMode?: FBXClusterMode;\r\n}\r\n\r\n/** Represents a single bone (cluster) in the FBX skeleton */\r\nexport interface FBXBoneData {\r\n /** The Model node ID for this bone */\r\n modelId: number;\r\n /** Bone name (from the Model node) */\r\n name: string;\r\n /** Index of this bone in the skeleton */\r\n index: number;\r\n /** Index of the parent bone (-1 for root) */\r\n parentIndex: number;\r\n /** Whether this bone corresponds to an FBX Cluster with vertex weights */\r\n isCluster: boolean;\r\n /** Local translation from parent (Lcl Translation) */\r\n translation: [number, number, number];\r\n /** Local rotation in degrees (Lcl Rotation) */\r\n rotation: [number, number, number];\r\n /** Pre-rotation in degrees (applied before Lcl Rotation) */\r\n preRotation: [number, number, number];\r\n /** Post-rotation in degrees (applied after Lcl Rotation, inverted) */\r\n postRotation: [number, number, number];\r\n /** Rotation pivot point */\r\n rotationPivot: [number, number, number];\r\n /** Scaling pivot point */\r\n scalingPivot: [number, number, number];\r\n /** Rotation offset */\r\n rotationOffset: [number, number, number];\r\n /** Scaling offset */\r\n scalingOffset: [number, number, number];\r\n /** Local scale (Lcl Scaling) */\r\n scale: [number, number, number];\r\n /** Rotation order: 0=XYZ, 1=XZY, 2=YZX, 3=YXZ, 4=ZXY, 5=ZYX */\r\n rotationOrder: number;\r\n /** FBX transform inheritance mode. 0=RrSs, 1=RSrs, 2=Rrs */\r\n inheritType: number;\r\n /** Cluster skinning mode */\r\n clusterMode: FBXClusterMode;\r\n /** Bind pose transform matrix (cluster Transform, 4x4) */\r\n bindPoseMatrix: Float64Array | null;\r\n /** Bone's world transform at bind time (cluster TransformLink, 4x4) */\r\n transformLinkMatrix: Float64Array | null;\r\n /** Associate model world transform at bind time (cluster TransformAssociateModel, 4x4) */\r\n transformAssociateModelMatrix: Float64Array | null;\r\n /** Model's absolute matrix from the FBX BindPose, when present */\r\n modelBindPoseMatrix: Float64Array | null;\r\n /** Recoverable bind/skinning diagnostics for this bone */\r\n diagnostics: FBXSkinDiagnostic[];\r\n}\r\n\r\n/** Represents a skin deformer with its clusters */\r\nexport interface FBXSkinData {\r\n /** Skin deformer ID */\r\n id: number;\r\n /** Geometry ID this skin is attached to */\r\n geometryId: number;\r\n /** Mesh model world matrix from the FBX BindPose, when present */\r\n meshBindPoseMatrix: Float64Array | null;\r\n /** Bones in this skeleton */\r\n bones: FBXBoneData[];\r\n /** Per-vertex bone indices, sorted by descending weight and capped for Babylon skinning */\r\n boneIndices: number[][];\r\n /** Per-vertex bone weights, matching boneIndices */\r\n boneWeights: number[][];\r\n /** Recoverable skinning/bind diagnostics */\r\n diagnostics: FBXSkinDiagnostic[];\r\n}\r\n\r\n/**\r\n * Extract all skin deformers from the FBX scene.\r\n * Returns skin data including bone hierarchy and vertex weights.\r\n */\r\nexport function extractSkins(objectMap: FBXObjectMap): FBXSkinData[] {\r\n const skins: FBXSkinData[] = [];\r\n\r\n for (const [id, node] of Array.from(objectMap.objects)) {\r\n if (node.name === \"Deformer\" && getPropertyValue<string>(node, 2) === \"Skin\") {\r\n const skin = extractSkin(id, node, objectMap);\r\n if (skin) {\r\n skins.push(skin);\r\n }\r\n }\r\n }\r\n\r\n return skins;\r\n}\r\n\r\nfunction extractSkin(skinId: number, _skinNode: FBXNode, objectMap: FBXObjectMap): FBXSkinData | null {\r\n // Find the geometry this skin is attached to\r\n // Skin is a child of the geometry in FBX connection graph\r\n const skinParent = objectMap.parentOf.get(skinId);\r\n if (!skinParent) {\r\n return null;\r\n }\r\n\r\n const geometryId = skinParent.id;\r\n const geometryNode = objectMap.objects.get(geometryId);\r\n if (!geometryNode || geometryNode.name !== \"Geometry\") {\r\n return null;\r\n }\r\n const modelParent = objectMap.parentOf.get(geometryId);\r\n const modelParentNode = modelParent ? objectMap.objects.get(modelParent.id) : undefined;\r\n const meshModelId = modelParentNode?.name === \"Model\" ? modelParent!.id : undefined;\r\n\r\n // Find all clusters (children of this skin)\r\n const clusterEntries = getChildren(objectMap, skinId, \"Deformer\");\r\n if (clusterEntries.length === 0) {\r\n return null;\r\n }\r\n\r\n // For each cluster, find the connected bone Model\r\n // Connection graph: BoneModel → Cluster (bone is child of cluster)\r\n const boneModelMap = new Map<number, { clusterId: number; clusterNode: FBXNode }>();\r\n for (const { id: clusterId, node: clusterNode } of clusterEntries) {\r\n const subType = getPropertyValue<string>(clusterNode, 2);\r\n if (subType !== \"Cluster\") {\r\n continue;\r\n }\r\n\r\n // The bone Model is a child of the Cluster\r\n const boneChildren = getChildren(objectMap, clusterId, \"Model\");\r\n if (boneChildren.length > 0) {\r\n boneModelMap.set(boneChildren[0].id, { clusterId, clusterNode });\r\n }\r\n }\r\n\r\n // Build bone hierarchy from Model parent-child relationships. Include\r\n // skeleton-like ancestors even when they are not weighted clusters; some\r\n // rigs (for example 3ds Max Biped) animate a non-cluster root above the\r\n // clustered bones.\r\n const bindPoseMatrices = extractBindPoseMatrices(geometryId, objectMap);\r\n const skinDiagnostics: FBXSkinDiagnostic[] = [];\r\n const bones = buildBoneHierarchy(boneModelMap, bindPoseMatrices, objectMap, skinDiagnostics);\r\n if (bones.length === 0) {\r\n return null;\r\n }\r\n\r\n // Extract per-vertex weights from clusters\r\n const { boneIndices, boneWeights } = extractVertexWeights(bones, boneModelMap, objectMap);\r\n\r\n return {\r\n id: skinId,\r\n geometryId,\r\n meshBindPoseMatrix: meshModelId !== undefined ? (bindPoseMatrices.get(meshModelId) ?? null) : null,\r\n bones,\r\n boneIndices,\r\n boneWeights,\r\n diagnostics: skinDiagnostics,\r\n };\r\n}\r\n\r\n/**\r\n * Build a flat ordered bone list with parent indices from the FBX Model hierarchy.\r\n */\r\nfunction buildBoneHierarchy(\r\n boneModelMap: Map<number, { clusterId: number; clusterNode: FBXNode }>,\r\n bindPoseMatrices: Map<number, Float64Array>,\r\n objectMap: FBXObjectMap,\r\n skinDiagnostics: FBXSkinDiagnostic[]\r\n): FBXBoneData[] {\r\n const bones: FBXBoneData[] = [];\r\n const visited = new Set<number>();\r\n const skeletonModelIds = collectSkeletonModelIds(boneModelMap, objectMap);\r\n const parentByModelId = buildSkeletonParentMap(skeletonModelIds, objectMap);\r\n const childrenByModelId = buildSkeletonChildrenMap(skeletonModelIds, parentByModelId);\r\n\r\n const rootBoneIds = Array.from(skeletonModelIds).filter((modelId) => !parentByModelId.has(modelId));\r\n\r\n // BFS to build ordered list\r\n const queue: { modelId: number; parentIndex: number }[] = rootBoneIds.map((id) => ({\r\n modelId: id,\r\n parentIndex: -1,\r\n }));\r\n\r\n while (queue.length > 0) {\r\n const { modelId, parentIndex } = queue.shift()!;\r\n if (visited.has(modelId)) {\r\n continue;\r\n }\r\n visited.add(modelId);\r\n\r\n const modelNode = objectMap.objects.get(modelId);\r\n if (!modelNode) {\r\n continue;\r\n }\r\n\r\n const boneIndex = bones.length;\r\n\r\n const clusterInfo = boneModelMap.get(modelId);\r\n const transform = extractBoneTransform(modelNode);\r\n const { bindPoseMatrix, transformLinkMatrix, transformAssociateModelMatrix, clusterMode } = clusterInfo\r\n ? extractClusterMatrices(clusterInfo.clusterNode)\r\n : { bindPoseMatrix: null, transformLinkMatrix: null, transformAssociateModelMatrix: null, clusterMode: \"Unknown\" as const };\r\n const diagnostics = createBoneDiagnostics(\r\n modelId,\r\n cleanFBXName(getPropertyValue<string>(modelNode, 1) ?? `Bone${boneIndex}`),\r\n clusterInfo !== undefined,\r\n clusterMode,\r\n bindPoseMatrix,\r\n transformLinkMatrix,\r\n transformAssociateModelMatrix,\r\n bindPoseMatrices.get(modelId) ?? null\r\n );\r\n skinDiagnostics.push(...diagnostics);\r\n\r\n bones.push({\r\n modelId,\r\n name: cleanFBXName(getPropertyValue<string>(modelNode, 1) ?? `Bone${boneIndex}`),\r\n index: boneIndex,\r\n parentIndex,\r\n isCluster: clusterInfo !== undefined,\r\n translation: transform.translation,\r\n rotation: transform.rotation,\r\n preRotation: transform.preRotation,\r\n postRotation: transform.postRotation,\r\n rotationPivot: transform.rotationPivot,\r\n scalingPivot: transform.scalingPivot,\r\n rotationOffset: transform.rotationOffset,\r\n scalingOffset: transform.scalingOffset,\r\n scale: transform.scale,\r\n rotationOrder: transform.rotationOrder,\r\n inheritType: transform.inheritType,\r\n clusterMode,\r\n bindPoseMatrix,\r\n transformLinkMatrix,\r\n transformAssociateModelMatrix,\r\n modelBindPoseMatrix: bindPoseMatrices.get(modelId) ?? null,\r\n diagnostics,\r\n });\r\n\r\n for (const childId of childrenByModelId.get(modelId) ?? []) {\r\n if (!visited.has(childId)) {\r\n queue.push({ modelId: childId, parentIndex: boneIndex });\r\n }\r\n }\r\n }\r\n\r\n return bones;\r\n}\r\n\r\nfunction extractBindPoseMatrices(geometryId: number, objectMap: FBXObjectMap): Map<number, Float64Array> {\r\n const modelParent = objectMap.parentOf.get(geometryId);\r\n const modelParentNode = modelParent ? objectMap.objects.get(modelParent.id) : undefined;\r\n const modelId = modelParentNode?.name === \"Model\" ? modelParent!.id : undefined;\r\n if (modelId === undefined) {\r\n return new Map();\r\n }\r\n\r\n for (const [, poseNode] of Array.from(objectMap.objects)) {\r\n if (poseNode.name !== \"Pose\" || getPropertyValue<string>(poseNode, 2) !== \"BindPose\") {\r\n continue;\r\n }\r\n\r\n const matrices = new Map<number, Float64Array>();\r\n for (const poseChild of poseNode.children) {\r\n if (poseChild.name !== \"PoseNode\") {\r\n continue;\r\n }\r\n\r\n const nodeChild = findChildByName(poseChild, \"Node\");\r\n const matrixChild = findChildByName(poseChild, \"Matrix\");\r\n const nodeId = nodeChild?.properties[0]?.value;\r\n const matrixValue = matrixChild?.properties[0]?.value;\r\n if (typeof nodeId !== \"number\") {\r\n continue;\r\n }\r\n\r\n const matrix = toFloat64Array(matrixValue);\r\n if (matrix?.length === 16) {\r\n matrices.set(nodeId, matrix);\r\n }\r\n }\r\n\r\n if (matrices.has(modelId)) {\r\n return matrices;\r\n }\r\n }\r\n\r\n return new Map();\r\n}\r\n\r\nfunction buildSkeletonChildrenMap(skeletonModelIds: Set<number>, parentByModelId: Map<number, number>): Map<number, number[]> {\r\n const childrenByModelId = new Map<number, number[]>();\r\n\r\n for (const modelId of Array.from(skeletonModelIds)) {\r\n const parentId = parentByModelId.get(modelId);\r\n if (parentId === undefined) {\r\n continue;\r\n }\r\n\r\n if (!childrenByModelId.has(parentId)) {\r\n childrenByModelId.set(parentId, []);\r\n }\r\n childrenByModelId.get(parentId)!.push(modelId);\r\n }\r\n\r\n return childrenByModelId;\r\n}\r\n\r\nfunction collectSkeletonModelIds(boneModelMap: Map<number, { clusterId: number; clusterNode: FBXNode }>, objectMap: FBXObjectMap): Set<number> {\r\n const skeletonModelIds = new Set<number>(Array.from(boneModelMap.keys()));\r\n\r\n for (const modelId of Array.from(boneModelMap.keys())) {\r\n let parentId = findModelParentId(modelId, objectMap);\r\n while (parentId !== undefined) {\r\n const parentNode = objectMap.objects.get(parentId);\r\n if (!parentNode || parentNode.name !== \"Model\") {\r\n break;\r\n }\r\n\r\n skeletonModelIds.add(parentId);\r\n parentId = findModelParentId(parentId, objectMap);\r\n }\r\n }\r\n\r\n return skeletonModelIds;\r\n}\r\n\r\nfunction buildSkeletonParentMap(skeletonModelIds: Set<number>, objectMap: FBXObjectMap): Map<number, number> {\r\n const parentByModelId = new Map<number, number>();\r\n\r\n for (const modelId of Array.from(skeletonModelIds)) {\r\n let parentId = findModelParentId(modelId, objectMap);\r\n while (parentId !== undefined) {\r\n if (skeletonModelIds.has(parentId)) {\r\n parentByModelId.set(modelId, parentId);\r\n break;\r\n }\r\n parentId = findModelParentId(parentId, objectMap);\r\n }\r\n }\r\n\r\n return parentByModelId;\r\n}\r\n\r\nfunction findModelParentId(modelId: number, objectMap: FBXObjectMap): number | undefined {\r\n const parentConnection = objectMap.connections.find((conn) => conn.type === \"OO\" && conn.childId === modelId && objectMap.objects.get(conn.parentId)?.name === \"Model\");\r\n return parentConnection?.parentId;\r\n}\r\n\r\nexport function isSkeletonModel(modelNode: FBXNode): boolean {\r\n const subType = getPropertyValue<string>(modelNode, 2);\r\n return subType === \"Root\" || subType === \"LimbNode\";\r\n}\r\n\r\nexport function extractBoneTransform(modelNode: FBXNode): {\r\n translation: [number, number, number];\r\n rotation: [number, number, number];\r\n preRotation: [number, number, number];\r\n postRotation: [number, number, number];\r\n rotationPivot: [number, number, number];\r\n scalingPivot: [number, number, number];\r\n rotationOffset: [number, number, number];\r\n scalingOffset: [number, number, number];\r\n scale: [number, number, number];\r\n rotationOrder: number;\r\n inheritType: number;\r\n} {\r\n const translation: [number, number, number] = [0, 0, 0];\r\n const rotation: [number, number, number] = [0, 0, 0];\r\n const preRotation: [number, number, number] = [0, 0, 0];\r\n const postRotation: [number, number, number] = [0, 0, 0];\r\n const rotationPivot: [number, number, number] = [0, 0, 0];\r\n const scalingPivot: [number, number, number] = [0, 0, 0];\r\n const rotationOffset: [number, number, number] = [0, 0, 0];\r\n const scalingOffset: [number, number, number] = [0, 0, 0];\r\n const scale: [number, number, number] = [1, 1, 1];\r\n let rotationOrder = 0;\r\n let inheritType = 1;\r\n\r\n const props70 = findChildByName(modelNode, \"Properties70\");\r\n if (!props70) {\r\n return { translation, rotation, preRotation, postRotation, rotationPivot, scalingPivot, rotationOffset, scalingOffset, scale, rotationOrder, inheritType };\r\n }\r\n\r\n for (const p of props70.children) {\r\n if (p.name !== \"P\") {\r\n continue;\r\n }\r\n const propName = getPropertyValue<string>(p, 0);\r\n if (!propName) {\r\n continue;\r\n }\r\n\r\n switch (propName) {\r\n case \"Lcl Translation\":\r\n translation[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n translation[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n translation[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"Lcl Rotation\":\r\n rotation[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n rotation[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n rotation[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"PreRotation\":\r\n preRotation[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n preRotation[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n preRotation[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"PostRotation\":\r\n postRotation[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n postRotation[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n postRotation[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"RotationPivot\":\r\n rotationPivot[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n rotationPivot[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n rotationPivot[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"ScalingPivot\":\r\n scalingPivot[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n scalingPivot[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n scalingPivot[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"RotationOffset\":\r\n rotationOffset[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n rotationOffset[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n rotationOffset[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"ScalingOffset\":\r\n scalingOffset[0] = toNumber(p.properties[4]?.value) ?? 0;\r\n scalingOffset[1] = toNumber(p.properties[5]?.value) ?? 0;\r\n scalingOffset[2] = toNumber(p.properties[6]?.value) ?? 0;\r\n break;\r\n case \"Lcl Scaling\":\r\n scale[0] = toNumber(p.properties[4]?.value) ?? 1;\r\n scale[1] = toNumber(p.properties[5]?.value) ?? 1;\r\n scale[2] = toNumber(p.properties[6]?.value) ?? 1;\r\n break;\r\n case \"RotationOrder\":\r\n rotationOrder = toNumber(p.properties[4]?.value) ?? 0;\r\n break;\r\n case \"InheritType\":\r\n inheritType = toNumber(p.properties[4]?.value) ?? 1;\r\n break;\r\n }\r\n }\r\n\r\n return { translation, rotation, preRotation, postRotation, rotationPivot, scalingPivot, rotationOffset, scalingOffset, scale, rotationOrder, inheritType };\r\n}\r\n\r\nfunction extractClusterMatrices(clusterNode: FBXNode): {\r\n bindPoseMatrix: Float64Array | null;\r\n transformLinkMatrix: Float64Array | null;\r\n transformAssociateModelMatrix: Float64Array | null;\r\n clusterMode: FBXClusterMode;\r\n} {\r\n let bindPoseMatrix: Float64Array | null = null;\r\n let transformLinkMatrix: Float64Array | null = null;\r\n let transformAssociateModelMatrix: Float64Array | null = null;\r\n let clusterMode: FBXClusterMode = \"Normalize\";\r\n\r\n const transformNode = findChildByName(clusterNode, \"Transform\");\r\n if (transformNode && transformNode.properties[0]) {\r\n const val = transformNode.properties[0].value;\r\n if (val instanceof Float64Array && val.length === 16) {\r\n bindPoseMatrix = val;\r\n } else if (val instanceof Float32Array && val.length === 16) {\r\n bindPoseMatrix = new Float64Array(val);\r\n }\r\n }\r\n\r\n const transformLinkNode = findChildByName(clusterNode, \"TransformLink\");\r\n if (transformLinkNode && transformLinkNode.properties[0]) {\r\n const val = transformLinkNode.properties[0].value;\r\n if (val instanceof Float64Array && val.length === 16) {\r\n transformLinkMatrix = val;\r\n } else if (val instanceof Float32Array && val.length === 16) {\r\n transformLinkMatrix = new Float64Array(val);\r\n }\r\n }\r\n\r\n const transformAssociateModelNode = findChildByName(clusterNode, \"TransformAssociateModel\");\r\n if (transformAssociateModelNode && transformAssociateModelNode.properties[0]) {\r\n const val = transformAssociateModelNode.properties[0].value;\r\n if (val instanceof Float64Array && val.length === 16) {\r\n transformAssociateModelMatrix = val;\r\n } else if (val instanceof Float32Array && val.length === 16) {\r\n transformAssociateModelMatrix = new Float64Array(val);\r\n }\r\n }\r\n\r\n const modeNode = findChildByName(clusterNode, \"Mode\");\r\n const mode = modeNode ? getPropertyValue<string>(modeNode, 0) : undefined;\r\n if (mode === \"Normalize\" || mode === \"Additive\" || mode === \"TotalOne\") {\r\n clusterMode = mode;\r\n } else if (mode) {\r\n clusterMode = \"Unknown\";\r\n }\r\n\r\n return { bindPoseMatrix, transformLinkMatrix, transformAssociateModelMatrix, clusterMode };\r\n}\r\n\r\nfunction createBoneDiagnostics(\r\n modelId: number,\r\n boneName: string,\r\n isCluster: boolean,\r\n clusterMode: FBXClusterMode,\r\n bindPoseMatrix: Float64Array | null,\r\n transformLinkMatrix: Float64Array | null,\r\n transformAssociateModelMatrix: Float64Array | null,\r\n modelBindPoseMatrix: Float64Array | null\r\n): FBXSkinDiagnostic[] {\r\n if (!isCluster) {\r\n return [];\r\n }\r\n\r\n const diagnostics: FBXSkinDiagnostic[] = [];\r\n if (clusterMode === \"Additive\" || clusterMode === \"TotalOne\") {\r\n diagnostics.push({\r\n type: \"cluster-mode-runtime-unsupported\",\r\n message: `Cluster mode '${clusterMode}' is preserved but not applied by Babylon linear blend skinning.`,\r\n boneModelId: modelId,\r\n boneName,\r\n clusterMode,\r\n });\r\n }\r\n if (!bindPoseMatrix) {\r\n diagnostics.push({\r\n type: \"missing-cluster-transform\",\r\n message: \"Cluster is missing Transform matrix; falling back to rest/bind-pose data.\",\r\n boneModelId: modelId,\r\n boneName,\r\n clusterMode,\r\n });\r\n }\r\n if (!transformLinkMatrix) {\r\n diagnostics.push({\r\n type: \"missing-cluster-transform-link\",\r\n message: \"Cluster is missing TransformLink matrix; falling back to model bind pose or rest transform.\",\r\n boneModelId: modelId,\r\n boneName,\r\n clusterMode,\r\n });\r\n }\r\n if (!modelBindPoseMatrix) {\r\n diagnostics.push({\r\n type: \"missing-bind-pose-matrix\",\r\n message: \"No BindPose matrix was found for this bone model.\",\r\n boneModelId: modelId,\r\n boneName,\r\n clusterMode,\r\n });\r\n }\r\n if (transformAssociateModelMatrix) {\r\n diagnostics.push({\r\n type: \"associate-model-present\",\r\n message: \"TransformAssociateModel is preserved for future associate-model skinning semantics.\",\r\n boneModelId: modelId,\r\n boneName,\r\n clusterMode,\r\n });\r\n }\r\n\r\n return diagnostics;\r\n}\r\n\r\n/**\r\n * Extract per-vertex bone indices and weights from cluster data.\r\n * Returns arrays indexed by control point index.\r\n */\r\nfunction extractVertexWeights(\r\n bones: FBXBoneData[],\r\n boneModelMap: Map<number, { clusterId: number; clusterNode: FBXNode }>,\r\n objectMap: FBXObjectMap\r\n): { boneIndices: number[][]; boneWeights: number[][] } {\r\n // We need to find the max vertex index to size our arrays\r\n let maxVertexIndex = 0;\r\n\r\n // First pass: find max vertex index\r\n for (const bone of bones) {\r\n const clusterInfo = boneModelMap.get(bone.modelId);\r\n if (!clusterInfo) {\r\n continue;\r\n }\r\n\r\n const indexesNode = findChildByName(clusterInfo.clusterNode, \"Indexes\");\r\n if (!indexesNode) {\r\n continue;\r\n }\r\n\r\n const indexes = toInt32Array(indexesNode.properties[0]?.value);\r\n if (!indexes) {\r\n continue;\r\n }\r\n\r\n for (let i = 0; i < indexes.length; i++) {\r\n if (indexes[i] > maxVertexIndex) {\r\n maxVertexIndex = indexes[i];\r\n }\r\n }\r\n }\r\n\r\n // Initialize arrays\r\n const vertexCount = maxVertexIndex + 1;\r\n const boneIndices: number[][] = new Array(vertexCount);\r\n const boneWeights: number[][] = new Array(vertexCount);\r\n for (let i = 0; i < vertexCount; i++) {\r\n boneIndices[i] = [];\r\n boneWeights[i] = [];\r\n }\r\n\r\n // Second pass: collect influences\r\n for (const bone of bones) {\r\n const clusterInfo = boneModelMap.get(bone.modelId);\r\n if (!clusterInfo) {\r\n continue;\r\n }\r\n\r\n const indexesNode = findChildByName(clusterInfo.clusterNode, \"Indexes\");\r\n const weightsNode = findChildByName(clusterInfo.clusterNode, \"Weights\");\r\n if (!indexesNode || !weightsNode) {\r\n continue;\r\n }\r\n\r\n const indexes = toInt32Array(indexesNode.properties[0]?.value);\r\n const weights = toFloat64Array(weightsNode.properties[0]?.value);\r\n if (!indexes || !weights) {\r\n continue;\r\n }\r\n\r\n for (let i = 0; i < indexes.length; i++) {\r\n const vertIdx = indexes[i];\r\n boneIndices[vertIdx].push(bone.index);\r\n boneWeights[vertIdx].push(weights[i]);\r\n }\r\n }\r\n\r\n // Sort by weight descending and cap to Babylon's primary + extra influence buffers.\r\n for (let i = 0; i < vertexCount; i++) {\r\n if (boneIndices[i].length === 0) {\r\n continue;\r\n }\r\n\r\n const pairs = boneIndices[i].map((bi, idx) => ({\r\n index: bi,\r\n weight: boneWeights[i][idx],\r\n }));\r\n pairs.sort((a, b) => b.weight - a.weight);\r\n const cappedPairs = pairs.slice(0, MAX_BONE_INFLUENCES);\r\n boneIndices[i] = cappedPairs.map((p) => p.index);\r\n boneWeights[i] = cappedPairs.map((p) => p.weight);\r\n }\r\n\r\n // Normalize weights to sum to 1.0\r\n for (let i = 0; i < vertexCount; i++) {\r\n const sum = boneWeights[i].reduce((a, b) => a + b, 0);\r\n if (sum > 0) {\r\n for (let j = 0; j < boneWeights[i].length; j++) {\r\n boneWeights[i][j] /= sum;\r\n }\r\n }\r\n }\r\n\r\n return { boneIndices, boneWeights };\r\n}\r\n\r\n// ── Utilities ──────────────────────────────────────────────────────────────────\r\n\r\nfunction toNumber(value: unknown): number | undefined {\r\n if (typeof value === \"number\") {\r\n return value;\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction toInt32Array(value: unknown): Int32Array | null {\r\n if (value instanceof Int32Array) {\r\n return value;\r\n }\r\n if (value instanceof Float64Array) {\r\n const result = new Int32Array(value.length);\r\n for (let i = 0; i < value.length; i++) {\r\n result[i] = Math.round(value[i]);\r\n }\r\n return result;\r\n }\r\n return null;\r\n}\r\n\r\nfunction toFloat64Array(value: unknown): Float64Array | null {\r\n if (value instanceof Float64Array) {\r\n return value;\r\n }\r\n if (value instanceof Float32Array) {\r\n return new Float64Array(value);\r\n }\r\n return null;\r\n}\r\n"]}
@@ -0,0 +1,21 @@
1
+ import { Matrix } from "@babylonjs/core/Maths/math.vector.js";
2
+ export type FBXVector3 = [number, number, number];
3
+ export interface FBXTransformComponents {
4
+ translation: FBXVector3;
5
+ rotation: FBXVector3;
6
+ scale: FBXVector3;
7
+ preRotation: FBXVector3;
8
+ postRotation: FBXVector3;
9
+ rotationPivot: FBXVector3;
10
+ scalingPivot: FBXVector3;
11
+ rotationOffset: FBXVector3;
12
+ scalingOffset: FBXVector3;
13
+ rotationOrder: number;
14
+ inheritType?: number;
15
+ }
16
+ export declare function eulerToMatrixXYZ(rx: number, ry: number, rz: number): Matrix;
17
+ export declare function eulerToMatrix(rx: number, ry: number, rz: number, order: number): Matrix;
18
+ export declare function computeFBXGeometricMatrix(translation: FBXVector3, rotation: FBXVector3, scale: FBXVector3): Matrix;
19
+ export declare function computeFBXGeometricDeltaMatrix(rotation: FBXVector3, scale: FBXVector3): Matrix;
20
+ export declare function computeFBXGeometricNormalMatrix(rotation: FBXVector3, scale: FBXVector3): Matrix;
21
+ export declare function computeFBXLocalMatrix(components: FBXTransformComponents): Matrix;