@babylonjs/loaders
Version:
For usage documentation please visit https://doc.babylonjs.com/features/featuresDeepDive/importers/loadingFileTypes/.
101 lines • 4.01 kB
JavaScript
/**
* Adding an exception here will break traversing through the glTF object tree.
* This is used for properties that might not be in the glTF object model, but are optional and have a default value.
* For example, the path /nodes/\{\}/extensions/KHR_node_visibility/visible is optional - the object can be deferred without the object fully existing.
*/
export const OptionalPathExceptionsList = [
{
// get the node as object when reading an extension
regex: new RegExp(`^/nodes/\\d+/extensions/`),
},
];
/**
* A converter that takes a glTF Object Model JSON Pointer
* and transforms it into an ObjectAccessorContainer, allowing
* objects referenced in the glTF to be associated with their
* respective Babylon.js objects.
*/
export class GLTFPathToObjectConverter {
constructor(_gltf, _infoTree) {
this._gltf = _gltf;
this._infoTree = _infoTree;
}
/**
* The pointer string is represented by a [JSON pointer](https://datatracker.ietf.org/doc/html/rfc6901).
* See also https://github.com/KhronosGroup/glTF/blob/main/specification/2.0/ObjectModel.adoc#core-pointers
* <animationPointer> := /<rootNode>/<assetIndex>/<propertyPath>
* <rootNode> := "nodes" | "materials" | "meshes" | "cameras" | "extensions"
* <assetIndex> := <digit> | <name>
* <propertyPath> := <extensionPath> | <standardPath>
* <extensionPath> := "extensions"/<name>/<standardPath>
* <standardPath> := <name> | <name>/<standardPath>
* <name> := W+
* <digit> := D+
*
* Examples:
* - "/nodes/0/rotation"
* - "/nodes.length"
* - "/materials/2/emissiveFactor"
* - "/materials/2/pbrMetallicRoughness/baseColorFactor"
* - "/materials/2/extensions/KHR_materials_emissive_strength/emissiveStrength"
*
* @param path The path to convert
* @returns The object and info associated with the path
*/
convert(path) {
let objectTree = this._gltf;
let infoTree = this._infoTree;
let target = undefined;
if (!path.startsWith("/")) {
throw new Error("Path must start with a /");
}
const parts = path.split("/");
parts.shift();
//if the last part has ".length" in it, separate that as an extra part
if (parts[parts.length - 1].includes(".length")) {
const lastPart = parts[parts.length - 1];
const split = lastPart.split(".");
parts.pop();
parts.push(...split);
}
let ignoreObjectTree = false;
for (const part of parts) {
const isLength = part === "length";
if (isLength && !infoTree.__array__) {
throw new Error(`Path ${path} is invalid`);
}
if (infoTree.__ignoreObjectTree__) {
ignoreObjectTree = true;
}
if (infoTree.__array__ && !isLength) {
infoTree = infoTree.__array__;
}
else {
infoTree = infoTree[part];
if (!infoTree) {
throw new Error(`Path ${path} is invalid`);
}
}
if (!ignoreObjectTree) {
if (objectTree === undefined) {
// check if the path is in the exception list. If it is, break and return the last object that was found
const exception = OptionalPathExceptionsList.find((e) => e.regex.test(path));
if (!exception) {
throw new Error(`Path ${path} is invalid`);
}
}
else if (!isLength) {
objectTree = objectTree?.[part];
}
}
if (infoTree.__target__ || isLength) {
target = objectTree;
}
}
return {
object: target,
info: infoTree,
};
}
}
//# sourceMappingURL=gltfPathToObjectConverter.js.map