@babylonjs/loaders 9.26.0 → 9.26.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/fbxConstraintBehavior.d.ts +188 -0
- package/FBX/fbxConstraintBehavior.js +532 -0
- package/FBX/fbxConstraintBehavior.js.map +1 -0
- package/FBX/fbxFileLoader.pure.d.ts +175 -6
- package/FBX/fbxFileLoader.pure.js +1521 -286
- package/FBX/fbxFileLoader.pure.js.map +1 -1
- package/FBX/index.d.ts +4 -1
- package/FBX/index.js +1 -0
- package/FBX/index.js.map +1 -1
- package/FBX/interpreter/animation.d.ts +73 -33
- package/FBX/interpreter/animation.js +620 -223
- package/FBX/interpreter/animation.js.map +1 -1
- package/FBX/interpreter/animationCurve.d.ts +91 -0
- package/FBX/interpreter/animationCurve.js +623 -0
- package/FBX/interpreter/animationCurve.js.map +1 -0
- package/FBX/interpreter/blendShapes.js +9 -18
- package/FBX/interpreter/blendShapes.js.map +1 -1
- package/FBX/interpreter/connections.d.ts +1 -1
- package/FBX/interpreter/connections.js +173 -34
- package/FBX/interpreter/connections.js.map +1 -1
- package/FBX/interpreter/constraints.d.ts +68 -0
- package/FBX/interpreter/constraints.js +143 -0
- package/FBX/interpreter/constraints.js.map +1 -0
- package/FBX/interpreter/fbxInterpreter.d.ts +89 -4
- package/FBX/interpreter/fbxInterpreter.js +474 -122
- package/FBX/interpreter/fbxInterpreter.js.map +1 -1
- package/FBX/interpreter/geometry.d.ts +1 -1
- package/FBX/interpreter/geometry.js +150 -92
- package/FBX/interpreter/geometry.js.map +1 -1
- package/FBX/interpreter/legacyDocument.d.ts +11 -0
- package/FBX/interpreter/legacyDocument.js +425 -0
- package/FBX/interpreter/legacyDocument.js.map +1 -0
- package/FBX/interpreter/materialModel.d.ts +71 -0
- package/FBX/interpreter/materialModel.js +622 -0
- package/FBX/interpreter/materialModel.js.map +1 -0
- package/FBX/interpreter/materials.d.ts +13 -1
- package/FBX/interpreter/materials.js +88 -5
- package/FBX/interpreter/materials.js.map +1 -1
- package/FBX/interpreter/nodeTransform.d.ts +32 -0
- package/FBX/interpreter/nodeTransform.js +54 -0
- package/FBX/interpreter/nodeTransform.js.map +1 -0
- package/FBX/interpreter/nurbs.d.ts +99 -0
- package/FBX/interpreter/nurbs.js +627 -0
- package/FBX/interpreter/nurbs.js.map +1 -0
- package/FBX/interpreter/propertyTemplates.d.ts +23 -0
- package/FBX/interpreter/propertyTemplates.js +109 -2
- package/FBX/interpreter/propertyTemplates.js.map +1 -1
- package/FBX/interpreter/rig.js +16 -0
- package/FBX/interpreter/rig.js.map +1 -1
- package/FBX/interpreter/sceneDiagnostics.js +28 -3
- package/FBX/interpreter/sceneDiagnostics.js.map +1 -1
- package/FBX/interpreter/skeleton.d.ts +4 -18
- package/FBX/interpreter/skeleton.js +16 -94
- package/FBX/interpreter/skeleton.js.map +1 -1
- package/FBX/parsers/fbxAsciiParser.js +119 -9
- package/FBX/parsers/fbxAsciiParser.js.map +1 -1
- package/FBX/parsers/fbxBinaryParser.js +74 -22
- package/FBX/parsers/fbxBinaryParser.js.map +1 -1
- package/FBX/pure.d.ts +1 -0
- package/FBX/pure.js +1 -0
- package/FBX/pure.js.map +1 -1
- package/FBX/types/fbxTypes.d.ts +18 -2
- package/FBX/types/fbxTypes.js +40 -12
- package/FBX/types/fbxTypes.js.map +1 -1
- package/USD/usdCommandProtocol.d.ts +20 -4
- package/USD/usdCommandProtocol.js +25 -3
- package/USD/usdCommandProtocol.js.map +1 -1
- package/USD/usdFileLoader.pure.js +2 -0
- package/USD/usdFileLoader.pure.js.map +1 -1
- package/USD/usdSceneMaterializer.js +430 -92
- package/USD/usdSceneMaterializer.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention, jsdoc/require-param, jsdoc/require-returns */
|
|
2
|
+
/**
|
|
3
|
+
* FBX 5.x (and older) documents store everything at the top level: one `Model` node per object with inline
|
|
4
|
+
* geometry, materials, skin links, lights and cameras, and `Children` lists instead of connections. This module
|
|
5
|
+
* rewrites such a document into the 6.x layout (`Objects`, `Connections`, `Takes`) so the rest of the importer
|
|
6
|
+
* can treat both the same way. The rules follow the FBX SDK's legacy reader as implemented by ufbx.
|
|
7
|
+
*/
|
|
8
|
+
import { findChildByName, findDocumentNode, getPropertyValue, getNodeArray, cleanFBXName } from "../types/fbxTypes.js";
|
|
9
|
+
const str = (value) => ({ type: "string", value });
|
|
10
|
+
const num = (value) => ({ type: "float64", value });
|
|
11
|
+
const int = (value) => ({ type: "int32", value });
|
|
12
|
+
const makeNode = (name, properties = [], children = []) => ({ name, properties, children });
|
|
13
|
+
function property60(name, typeName, flags, ...values) {
|
|
14
|
+
return makeNode("Property", [str(name), str(typeName), str(flags), ...values.map((v) => (typeof v === "string" ? str(v) : num(v)))]);
|
|
15
|
+
}
|
|
16
|
+
function property70(name, typeName, flags, ...values) {
|
|
17
|
+
return makeNode("P", [str(name), str(typeName), str(""), str(flags), ...values.map((v) => (typeof v === "string" ? str(v) : num(v)))]);
|
|
18
|
+
}
|
|
19
|
+
const arrayNode = (name, array) => makeNode(name, [{ type: array instanceof Int32Array ? "int32[]" : "float64[]", value: array }]);
|
|
20
|
+
/** True for pre-6000 files: no `Objects` section but top-level `Model` nodes. */
|
|
21
|
+
export function isLegacyDocument(doc) {
|
|
22
|
+
return !findDocumentNode(doc, "Objects") && doc.nodes.some((n) => n.name === "Model");
|
|
23
|
+
}
|
|
24
|
+
function numberAt(node, index = 0) {
|
|
25
|
+
const v = node?.properties[index]?.value;
|
|
26
|
+
return typeof v === "number" ? v : typeof v === "boolean" ? (v ? 1 : 0) : undefined;
|
|
27
|
+
}
|
|
28
|
+
function numbers(node) {
|
|
29
|
+
if (!node) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const array = getNodeArray(node);
|
|
33
|
+
return array ? Array.from(array) : [];
|
|
34
|
+
}
|
|
35
|
+
function toFloat64(node) {
|
|
36
|
+
const array = node ? getNodeArray(node) : null;
|
|
37
|
+
if (!array) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return array instanceof Float64Array ? array : Float64Array.from(array);
|
|
41
|
+
}
|
|
42
|
+
function toInt32(node) {
|
|
43
|
+
const array = node ? getNodeArray(node) : null;
|
|
44
|
+
if (!array) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
return array instanceof Int32Array ? array : Int32Array.from(array, (v) => Math.trunc(v));
|
|
48
|
+
}
|
|
49
|
+
/** Default (rest) value of a `Channel` tree: T/R/S each with X/Y/Z sub channels carrying `Default`. */
|
|
50
|
+
function readTransformChannelDefaults(transformChannel, out) {
|
|
51
|
+
if (!transformChannel) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const sub of transformChannel.children) {
|
|
55
|
+
if (sub.name !== "Channel") {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const kind = getPropertyValue(sub, 0);
|
|
59
|
+
const key = kind === "T" ? "t" : kind === "R" ? "r" : kind === "S" ? "s" : undefined;
|
|
60
|
+
if (!key || out[key]) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const values = [0, 0, 0];
|
|
64
|
+
let any = false;
|
|
65
|
+
for (const axis of sub.children) {
|
|
66
|
+
if (axis.name !== "Channel") {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const axisName = getPropertyValue(axis, 0);
|
|
70
|
+
const index = axisName === "X" ? 0 : axisName === "Y" ? 1 : axisName === "Z" ? 2 : -1;
|
|
71
|
+
const value = numberAt(findChildByName(axis, "Default"));
|
|
72
|
+
if (index >= 0 && value !== undefined) {
|
|
73
|
+
values[index] = value;
|
|
74
|
+
any = true;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (any) {
|
|
78
|
+
out[key] = values;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function channelHasKeys(channel) {
|
|
83
|
+
const keys = findChildByName(channel, "Key");
|
|
84
|
+
if (keys && keys.properties.length > 0) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
return channel.children.some((c) => c.name === "Channel" && channelHasKeys(c));
|
|
88
|
+
}
|
|
89
|
+
function uniqueName(ctx, name) {
|
|
90
|
+
const count = ctx.usedNames.get(name) ?? 0;
|
|
91
|
+
ctx.usedNames.set(name, count + 1);
|
|
92
|
+
return count === 0 ? name : `${name} (${count + 1})`;
|
|
93
|
+
}
|
|
94
|
+
function connect(ctx, child, parent) {
|
|
95
|
+
ctx.connections.push(makeNode("C", [str("OO"), str(child), str(parent)]));
|
|
96
|
+
}
|
|
97
|
+
function normalize3(array) {
|
|
98
|
+
const out = new Float64Array(array.length);
|
|
99
|
+
for (let i = 0; i + 2 < array.length; i += 3) {
|
|
100
|
+
const x = array[i], y = array[i + 1], z = array[i + 2];
|
|
101
|
+
const len = Math.hypot(x, y, z);
|
|
102
|
+
if (len > 0) {
|
|
103
|
+
out[i] = x / len;
|
|
104
|
+
out[i + 1] = y / len;
|
|
105
|
+
out[i + 2] = z / len;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
/** Converts the inline polygon mesh of a legacy model into 6.x-style geometry children. */
|
|
111
|
+
function convertMesh(model, version) {
|
|
112
|
+
const vertices = toFloat64(findChildByName(model, "Vertices"));
|
|
113
|
+
const indices = toInt32(findChildByName(model, "PolygonVertexIndex"));
|
|
114
|
+
if (!vertices || !indices) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
const numVertices = Math.floor(vertices.length / 3);
|
|
118
|
+
const numIndices = indices.length;
|
|
119
|
+
if (numIndices > 0 && indices[numIndices - 1] >= 0) {
|
|
120
|
+
indices[numIndices - 1] = ~indices[numIndices - 1];
|
|
121
|
+
}
|
|
122
|
+
const out = [makeNode("GeometryVersion", [int(124)]), arrayNode("Vertices", vertices), arrayNode("PolygonVertexIndex", indices)];
|
|
123
|
+
// Normals are per vertex or per polygon-vertex, decided by their count (5000 files prefer per vertex).
|
|
124
|
+
const normals = toFloat64(findChildByName(model, "Normals"));
|
|
125
|
+
if (normals) {
|
|
126
|
+
const count = Math.floor(normals.length / 3);
|
|
127
|
+
const perVertex = count === numVertices;
|
|
128
|
+
const perIndex = count === numIndices;
|
|
129
|
+
const mapping = perVertex && (!perIndex || version === 5000) ? "ByVertice" : perIndex ? "ByPolygonVertex" : undefined;
|
|
130
|
+
if (mapping) {
|
|
131
|
+
out.push(makeNode("LayerElementNormal", [int(0)], [
|
|
132
|
+
makeNode("Version", [int(101)]),
|
|
133
|
+
makeNode("Name", [str("")]),
|
|
134
|
+
makeNode("MappingInformationType", [str(mapping)]),
|
|
135
|
+
makeNode("ReferenceInformationType", [str("Direct")]),
|
|
136
|
+
arrayNode("Normals", normalize3(normals)),
|
|
137
|
+
]));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const uvInfo = findChildByName(model, "GeometryUVInfo");
|
|
141
|
+
const uv = uvInfo ? toFloat64(findChildByName(uvInfo, "TextureUV")) : null;
|
|
142
|
+
if (uvInfo && uv) {
|
|
143
|
+
const uvIndex = toInt32(findChildByName(uvInfo, "TextureUVVerticeIndex"));
|
|
144
|
+
const numUV = Math.floor(uv.length / 2);
|
|
145
|
+
let element;
|
|
146
|
+
const base = [makeNode("Version", [int(101)]), makeNode("Name", [str("")])];
|
|
147
|
+
if (uvIndex && uvIndex.length === numIndices) {
|
|
148
|
+
element = makeNode("LayerElementUV", [int(0)], [
|
|
149
|
+
...base,
|
|
150
|
+
makeNode("MappingInformationType", [str("ByPolygonVertex")]),
|
|
151
|
+
makeNode("ReferenceInformationType", [str("IndexToDirect")]),
|
|
152
|
+
arrayNode("UV", uv),
|
|
153
|
+
arrayNode("UVIndex", uvIndex),
|
|
154
|
+
]);
|
|
155
|
+
}
|
|
156
|
+
else if (numUV === numIndices) {
|
|
157
|
+
element = makeNode("LayerElementUV", [int(0)], [...base, makeNode("MappingInformationType", [str("ByPolygonVertex")]), makeNode("ReferenceInformationType", [str("Direct")]), arrayNode("UV", uv)]);
|
|
158
|
+
}
|
|
159
|
+
else if (numUV === numVertices) {
|
|
160
|
+
element = makeNode("LayerElementUV", [int(0)], [...base, makeNode("MappingInformationType", [str("ByVertice")]), makeNode("ReferenceInformationType", [str("Direct")]), arrayNode("UV", uv)]);
|
|
161
|
+
}
|
|
162
|
+
if (element) {
|
|
163
|
+
out.push(element);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const assignation = getPropertyValue(findChildByName(model, "MaterialAssignation") ?? makeNode("x"), 0);
|
|
167
|
+
const materials = toInt32(findChildByName(model, "Materials"));
|
|
168
|
+
if (materials && materials.length > 0) {
|
|
169
|
+
const mapping = assignation === "ByPolygon" ? "ByPolygon" : "AllSame";
|
|
170
|
+
out.push(makeNode("LayerElementMaterial", [int(0)], [
|
|
171
|
+
makeNode("Version", [int(101)]),
|
|
172
|
+
makeNode("Name", [str("")]),
|
|
173
|
+
makeNode("MappingInformationType", [str(mapping)]),
|
|
174
|
+
makeNode("ReferenceInformationType", [str("IndexToDirect")]),
|
|
175
|
+
arrayNode("Materials", mapping === "AllSame" ? Int32Array.of(materials[0]) : materials),
|
|
176
|
+
]));
|
|
177
|
+
}
|
|
178
|
+
// Blend shapes keep the 6.x inline form; the connection resolver turns them into deformers.
|
|
179
|
+
for (const child of model.children) {
|
|
180
|
+
if (child.name === "Shape") {
|
|
181
|
+
out.push(child);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return out;
|
|
185
|
+
}
|
|
186
|
+
/** Legacy materials are embedded in the model; each becomes its own object connected to the model. */
|
|
187
|
+
function convertMaterials(ctx, model, modelName) {
|
|
188
|
+
for (const child of model.children) {
|
|
189
|
+
if (child.name !== "Material") {
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const rawName = getPropertyValue(child, 0) ?? "Material";
|
|
193
|
+
const name = uniqueName(ctx, rawName.includes("::") ? rawName : `Material::${rawName}`);
|
|
194
|
+
const color = (source, target) => {
|
|
195
|
+
const values = numbers(findChildByName(child, source));
|
|
196
|
+
return values.length >= 3 ? property60(target, "ColorRGB", "", values[0], values[1], values[2]) : null;
|
|
197
|
+
};
|
|
198
|
+
const scalar = (source, target) => {
|
|
199
|
+
const value = numberAt(findChildByName(child, source));
|
|
200
|
+
return value !== undefined ? property60(target, "double", "", value) : null;
|
|
201
|
+
};
|
|
202
|
+
const properties = [
|
|
203
|
+
color("Ambient", "AmbientColor"),
|
|
204
|
+
color("Diffuse", "DiffuseColor"),
|
|
205
|
+
color("Specular", "SpecularColor"),
|
|
206
|
+
color("Emissive", "EmissiveColor"),
|
|
207
|
+
scalar("Shininess", "Shininess"),
|
|
208
|
+
scalar("Alpha", "Opacity"),
|
|
209
|
+
scalar("Reflectivity", "ReflectionFactor"),
|
|
210
|
+
].filter((p) => p !== null);
|
|
211
|
+
const shading = findChildByName(child, "ShadingModel");
|
|
212
|
+
ctx.objects.push(makeNode("Material", [str(name), str("")], [makeNode("Version", [int(100)]), shading ?? makeNode("ShadingModel", [str("phong")]), makeNode("Properties60", [], properties)]));
|
|
213
|
+
connect(ctx, name, modelName);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/** `Link` children are skin clusters bound to other models. */
|
|
217
|
+
function convertLinks(ctx, model, modelName) {
|
|
218
|
+
const links = model.children.filter((c) => c.name === "Link");
|
|
219
|
+
if (links.length === 0) {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
const skinName = uniqueName(ctx, `Deformer::Skin ${cleanFBXName(modelName)}`);
|
|
223
|
+
ctx.objects.push(makeNode("Deformer", [str(skinName), str("Skin")], [makeNode("Version", [int(100)]), makeNode("Link_DeformAcuracy", [num(50)])]));
|
|
224
|
+
connect(ctx, skinName, modelName);
|
|
225
|
+
for (const link of links) {
|
|
226
|
+
const boneName = getPropertyValue(link, 0) ?? "";
|
|
227
|
+
const clusterName = uniqueName(ctx, `SubDeformer::Cluster ${cleanFBXName(modelName)} ${cleanFBXName(boneName)}`);
|
|
228
|
+
const children = [makeNode("Version", [int(100)]), makeNode("Mode", [str("Normalize")])];
|
|
229
|
+
for (const arrayName of ["Indexes", "Weights", "Transform", "TransformLink"]) {
|
|
230
|
+
const child = findChildByName(link, arrayName);
|
|
231
|
+
if (!child) {
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const array = arrayName === "Indexes" ? toInt32(child) : toFloat64(child);
|
|
235
|
+
if (array) {
|
|
236
|
+
children.push(arrayNode(arrayName, array));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
ctx.objects.push(makeNode("Deformer", [str(clusterName), str("Cluster")], children));
|
|
240
|
+
connect(ctx, clusterName, skinName);
|
|
241
|
+
connect(ctx, boneName, clusterName);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
function lightProperties(model) {
|
|
245
|
+
const out = [];
|
|
246
|
+
const lightType = numberAt(findChildByName(model, "LightType"));
|
|
247
|
+
if (lightType !== undefined) {
|
|
248
|
+
out.push(property60("LightType", "enum", "", lightType));
|
|
249
|
+
}
|
|
250
|
+
const color = numbers(findChildByName(model, "Color"));
|
|
251
|
+
if (color.length >= 3) {
|
|
252
|
+
out.push(property60("Color", "ColorRGB", "", color[0], color[1], color[2]));
|
|
253
|
+
}
|
|
254
|
+
const scalars = [
|
|
255
|
+
["Intensity", "Intensity"],
|
|
256
|
+
["ConeAngle", "OuterAngle"],
|
|
257
|
+
["HotSpot", "InnerAngle"],
|
|
258
|
+
];
|
|
259
|
+
for (const [source, target] of scalars) {
|
|
260
|
+
const value = numberAt(findChildByName(model, source));
|
|
261
|
+
if (value !== undefined) {
|
|
262
|
+
out.push(property60(target, "double", "", value));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const castShadows = numberAt(findChildByName(model, "CastShadows"));
|
|
266
|
+
if (castShadows !== undefined) {
|
|
267
|
+
out.push(property60("CastShadows", "bool", "", castShadows));
|
|
268
|
+
}
|
|
269
|
+
return out;
|
|
270
|
+
}
|
|
271
|
+
function cameraProperties(model) {
|
|
272
|
+
const out = [];
|
|
273
|
+
const scalars = [
|
|
274
|
+
["Aperture", "FieldOfView"],
|
|
275
|
+
["FieldOfViewXProperty", "FieldOfViewX"],
|
|
276
|
+
["FieldOfViewYProperty", "FieldOfViewY"],
|
|
277
|
+
["SqueezeRatio", "FilmSqueezeRatio"],
|
|
278
|
+
["FocalLength", "FocalLength"],
|
|
279
|
+
["AspectType", "AspectRatioMode"],
|
|
280
|
+
["AspectW", "AspectWidth"],
|
|
281
|
+
["AspectH", "AspectHeight"],
|
|
282
|
+
["ApertureMode", "ApertureMode"],
|
|
283
|
+
["NearPlane", "NearPlane"],
|
|
284
|
+
["FarPlane", "FarPlane"],
|
|
285
|
+
["Roll", "Roll"],
|
|
286
|
+
];
|
|
287
|
+
for (const [source, target] of scalars) {
|
|
288
|
+
const value = numberAt(findChildByName(model, source));
|
|
289
|
+
if (value !== undefined) {
|
|
290
|
+
out.push(property60(target, target === "AspectRatioMode" || target === "ApertureMode" ? "enum" : "double", "", value));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
const aperture = numbers(findChildByName(model, "CameraAperture"));
|
|
294
|
+
if (aperture.length >= 2) {
|
|
295
|
+
out.push(property60("FilmWidth", "double", "", aperture[0]));
|
|
296
|
+
out.push(property60("FilmHeight", "double", "", aperture[1]));
|
|
297
|
+
}
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
300
|
+
function convertModel(ctx, model) {
|
|
301
|
+
const rawName = getPropertyValue(model, 0) ?? "Model";
|
|
302
|
+
const typeName = getPropertyValue(findChildByName(model, "Type") ?? makeNode("x"), 0);
|
|
303
|
+
const hasVertices = !!findChildByName(model, "Vertices");
|
|
304
|
+
const subType = typeName === "Light" ? "Light" : typeName === "Camera" ? "Camera" : typeName === "LimbNode" ? "LimbNode" : hasVertices ? "Mesh" : "Null";
|
|
305
|
+
// Rest transform: defaults of the model's own channels, else of the take channels.
|
|
306
|
+
const defaults = {};
|
|
307
|
+
const ownTransform = model.children.find((c) => c.name === "Channel" && getPropertyValue(c, 0) === "Transform");
|
|
308
|
+
readTransformChannelDefaults(ownTransform, defaults);
|
|
309
|
+
readTransformChannelDefaults(ctx.takeDefaults.get(rawName), defaults);
|
|
310
|
+
const t = defaults.t ?? [0, 0, 0];
|
|
311
|
+
const r = defaults.r ?? [0, 0, 0];
|
|
312
|
+
const s = defaults.s ?? [1, 1, 1];
|
|
313
|
+
const properties = [
|
|
314
|
+
property60("Lcl Translation", "Lcl Translation", "A+", t[0], t[1], t[2]),
|
|
315
|
+
property60("Lcl Rotation", "Lcl Rotation", "A+", r[0], r[1], r[2]),
|
|
316
|
+
property60("Lcl Scaling", "Lcl Scaling", "A+", s[0], s[1], s[2]),
|
|
317
|
+
];
|
|
318
|
+
const visibilityChannel = model.children.find((c) => c.name === "Channel" && getPropertyValue(c, 0) === "Visibility");
|
|
319
|
+
const visibility = numberAt(findChildByName(visibilityChannel ?? makeNode("x"), "Default"));
|
|
320
|
+
if (visibility !== undefined && visibility <= 0) {
|
|
321
|
+
properties.push(property60("Visibility", "Visibility", "A+", 0));
|
|
322
|
+
}
|
|
323
|
+
if (subType === "Light") {
|
|
324
|
+
properties.push(...lightProperties(model));
|
|
325
|
+
}
|
|
326
|
+
else if (subType === "Camera") {
|
|
327
|
+
properties.push(...cameraProperties(model));
|
|
328
|
+
}
|
|
329
|
+
else if (subType === "LimbNode") {
|
|
330
|
+
const size = numberAt(findChildByName(findChildByName(model, "Properties") ?? makeNode("x"), "Size"));
|
|
331
|
+
if (size !== undefined) {
|
|
332
|
+
properties.push(property60("Size", "double", "", size));
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
const children = [makeNode("Version", [int(232)]), makeNode("Properties60", [], properties)];
|
|
336
|
+
for (const name of ["Culling", "Position", "Up", "LookAt"]) {
|
|
337
|
+
const child = findChildByName(model, name);
|
|
338
|
+
if (child) {
|
|
339
|
+
children.push(child);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (subType === "Mesh") {
|
|
343
|
+
children.push(...convertMesh(model, ctx.version));
|
|
344
|
+
}
|
|
345
|
+
ctx.objects.push(makeNode("Model", [str(rawName), str(subType)], children));
|
|
346
|
+
convertMaterials(ctx, model, rawName);
|
|
347
|
+
convertLinks(ctx, model, rawName);
|
|
348
|
+
const childNames = findChildByName(model, "Children");
|
|
349
|
+
for (const p of childNames?.properties ?? []) {
|
|
350
|
+
if (typeof p.value === "string") {
|
|
351
|
+
connect(ctx, p.value, rawName);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
/** Rewrites a legacy document into the 6.x object/connection layout. */
|
|
356
|
+
export function upgradeLegacyDocument(doc) {
|
|
357
|
+
const headerVersion = numberAt(findChildByName(findDocumentNode(doc, "FBXHeaderExtension") ?? makeNode("x"), "FBXVersion"));
|
|
358
|
+
const version = Math.max(doc.version, headerVersion ?? 0);
|
|
359
|
+
const ctx = { version, objects: [], connections: [], usedNames: new Map(), takeDefaults: new Map() };
|
|
360
|
+
// Takes keep their format; their Transform channels also provide rest values for models without own channels.
|
|
361
|
+
const takes = findDocumentNode(doc, "Takes");
|
|
362
|
+
for (const take of takes?.children ?? []) {
|
|
363
|
+
if (take.name !== "Take") {
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
for (const model of take.children) {
|
|
367
|
+
if (model.name !== "Model") {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const name = getPropertyValue(model, 0);
|
|
371
|
+
const transform = model.children.find((c) => c.name === "Channel" && getPropertyValue(c, 0) === "Transform");
|
|
372
|
+
if (name && transform && !ctx.takeDefaults.has(name)) {
|
|
373
|
+
ctx.takeDefaults.set(name, transform);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const models = doc.nodes.filter((n) => n.name === "Model");
|
|
378
|
+
for (const model of models) {
|
|
379
|
+
convertModel(ctx, model);
|
|
380
|
+
}
|
|
381
|
+
// Animation stored directly on models (pre-5800) becomes an implicit take, as in the SDK.
|
|
382
|
+
const implicitModels = [];
|
|
383
|
+
for (const model of models) {
|
|
384
|
+
const channels = model.children.filter((c) => c.name === "Channel" && channelHasKeys(c));
|
|
385
|
+
if (channels.length > 0) {
|
|
386
|
+
implicitModels.push(makeNode("Model", [str(getPropertyValue(model, 0) ?? "")], channels));
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
let takesNode = takes;
|
|
390
|
+
if (implicitModels.length > 0) {
|
|
391
|
+
const implicitTake = makeNode("Take", [str("(internal)")], implicitModels);
|
|
392
|
+
takesNode = takes ? makeNode("Takes", takes.properties, [...takes.children, implicitTake]) : makeNode("Takes", [], [implicitTake]);
|
|
393
|
+
}
|
|
394
|
+
// Frame rate from the legacy settings block.
|
|
395
|
+
const settings = findDocumentNode(doc, "Settings") ?? findChildByName(findDocumentNode(doc, "Version5") ?? makeNode("x"), "Settings");
|
|
396
|
+
const frameRateNode = settings ? findChildByName(settings, "FrameRate") : undefined;
|
|
397
|
+
const frameRateValue = frameRateNode?.properties[0]?.value;
|
|
398
|
+
const fps = typeof frameRateValue === "number" ? frameRateValue : typeof frameRateValue === "string" ? Number.parseFloat(frameRateValue) : NaN;
|
|
399
|
+
const extra = [];
|
|
400
|
+
let replacedGlobalSettings = false;
|
|
401
|
+
if (Number.isFinite(fps) && fps > 0) {
|
|
402
|
+
const existing = findDocumentNode(doc, "GlobalSettings");
|
|
403
|
+
if (existing) {
|
|
404
|
+
// Keep the file's axis, unit and other settings; only add the frame rate when the block lacks one.
|
|
405
|
+
const props70 = findChildByName(existing, "Properties70");
|
|
406
|
+
const container = props70 ?? findChildByName(existing, "Properties60") ?? makeNode("Properties60");
|
|
407
|
+
const hasTimeMode = container.children.some((c) => getPropertyValue(c, 0) === "TimeMode");
|
|
408
|
+
const property = props70 ? property70 : property60;
|
|
409
|
+
const frameRate = hasTimeMode ? [] : [property("TimeMode", "enum", "", 14), property("CustomFrameRate", "double", "", fps)];
|
|
410
|
+
const children = existing.children.filter((c) => c !== container);
|
|
411
|
+
extra.push(makeNode("GlobalSettings", existing.properties, [...children, makeNode(container.name, container.properties, [...container.children, ...frameRate])]));
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
extra.push(makeNode("GlobalSettings", [], [makeNode("Version", [int(1000)]), makeNode("Properties60", [], [property60("TimeMode", "enum", "", 14), property60("CustomFrameRate", "double", "", fps)])]));
|
|
415
|
+
}
|
|
416
|
+
replacedGlobalSettings = true;
|
|
417
|
+
}
|
|
418
|
+
const kept = doc.nodes.filter((n) => n.name !== "Model" && n.name !== "Takes" && n.name !== "Settings" && (n.name !== "GlobalSettings" || !replacedGlobalSettings));
|
|
419
|
+
const nodes = [...kept, ...extra, makeNode("Objects", [], ctx.objects), makeNode("Connections", [], ctx.connections)];
|
|
420
|
+
if (takesNode) {
|
|
421
|
+
nodes.push(takesNode);
|
|
422
|
+
}
|
|
423
|
+
return { ...doc, version, nodes };
|
|
424
|
+
}
|
|
425
|
+
//# sourceMappingURL=legacyDocument.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"legacyDocument.js","sourceRoot":"","sources":["../../../../../dev/loaders/src/FBX/interpreter/legacyDocument.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/naming-convention, jsdoc/require-param, jsdoc/require-returns */\n/**\n * FBX 5.x (and older) documents store everything at the top level: one `Model` node per object with inline\n * geometry, materials, skin links, lights and cameras, and `Children` lists instead of connections. This module\n * rewrites such a document into the 6.x layout (`Objects`, `Connections`, `Takes`) so the rest of the importer\n * can treat both the same way. The rules follow the FBX SDK's legacy reader as implemented by ufbx.\n */\nimport { type FBXDocument, type FBXNode, type FBXProperty, findChildByName, findDocumentNode, getPropertyValue, getNodeArray, cleanFBXName } from \"../types/fbxTypes\";\n\nconst str = (value: string): FBXProperty => ({ type: \"string\", value });\nconst num = (value: number): FBXProperty => ({ type: \"float64\", value });\nconst int = (value: number): FBXProperty => ({ type: \"int32\", value });\nconst makeNode = (name: string, properties: FBXProperty[] = [], children: FBXNode[] = []): FBXNode => ({ name, properties, children });\nfunction property60(name: string, typeName: string, flags: string, ...values: (number | string)[]): FBXNode {\n return makeNode(\"Property\", [str(name), str(typeName), str(flags), ...values.map((v) => (typeof v === \"string\" ? str(v) : num(v)))]);\n}\nfunction property70(name: string, typeName: string, flags: string, ...values: (number | string)[]): FBXNode {\n return makeNode(\"P\", [str(name), str(typeName), str(\"\"), str(flags), ...values.map((v) => (typeof v === \"string\" ? str(v) : num(v)))]);\n}\nconst arrayNode = (name: string, array: Float64Array | Int32Array): FBXNode => makeNode(name, [{ type: array instanceof Int32Array ? \"int32[]\" : \"float64[]\", value: array }]);\n\n/** True for pre-6000 files: no `Objects` section but top-level `Model` nodes. */\nexport function isLegacyDocument(doc: FBXDocument): boolean {\n return !findDocumentNode(doc, \"Objects\") && doc.nodes.some((n) => n.name === \"Model\");\n}\n\nfunction numberAt(node: FBXNode | undefined, index = 0): number | undefined {\n const v = node?.properties[index]?.value;\n return typeof v === \"number\" ? v : typeof v === \"boolean\" ? (v ? 1 : 0) : undefined;\n}\n\nfunction numbers(node: FBXNode | undefined): number[] {\n if (!node) {\n return [];\n }\n const array = getNodeArray(node);\n return array ? Array.from(array as ArrayLike<number>) : [];\n}\n\nfunction toFloat64(node: FBXNode | undefined): Float64Array | null {\n const array = node ? getNodeArray(node) : null;\n if (!array) {\n return null;\n }\n return array instanceof Float64Array ? array : Float64Array.from(array as ArrayLike<number>);\n}\n\nfunction toInt32(node: FBXNode | undefined): Int32Array | null {\n const array = node ? getNodeArray(node) : null;\n if (!array) {\n return null;\n }\n return array instanceof Int32Array ? array : Int32Array.from(array as ArrayLike<number>, (v) => Math.trunc(v));\n}\n\n/** Default (rest) value of a `Channel` tree: T/R/S each with X/Y/Z sub channels carrying `Default`. */\nfunction readTransformChannelDefaults(transformChannel: FBXNode | undefined, out: { t?: number[]; r?: number[]; s?: number[] }): void {\n if (!transformChannel) {\n return;\n }\n for (const sub of transformChannel.children) {\n if (sub.name !== \"Channel\") {\n continue;\n }\n const kind = getPropertyValue<string>(sub, 0);\n const key = kind === \"T\" ? \"t\" : kind === \"R\" ? \"r\" : kind === \"S\" ? \"s\" : undefined;\n if (!key || out[key]) {\n continue;\n }\n const values = [0, 0, 0];\n let any = false;\n for (const axis of sub.children) {\n if (axis.name !== \"Channel\") {\n continue;\n }\n const axisName = getPropertyValue<string>(axis, 0);\n const index = axisName === \"X\" ? 0 : axisName === \"Y\" ? 1 : axisName === \"Z\" ? 2 : -1;\n const value = numberAt(findChildByName(axis, \"Default\"));\n if (index >= 0 && value !== undefined) {\n values[index] = value;\n any = true;\n }\n }\n if (any) {\n out[key] = values;\n }\n }\n}\n\nfunction channelHasKeys(channel: FBXNode): boolean {\n const keys = findChildByName(channel, \"Key\");\n if (keys && keys.properties.length > 0) {\n return true;\n }\n return channel.children.some((c) => c.name === \"Channel\" && channelHasKeys(c));\n}\n\ninterface LegacyContext {\n version: number;\n objects: FBXNode[];\n connections: FBXNode[];\n usedNames: Map<string, number>;\n /** Model raw name → transform channel defaults from the takes */\n takeDefaults: Map<string, FBXNode>;\n}\n\nfunction uniqueName(ctx: LegacyContext, name: string): string {\n const count = ctx.usedNames.get(name) ?? 0;\n ctx.usedNames.set(name, count + 1);\n return count === 0 ? name : `${name} (${count + 1})`;\n}\n\nfunction connect(ctx: LegacyContext, child: string, parent: string): void {\n ctx.connections.push(makeNode(\"C\", [str(\"OO\"), str(child), str(parent)]));\n}\n\nfunction normalize3(array: Float64Array): Float64Array {\n const out = new Float64Array(array.length);\n for (let i = 0; i + 2 < array.length; i += 3) {\n const x = array[i],\n y = array[i + 1],\n z = array[i + 2];\n const len = Math.hypot(x, y, z);\n if (len > 0) {\n out[i] = x / len;\n out[i + 1] = y / len;\n out[i + 2] = z / len;\n }\n }\n return out;\n}\n\n/** Converts the inline polygon mesh of a legacy model into 6.x-style geometry children. */\nfunction convertMesh(model: FBXNode, version: number): FBXNode[] {\n const vertices = toFloat64(findChildByName(model, \"Vertices\"));\n const indices = toInt32(findChildByName(model, \"PolygonVertexIndex\"));\n if (!vertices || !indices) {\n return [];\n }\n const numVertices = Math.floor(vertices.length / 3);\n const numIndices = indices.length;\n if (numIndices > 0 && indices[numIndices - 1] >= 0) {\n indices[numIndices - 1] = ~indices[numIndices - 1];\n }\n const out: FBXNode[] = [makeNode(\"GeometryVersion\", [int(124)]), arrayNode(\"Vertices\", vertices), arrayNode(\"PolygonVertexIndex\", indices)];\n\n // Normals are per vertex or per polygon-vertex, decided by their count (5000 files prefer per vertex).\n const normals = toFloat64(findChildByName(model, \"Normals\"));\n if (normals) {\n const count = Math.floor(normals.length / 3);\n const perVertex = count === numVertices;\n const perIndex = count === numIndices;\n const mapping = perVertex && (!perIndex || version === 5000) ? \"ByVertice\" : perIndex ? \"ByPolygonVertex\" : undefined;\n if (mapping) {\n out.push(\n makeNode(\n \"LayerElementNormal\",\n [int(0)],\n [\n makeNode(\"Version\", [int(101)]),\n makeNode(\"Name\", [str(\"\")]),\n makeNode(\"MappingInformationType\", [str(mapping)]),\n makeNode(\"ReferenceInformationType\", [str(\"Direct\")]),\n arrayNode(\"Normals\", normalize3(normals)),\n ]\n )\n );\n }\n }\n\n const uvInfo = findChildByName(model, \"GeometryUVInfo\");\n const uv = uvInfo ? toFloat64(findChildByName(uvInfo, \"TextureUV\")) : null;\n if (uvInfo && uv) {\n const uvIndex = toInt32(findChildByName(uvInfo, \"TextureUVVerticeIndex\"));\n const numUV = Math.floor(uv.length / 2);\n let element: FBXNode | undefined;\n const base = [makeNode(\"Version\", [int(101)]), makeNode(\"Name\", [str(\"\")])];\n if (uvIndex && uvIndex.length === numIndices) {\n element = makeNode(\n \"LayerElementUV\",\n [int(0)],\n [\n ...base,\n makeNode(\"MappingInformationType\", [str(\"ByPolygonVertex\")]),\n makeNode(\"ReferenceInformationType\", [str(\"IndexToDirect\")]),\n arrayNode(\"UV\", uv),\n arrayNode(\"UVIndex\", uvIndex),\n ]\n );\n } else if (numUV === numIndices) {\n element = makeNode(\n \"LayerElementUV\",\n [int(0)],\n [...base, makeNode(\"MappingInformationType\", [str(\"ByPolygonVertex\")]), makeNode(\"ReferenceInformationType\", [str(\"Direct\")]), arrayNode(\"UV\", uv)]\n );\n } else if (numUV === numVertices) {\n element = makeNode(\n \"LayerElementUV\",\n [int(0)],\n [...base, makeNode(\"MappingInformationType\", [str(\"ByVertice\")]), makeNode(\"ReferenceInformationType\", [str(\"Direct\")]), arrayNode(\"UV\", uv)]\n );\n }\n if (element) {\n out.push(element);\n }\n }\n\n const assignation = getPropertyValue<string>(findChildByName(model, \"MaterialAssignation\") ?? makeNode(\"x\"), 0);\n const materials = toInt32(findChildByName(model, \"Materials\"));\n if (materials && materials.length > 0) {\n const mapping = assignation === \"ByPolygon\" ? \"ByPolygon\" : \"AllSame\";\n out.push(\n makeNode(\n \"LayerElementMaterial\",\n [int(0)],\n [\n makeNode(\"Version\", [int(101)]),\n makeNode(\"Name\", [str(\"\")]),\n makeNode(\"MappingInformationType\", [str(mapping)]),\n makeNode(\"ReferenceInformationType\", [str(\"IndexToDirect\")]),\n arrayNode(\"Materials\", mapping === \"AllSame\" ? Int32Array.of(materials[0]) : materials),\n ]\n )\n );\n }\n\n // Blend shapes keep the 6.x inline form; the connection resolver turns them into deformers.\n for (const child of model.children) {\n if (child.name === \"Shape\") {\n out.push(child);\n }\n }\n return out;\n}\n\n/** Legacy materials are embedded in the model; each becomes its own object connected to the model. */\nfunction convertMaterials(ctx: LegacyContext, model: FBXNode, modelName: string): void {\n for (const child of model.children) {\n if (child.name !== \"Material\") {\n continue;\n }\n const rawName = getPropertyValue<string>(child, 0) ?? \"Material\";\n const name = uniqueName(ctx, rawName.includes(\"::\") ? rawName : `Material::${rawName}`);\n const color = (source: string, target: string): FBXNode | null => {\n const values = numbers(findChildByName(child, source));\n return values.length >= 3 ? property60(target, \"ColorRGB\", \"\", values[0], values[1], values[2]) : null;\n };\n const scalar = (source: string, target: string): FBXNode | null => {\n const value = numberAt(findChildByName(child, source));\n return value !== undefined ? property60(target, \"double\", \"\", value) : null;\n };\n const properties = [\n color(\"Ambient\", \"AmbientColor\"),\n color(\"Diffuse\", \"DiffuseColor\"),\n color(\"Specular\", \"SpecularColor\"),\n color(\"Emissive\", \"EmissiveColor\"),\n scalar(\"Shininess\", \"Shininess\"),\n scalar(\"Alpha\", \"Opacity\"),\n scalar(\"Reflectivity\", \"ReflectionFactor\"),\n ].filter((p): p is FBXNode => p !== null);\n const shading = findChildByName(child, \"ShadingModel\");\n ctx.objects.push(\n makeNode(\n \"Material\",\n [str(name), str(\"\")],\n [makeNode(\"Version\", [int(100)]), shading ?? makeNode(\"ShadingModel\", [str(\"phong\")]), makeNode(\"Properties60\", [], properties)]\n )\n );\n connect(ctx, name, modelName);\n }\n}\n\n/** `Link` children are skin clusters bound to other models. */\nfunction convertLinks(ctx: LegacyContext, model: FBXNode, modelName: string): void {\n const links = model.children.filter((c) => c.name === \"Link\");\n if (links.length === 0) {\n return;\n }\n const skinName = uniqueName(ctx, `Deformer::Skin ${cleanFBXName(modelName)}`);\n ctx.objects.push(makeNode(\"Deformer\", [str(skinName), str(\"Skin\")], [makeNode(\"Version\", [int(100)]), makeNode(\"Link_DeformAcuracy\", [num(50)])]));\n connect(ctx, skinName, modelName);\n for (const link of links) {\n const boneName = getPropertyValue<string>(link, 0) ?? \"\";\n const clusterName = uniqueName(ctx, `SubDeformer::Cluster ${cleanFBXName(modelName)} ${cleanFBXName(boneName)}`);\n const children: FBXNode[] = [makeNode(\"Version\", [int(100)]), makeNode(\"Mode\", [str(\"Normalize\")])];\n for (const arrayName of [\"Indexes\", \"Weights\", \"Transform\", \"TransformLink\"]) {\n const child = findChildByName(link, arrayName);\n if (!child) {\n continue;\n }\n const array = arrayName === \"Indexes\" ? toInt32(child) : toFloat64(child);\n if (array) {\n children.push(arrayNode(arrayName, array));\n }\n }\n ctx.objects.push(makeNode(\"Deformer\", [str(clusterName), str(\"Cluster\")], children));\n connect(ctx, clusterName, skinName);\n connect(ctx, boneName, clusterName);\n }\n}\n\nfunction lightProperties(model: FBXNode): FBXNode[] {\n const out: FBXNode[] = [];\n const lightType = numberAt(findChildByName(model, \"LightType\"));\n if (lightType !== undefined) {\n out.push(property60(\"LightType\", \"enum\", \"\", lightType));\n }\n const color = numbers(findChildByName(model, \"Color\"));\n if (color.length >= 3) {\n out.push(property60(\"Color\", \"ColorRGB\", \"\", color[0], color[1], color[2]));\n }\n const scalars: [string, string][] = [\n [\"Intensity\", \"Intensity\"],\n [\"ConeAngle\", \"OuterAngle\"],\n [\"HotSpot\", \"InnerAngle\"],\n ];\n for (const [source, target] of scalars) {\n const value = numberAt(findChildByName(model, source));\n if (value !== undefined) {\n out.push(property60(target, \"double\", \"\", value));\n }\n }\n const castShadows = numberAt(findChildByName(model, \"CastShadows\"));\n if (castShadows !== undefined) {\n out.push(property60(\"CastShadows\", \"bool\", \"\", castShadows));\n }\n return out;\n}\n\nfunction cameraProperties(model: FBXNode): FBXNode[] {\n const out: FBXNode[] = [];\n const scalars: [string, string][] = [\n [\"Aperture\", \"FieldOfView\"],\n [\"FieldOfViewXProperty\", \"FieldOfViewX\"],\n [\"FieldOfViewYProperty\", \"FieldOfViewY\"],\n [\"SqueezeRatio\", \"FilmSqueezeRatio\"],\n [\"FocalLength\", \"FocalLength\"],\n [\"AspectType\", \"AspectRatioMode\"],\n [\"AspectW\", \"AspectWidth\"],\n [\"AspectH\", \"AspectHeight\"],\n [\"ApertureMode\", \"ApertureMode\"],\n [\"NearPlane\", \"NearPlane\"],\n [\"FarPlane\", \"FarPlane\"],\n [\"Roll\", \"Roll\"],\n ];\n for (const [source, target] of scalars) {\n const value = numberAt(findChildByName(model, source));\n if (value !== undefined) {\n out.push(property60(target, target === \"AspectRatioMode\" || target === \"ApertureMode\" ? \"enum\" : \"double\", \"\", value));\n }\n }\n const aperture = numbers(findChildByName(model, \"CameraAperture\"));\n if (aperture.length >= 2) {\n out.push(property60(\"FilmWidth\", \"double\", \"\", aperture[0]));\n out.push(property60(\"FilmHeight\", \"double\", \"\", aperture[1]));\n }\n return out;\n}\n\nfunction convertModel(ctx: LegacyContext, model: FBXNode): void {\n const rawName = getPropertyValue<string>(model, 0) ?? \"Model\";\n const typeName = getPropertyValue<string>(findChildByName(model, \"Type\") ?? makeNode(\"x\"), 0);\n const hasVertices = !!findChildByName(model, \"Vertices\");\n const subType = typeName === \"Light\" ? \"Light\" : typeName === \"Camera\" ? \"Camera\" : typeName === \"LimbNode\" ? \"LimbNode\" : hasVertices ? \"Mesh\" : \"Null\";\n\n // Rest transform: defaults of the model's own channels, else of the take channels.\n const defaults: { t?: number[]; r?: number[]; s?: number[] } = {};\n const ownTransform = model.children.find((c) => c.name === \"Channel\" && getPropertyValue<string>(c, 0) === \"Transform\");\n readTransformChannelDefaults(ownTransform, defaults);\n readTransformChannelDefaults(ctx.takeDefaults.get(rawName), defaults);\n const t = defaults.t ?? [0, 0, 0];\n const r = defaults.r ?? [0, 0, 0];\n const s = defaults.s ?? [1, 1, 1];\n const properties: FBXNode[] = [\n property60(\"Lcl Translation\", \"Lcl Translation\", \"A+\", t[0], t[1], t[2]),\n property60(\"Lcl Rotation\", \"Lcl Rotation\", \"A+\", r[0], r[1], r[2]),\n property60(\"Lcl Scaling\", \"Lcl Scaling\", \"A+\", s[0], s[1], s[2]),\n ];\n const visibilityChannel = model.children.find((c) => c.name === \"Channel\" && getPropertyValue<string>(c, 0) === \"Visibility\");\n const visibility = numberAt(findChildByName(visibilityChannel ?? makeNode(\"x\"), \"Default\"));\n if (visibility !== undefined && visibility <= 0) {\n properties.push(property60(\"Visibility\", \"Visibility\", \"A+\", 0));\n }\n if (subType === \"Light\") {\n properties.push(...lightProperties(model));\n } else if (subType === \"Camera\") {\n properties.push(...cameraProperties(model));\n } else if (subType === \"LimbNode\") {\n const size = numberAt(findChildByName(findChildByName(model, \"Properties\") ?? makeNode(\"x\"), \"Size\"));\n if (size !== undefined) {\n properties.push(property60(\"Size\", \"double\", \"\", size));\n }\n }\n\n const children: FBXNode[] = [makeNode(\"Version\", [int(232)]), makeNode(\"Properties60\", [], properties)];\n for (const name of [\"Culling\", \"Position\", \"Up\", \"LookAt\"]) {\n const child = findChildByName(model, name);\n if (child) {\n children.push(child);\n }\n }\n if (subType === \"Mesh\") {\n children.push(...convertMesh(model, ctx.version));\n }\n ctx.objects.push(makeNode(\"Model\", [str(rawName), str(subType)], children));\n\n convertMaterials(ctx, model, rawName);\n convertLinks(ctx, model, rawName);\n\n const childNames = findChildByName(model, \"Children\");\n for (const p of childNames?.properties ?? []) {\n if (typeof p.value === \"string\") {\n connect(ctx, p.value, rawName);\n }\n }\n}\n\n/** Rewrites a legacy document into the 6.x object/connection layout. */\nexport function upgradeLegacyDocument(doc: FBXDocument): FBXDocument {\n const headerVersion = numberAt(findChildByName(findDocumentNode(doc, \"FBXHeaderExtension\") ?? makeNode(\"x\"), \"FBXVersion\"));\n const version = Math.max(doc.version, headerVersion ?? 0);\n const ctx: LegacyContext = { version, objects: [], connections: [], usedNames: new Map(), takeDefaults: new Map() };\n\n // Takes keep their format; their Transform channels also provide rest values for models without own channels.\n const takes = findDocumentNode(doc, \"Takes\");\n for (const take of takes?.children ?? []) {\n if (take.name !== \"Take\") {\n continue;\n }\n for (const model of take.children) {\n if (model.name !== \"Model\") {\n continue;\n }\n const name = getPropertyValue<string>(model, 0);\n const transform = model.children.find((c) => c.name === \"Channel\" && getPropertyValue<string>(c, 0) === \"Transform\");\n if (name && transform && !ctx.takeDefaults.has(name)) {\n ctx.takeDefaults.set(name, transform);\n }\n }\n }\n\n const models = doc.nodes.filter((n) => n.name === \"Model\");\n for (const model of models) {\n convertModel(ctx, model);\n }\n\n // Animation stored directly on models (pre-5800) becomes an implicit take, as in the SDK.\n const implicitModels: FBXNode[] = [];\n for (const model of models) {\n const channels = model.children.filter((c) => c.name === \"Channel\" && channelHasKeys(c));\n if (channels.length > 0) {\n implicitModels.push(makeNode(\"Model\", [str(getPropertyValue<string>(model, 0) ?? \"\")], channels));\n }\n }\n let takesNode = takes;\n if (implicitModels.length > 0) {\n const implicitTake = makeNode(\"Take\", [str(\"(internal)\")], implicitModels);\n takesNode = takes ? makeNode(\"Takes\", takes.properties, [...takes.children, implicitTake]) : makeNode(\"Takes\", [], [implicitTake]);\n }\n\n // Frame rate from the legacy settings block.\n const settings = findDocumentNode(doc, \"Settings\") ?? findChildByName(findDocumentNode(doc, \"Version5\") ?? makeNode(\"x\"), \"Settings\");\n const frameRateNode = settings ? findChildByName(settings, \"FrameRate\") : undefined;\n const frameRateValue = frameRateNode?.properties[0]?.value;\n const fps = typeof frameRateValue === \"number\" ? frameRateValue : typeof frameRateValue === \"string\" ? Number.parseFloat(frameRateValue) : NaN;\n const extra: FBXNode[] = [];\n let replacedGlobalSettings = false;\n if (Number.isFinite(fps) && fps > 0) {\n const existing = findDocumentNode(doc, \"GlobalSettings\");\n if (existing) {\n // Keep the file's axis, unit and other settings; only add the frame rate when the block lacks one.\n const props70 = findChildByName(existing, \"Properties70\");\n const container = props70 ?? findChildByName(existing, \"Properties60\") ?? makeNode(\"Properties60\");\n const hasTimeMode = container.children.some((c) => getPropertyValue<string>(c, 0) === \"TimeMode\");\n const property = props70 ? property70 : property60;\n const frameRate = hasTimeMode ? [] : [property(\"TimeMode\", \"enum\", \"\", 14), property(\"CustomFrameRate\", \"double\", \"\", fps)];\n const children = existing.children.filter((c) => c !== container);\n extra.push(makeNode(\"GlobalSettings\", existing.properties, [...children, makeNode(container.name, container.properties, [...container.children, ...frameRate])]));\n } else {\n extra.push(\n makeNode(\n \"GlobalSettings\",\n [],\n [makeNode(\"Version\", [int(1000)]), makeNode(\"Properties60\", [], [property60(\"TimeMode\", \"enum\", \"\", 14), property60(\"CustomFrameRate\", \"double\", \"\", fps)])]\n )\n );\n }\n replacedGlobalSettings = true;\n }\n\n const kept = doc.nodes.filter((n) => n.name !== \"Model\" && n.name !== \"Takes\" && n.name !== \"Settings\" && (n.name !== \"GlobalSettings\" || !replacedGlobalSettings));\n const nodes = [...kept, ...extra, makeNode(\"Objects\", [], ctx.objects), makeNode(\"Connections\", [], ctx.connections)];\n if (takesNode) {\n nodes.push(takesNode);\n }\n return { ...doc, version, nodes };\n}\n"]}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified material model.
|
|
3
|
+
*
|
|
4
|
+
* FBX materials come in many vendor flavours: the classic FbxSurfaceLambert / FbxSurfacePhong, Autodesk Standard
|
|
5
|
+
* Surface (OSL), Arnold aiStandardSurface, 3ds Max Physical Material, 3ds Max PBR (metal/rough and spec/gloss),
|
|
6
|
+
* the 3ds Max glTF material, OpenPBR, and Maya's Stingray PBS ShaderFX graph. Each stores its parameters under
|
|
7
|
+
* different property names, sometimes behind a shader binding table.
|
|
8
|
+
*
|
|
9
|
+
* This module resolves any of them into two parameter sets, mirroring how the FBX SDK / ufbx expose materials:
|
|
10
|
+
* `fbx` (the classic Lambert/Phong parameters) and `pbr` (a physically based parameter set), plus feature flags.
|
|
11
|
+
*/
|
|
12
|
+
import { type FBXPropertyValue } from "../types/fbxTypes.js";
|
|
13
|
+
export type FBXShaderType = "unknown" | "fbxLambert" | "fbxPhong" | "oslStandardSurface" | "arnoldStandardSurface" | "3dsMaxPhysicalMaterial" | "3dsMaxPbrMetalRough" | "3dsMaxPbrSpecGloss" | "gltfMaterial" | "openPbrMaterial" | "shaderFxGraph";
|
|
14
|
+
/** One resolved material parameter: a constant value and/or a texture. */
|
|
15
|
+
export interface FBXMaterialMap<TTexture> {
|
|
16
|
+
/** Constant value (r,g,b,a for colours, x for scalars) */
|
|
17
|
+
value?: number[];
|
|
18
|
+
/** Number of meaningful components in `value` (1 for scalars, 3 or 4 for colours) */
|
|
19
|
+
valueComponents: number;
|
|
20
|
+
/** Texture bound to the parameter */
|
|
21
|
+
texture?: TTexture;
|
|
22
|
+
/** Whether the texture is enabled (some vendors carry an explicit toggle) */
|
|
23
|
+
textureEnabled: boolean;
|
|
24
|
+
}
|
|
25
|
+
export type FBXFbxMapName = "diffuseFactor" | "diffuseColor" | "specularFactor" | "specularColor" | "specularExponent" | "reflectionFactor" | "reflectionColor" | "transparencyFactor" | "transparencyColor" | "emissionFactor" | "emissionColor" | "ambientFactor" | "ambientColor" | "normalMap" | "bump" | "bumpFactor" | "displacement" | "displacementFactor" | "vectorDisplacement" | "vectorDisplacementFactor";
|
|
26
|
+
export type FBXPbrMapName = "baseFactor" | "baseColor" | "roughness" | "metalness" | "diffuseRoughness" | "specularFactor" | "specularColor" | "specularIor" | "specularAnisotropy" | "specularRotation" | "transmissionFactor" | "transmissionColor" | "transmissionDepth" | "transmissionScatter" | "transmissionScatterAnisotropy" | "transmissionDispersion" | "transmissionRoughness" | "transmissionExtraRoughness" | "transmissionPriority" | "transmissionEnableInAov" | "subsurfaceFactor" | "subsurfaceColor" | "subsurfaceRadius" | "subsurfaceScale" | "subsurfaceAnisotropy" | "subsurfaceTintColor" | "subsurfaceType" | "sheenFactor" | "sheenColor" | "sheenRoughness" | "coatFactor" | "coatColor" | "coatRoughness" | "coatIor" | "coatAnisotropy" | "coatRotation" | "coatNormal" | "coatAffectBaseColor" | "coatAffectBaseRoughness" | "thinFilmFactor" | "thinFilmThickness" | "thinFilmIor" | "emissionFactor" | "emissionColor" | "opacity" | "indirectDiffuse" | "indirectSpecular" | "normalMap" | "tangentMap" | "displacementMap" | "matteFactor" | "matteColor" | "ambientOcclusion" | "glossiness" | "coatGlossiness" | "transmissionGlossiness";
|
|
27
|
+
export type FBXMaterialFeatureName = "pbr" | "metalness" | "diffuse" | "specular" | "emission" | "transmission" | "coat" | "sheen" | "opacity" | "ambientOcclusion" | "matte" | "unlit" | "ior" | "diffuseRoughness" | "transmissionRoughness" | "thinWalled" | "caustics" | "exitToBackground" | "internalReflections" | "doubleSided" | "roughnessAsGlossiness" | "coatRoughnessAsGlossiness" | "transmissionRoughnessAsGlossiness";
|
|
28
|
+
export interface FBXMaterialModel<TTexture> {
|
|
29
|
+
shaderType: FBXShaderType;
|
|
30
|
+
/** Prefix material property names carry for this shader (e.g. "3dsMax|Parameters|") */
|
|
31
|
+
shaderPropPrefix: string;
|
|
32
|
+
fbx: Partial<Record<FBXFbxMapName, FBXMaterialMap<TTexture>>>;
|
|
33
|
+
pbr: Partial<Record<FBXPbrMapName, FBXMaterialMap<TTexture>>>;
|
|
34
|
+
features: Partial<Record<FBXMaterialFeatureName, {
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
explicit: boolean;
|
|
37
|
+
}>>;
|
|
38
|
+
}
|
|
39
|
+
/** Inputs needed to resolve a material, independent of the parse representation. */
|
|
40
|
+
export interface FBXMaterialSource<TTexture> {
|
|
41
|
+
/** "lambert", "phong", "unknown", ... as written in the file */
|
|
42
|
+
shadingModelName: string;
|
|
43
|
+
/** Property values by full property name (e.g. "DiffuseColor", "Maya|baseColor", "3dsMax|Parameters|roughness") */
|
|
44
|
+
props: Map<string, {
|
|
45
|
+
type: string;
|
|
46
|
+
values: FBXPropertyValue[];
|
|
47
|
+
}>;
|
|
48
|
+
/** Textures connected to the material by property name (OP connection property) */
|
|
49
|
+
texturesByProp: Map<string, TTexture>;
|
|
50
|
+
/** Connected shader implementation, when any */
|
|
51
|
+
shader?: {
|
|
52
|
+
renderApi: string;
|
|
53
|
+
/** shader semantic name -> material property names */
|
|
54
|
+
bindings: Map<string, string[]>;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Shader types whose parameters are physically based; the loader emits PBR materials for them by default. */
|
|
58
|
+
export declare function isPbrShaderType(type: FBXShaderType): boolean;
|
|
59
|
+
/** Detects the shader flavour of a material from its shading model name, connected shader and 3ds Max class ids. */
|
|
60
|
+
export declare function detectShaderType<T>(source: FBXMaterialSource<T>): {
|
|
61
|
+
shaderType: FBXShaderType;
|
|
62
|
+
shaderPropPrefix: string;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Resolves the unified material model from raw material data.
|
|
66
|
+
*/
|
|
67
|
+
export declare function resolveMaterialModel<T>(source: FBXMaterialSource<T>): FBXMaterialModel<T>;
|
|
68
|
+
/** Colour (first three components) of a map, or undefined. */
|
|
69
|
+
export declare function mapColor<T>(map: FBXMaterialMap<T> | undefined): [number, number, number] | undefined;
|
|
70
|
+
/** Scalar (first component) of a map, or undefined. */
|
|
71
|
+
export declare function mapScalar<T>(map: FBXMaterialMap<T> | undefined): number | undefined;
|