@babylonjs/loaders 9.12.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.
- package/FBX/fbxFileLoader.d.ts +194 -0
- package/FBX/fbxFileLoader.js +2440 -0
- package/FBX/fbxFileLoader.js.map +1 -0
- package/FBX/fbxFileLoader.metadata.d.ts +11 -0
- package/FBX/fbxFileLoader.metadata.js +11 -0
- package/FBX/fbxFileLoader.metadata.js.map +1 -0
- package/FBX/index.d.ts +3 -0
- package/FBX/index.js +3 -0
- package/FBX/index.js.map +1 -0
- package/FBX/interpreter/animation.d.ts +122 -0
- package/FBX/interpreter/animation.js +648 -0
- package/FBX/interpreter/animation.js.map +1 -0
- package/FBX/interpreter/blendShapes.d.ts +44 -0
- package/FBX/interpreter/blendShapes.js +192 -0
- package/FBX/interpreter/blendShapes.js.map +1 -0
- package/FBX/interpreter/connections.d.ts +95 -0
- package/FBX/interpreter/connections.js +233 -0
- package/FBX/interpreter/connections.js.map +1 -0
- package/FBX/interpreter/fbxInterpreter.d.ts +149 -0
- package/FBX/interpreter/fbxInterpreter.js +496 -0
- package/FBX/interpreter/fbxInterpreter.js.map +1 -0
- package/FBX/interpreter/geometry.d.ts +55 -0
- package/FBX/interpreter/geometry.js +573 -0
- package/FBX/interpreter/geometry.js.map +1 -0
- package/FBX/interpreter/materials.d.ts +50 -0
- package/FBX/interpreter/materials.js +144 -0
- package/FBX/interpreter/materials.js.map +1 -0
- package/FBX/interpreter/propertyTemplates.d.ts +22 -0
- package/FBX/interpreter/propertyTemplates.js +125 -0
- package/FBX/interpreter/propertyTemplates.js.map +1 -0
- package/FBX/interpreter/rig.d.ts +20 -0
- package/FBX/interpreter/rig.js +259 -0
- package/FBX/interpreter/rig.js.map +1 -0
- package/FBX/interpreter/sceneDiagnostics.d.ts +14 -0
- package/FBX/interpreter/sceneDiagnostics.js +55 -0
- package/FBX/interpreter/sceneDiagnostics.js.map +1 -0
- package/FBX/interpreter/skeleton.d.ts +93 -0
- package/FBX/interpreter/skeleton.js +515 -0
- package/FBX/interpreter/skeleton.js.map +1 -0
- package/FBX/interpreter/transform.d.ts +21 -0
- package/FBX/interpreter/transform.js +92 -0
- package/FBX/interpreter/transform.js.map +1 -0
- package/FBX/parsers/fbxAsciiParser.d.ts +5 -0
- package/FBX/parsers/fbxAsciiParser.js +330 -0
- package/FBX/parsers/fbxAsciiParser.js.map +1 -0
- package/FBX/parsers/fbxBinaryParser.d.ts +6 -0
- package/FBX/parsers/fbxBinaryParser.js +255 -0
- package/FBX/parsers/fbxBinaryParser.js.map +1 -0
- package/FBX/parsers/zlibInflate.d.ts +7 -0
- package/FBX/parsers/zlibInflate.js +350 -0
- package/FBX/parsers/zlibInflate.js.map +1 -0
- package/FBX/types/fbxTypes.d.ts +54 -0
- package/FBX/types/fbxTypes.js +66 -0
- package/FBX/types/fbxTypes.js.map +1 -0
- package/SPLAT/gaussianSplattingStream.d.ts +341 -0
- package/SPLAT/gaussianSplattingStream.js +976 -0
- package/SPLAT/gaussianSplattingStream.js.map +1 -0
- package/SPLAT/gaussianSplattingWorkBuffer.d.ts +51 -0
- package/SPLAT/gaussianSplattingWorkBuffer.js +159 -0
- package/SPLAT/gaussianSplattingWorkBuffer.js.map +1 -0
- package/SPLAT/gaussianSplattingWorkBufferShaders.d.ts +25 -0
- package/SPLAT/gaussianSplattingWorkBufferShaders.js +255 -0
- package/SPLAT/gaussianSplattingWorkBufferShaders.js.map +1 -0
- package/SPLAT/index.d.ts +1 -0
- package/SPLAT/index.js +1 -0
- package/SPLAT/index.js.map +1 -1
- package/SPLAT/sog.js +18 -16
- package/SPLAT/sog.js.map +1 -1
- package/SPLAT/splatFileLoader.d.ts +8 -0
- package/SPLAT/splatFileLoader.js +49 -0
- package/SPLAT/splatFileLoader.js.map +1 -1
- package/dynamic.js +9 -0
- package/dynamic.js.map +1 -1
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/index.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention, jsdoc/require-param, jsdoc/require-returns */
|
|
2
|
+
import { findDocumentNode, findChildByName, getPropertyValue, cleanFBXName } from "../types/fbxTypes.js";
|
|
3
|
+
import { resolveConnections, getChildren } from "./connections.js";
|
|
4
|
+
import { extractGeometry } from "./geometry.js";
|
|
5
|
+
import { extractMaterial } from "./materials.js";
|
|
6
|
+
import { extractSkins } from "./skeleton.js";
|
|
7
|
+
import { resolveRigs } from "./rig.js";
|
|
8
|
+
import { extractAnimations } from "./animation.js";
|
|
9
|
+
import { extractBlendShapes } from "./blendShapes.js";
|
|
10
|
+
import { extractSceneDiagnostics } from "./sceneDiagnostics.js";
|
|
11
|
+
import { extractPropertyTemplates, getPropertyTemplate, resolveNumberProperty, resolvePropertyValue, resolveVector3Property, } from "./propertyTemplates.js";
|
|
12
|
+
/**
|
|
13
|
+
* Interpret a parsed FBX document into scene data.
|
|
14
|
+
*/
|
|
15
|
+
export function interpretFBX(doc) {
|
|
16
|
+
const objectMap = resolveConnections(doc);
|
|
17
|
+
const propertyTemplates = extractPropertyTemplates(doc);
|
|
18
|
+
// Extract global settings
|
|
19
|
+
const globalSettings = extractGlobalSettings(doc);
|
|
20
|
+
// Extract all materials
|
|
21
|
+
const materials = [];
|
|
22
|
+
for (const [id, node] of Array.from(objectMap.objects)) {
|
|
23
|
+
if (node.name === "Material") {
|
|
24
|
+
materials.push(extractMaterial(node, id, objectMap, propertyTemplates));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
// Extract all geometries
|
|
28
|
+
const geometries = [];
|
|
29
|
+
for (const [id, node] of Array.from(objectMap.objects)) {
|
|
30
|
+
if (node.name === "Geometry") {
|
|
31
|
+
const subType = getPropertyValue(node, 2);
|
|
32
|
+
if (subType === "Mesh") {
|
|
33
|
+
geometries.push(extractGeometry(node, id));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Extract skeleton/skinning data
|
|
38
|
+
const skins = extractSkins(objectMap);
|
|
39
|
+
const rigs = resolveRigs(objectMap, skins);
|
|
40
|
+
// Extract blend shape data
|
|
41
|
+
const blendShapes = extractBlendShapes(objectMap);
|
|
42
|
+
// Extract animation data
|
|
43
|
+
const animations = extractAnimations(objectMap);
|
|
44
|
+
// Extract cameras and lights from NodeAttribute objects
|
|
45
|
+
const cameras = extractCameras(objectMap, propertyTemplates);
|
|
46
|
+
const lights = extractLights(objectMap, propertyTemplates);
|
|
47
|
+
const diagnostics = extractSceneDiagnostics(objectMap);
|
|
48
|
+
// Build model hierarchy
|
|
49
|
+
const rootModels = buildModelHierarchy(objectMap, geometries, materials, propertyTemplates);
|
|
50
|
+
return {
|
|
51
|
+
rootModels,
|
|
52
|
+
geometries,
|
|
53
|
+
materials,
|
|
54
|
+
skins,
|
|
55
|
+
rigs,
|
|
56
|
+
blendShapes,
|
|
57
|
+
animations,
|
|
58
|
+
cameras,
|
|
59
|
+
lights,
|
|
60
|
+
diagnostics,
|
|
61
|
+
...globalSettings,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
// ── Model Hierarchy ────────────────────────────────────────────────────────────
|
|
65
|
+
function buildModelHierarchy(objectMap, geometries, materials, propertyTemplates) {
|
|
66
|
+
const geometryMap = new Map();
|
|
67
|
+
for (const g of geometries) {
|
|
68
|
+
geometryMap.set(g.id, g);
|
|
69
|
+
}
|
|
70
|
+
const materialMap = new Map();
|
|
71
|
+
for (const m of materials) {
|
|
72
|
+
materialMap.set(m.id, m);
|
|
73
|
+
}
|
|
74
|
+
// Find root models (those connected to ID 0, which is the scene root)
|
|
75
|
+
const rootChildren = objectMap.childrenOf.get(0) ?? [];
|
|
76
|
+
const rootModels = [];
|
|
77
|
+
for (const { id } of rootChildren) {
|
|
78
|
+
const node = objectMap.objects.get(id);
|
|
79
|
+
if (node && node.name === "Model") {
|
|
80
|
+
rootModels.push(buildModel(id, node, objectMap, geometryMap, materialMap, propertyTemplates));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return rootModels;
|
|
84
|
+
}
|
|
85
|
+
function buildModel(modelId, modelNode, objectMap, geometryMap, materialMap, propertyTemplates) {
|
|
86
|
+
const name = cleanFBXName(getPropertyValue(modelNode, 1) ?? "Model");
|
|
87
|
+
const subType = getPropertyValue(modelNode, 2) ?? "Null";
|
|
88
|
+
// Find attached geometry
|
|
89
|
+
const geomChildren = getChildren(objectMap, modelId, "Geometry");
|
|
90
|
+
const geometry = geomChildren.length > 0 ? geometryMap.get(geomChildren[0].id) : undefined;
|
|
91
|
+
// Find attached materials
|
|
92
|
+
const matChildren = getChildren(objectMap, modelId, "Material");
|
|
93
|
+
const modelMaterials = [];
|
|
94
|
+
for (const { id } of matChildren) {
|
|
95
|
+
const mat = materialMap.get(id);
|
|
96
|
+
if (mat) {
|
|
97
|
+
modelMaterials.push(mat);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// Extract transform
|
|
101
|
+
const transform = extractTransform(modelNode, getPropertyTemplate(propertyTemplates, "Model", "FbxNode") ?? getPropertyTemplate(propertyTemplates, "Model"));
|
|
102
|
+
// Recursively build child models
|
|
103
|
+
const childModelNodes = getChildren(objectMap, modelId, "Model");
|
|
104
|
+
const children = [];
|
|
105
|
+
for (const { id, node } of childModelNodes) {
|
|
106
|
+
children.push(buildModel(id, node, objectMap, geometryMap, materialMap, propertyTemplates));
|
|
107
|
+
}
|
|
108
|
+
// Extract culling
|
|
109
|
+
const cullingNode = modelNode.children.find((c) => c.name === "Culling");
|
|
110
|
+
const cullingOff = cullingNode ? getPropertyValue(cullingNode, 0) === "CullingOff" : false;
|
|
111
|
+
// Extract user-defined custom properties
|
|
112
|
+
const customProperties = extractCustomProperties(modelNode);
|
|
113
|
+
return {
|
|
114
|
+
id: modelId,
|
|
115
|
+
name,
|
|
116
|
+
subType,
|
|
117
|
+
geometry,
|
|
118
|
+
materials: modelMaterials,
|
|
119
|
+
children,
|
|
120
|
+
cullingOff,
|
|
121
|
+
customProperties,
|
|
122
|
+
...transform,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
function extractTransform(modelNode, template) {
|
|
126
|
+
const translation = resolveVector3Property(modelNode, template, "Lcl Translation", [0, 0, 0]);
|
|
127
|
+
const rotation = resolveVector3Property(modelNode, template, "Lcl Rotation", [0, 0, 0]);
|
|
128
|
+
const scale = resolveVector3Property(modelNode, template, "Lcl Scaling", [1, 1, 1]);
|
|
129
|
+
const preRotation = resolveVector3Property(modelNode, template, "PreRotation", [0, 0, 0]);
|
|
130
|
+
const postRotation = resolveVector3Property(modelNode, template, "PostRotation", [0, 0, 0]);
|
|
131
|
+
const rotationPivot = resolveVector3Property(modelNode, template, "RotationPivot", [0, 0, 0]);
|
|
132
|
+
const scalingPivot = resolveVector3Property(modelNode, template, "ScalingPivot", [0, 0, 0]);
|
|
133
|
+
const rotationOffset = resolveVector3Property(modelNode, template, "RotationOffset", [0, 0, 0]);
|
|
134
|
+
const scalingOffset = resolveVector3Property(modelNode, template, "ScalingOffset", [0, 0, 0]);
|
|
135
|
+
const geometricTranslation = resolveVector3Property(modelNode, template, "GeometricTranslation", [0, 0, 0]);
|
|
136
|
+
const geometricRotation = resolveVector3Property(modelNode, template, "GeometricRotation", [0, 0, 0]);
|
|
137
|
+
const geometricScaling = resolveVector3Property(modelNode, template, "GeometricScaling", [1, 1, 1]);
|
|
138
|
+
const rotationOrder = resolveNumberProperty(modelNode, template, "RotationOrder", 0);
|
|
139
|
+
const inheritType = resolveNumberProperty(modelNode, template, "InheritType", 1);
|
|
140
|
+
const diagnostics = inheritType !== 1 && inheritType !== 2
|
|
141
|
+
? [
|
|
142
|
+
`InheritType ${inheritType} is parsed and preserved; runtime parent-scale inheritance remains gated to avoid changing existing visual behavior without a fixture-specific baseline.`,
|
|
143
|
+
]
|
|
144
|
+
: [];
|
|
145
|
+
return {
|
|
146
|
+
translation,
|
|
147
|
+
rotation,
|
|
148
|
+
scale,
|
|
149
|
+
preRotation,
|
|
150
|
+
postRotation,
|
|
151
|
+
rotationPivot,
|
|
152
|
+
scalingPivot,
|
|
153
|
+
rotationOffset,
|
|
154
|
+
scalingOffset,
|
|
155
|
+
geometricTranslation,
|
|
156
|
+
geometricRotation,
|
|
157
|
+
geometricScaling,
|
|
158
|
+
rotationOrder,
|
|
159
|
+
inheritType,
|
|
160
|
+
diagnostics,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function extractGlobalSettings(doc) {
|
|
164
|
+
const defaults = {
|
|
165
|
+
upAxis: 1,
|
|
166
|
+
upAxisSign: 1,
|
|
167
|
+
frontAxis: 2,
|
|
168
|
+
frontAxisSign: 1,
|
|
169
|
+
coordAxis: 0,
|
|
170
|
+
coordAxisSign: 1,
|
|
171
|
+
unitScaleFactor: 1,
|
|
172
|
+
};
|
|
173
|
+
const gsNode = findDocumentNode(doc, "GlobalSettings");
|
|
174
|
+
if (!gsNode) {
|
|
175
|
+
return defaults;
|
|
176
|
+
}
|
|
177
|
+
const props70 = gsNode.children.find((c) => c.name === "Properties70");
|
|
178
|
+
if (!props70) {
|
|
179
|
+
return defaults;
|
|
180
|
+
}
|
|
181
|
+
for (const p of props70.children) {
|
|
182
|
+
if (p.name !== "P") {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const propName = getPropertyValue(p, 0);
|
|
186
|
+
const value = toNumber(p.properties[4]?.value);
|
|
187
|
+
if (propName && value !== undefined) {
|
|
188
|
+
switch (propName) {
|
|
189
|
+
case "UpAxis":
|
|
190
|
+
defaults.upAxis = value;
|
|
191
|
+
break;
|
|
192
|
+
case "UpAxisSign":
|
|
193
|
+
defaults.upAxisSign = value;
|
|
194
|
+
break;
|
|
195
|
+
case "FrontAxis":
|
|
196
|
+
defaults.frontAxis = value;
|
|
197
|
+
break;
|
|
198
|
+
case "FrontAxisSign":
|
|
199
|
+
defaults.frontAxisSign = value;
|
|
200
|
+
break;
|
|
201
|
+
case "CoordAxis":
|
|
202
|
+
defaults.coordAxis = value;
|
|
203
|
+
break;
|
|
204
|
+
case "CoordAxisSign":
|
|
205
|
+
defaults.coordAxisSign = value;
|
|
206
|
+
break;
|
|
207
|
+
case "UnitScaleFactor":
|
|
208
|
+
defaults.unitScaleFactor = value;
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return defaults;
|
|
214
|
+
}
|
|
215
|
+
// ── Cameras & Lights ──────────────────────────────────────────────────────────
|
|
216
|
+
const SYSTEM_PROPERTIES = new Set([
|
|
217
|
+
"Lcl Translation",
|
|
218
|
+
"Lcl Rotation",
|
|
219
|
+
"Lcl Scaling",
|
|
220
|
+
"PreRotation",
|
|
221
|
+
"PostRotation",
|
|
222
|
+
"RotationPivot",
|
|
223
|
+
"ScalingPivot",
|
|
224
|
+
"RotationOffset",
|
|
225
|
+
"ScalingOffset",
|
|
226
|
+
"RotationOrder",
|
|
227
|
+
"GeometricTranslation",
|
|
228
|
+
"GeometricRotation",
|
|
229
|
+
"GeometricScaling",
|
|
230
|
+
"Visibility",
|
|
231
|
+
"InheritType",
|
|
232
|
+
"ScalingMax",
|
|
233
|
+
"DefaultAttributeIndex",
|
|
234
|
+
"currentUVSet",
|
|
235
|
+
"lockInfluenceWeights",
|
|
236
|
+
]);
|
|
237
|
+
function extractCustomProperties(modelNode) {
|
|
238
|
+
const props70 = findChildByName(modelNode, "Properties70");
|
|
239
|
+
if (!props70) {
|
|
240
|
+
return undefined;
|
|
241
|
+
}
|
|
242
|
+
const custom = {};
|
|
243
|
+
let hasAny = false;
|
|
244
|
+
for (const p of props70.children) {
|
|
245
|
+
if (p.name !== "P") {
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
const propName = getPropertyValue(p, 0);
|
|
249
|
+
if (!propName || SYSTEM_PROPERTIES.has(propName)) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
// Accept user-defined properties (type starts with something other than standard types)
|
|
253
|
+
// Standard FBX types: "KString", "Number", "double", "int", "bool", "Lcl"...
|
|
254
|
+
// User properties often have types like "KString", but are in the UDP (User Defined Properties) section
|
|
255
|
+
// Heuristic: if not in SYSTEM_PROPERTIES set, it's user-defined
|
|
256
|
+
const val = p.properties[4]?.value;
|
|
257
|
+
if (val === undefined) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
if (typeof val === "string") {
|
|
261
|
+
custom[propName] = val;
|
|
262
|
+
hasAny = true;
|
|
263
|
+
}
|
|
264
|
+
else if (typeof val === "number") {
|
|
265
|
+
custom[propName] = val;
|
|
266
|
+
hasAny = true;
|
|
267
|
+
}
|
|
268
|
+
else if (typeof val === "boolean") {
|
|
269
|
+
custom[propName] = val;
|
|
270
|
+
hasAny = true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return hasAny ? custom : undefined;
|
|
274
|
+
}
|
|
275
|
+
const CAMERA_PROPERTIES = new Set([
|
|
276
|
+
"FieldOfView",
|
|
277
|
+
"FieldOfViewX",
|
|
278
|
+
"FieldOfViewY",
|
|
279
|
+
"NearPlane",
|
|
280
|
+
"FarPlane",
|
|
281
|
+
"AspectWidth",
|
|
282
|
+
"AspectHeight",
|
|
283
|
+
"FilmAspectRatio",
|
|
284
|
+
"FocalLength",
|
|
285
|
+
"FilmWidth",
|
|
286
|
+
"FilmHeight",
|
|
287
|
+
"ApertureWidth",
|
|
288
|
+
"ApertureHeight",
|
|
289
|
+
"CameraProjectionType",
|
|
290
|
+
"ProjectionType",
|
|
291
|
+
"OrthoZoom",
|
|
292
|
+
"Roll",
|
|
293
|
+
"ApertureMode",
|
|
294
|
+
]);
|
|
295
|
+
const LIGHT_PROPERTIES = new Set([
|
|
296
|
+
"LightType",
|
|
297
|
+
"Color",
|
|
298
|
+
"Intensity",
|
|
299
|
+
"InnerAngle",
|
|
300
|
+
"OuterAngle",
|
|
301
|
+
"ConeAngle",
|
|
302
|
+
"DecayType",
|
|
303
|
+
"DecayStart",
|
|
304
|
+
"EnableNearAttenuation",
|
|
305
|
+
"EnableFarAttenuation",
|
|
306
|
+
"CastShadow",
|
|
307
|
+
"Shadow",
|
|
308
|
+
]);
|
|
309
|
+
function extractCameras(objectMap, templates) {
|
|
310
|
+
const cameras = [];
|
|
311
|
+
const cameraTemplate = getPropertyTemplate(templates, "NodeAttribute", "FbxCamera") ?? getPropertyTemplate(templates, "NodeAttribute");
|
|
312
|
+
for (const [id, node] of Array.from(objectMap.objects)) {
|
|
313
|
+
if (node.name !== "NodeAttribute") {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
const subType = getPropertyValue(node, 2);
|
|
317
|
+
if (subType !== "Camera") {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
// Find the model this camera is attached to (parent)
|
|
321
|
+
const parent = objectMap.parentOf.get(id);
|
|
322
|
+
if (!parent) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const parentNode = objectMap.objects.get(parent.id);
|
|
326
|
+
if (!parentNode || parentNode.name !== "Model") {
|
|
327
|
+
continue;
|
|
328
|
+
}
|
|
329
|
+
const name = cleanFBXName(getPropertyValue(parentNode, 1) ?? "Camera");
|
|
330
|
+
const nearPlane = resolveNumberProperty(node, cameraTemplate, "NearPlane", 0.1);
|
|
331
|
+
const farPlane = resolveNumberProperty(node, cameraTemplate, "FarPlane", 10000);
|
|
332
|
+
const aspectRatio = resolveCameraAspectRatio(node, cameraTemplate);
|
|
333
|
+
const projectionType = resolveNumberProperty(node, cameraTemplate, "CameraProjectionType", 0) === 1 || resolveNumberProperty(node, cameraTemplate, "ProjectionType", 0) === 1
|
|
334
|
+
? "orthographic"
|
|
335
|
+
: "perspective";
|
|
336
|
+
const focalLength = toNumber(resolvePropertyValue(node, cameraTemplate, "FocalLength"));
|
|
337
|
+
const filmWidth = toNumber(resolvePropertyValue(node, cameraTemplate, "FilmWidth")) ?? toNumber(resolvePropertyValue(node, cameraTemplate, "ApertureWidth"));
|
|
338
|
+
const filmHeight = toNumber(resolvePropertyValue(node, cameraTemplate, "FilmHeight")) ?? toNumber(resolvePropertyValue(node, cameraTemplate, "ApertureHeight"));
|
|
339
|
+
const orthoZoom = toNumber(resolvePropertyValue(node, cameraTemplate, "OrthoZoom"));
|
|
340
|
+
const roll = toNumber(resolvePropertyValue(node, cameraTemplate, "Roll"));
|
|
341
|
+
const fieldOfView = resolveCameraFieldOfView(node, cameraTemplate, aspectRatio, focalLength, filmHeight);
|
|
342
|
+
const diagnostics = [];
|
|
343
|
+
if (projectionType === "orthographic" && orthoZoom === undefined) {
|
|
344
|
+
diagnostics.push("Orthographic camera has no OrthoZoom; runtime orthographic bounds use a fallback.");
|
|
345
|
+
}
|
|
346
|
+
if (focalLength !== undefined && filmHeight === undefined && resolvePropertyValue(node, cameraTemplate, "FieldOfView") === undefined) {
|
|
347
|
+
diagnostics.push("FocalLength is present without FilmHeight; default field of view fallback may be used.");
|
|
348
|
+
}
|
|
349
|
+
cameras.push({
|
|
350
|
+
modelId: parent.id,
|
|
351
|
+
name,
|
|
352
|
+
fieldOfView,
|
|
353
|
+
nearPlane,
|
|
354
|
+
farPlane,
|
|
355
|
+
aspectRatio,
|
|
356
|
+
projectionType,
|
|
357
|
+
focalLength,
|
|
358
|
+
filmWidth,
|
|
359
|
+
filmHeight,
|
|
360
|
+
orthoZoom,
|
|
361
|
+
roll,
|
|
362
|
+
unknownProperties: collectUnknownLocalProperties(node, CAMERA_PROPERTIES),
|
|
363
|
+
diagnostics,
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
return cameras;
|
|
367
|
+
}
|
|
368
|
+
function extractLights(objectMap, templates) {
|
|
369
|
+
const lights = [];
|
|
370
|
+
const lightTemplate = getPropertyTemplate(templates, "NodeAttribute", "FbxLight") ?? getPropertyTemplate(templates, "NodeAttribute");
|
|
371
|
+
for (const [id, node] of Array.from(objectMap.objects)) {
|
|
372
|
+
if (node.name !== "NodeAttribute") {
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
const subType = getPropertyValue(node, 2);
|
|
376
|
+
if (subType !== "Light") {
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
// Find the model this light is attached to
|
|
380
|
+
const parent = objectMap.parentOf.get(id);
|
|
381
|
+
if (!parent) {
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
384
|
+
const parentNode = objectMap.objects.get(parent.id);
|
|
385
|
+
if (!parentNode || parentNode.name !== "Model") {
|
|
386
|
+
continue;
|
|
387
|
+
}
|
|
388
|
+
const name = cleanFBXName(getPropertyValue(parentNode, 1) ?? "Light");
|
|
389
|
+
const lightType = resolveNumberProperty(node, lightTemplate, "LightType", 0);
|
|
390
|
+
const color = resolveVector3Property(node, lightTemplate, "Color", [1, 1, 1]);
|
|
391
|
+
const intensity = resolveNumberProperty(node, lightTemplate, "Intensity", 100) / 100;
|
|
392
|
+
const outerAngle = toNumber(resolvePropertyValue(node, lightTemplate, "OuterAngle")) ?? toNumber(resolvePropertyValue(node, lightTemplate, "ConeAngle"));
|
|
393
|
+
const innerAngle = toNumber(resolvePropertyValue(node, lightTemplate, "InnerAngle"));
|
|
394
|
+
const coneAngle = outerAngle ?? 45;
|
|
395
|
+
const decayType = resolveNumberProperty(node, lightTemplate, "DecayType", 2);
|
|
396
|
+
const decayStart = toNumber(resolvePropertyValue(node, lightTemplate, "DecayStart"));
|
|
397
|
+
const enableNearAttenuation = toBoolean(resolvePropertyValue(node, lightTemplate, "EnableNearAttenuation"));
|
|
398
|
+
const enableFarAttenuation = toBoolean(resolvePropertyValue(node, lightTemplate, "EnableFarAttenuation"));
|
|
399
|
+
const castShadows = toBoolean(resolvePropertyValue(node, lightTemplate, "CastShadow")) ??
|
|
400
|
+
toBoolean(resolvePropertyValue(parentNode, undefined, "CastShadow")) ??
|
|
401
|
+
toBoolean(resolvePropertyValue(parentNode, undefined, "Shadow"));
|
|
402
|
+
const diagnostics = [];
|
|
403
|
+
if (decayType !== 2) {
|
|
404
|
+
diagnostics.push(`DecayType ${decayType} is preserved as metadata; Babylon falloff is not remapped in this pass.`);
|
|
405
|
+
}
|
|
406
|
+
if (decayStart !== undefined) {
|
|
407
|
+
diagnostics.push("DecayStart is preserved as metadata and is not mapped to Babylon light range.");
|
|
408
|
+
}
|
|
409
|
+
lights.push({
|
|
410
|
+
modelId: parent.id,
|
|
411
|
+
name,
|
|
412
|
+
lightType,
|
|
413
|
+
color,
|
|
414
|
+
intensity,
|
|
415
|
+
coneAngle,
|
|
416
|
+
decayType,
|
|
417
|
+
innerAngle,
|
|
418
|
+
outerAngle,
|
|
419
|
+
decayStart,
|
|
420
|
+
enableNearAttenuation,
|
|
421
|
+
enableFarAttenuation,
|
|
422
|
+
castShadows,
|
|
423
|
+
unknownProperties: collectUnknownLocalProperties(node, LIGHT_PROPERTIES),
|
|
424
|
+
diagnostics,
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
return lights;
|
|
428
|
+
}
|
|
429
|
+
// ── Utilities ──────────────────────────────────────────────────────────────────
|
|
430
|
+
function toNumber(value) {
|
|
431
|
+
if (typeof value === "number") {
|
|
432
|
+
return value;
|
|
433
|
+
}
|
|
434
|
+
return undefined;
|
|
435
|
+
}
|
|
436
|
+
function toBoolean(value) {
|
|
437
|
+
if (typeof value === "boolean") {
|
|
438
|
+
return value;
|
|
439
|
+
}
|
|
440
|
+
if (typeof value === "number") {
|
|
441
|
+
return value !== 0;
|
|
442
|
+
}
|
|
443
|
+
return undefined;
|
|
444
|
+
}
|
|
445
|
+
function resolveCameraAspectRatio(node, template) {
|
|
446
|
+
const filmAspectRatio = toNumber(resolvePropertyValue(node, template, "FilmAspectRatio"));
|
|
447
|
+
if (filmAspectRatio !== undefined && filmAspectRatio > 0) {
|
|
448
|
+
return filmAspectRatio;
|
|
449
|
+
}
|
|
450
|
+
const aspectWidth = toNumber(resolvePropertyValue(node, template, "AspectWidth"));
|
|
451
|
+
const aspectHeight = toNumber(resolvePropertyValue(node, template, "AspectHeight"));
|
|
452
|
+
if (aspectWidth !== undefined && aspectHeight !== undefined && aspectWidth > 0 && aspectHeight > 0) {
|
|
453
|
+
return aspectWidth / aspectHeight;
|
|
454
|
+
}
|
|
455
|
+
return 0;
|
|
456
|
+
}
|
|
457
|
+
function resolveCameraFieldOfView(node, template, aspectRatio, focalLength, filmHeight) {
|
|
458
|
+
const verticalFov = toNumber(resolvePropertyValue(node, template, "FieldOfViewY")) ?? toNumber(resolvePropertyValue(node, template, "FieldOfView"));
|
|
459
|
+
if (verticalFov !== undefined) {
|
|
460
|
+
return verticalFov;
|
|
461
|
+
}
|
|
462
|
+
const horizontalFov = toNumber(resolvePropertyValue(node, template, "FieldOfViewX"));
|
|
463
|
+
if (horizontalFov !== undefined) {
|
|
464
|
+
if (aspectRatio > 0) {
|
|
465
|
+
return radiansToDegrees(2 * Math.atan(Math.tan(degreesToRadians(horizontalFov) / 2) / aspectRatio));
|
|
466
|
+
}
|
|
467
|
+
return horizontalFov;
|
|
468
|
+
}
|
|
469
|
+
if (focalLength !== undefined && focalLength > 0 && filmHeight !== undefined && filmHeight > 0) {
|
|
470
|
+
return radiansToDegrees(2 * Math.atan((filmHeight * 25.4) / (2 * focalLength)));
|
|
471
|
+
}
|
|
472
|
+
return 45;
|
|
473
|
+
}
|
|
474
|
+
function collectUnknownLocalProperties(node, known) {
|
|
475
|
+
const unknown = new Set();
|
|
476
|
+
for (const containerName of ["Properties70", "Properties60"]) {
|
|
477
|
+
const container = findChildByName(node, containerName);
|
|
478
|
+
for (const propertyNode of container?.children ?? []) {
|
|
479
|
+
if (propertyNode.name !== "P" && propertyNode.name !== "Property") {
|
|
480
|
+
continue;
|
|
481
|
+
}
|
|
482
|
+
const propertyName = getPropertyValue(propertyNode, 0);
|
|
483
|
+
if (propertyName && !known.has(propertyName)) {
|
|
484
|
+
unknown.add(propertyName);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
return Array.from(unknown).sort();
|
|
489
|
+
}
|
|
490
|
+
function degreesToRadians(degrees) {
|
|
491
|
+
return (degrees * Math.PI) / 180;
|
|
492
|
+
}
|
|
493
|
+
function radiansToDegrees(radians) {
|
|
494
|
+
return (radians * 180) / Math.PI;
|
|
495
|
+
}
|
|
496
|
+
//# sourceMappingURL=fbxInterpreter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fbxInterpreter.js","sourceRoot":"","sources":["../../../../../dev/loaders/src/FBX/interpreter/fbxInterpreter.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/naming-convention, jsdoc/require-param, jsdoc/require-returns */\r\nimport { type FBXDocument, type FBXNode, findDocumentNode, findChildByName, getPropertyValue, cleanFBXName } from \"../types/fbxTypes\";\r\n\r\nimport { resolveConnections, getChildren, type FBXObjectMap } from \"./connections\";\r\nimport { extractGeometry, type FBXGeometryData } from \"./geometry\";\r\nimport { extractMaterial, type FBXMaterialData } from \"./materials\";\r\nimport { extractSkins, type FBXSkinData } from \"./skeleton\";\r\nimport { resolveRigs, type FBXRigData } from \"./rig\";\r\nimport { extractAnimations, type FBXAnimationStackData } from \"./animation\";\r\nimport { extractBlendShapes, type FBXBlendShapeData } from \"./blendShapes\";\r\nimport { extractSceneDiagnostics, type FBXSceneDiagnostic } from \"./sceneDiagnostics\";\r\nimport {\r\n extractPropertyTemplates,\r\n getPropertyTemplate,\r\n resolveNumberProperty,\r\n resolvePropertyValue,\r\n resolveVector3Property,\r\n type FBXPropertyTemplate,\r\n type FBXPropertyTemplateMap,\r\n} from \"./propertyTemplates\";\r\n\r\n/** Represents a model (transform node) in the FBX scene */\r\nexport interface FBXModelData {\r\n id: number;\r\n name: string;\r\n subType: string;\r\n /** Geometry attached to this model (if it's a Mesh type) */\r\n geometry?: FBXGeometryData;\r\n /** Materials assigned to this model */\r\n materials: FBXMaterialData[];\r\n /** Child models */\r\n children: FBXModelData[];\r\n /** Transform properties */\r\n translation: [number, number, number];\r\n rotation: [number, number, number];\r\n scale: [number, number, number];\r\n /** PreRotation (applied before Lcl Rotation, in degrees) */\r\n preRotation: [number, number, number];\r\n /** PostRotation (applied after Lcl Rotation, inverted, in degrees) */\r\n postRotation: [number, number, number];\r\n /** RotationPivot — point around which rotation occurs */\r\n rotationPivot: [number, number, number];\r\n /** ScalingPivot — point around which scaling occurs */\r\n scalingPivot: [number, number, number];\r\n /** RotationOffset — translation after rotation pivot */\r\n rotationOffset: [number, number, number];\r\n /** ScalingOffset — translation after scaling pivot */\r\n scalingOffset: [number, number, number];\r\n /** Geometric transforms — applied to geometry only, do not affect children */\r\n geometricTranslation: [number, number, number];\r\n geometricRotation: [number, number, number];\r\n geometricScaling: [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 /** Whether backface culling is disabled (\"CullingOff\") */\r\n cullingOff: boolean;\r\n /** User-defined custom properties from Properties70 */\r\n customProperties?: Record<string, string | number | boolean>;\r\n /** Recoverable model import diagnostics */\r\n diagnostics: string[];\r\n}\r\n\r\n/** Camera data extracted from FBX */\r\nexport interface FBXCameraData {\r\n /** Model ID this camera is attached to */\r\n modelId: number;\r\n /** Camera name */\r\n name: string;\r\n /** Field of view in degrees */\r\n fieldOfView: number;\r\n /** Near clip plane */\r\n nearPlane: number;\r\n /** Far clip plane */\r\n farPlane: number;\r\n /** Aspect ratio (width/height), 0 = use viewport */\r\n aspectRatio: number;\r\n /** Projection type */\r\n projectionType: \"perspective\" | \"orthographic\";\r\n /** Focal length in millimeters when present */\r\n focalLength?: number;\r\n /** Filmback width in inches when present */\r\n filmWidth?: number;\r\n /** Filmback height in inches when present */\r\n filmHeight?: number;\r\n /** Orthographic zoom/height when present */\r\n orthoZoom?: number;\r\n /** Camera roll in degrees when present */\r\n roll?: number;\r\n /** Known unsupported or unrecognized camera properties */\r\n unknownProperties: string[];\r\n /** Recoverable camera import diagnostics */\r\n diagnostics: string[];\r\n}\r\n\r\n/** Light data extracted from FBX */\r\nexport interface FBXLightData {\r\n /** Model ID this light is attached to */\r\n modelId: number;\r\n /** Light name */\r\n name: string;\r\n /** Light type: 0=Point, 1=Directional, 2=Spot */\r\n lightType: number;\r\n /** Color [r,g,b] 0-1 */\r\n color: [number, number, number];\r\n /** Intensity multiplier */\r\n intensity: number;\r\n /** Cone angle in degrees (for spot lights) */\r\n coneAngle: number;\r\n /** Decay type: 0=None, 1=Linear, 2=Quadratic */\r\n decayType: number;\r\n /** Inner cone angle in degrees for spot lights */\r\n innerAngle?: number;\r\n /** Outer cone angle in degrees for spot lights */\r\n outerAngle?: number;\r\n /** Distance at which FBX attenuation starts; preserved as metadata */\r\n decayStart?: number;\r\n /** Whether FBX near attenuation is enabled */\r\n enableNearAttenuation?: boolean;\r\n /** Whether FBX far attenuation is enabled */\r\n enableFarAttenuation?: boolean;\r\n /** Whether the source light requested shadow casting */\r\n castShadows?: boolean;\r\n /** Known unsupported or unrecognized light properties */\r\n unknownProperties: string[];\r\n /** Recoverable light import diagnostics */\r\n diagnostics: string[];\r\n}\r\n\r\n/** Result of interpreting an FBX document */\r\nexport interface FBXSceneData {\r\n /** All root-level models */\r\n rootModels: FBXModelData[];\r\n /** All geometries in the scene */\r\n geometries: FBXGeometryData[];\r\n /** All materials in the scene */\r\n materials: FBXMaterialData[];\r\n /** Skin deformers (skeletons + vertex weights) */\r\n skins: FBXSkinData[];\r\n /** Resolved deformation rigs shared by one or more skins */\r\n rigs: FBXRigData[];\r\n /** Blend shape deformers (morph targets) */\r\n blendShapes: FBXBlendShapeData[];\r\n /** Animation stacks (clips) */\r\n animations: FBXAnimationStackData[];\r\n /** Cameras */\r\n cameras: FBXCameraData[];\r\n /** Lights */\r\n lights: FBXLightData[];\r\n /** Scene-level unsupported feature diagnostics */\r\n diagnostics: FBXSceneDiagnostic[];\r\n /** Global settings */\r\n upAxis: number;\r\n upAxisSign: number;\r\n frontAxis: number;\r\n frontAxisSign: number;\r\n coordAxis: number;\r\n coordAxisSign: number;\r\n unitScaleFactor: number;\r\n}\r\n\r\n/**\r\n * Interpret a parsed FBX document into scene data.\r\n */\r\nexport function interpretFBX(doc: FBXDocument): FBXSceneData {\r\n const objectMap = resolveConnections(doc);\r\n const propertyTemplates = extractPropertyTemplates(doc);\r\n\r\n // Extract global settings\r\n const globalSettings = extractGlobalSettings(doc);\r\n\r\n // Extract all materials\r\n const materials: FBXMaterialData[] = [];\r\n for (const [id, node] of Array.from(objectMap.objects)) {\r\n if (node.name === \"Material\") {\r\n materials.push(extractMaterial(node, id, objectMap, propertyTemplates));\r\n }\r\n }\r\n\r\n // Extract all geometries\r\n const geometries: FBXGeometryData[] = [];\r\n for (const [id, node] of Array.from(objectMap.objects)) {\r\n if (node.name === \"Geometry\") {\r\n const subType = getPropertyValue<string>(node, 2);\r\n if (subType === \"Mesh\") {\r\n geometries.push(extractGeometry(node, id));\r\n }\r\n }\r\n }\r\n\r\n // Extract skeleton/skinning data\r\n const skins = extractSkins(objectMap);\r\n const rigs = resolveRigs(objectMap, skins);\r\n\r\n // Extract blend shape data\r\n const blendShapes = extractBlendShapes(objectMap);\r\n\r\n // Extract animation data\r\n const animations = extractAnimations(objectMap);\r\n\r\n // Extract cameras and lights from NodeAttribute objects\r\n const cameras = extractCameras(objectMap, propertyTemplates);\r\n const lights = extractLights(objectMap, propertyTemplates);\r\n const diagnostics = extractSceneDiagnostics(objectMap);\r\n\r\n // Build model hierarchy\r\n const rootModels = buildModelHierarchy(objectMap, geometries, materials, propertyTemplates);\r\n\r\n return {\r\n rootModels,\r\n geometries,\r\n materials,\r\n skins,\r\n rigs,\r\n blendShapes,\r\n animations,\r\n cameras,\r\n lights,\r\n diagnostics,\r\n ...globalSettings,\r\n };\r\n}\r\n\r\n// ── Model Hierarchy ────────────────────────────────────────────────────────────\r\n\r\nfunction buildModelHierarchy(objectMap: FBXObjectMap, geometries: FBXGeometryData[], materials: FBXMaterialData[], propertyTemplates: FBXPropertyTemplateMap): FBXModelData[] {\r\n const geometryMap = new Map<number, FBXGeometryData>();\r\n for (const g of geometries) {\r\n geometryMap.set(g.id, g);\r\n }\r\n\r\n const materialMap = new Map<number, FBXMaterialData>();\r\n for (const m of materials) {\r\n materialMap.set(m.id, m);\r\n }\r\n\r\n // Find root models (those connected to ID 0, which is the scene root)\r\n const rootChildren = objectMap.childrenOf.get(0) ?? [];\r\n const rootModels: FBXModelData[] = [];\r\n\r\n for (const { id } of rootChildren) {\r\n const node = objectMap.objects.get(id);\r\n if (node && node.name === \"Model\") {\r\n rootModels.push(buildModel(id, node, objectMap, geometryMap, materialMap, propertyTemplates));\r\n }\r\n }\r\n\r\n return rootModels;\r\n}\r\n\r\nfunction buildModel(\r\n modelId: number,\r\n modelNode: FBXNode,\r\n objectMap: FBXObjectMap,\r\n geometryMap: Map<number, FBXGeometryData>,\r\n materialMap: Map<number, FBXMaterialData>,\r\n propertyTemplates: FBXPropertyTemplateMap\r\n): FBXModelData {\r\n const name = cleanFBXName(getPropertyValue<string>(modelNode, 1) ?? \"Model\");\r\n const subType = getPropertyValue<string>(modelNode, 2) ?? \"Null\";\r\n\r\n // Find attached geometry\r\n const geomChildren = getChildren(objectMap, modelId, \"Geometry\");\r\n const geometry = geomChildren.length > 0 ? geometryMap.get(geomChildren[0].id) : undefined;\r\n\r\n // Find attached materials\r\n const matChildren = getChildren(objectMap, modelId, \"Material\");\r\n const modelMaterials: FBXMaterialData[] = [];\r\n for (const { id } of matChildren) {\r\n const mat = materialMap.get(id);\r\n if (mat) {\r\n modelMaterials.push(mat);\r\n }\r\n }\r\n\r\n // Extract transform\r\n const transform = extractTransform(modelNode, getPropertyTemplate(propertyTemplates, \"Model\", \"FbxNode\") ?? getPropertyTemplate(propertyTemplates, \"Model\"));\r\n\r\n // Recursively build child models\r\n const childModelNodes = getChildren(objectMap, modelId, \"Model\");\r\n const children: FBXModelData[] = [];\r\n for (const { id, node } of childModelNodes) {\r\n children.push(buildModel(id, node, objectMap, geometryMap, materialMap, propertyTemplates));\r\n }\r\n\r\n // Extract culling\r\n const cullingNode = modelNode.children.find((c) => c.name === \"Culling\");\r\n const cullingOff = cullingNode ? getPropertyValue<string>(cullingNode, 0) === \"CullingOff\" : false;\r\n\r\n // Extract user-defined custom properties\r\n const customProperties = extractCustomProperties(modelNode);\r\n\r\n return {\r\n id: modelId,\r\n name,\r\n subType,\r\n geometry,\r\n materials: modelMaterials,\r\n children,\r\n cullingOff,\r\n customProperties,\r\n ...transform,\r\n };\r\n}\r\n\r\nfunction extractTransform(\r\n modelNode: FBXNode,\r\n template?: FBXPropertyTemplate\r\n): {\r\n translation: [number, number, number];\r\n rotation: [number, number, number];\r\n scale: [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 geometricTranslation: [number, number, number];\r\n geometricRotation: [number, number, number];\r\n geometricScaling: [number, number, number];\r\n rotationOrder: number;\r\n inheritType: number;\r\n diagnostics: string[];\r\n} {\r\n const translation = resolveVector3Property(modelNode, template, \"Lcl Translation\", [0, 0, 0]);\r\n const rotation = resolveVector3Property(modelNode, template, \"Lcl Rotation\", [0, 0, 0]);\r\n const scale = resolveVector3Property(modelNode, template, \"Lcl Scaling\", [1, 1, 1]);\r\n const preRotation = resolveVector3Property(modelNode, template, \"PreRotation\", [0, 0, 0]);\r\n const postRotation = resolveVector3Property(modelNode, template, \"PostRotation\", [0, 0, 0]);\r\n const rotationPivot = resolveVector3Property(modelNode, template, \"RotationPivot\", [0, 0, 0]);\r\n const scalingPivot = resolveVector3Property(modelNode, template, \"ScalingPivot\", [0, 0, 0]);\r\n const rotationOffset = resolveVector3Property(modelNode, template, \"RotationOffset\", [0, 0, 0]);\r\n const scalingOffset = resolveVector3Property(modelNode, template, \"ScalingOffset\", [0, 0, 0]);\r\n const geometricTranslation = resolveVector3Property(modelNode, template, \"GeometricTranslation\", [0, 0, 0]);\r\n const geometricRotation = resolveVector3Property(modelNode, template, \"GeometricRotation\", [0, 0, 0]);\r\n const geometricScaling = resolveVector3Property(modelNode, template, \"GeometricScaling\", [1, 1, 1]);\r\n const rotationOrder = resolveNumberProperty(modelNode, template, \"RotationOrder\", 0);\r\n const inheritType = resolveNumberProperty(modelNode, template, \"InheritType\", 1);\r\n const diagnostics =\r\n inheritType !== 1 && inheritType !== 2\r\n ? [\r\n `InheritType ${inheritType} is parsed and preserved; runtime parent-scale inheritance remains gated to avoid changing existing visual behavior without a fixture-specific baseline.`,\r\n ]\r\n : [];\r\n\r\n return {\r\n translation,\r\n rotation,\r\n scale,\r\n preRotation,\r\n postRotation,\r\n rotationPivot,\r\n scalingPivot,\r\n rotationOffset,\r\n scalingOffset,\r\n geometricTranslation,\r\n geometricRotation,\r\n geometricScaling,\r\n rotationOrder,\r\n inheritType,\r\n diagnostics,\r\n };\r\n}\r\n\r\n// ── Global Settings ────────────────────────────────────────────────────────────\r\n\r\ninterface GlobalSettings {\r\n upAxis: number;\r\n upAxisSign: number;\r\n frontAxis: number;\r\n frontAxisSign: number;\r\n coordAxis: number;\r\n coordAxisSign: number;\r\n unitScaleFactor: number;\r\n}\r\n\r\nfunction extractGlobalSettings(doc: FBXDocument): GlobalSettings {\r\n const defaults: GlobalSettings = {\r\n upAxis: 1,\r\n upAxisSign: 1,\r\n frontAxis: 2,\r\n frontAxisSign: 1,\r\n coordAxis: 0,\r\n coordAxisSign: 1,\r\n unitScaleFactor: 1,\r\n };\r\n\r\n const gsNode = findDocumentNode(doc, \"GlobalSettings\");\r\n if (!gsNode) {\r\n return defaults;\r\n }\r\n\r\n const props70 = gsNode.children.find((c) => c.name === \"Properties70\");\r\n if (!props70) {\r\n return defaults;\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 const value = toNumber(p.properties[4]?.value);\r\n if (propName && value !== undefined) {\r\n switch (propName) {\r\n case \"UpAxis\":\r\n defaults.upAxis = value;\r\n break;\r\n case \"UpAxisSign\":\r\n defaults.upAxisSign = value;\r\n break;\r\n case \"FrontAxis\":\r\n defaults.frontAxis = value;\r\n break;\r\n case \"FrontAxisSign\":\r\n defaults.frontAxisSign = value;\r\n break;\r\n case \"CoordAxis\":\r\n defaults.coordAxis = value;\r\n break;\r\n case \"CoordAxisSign\":\r\n defaults.coordAxisSign = value;\r\n break;\r\n case \"UnitScaleFactor\":\r\n defaults.unitScaleFactor = value;\r\n break;\r\n }\r\n }\r\n }\r\n\r\n return defaults;\r\n}\r\n\r\n// ── Cameras & Lights ──────────────────────────────────────────────────────────\r\n\r\nconst SYSTEM_PROPERTIES = new Set([\r\n \"Lcl Translation\",\r\n \"Lcl Rotation\",\r\n \"Lcl Scaling\",\r\n \"PreRotation\",\r\n \"PostRotation\",\r\n \"RotationPivot\",\r\n \"ScalingPivot\",\r\n \"RotationOffset\",\r\n \"ScalingOffset\",\r\n \"RotationOrder\",\r\n \"GeometricTranslation\",\r\n \"GeometricRotation\",\r\n \"GeometricScaling\",\r\n \"Visibility\",\r\n \"InheritType\",\r\n \"ScalingMax\",\r\n \"DefaultAttributeIndex\",\r\n \"currentUVSet\",\r\n \"lockInfluenceWeights\",\r\n]);\r\n\r\nfunction extractCustomProperties(modelNode: FBXNode): Record<string, string | number | boolean> | undefined {\r\n const props70 = findChildByName(modelNode, \"Properties70\");\r\n if (!props70) {\r\n return undefined;\r\n }\r\n\r\n const custom: Record<string, string | number | boolean> = {};\r\n let hasAny = false;\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 || SYSTEM_PROPERTIES.has(propName)) {\r\n continue;\r\n }\r\n\r\n // Accept user-defined properties (type starts with something other than standard types)\r\n // Standard FBX types: \"KString\", \"Number\", \"double\", \"int\", \"bool\", \"Lcl\"...\r\n // User properties often have types like \"KString\", but are in the UDP (User Defined Properties) section\r\n // Heuristic: if not in SYSTEM_PROPERTIES set, it's user-defined\r\n const val = p.properties[4]?.value;\r\n if (val === undefined) {\r\n continue;\r\n }\r\n\r\n if (typeof val === \"string\") {\r\n custom[propName] = val;\r\n hasAny = true;\r\n } else if (typeof val === \"number\") {\r\n custom[propName] = val;\r\n hasAny = true;\r\n } else if (typeof val === \"boolean\") {\r\n custom[propName] = val;\r\n hasAny = true;\r\n }\r\n }\r\n\r\n return hasAny ? custom : undefined;\r\n}\r\n\r\nconst CAMERA_PROPERTIES = new Set([\r\n \"FieldOfView\",\r\n \"FieldOfViewX\",\r\n \"FieldOfViewY\",\r\n \"NearPlane\",\r\n \"FarPlane\",\r\n \"AspectWidth\",\r\n \"AspectHeight\",\r\n \"FilmAspectRatio\",\r\n \"FocalLength\",\r\n \"FilmWidth\",\r\n \"FilmHeight\",\r\n \"ApertureWidth\",\r\n \"ApertureHeight\",\r\n \"CameraProjectionType\",\r\n \"ProjectionType\",\r\n \"OrthoZoom\",\r\n \"Roll\",\r\n \"ApertureMode\",\r\n]);\r\n\r\nconst LIGHT_PROPERTIES = new Set([\r\n \"LightType\",\r\n \"Color\",\r\n \"Intensity\",\r\n \"InnerAngle\",\r\n \"OuterAngle\",\r\n \"ConeAngle\",\r\n \"DecayType\",\r\n \"DecayStart\",\r\n \"EnableNearAttenuation\",\r\n \"EnableFarAttenuation\",\r\n \"CastShadow\",\r\n \"Shadow\",\r\n]);\r\n\r\nfunction extractCameras(objectMap: FBXObjectMap, templates: FBXPropertyTemplateMap): FBXCameraData[] {\r\n const cameras: FBXCameraData[] = [];\r\n const cameraTemplate = getPropertyTemplate(templates, \"NodeAttribute\", \"FbxCamera\") ?? getPropertyTemplate(templates, \"NodeAttribute\");\r\n\r\n for (const [id, node] of Array.from(objectMap.objects)) {\r\n if (node.name !== \"NodeAttribute\") {\r\n continue;\r\n }\r\n const subType = getPropertyValue<string>(node, 2);\r\n if (subType !== \"Camera\") {\r\n continue;\r\n }\r\n\r\n // Find the model this camera is attached to (parent)\r\n const parent = objectMap.parentOf.get(id);\r\n if (!parent) {\r\n continue;\r\n }\r\n const parentNode = objectMap.objects.get(parent.id);\r\n if (!parentNode || parentNode.name !== \"Model\") {\r\n continue;\r\n }\r\n\r\n const name = cleanFBXName(getPropertyValue<string>(parentNode, 1) ?? \"Camera\");\r\n\r\n const nearPlane = resolveNumberProperty(node, cameraTemplate, \"NearPlane\", 0.1);\r\n const farPlane = resolveNumberProperty(node, cameraTemplate, \"FarPlane\", 10000);\r\n const aspectRatio = resolveCameraAspectRatio(node, cameraTemplate);\r\n const projectionType =\r\n resolveNumberProperty(node, cameraTemplate, \"CameraProjectionType\", 0) === 1 || resolveNumberProperty(node, cameraTemplate, \"ProjectionType\", 0) === 1\r\n ? \"orthographic\"\r\n : \"perspective\";\r\n const focalLength = toNumber(resolvePropertyValue(node, cameraTemplate, \"FocalLength\"));\r\n const filmWidth = toNumber(resolvePropertyValue(node, cameraTemplate, \"FilmWidth\")) ?? toNumber(resolvePropertyValue(node, cameraTemplate, \"ApertureWidth\"));\r\n const filmHeight = toNumber(resolvePropertyValue(node, cameraTemplate, \"FilmHeight\")) ?? toNumber(resolvePropertyValue(node, cameraTemplate, \"ApertureHeight\"));\r\n const orthoZoom = toNumber(resolvePropertyValue(node, cameraTemplate, \"OrthoZoom\"));\r\n const roll = toNumber(resolvePropertyValue(node, cameraTemplate, \"Roll\"));\r\n const fieldOfView = resolveCameraFieldOfView(node, cameraTemplate, aspectRatio, focalLength, filmHeight);\r\n const diagnostics: string[] = [];\r\n if (projectionType === \"orthographic\" && orthoZoom === undefined) {\r\n diagnostics.push(\"Orthographic camera has no OrthoZoom; runtime orthographic bounds use a fallback.\");\r\n }\r\n if (focalLength !== undefined && filmHeight === undefined && resolvePropertyValue(node, cameraTemplate, \"FieldOfView\") === undefined) {\r\n diagnostics.push(\"FocalLength is present without FilmHeight; default field of view fallback may be used.\");\r\n }\r\n\r\n cameras.push({\r\n modelId: parent.id,\r\n name,\r\n fieldOfView,\r\n nearPlane,\r\n farPlane,\r\n aspectRatio,\r\n projectionType,\r\n focalLength,\r\n filmWidth,\r\n filmHeight,\r\n orthoZoom,\r\n roll,\r\n unknownProperties: collectUnknownLocalProperties(node, CAMERA_PROPERTIES),\r\n diagnostics,\r\n });\r\n }\r\n\r\n return cameras;\r\n}\r\n\r\nfunction extractLights(objectMap: FBXObjectMap, templates: FBXPropertyTemplateMap): FBXLightData[] {\r\n const lights: FBXLightData[] = [];\r\n const lightTemplate = getPropertyTemplate(templates, \"NodeAttribute\", \"FbxLight\") ?? getPropertyTemplate(templates, \"NodeAttribute\");\r\n\r\n for (const [id, node] of Array.from(objectMap.objects)) {\r\n if (node.name !== \"NodeAttribute\") {\r\n continue;\r\n }\r\n const subType = getPropertyValue<string>(node, 2);\r\n if (subType !== \"Light\") {\r\n continue;\r\n }\r\n\r\n // Find the model this light is attached to\r\n const parent = objectMap.parentOf.get(id);\r\n if (!parent) {\r\n continue;\r\n }\r\n const parentNode = objectMap.objects.get(parent.id);\r\n if (!parentNode || parentNode.name !== \"Model\") {\r\n continue;\r\n }\r\n\r\n const name = cleanFBXName(getPropertyValue<string>(parentNode, 1) ?? \"Light\");\r\n\r\n const lightType = resolveNumberProperty(node, lightTemplate, \"LightType\", 0);\r\n const color = resolveVector3Property(node, lightTemplate, \"Color\", [1, 1, 1]);\r\n const intensity = resolveNumberProperty(node, lightTemplate, \"Intensity\", 100) / 100;\r\n const outerAngle = toNumber(resolvePropertyValue(node, lightTemplate, \"OuterAngle\")) ?? toNumber(resolvePropertyValue(node, lightTemplate, \"ConeAngle\"));\r\n const innerAngle = toNumber(resolvePropertyValue(node, lightTemplate, \"InnerAngle\"));\r\n const coneAngle = outerAngle ?? 45;\r\n const decayType = resolveNumberProperty(node, lightTemplate, \"DecayType\", 2);\r\n const decayStart = toNumber(resolvePropertyValue(node, lightTemplate, \"DecayStart\"));\r\n const enableNearAttenuation = toBoolean(resolvePropertyValue(node, lightTemplate, \"EnableNearAttenuation\"));\r\n const enableFarAttenuation = toBoolean(resolvePropertyValue(node, lightTemplate, \"EnableFarAttenuation\"));\r\n const castShadows =\r\n toBoolean(resolvePropertyValue(node, lightTemplate, \"CastShadow\")) ??\r\n toBoolean(resolvePropertyValue(parentNode, undefined, \"CastShadow\")) ??\r\n toBoolean(resolvePropertyValue(parentNode, undefined, \"Shadow\"));\r\n const diagnostics: string[] = [];\r\n if (decayType !== 2) {\r\n diagnostics.push(`DecayType ${decayType} is preserved as metadata; Babylon falloff is not remapped in this pass.`);\r\n }\r\n if (decayStart !== undefined) {\r\n diagnostics.push(\"DecayStart is preserved as metadata and is not mapped to Babylon light range.\");\r\n }\r\n\r\n lights.push({\r\n modelId: parent.id,\r\n name,\r\n lightType,\r\n color,\r\n intensity,\r\n coneAngle,\r\n decayType,\r\n innerAngle,\r\n outerAngle,\r\n decayStart,\r\n enableNearAttenuation,\r\n enableFarAttenuation,\r\n castShadows,\r\n unknownProperties: collectUnknownLocalProperties(node, LIGHT_PROPERTIES),\r\n diagnostics,\r\n });\r\n }\r\n\r\n return lights;\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 toBoolean(value: unknown): boolean | undefined {\r\n if (typeof value === \"boolean\") {\r\n return value;\r\n }\r\n if (typeof value === \"number\") {\r\n return value !== 0;\r\n }\r\n return undefined;\r\n}\r\n\r\nfunction resolveCameraAspectRatio(node: FBXNode, template?: FBXPropertyTemplate): number {\r\n const filmAspectRatio = toNumber(resolvePropertyValue(node, template, \"FilmAspectRatio\"));\r\n if (filmAspectRatio !== undefined && filmAspectRatio > 0) {\r\n return filmAspectRatio;\r\n }\r\n\r\n const aspectWidth = toNumber(resolvePropertyValue(node, template, \"AspectWidth\"));\r\n const aspectHeight = toNumber(resolvePropertyValue(node, template, \"AspectHeight\"));\r\n if (aspectWidth !== undefined && aspectHeight !== undefined && aspectWidth > 0 && aspectHeight > 0) {\r\n return aspectWidth / aspectHeight;\r\n }\r\n\r\n return 0;\r\n}\r\n\r\nfunction resolveCameraFieldOfView(\r\n node: FBXNode,\r\n template: FBXPropertyTemplate | undefined,\r\n aspectRatio: number,\r\n focalLength: number | undefined,\r\n filmHeight: number | undefined\r\n): number {\r\n const verticalFov = toNumber(resolvePropertyValue(node, template, \"FieldOfViewY\")) ?? toNumber(resolvePropertyValue(node, template, \"FieldOfView\"));\r\n if (verticalFov !== undefined) {\r\n return verticalFov;\r\n }\r\n\r\n const horizontalFov = toNumber(resolvePropertyValue(node, template, \"FieldOfViewX\"));\r\n if (horizontalFov !== undefined) {\r\n if (aspectRatio > 0) {\r\n return radiansToDegrees(2 * Math.atan(Math.tan(degreesToRadians(horizontalFov) / 2) / aspectRatio));\r\n }\r\n return horizontalFov;\r\n }\r\n\r\n if (focalLength !== undefined && focalLength > 0 && filmHeight !== undefined && filmHeight > 0) {\r\n return radiansToDegrees(2 * Math.atan((filmHeight * 25.4) / (2 * focalLength)));\r\n }\r\n\r\n return 45;\r\n}\r\n\r\nfunction collectUnknownLocalProperties(node: FBXNode, known: Set<string>): string[] {\r\n const unknown = new Set<string>();\r\n for (const containerName of [\"Properties70\", \"Properties60\"]) {\r\n const container = findChildByName(node, containerName);\r\n for (const propertyNode of container?.children ?? []) {\r\n if (propertyNode.name !== \"P\" && propertyNode.name !== \"Property\") {\r\n continue;\r\n }\r\n const propertyName = getPropertyValue<string>(propertyNode, 0);\r\n if (propertyName && !known.has(propertyName)) {\r\n unknown.add(propertyName);\r\n }\r\n }\r\n }\r\n return Array.from(unknown).sort();\r\n}\r\n\r\nfunction degreesToRadians(degrees: number): number {\r\n return (degrees * Math.PI) / 180;\r\n}\r\n\r\nfunction radiansToDegrees(radians: number): number {\r\n return (radians * 180) / Math.PI;\r\n}\r\n"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { type FBXNode } from "../types/fbxTypes.js";
|
|
2
|
+
/** A named UV set */
|
|
3
|
+
export interface FBXUVSet {
|
|
4
|
+
/** UV set name (e.g. "UVMap", "lightmap") */
|
|
5
|
+
name: string;
|
|
6
|
+
/** Per-vertex UV data [u,v, ...] (expanded to match triangle vertices) */
|
|
7
|
+
data: Float64Array;
|
|
8
|
+
}
|
|
9
|
+
/** Recoverable geometry import issue. */
|
|
10
|
+
export interface FBXGeometryDiagnostic {
|
|
11
|
+
/** Diagnostic category. */
|
|
12
|
+
type: "degenerate-polygon" | "triangulation-fallback" | "layer-index-out-of-bounds" | "layer-data-too-short";
|
|
13
|
+
/** Human-readable diagnostic message. */
|
|
14
|
+
message: string;
|
|
15
|
+
/** Polygon index associated with the diagnostic, if applicable. */
|
|
16
|
+
polygonIndex?: number;
|
|
17
|
+
/** Layer element name associated with the diagnostic, if applicable. */
|
|
18
|
+
layerName?: string;
|
|
19
|
+
/** Source data index associated with the diagnostic, if applicable. */
|
|
20
|
+
index?: number;
|
|
21
|
+
}
|
|
22
|
+
/** Parsed geometry data ready for Babylon consumption */
|
|
23
|
+
export interface FBXGeometryData {
|
|
24
|
+
/** Node ID from the FBX document */
|
|
25
|
+
id: number;
|
|
26
|
+
/** Geometry name */
|
|
27
|
+
name: string;
|
|
28
|
+
/** Flat array of vertex positions [x,y,z, x,y,z, ...] */
|
|
29
|
+
positions: Float64Array;
|
|
30
|
+
/** Triangle indices (already triangulated from n-gons) */
|
|
31
|
+
indices: Uint32Array;
|
|
32
|
+
/** Per-vertex normals [x,y,z, ...] (expanded to match triangle vertices) */
|
|
33
|
+
normals: Float64Array | null;
|
|
34
|
+
/** Per-vertex UVs [u,v, ...] (expanded to match triangle vertices) — first UV set for convenience */
|
|
35
|
+
uvs: Float64Array | null;
|
|
36
|
+
/** All UV sets (including the first) */
|
|
37
|
+
uvSets: FBXUVSet[];
|
|
38
|
+
/** Per-vertex colors [r,g,b,a, ...] (expanded to match triangle vertices) */
|
|
39
|
+
colors: Float32Array | null;
|
|
40
|
+
/** Per-vertex tangents [x,y,z,w, ...] expanded to match triangle vertices */
|
|
41
|
+
tangents: Float64Array | null;
|
|
42
|
+
/** Per-vertex binormals [x,y,z, ...] expanded to match triangle vertices */
|
|
43
|
+
binormals: Float64Array | null;
|
|
44
|
+
/** Control point index for each polygon-vertex (for skinning lookup) */
|
|
45
|
+
controlPointIndices: Uint32Array | null;
|
|
46
|
+
/** Per-triangle material index (which material each triangle belongs to) */
|
|
47
|
+
materialIndices: Int32Array | null;
|
|
48
|
+
/** Recoverable geometry import issues */
|
|
49
|
+
diagnostics: FBXGeometryDiagnostic[];
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extract geometry data from an FBX Geometry node.
|
|
53
|
+
* Handles polygon triangulation and layer element expansion.
|
|
54
|
+
*/
|
|
55
|
+
export declare function extractGeometry(geometryNode: FBXNode, nodeId: number): FBXGeometryData;
|