/// module BABYLON.GLTF2.Extensions { interface IMSFTLOD { ids: number[]; } // See https://github.com/sbtron/glTF/tree/MSFT_lod/extensions/Vendor/MSFT_lod for more information about this extension. export class MSFTLOD extends GLTFLoaderExtension { /** * Specify the minimal delay between LODs in ms (default = 250) */ public Delay = 250; public get name() { return "MSFT_lod"; } protected _traverseNode(loader: GLTFLoader, context: string, node: IGLTFNode, action: (node: IGLTFNode, parentNode: IGLTFNode) => boolean, parentNode: IGLTFNode): boolean { return this._loadExtension(context, node, (context, extension, onComplete) => { for (let i = extension.ids.length - 1; i >= 0; i--) { const lodNode = GLTFLoader._GetProperty(loader._gltf.nodes, extension.ids[i]); if (!lodNode) { throw new Error(context + ": Failed to find node " + extension.ids[i]); } loader._traverseNode(context, lodNode, action, parentNode); } loader._traverseNode(context, node, action, parentNode); onComplete(); }); } protected _loadNode(loader: GLTFLoader, context: string, node: IGLTFNode): boolean { return this._loadExtension(context, node, (context, extension, onComplete) => { const nodes = [node]; for (let index of extension.ids) { const lodNode = GLTFLoader._GetProperty(loader._gltf.nodes, index); if (!lodNode) { throw new Error(context + ": Failed to find node " + index); } nodes.push(lodNode); } loader._addLoaderPendingData(node); this._loadNodeLOD(loader, context, nodes, nodes.length - 1, () => { loader._removeLoaderPendingData(node); onComplete(); }); }); } private _loadNodeLOD(loader: GLTFLoader, context: string, nodes: IGLTFNode[], index: number, onComplete: () => void): void { loader._whenAction(() => { loader._loadNode(context, nodes[index]); }, () => { if (index !== nodes.length - 1) { const previousNode = nodes[index + 1]; previousNode.babylonMesh.setEnabled(false); } if (index === 0) { onComplete(); return; } setTimeout(() => { loader._tryCatchOnError(() => { this._loadNodeLOD(loader, context, nodes, index - 1, onComplete); }); }, this.Delay); }); } protected _loadMaterial(loader: GLTFLoader, context: string, material: IGLTFMaterial, assign: (babylonMaterial: Material, isNew: boolean) => void): boolean { return this._loadExtension(context, material, (context, extension, onComplete) => { const materials = [material]; for (let index of extension.ids) { const lodMaterial = GLTFLoader._GetProperty(loader._gltf.materials, index); if (!lodMaterial) { throw new Error(context + ": Failed to find material " + index); } materials.push(lodMaterial); } loader._addLoaderPendingData(material); this._loadMaterialLOD(loader, context, materials, materials.length - 1, assign, () => { loader._removeLoaderPendingData(material); onComplete(); }); }); } private _loadMaterialLOD(loader: GLTFLoader, context: string, materials: IGLTFMaterial[], index: number, assign: (babylonMaterial: Material, isNew: boolean) => void, onComplete: () => void): void { loader._loadMaterial(context, materials[index], (babylonMaterial, isNew) => { if (index === materials.length - 1) { assign(babylonMaterial, isNew); // Load the next LOD when the loader is ready to render. loader._executeWhenRenderReady(() => { this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete); }); } else { BaseTexture.WhenAllReady(babylonMaterial.getActiveTextures(), () => { assign(babylonMaterial, isNew); if (index === 0) { onComplete(); } else { setTimeout(() => { loader._tryCatchOnError(() => { this._loadMaterialLOD(loader, context, materials, index - 1, assign, onComplete); }); }, this.Delay); } }); } }); } } GLTFLoader.RegisterExtension(new MSFTLOD()); }