UNPKG

@babylonjs/loaders

Version:

The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.

754 lines 31 kB
import { Observable } from "@babylonjs/core/Misc/observable"; import { Tools } from "@babylonjs/core/Misc/tools"; import { SceneLoader } from "@babylonjs/core/Loading/sceneLoader"; import { AssetContainer } from "@babylonjs/core/assetContainer"; /** * Mode that determines the coordinate system to use. */ export var GLTFLoaderCoordinateSystemMode; (function (GLTFLoaderCoordinateSystemMode) { /** * Automatically convert the glTF right-handed data to the appropriate system based on the current coordinate system mode of the scene. */ GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["AUTO"] = 0] = "AUTO"; /** * Sets the useRightHandedSystem flag on the scene. */ GLTFLoaderCoordinateSystemMode[GLTFLoaderCoordinateSystemMode["FORCE_RIGHT_HANDED"] = 1] = "FORCE_RIGHT_HANDED"; })(GLTFLoaderCoordinateSystemMode || (GLTFLoaderCoordinateSystemMode = {})); /** * Mode that determines what animations will start. */ export var GLTFLoaderAnimationStartMode; (function (GLTFLoaderAnimationStartMode) { /** * No animation will start. */ GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["NONE"] = 0] = "NONE"; /** * The first animation will start. */ GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["FIRST"] = 1] = "FIRST"; /** * All animations will start. */ GLTFLoaderAnimationStartMode[GLTFLoaderAnimationStartMode["ALL"] = 2] = "ALL"; })(GLTFLoaderAnimationStartMode || (GLTFLoaderAnimationStartMode = {})); /** * Loader state. */ export var GLTFLoaderState; (function (GLTFLoaderState) { /** * The asset is loading. */ GLTFLoaderState[GLTFLoaderState["LOADING"] = 0] = "LOADING"; /** * The asset is ready for rendering. */ GLTFLoaderState[GLTFLoaderState["READY"] = 1] = "READY"; /** * The asset is completely loaded. */ GLTFLoaderState[GLTFLoaderState["COMPLETE"] = 2] = "COMPLETE"; })(GLTFLoaderState || (GLTFLoaderState = {})); /** * File loader for loading glTF files into a scene. */ var GLTFFileLoader = /** @class */ (function () { function GLTFFileLoader() { // -------------- // Common options // -------------- /** * Raised when the asset has been parsed */ this.onParsedObservable = new Observable(); // ---------- // V2 options // ---------- /** * The coordinate system mode. Defaults to AUTO. */ this.coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO; /** * The animation start mode. Defaults to FIRST. */ this.animationStartMode = GLTFLoaderAnimationStartMode.FIRST; /** * Defines if the loader should compile materials before raising the success callback. Defaults to false. */ this.compileMaterials = false; /** * Defines if the loader should also compile materials with clip planes. Defaults to false. */ this.useClipPlane = false; /** * Defines if the loader should compile shadow generators before raising the success callback. Defaults to false. */ this.compileShadowGenerators = false; /** * Defines if the Alpha blended materials are only applied as coverage. * If false, (default) The luminance of each pixel will reduce its opacity to simulate the behaviour of most physical materials. * If true, no extra effects are applied to transparent pixels. */ this.transparencyAsCoverage = false; /** * Function called before loading a url referenced by the asset. */ this.preprocessUrlAsync = function (url) { return Promise.resolve(url); }; /** * Observable raised when the loader creates a mesh after parsing the glTF properties of the mesh. */ this.onMeshLoadedObservable = new Observable(); /** * Observable raised when the loader creates a texture after parsing the glTF properties of the texture. */ this.onTextureLoadedObservable = new Observable(); /** * Observable raised when the loader creates a material after parsing the glTF properties of the material. */ this.onMaterialLoadedObservable = new Observable(); /** * Observable raised when the loader creates a camera after parsing the glTF properties of the camera. */ this.onCameraLoadedObservable = new Observable(); /** * Observable raised when the asset is completely loaded, immediately before the loader is disposed. * For assets with LODs, raised when all of the LODs are complete. * For assets without LODs, raised when the model is complete, immediately after the loader resolves the returned promise. */ this.onCompleteObservable = new Observable(); /** * Observable raised when an error occurs. */ this.onErrorObservable = new Observable(); /** * Observable raised after the loader is disposed. */ this.onDisposeObservable = new Observable(); /** * Observable raised after a loader extension is created. * Set additional options for a loader extension in this event. */ this.onExtensionLoadedObservable = new Observable(); /** * Defines if the loader should validate the asset. */ this.validate = false; /** * Observable raised after validation when validate is set to true. The event data is the result of the validation. */ this.onValidatedObservable = new Observable(); this._loader = null; /** * Name of the loader ("gltf") */ this.name = "gltf"; /** * Supported file extensions of the loader (.gltf, .glb) */ this.extensions = { ".gltf": { isBinary: false }, ".glb": { isBinary: true } }; this._logIndentLevel = 0; this._loggingEnabled = false; /** @hidden */ this._log = this._logDisabled; this._capturePerformanceCounters = false; /** @hidden */ this._startPerformanceCounter = this._startPerformanceCounterDisabled; /** @hidden */ this._endPerformanceCounter = this._endPerformanceCounterDisabled; } Object.defineProperty(GLTFFileLoader.prototype, "onParsed", { /** * Raised when the asset has been parsed */ set: function (callback) { if (this._onParsedObserver) { this.onParsedObservable.remove(this._onParsedObserver); } this._onParsedObserver = this.onParsedObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onMeshLoaded", { /** * Callback raised when the loader creates a mesh after parsing the glTF properties of the mesh. */ set: function (callback) { if (this._onMeshLoadedObserver) { this.onMeshLoadedObservable.remove(this._onMeshLoadedObserver); } this._onMeshLoadedObserver = this.onMeshLoadedObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onTextureLoaded", { /** * Callback raised when the loader creates a texture after parsing the glTF properties of the texture. */ set: function (callback) { if (this._onTextureLoadedObserver) { this.onTextureLoadedObservable.remove(this._onTextureLoadedObserver); } this._onTextureLoadedObserver = this.onTextureLoadedObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onMaterialLoaded", { /** * Callback raised when the loader creates a material after parsing the glTF properties of the material. */ set: function (callback) { if (this._onMaterialLoadedObserver) { this.onMaterialLoadedObservable.remove(this._onMaterialLoadedObserver); } this._onMaterialLoadedObserver = this.onMaterialLoadedObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onCameraLoaded", { /** * Callback raised when the loader creates a camera after parsing the glTF properties of the camera. */ set: function (callback) { if (this._onCameraLoadedObserver) { this.onCameraLoadedObservable.remove(this._onCameraLoadedObserver); } this._onCameraLoadedObserver = this.onCameraLoadedObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onComplete", { /** * Callback raised when the asset is completely loaded, immediately before the loader is disposed. * For assets with LODs, raised when all of the LODs are complete. * For assets without LODs, raised when the model is complete, immediately after the loader resolves the returned promise. */ set: function (callback) { if (this._onCompleteObserver) { this.onCompleteObservable.remove(this._onCompleteObserver); } this._onCompleteObserver = this.onCompleteObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onError", { /** * Callback raised when an error occurs. */ set: function (callback) { if (this._onErrorObserver) { this.onErrorObservable.remove(this._onErrorObserver); } this._onErrorObserver = this.onErrorObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onDispose", { /** * Callback raised after the loader is disposed. */ set: function (callback) { if (this._onDisposeObserver) { this.onDisposeObservable.remove(this._onDisposeObserver); } this._onDisposeObserver = this.onDisposeObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onExtensionLoaded", { /** * Callback raised after a loader extension is created. */ set: function (callback) { if (this._onExtensionLoadedObserver) { this.onExtensionLoadedObservable.remove(this._onExtensionLoadedObserver); } this._onExtensionLoadedObserver = this.onExtensionLoadedObservable.add(callback); }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "loggingEnabled", { /** * Defines if the loader logging is enabled. */ get: function () { return this._loggingEnabled; }, set: function (value) { if (this._loggingEnabled === value) { return; } this._loggingEnabled = value; if (this._loggingEnabled) { this._log = this._logEnabled; } else { this._log = this._logDisabled; } }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "capturePerformanceCounters", { /** * Defines if the loader should capture performance counters. */ get: function () { return this._capturePerformanceCounters; }, set: function (value) { if (this._capturePerformanceCounters === value) { return; } this._capturePerformanceCounters = value; if (this._capturePerformanceCounters) { this._startPerformanceCounter = this._startPerformanceCounterEnabled; this._endPerformanceCounter = this._endPerformanceCounterEnabled; } else { this._startPerformanceCounter = this._startPerformanceCounterDisabled; this._endPerformanceCounter = this._endPerformanceCounterDisabled; } }, enumerable: true, configurable: true }); Object.defineProperty(GLTFFileLoader.prototype, "onValidated", { /** * Callback raised after a loader extension is created. */ set: function (callback) { if (this._onValidatedObserver) { this.onValidatedObservable.remove(this._onValidatedObserver); } this._onValidatedObserver = this.onValidatedObservable.add(callback); }, enumerable: true, configurable: true }); /** * Disposes the loader, releases resources during load, and cancels any outstanding requests. */ GLTFFileLoader.prototype.dispose = function () { if (this._loader) { this._loader.dispose(); this._loader = null; } this._clear(); this.onDisposeObservable.notifyObservers(undefined); this.onDisposeObservable.clear(); }; /** @hidden */ GLTFFileLoader.prototype._clear = function () { this.preprocessUrlAsync = function (url) { return Promise.resolve(url); }; this.onMeshLoadedObservable.clear(); this.onTextureLoadedObservable.clear(); this.onMaterialLoadedObservable.clear(); this.onCameraLoadedObservable.clear(); this.onCompleteObservable.clear(); this.onExtensionLoadedObservable.clear(); }; /** * Imports one or more meshes from the loaded glTF data and adds them to the scene * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file * @param scene the scene the meshes should be added to * @param data the glTF data to load * @param rootUrl root url to load from * @param onProgress event that fires when loading progress has occured * @param fileName Defines the name of the file to load * @returns a promise containg the loaded meshes, particles, skeletons and animations */ GLTFFileLoader.prototype.importMeshAsync = function (meshesNames, scene, data, rootUrl, onProgress, fileName) { var _this = this; return this._parseAsync(scene, data, rootUrl, fileName).then(function (loaderData) { _this._log("Loading " + (fileName || "")); _this._loader = _this._getLoader(loaderData); return _this._loader.importMeshAsync(meshesNames, scene, loaderData, rootUrl, onProgress, fileName); }); }; /** * Imports all objects from the loaded glTF data and adds them to the scene * @param scene the scene the objects should be added to * @param data the glTF data to load * @param rootUrl root url to load from * @param onProgress event that fires when loading progress has occured * @param fileName Defines the name of the file to load * @returns a promise which completes when objects have been loaded to the scene */ GLTFFileLoader.prototype.loadAsync = function (scene, data, rootUrl, onProgress, fileName) { var _this = this; return this._parseAsync(scene, data, rootUrl, fileName).then(function (loaderData) { _this._log("Loading " + (fileName || "")); _this._loader = _this._getLoader(loaderData); return _this._loader.loadAsync(scene, loaderData, rootUrl, onProgress, fileName); }); }; /** * Load into an asset container. * @param scene The scene to load into * @param data The data to import * @param rootUrl The root url for scene and resources * @param onProgress The callback when the load progresses * @param fileName Defines the name of the file to load * @returns The loaded asset container */ GLTFFileLoader.prototype.loadAssetContainerAsync = function (scene, data, rootUrl, onProgress, fileName) { var _this = this; return this._parseAsync(scene, data, rootUrl, fileName).then(function (loaderData) { _this._log("Loading " + (fileName || "")); _this._loader = _this._getLoader(loaderData); return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fileName).then(function (result) { var container = new AssetContainer(scene); Array.prototype.push.apply(container.meshes, result.meshes); Array.prototype.push.apply(container.particleSystems, result.particleSystems); Array.prototype.push.apply(container.skeletons, result.skeletons); Array.prototype.push.apply(container.animationGroups, result.animationGroups); container.removeAllFromScene(); return container; }); }); }; /** * If the data string can be loaded directly. * @param data string contianing the file data * @returns if the data can be loaded directly */ GLTFFileLoader.prototype.canDirectLoad = function (data) { return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1)); }; /** * Instantiates a glTF file loader plugin. * @returns the created plugin */ GLTFFileLoader.prototype.createPlugin = function () { return new GLTFFileLoader(); }; Object.defineProperty(GLTFFileLoader.prototype, "loaderState", { /** * The loader state or null if the loader is not active. */ get: function () { return this._loader ? this._loader.state : null; }, enumerable: true, configurable: true }); /** * Returns a promise that resolves when the asset is completely loaded. * @returns a promise that resolves when the asset is completely loaded. */ GLTFFileLoader.prototype.whenCompleteAsync = function () { var _this = this; return new Promise(function (resolve, reject) { _this.onCompleteObservable.addOnce(function () { resolve(); }); _this.onErrorObservable.addOnce(function (reason) { reject(reason); }); }); }; GLTFFileLoader.prototype._parseAsync = function (scene, data, rootUrl, fileName) { var _this = this; return Promise.resolve().then(function () { var unpacked = (data instanceof ArrayBuffer) ? _this._unpackBinary(data) : { json: data, bin: null }; return _this._validateAsync(scene, unpacked.json, rootUrl, fileName).then(function () { _this._startPerformanceCounter("Parse JSON"); _this._log("JSON length: " + unpacked.json.length); var loaderData = { json: JSON.parse(unpacked.json), bin: unpacked.bin }; _this._endPerformanceCounter("Parse JSON"); _this.onParsedObservable.notifyObservers(loaderData); _this.onParsedObservable.clear(); return loaderData; }); }); }; GLTFFileLoader.prototype._validateAsync = function (scene, json, rootUrl, fileName) { var _this = this; if (!this.validate || typeof GLTFValidator === "undefined") { return Promise.resolve(); } this._startPerformanceCounter("Validate JSON"); var options = { externalResourceFunction: function (uri) { return _this.preprocessUrlAsync(rootUrl + uri) .then(function (url) { return scene._loadFileAsync(url, true, true); }) .then(function (data) { return new Uint8Array(data); }); } }; if (fileName && fileName.substr(0, 5) !== "data:") { options.uri = (rootUrl === "file:" ? fileName : "" + rootUrl + fileName); } return GLTFValidator.validateString(json, options).then(function (result) { _this._endPerformanceCounter("Validate JSON"); _this.onValidatedObservable.notifyObservers(result); _this.onValidatedObservable.clear(); }, function (reason) { _this._endPerformanceCounter("Validate JSON"); Tools.Warn("Failed to validate: " + reason); _this.onValidatedObservable.clear(); }); }; GLTFFileLoader.prototype._getLoader = function (loaderData) { var asset = loaderData.json.asset || {}; this._log("Asset version: " + asset.version); asset.minVersion && this._log("Asset minimum version: " + asset.minVersion); asset.generator && this._log("Asset generator: " + asset.generator); var version = GLTFFileLoader._parseVersion(asset.version); if (!version) { throw new Error("Invalid version: " + asset.version); } if (asset.minVersion !== undefined) { var minVersion = GLTFFileLoader._parseVersion(asset.minVersion); if (!minVersion) { throw new Error("Invalid minimum version: " + asset.minVersion); } if (GLTFFileLoader._compareVersion(minVersion, { major: 2, minor: 0 }) > 0) { throw new Error("Incompatible minimum version: " + asset.minVersion); } } var createLoaders = { 1: GLTFFileLoader._CreateGLTF1Loader, 2: GLTFFileLoader._CreateGLTF2Loader }; var createLoader = createLoaders[version.major]; if (!createLoader) { throw new Error("Unsupported version: " + asset.version); } return createLoader(this); }; GLTFFileLoader.prototype._unpackBinary = function (data) { this._startPerformanceCounter("Unpack binary"); this._log("Binary length: " + data.byteLength); var Binary = { Magic: 0x46546C67 }; var binaryReader = new BinaryReader(data); var magic = binaryReader.readUint32(); if (magic !== Binary.Magic) { throw new Error("Unexpected magic: " + magic); } var version = binaryReader.readUint32(); if (this.loggingEnabled) { this._log("Binary version: " + version); } var unpacked; switch (version) { case 1: { unpacked = this._unpackBinaryV1(binaryReader); break; } case 2: { unpacked = this._unpackBinaryV2(binaryReader); break; } default: { throw new Error("Unsupported version: " + version); } } this._endPerformanceCounter("Unpack binary"); return unpacked; }; GLTFFileLoader.prototype._unpackBinaryV1 = function (binaryReader) { var ContentFormat = { JSON: 0 }; var length = binaryReader.readUint32(); if (length != binaryReader.getLength()) { throw new Error("Length in header does not match actual data length: " + length + " != " + binaryReader.getLength()); } var contentLength = binaryReader.readUint32(); var contentFormat = binaryReader.readUint32(); var content; switch (contentFormat) { case ContentFormat.JSON: { content = GLTFFileLoader._decodeBufferToText(binaryReader.readUint8Array(contentLength)); break; } default: { throw new Error("Unexpected content format: " + contentFormat); } } var bytesRemaining = binaryReader.getLength() - binaryReader.getPosition(); var body = binaryReader.readUint8Array(bytesRemaining); return { json: content, bin: body }; }; GLTFFileLoader.prototype._unpackBinaryV2 = function (binaryReader) { var ChunkFormat = { JSON: 0x4E4F534A, BIN: 0x004E4942 }; var length = binaryReader.readUint32(); if (length !== binaryReader.getLength()) { throw new Error("Length in header does not match actual data length: " + length + " != " + binaryReader.getLength()); } // JSON chunk var chunkLength = binaryReader.readUint32(); var chunkFormat = binaryReader.readUint32(); if (chunkFormat !== ChunkFormat.JSON) { throw new Error("First chunk format is not JSON"); } var json = GLTFFileLoader._decodeBufferToText(binaryReader.readUint8Array(chunkLength)); // Look for BIN chunk var bin = null; while (binaryReader.getPosition() < binaryReader.getLength()) { var chunkLength_1 = binaryReader.readUint32(); var chunkFormat_1 = binaryReader.readUint32(); switch (chunkFormat_1) { case ChunkFormat.JSON: { throw new Error("Unexpected JSON chunk"); } case ChunkFormat.BIN: { bin = binaryReader.readUint8Array(chunkLength_1); break; } default: { // ignore unrecognized chunkFormat binaryReader.skipBytes(chunkLength_1); break; } } } return { json: json, bin: bin }; }; GLTFFileLoader._parseVersion = function (version) { if (version === "1.0" || version === "1.0.1") { return { major: 1, minor: 0 }; } var match = (version + "").match(/^(\d+)\.(\d+)/); if (!match) { return null; } return { major: parseInt(match[1]), minor: parseInt(match[2]) }; }; GLTFFileLoader._compareVersion = function (a, b) { if (a.major > b.major) { return 1; } if (a.major < b.major) { return -1; } if (a.minor > b.minor) { return 1; } if (a.minor < b.minor) { return -1; } return 0; }; GLTFFileLoader._decodeBufferToText = function (buffer) { if (typeof TextDecoder !== "undefined") { return new TextDecoder().decode(buffer); } var result = ""; var length = buffer.byteLength; for (var i = 0; i < length; i++) { result += String.fromCharCode(buffer[i]); } return result; }; /** @hidden */ GLTFFileLoader.prototype._logOpen = function (message) { this._log(message); this._logIndentLevel++; }; /** @hidden */ GLTFFileLoader.prototype._logClose = function () { --this._logIndentLevel; }; GLTFFileLoader.prototype._logEnabled = function (message) { var spaces = GLTFFileLoader._logSpaces.substr(0, this._logIndentLevel * 2); Tools.Log("" + spaces + message); }; GLTFFileLoader.prototype._logDisabled = function (message) { }; GLTFFileLoader.prototype._startPerformanceCounterEnabled = function (counterName) { Tools.StartPerformanceCounter(counterName); }; GLTFFileLoader.prototype._startPerformanceCounterDisabled = function (counterName) { }; GLTFFileLoader.prototype._endPerformanceCounterEnabled = function (counterName) { Tools.EndPerformanceCounter(counterName); }; GLTFFileLoader.prototype._endPerformanceCounterDisabled = function (counterName) { }; // ---------- // V1 options // ---------- /** * Set this property to false to disable incremental loading which delays the loader from calling the success callback until after loading the meshes and shaders. * Textures always loads asynchronously. For example, the success callback can compute the bounding information of the loaded meshes when incremental loading is disabled. * Defaults to true. * @hidden */ GLTFFileLoader.IncrementalLoading = true; /** * Set this property to true in order to work with homogeneous coordinates, available with some converters and exporters. * Defaults to false. See https://en.wikipedia.org/wiki/Homogeneous_coordinates. * @hidden */ GLTFFileLoader.HomogeneousCoordinates = false; GLTFFileLoader._logSpaces = " "; return GLTFFileLoader; }()); export { GLTFFileLoader }; var BinaryReader = /** @class */ (function () { function BinaryReader(arrayBuffer) { this._arrayBuffer = arrayBuffer; this._dataView = new DataView(arrayBuffer); this._byteOffset = 0; } BinaryReader.prototype.getPosition = function () { return this._byteOffset; }; BinaryReader.prototype.getLength = function () { return this._arrayBuffer.byteLength; }; BinaryReader.prototype.readUint32 = function () { var value = this._dataView.getUint32(this._byteOffset, true); this._byteOffset += 4; return value; }; BinaryReader.prototype.readUint8Array = function (length) { var value = new Uint8Array(this._arrayBuffer, this._byteOffset, length); this._byteOffset += length; return value; }; BinaryReader.prototype.skipBytes = function (length) { this._byteOffset += length; }; return BinaryReader; }()); if (SceneLoader) { SceneLoader.RegisterPlugin(new GLTFFileLoader()); } //# sourceMappingURL=glTFFileLoader.js.map