瀏覽代碼

Add support for Draco WebAssembly

Gary Hsu 7 年之前
父節點
當前提交
2804044e35

+ 5 - 2
Playground/debug.html

@@ -33,11 +33,14 @@
     <!-- jszip -->
     <script src="js/libs/jszip.min.js"></script>
     <script src="js/libs/fileSaver.js"></script>
-    <!--Monaco-->
+    <!-- Draco -->
+    <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+    <script src="https://preview.babylonjs.com/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
+    <!-- Monaco -->
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
-    <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/babylon.max.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 4 - 1
Playground/frame.html

@@ -24,9 +24,12 @@
     <meta name="theme-color" content="#ffffff">
 
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
+    <!-- Draco -->
+    <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+    <script src="https://preview.babylonjs.com/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
-    <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/babylon.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 4 - 1
Playground/full.html

@@ -24,9 +24,12 @@
         <meta name="theme-color" content="#ffffff">
 
         <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
+        <!-- Draco -->
+        <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
+        <script src="https://preview.babylonjs.com/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+        <script src="https://preview.babylonjs.com/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
         <!-- Babylon.js -->
         <script src="https://preview.babylonjs.com/cannon.js"></script>
-        <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/babylon.js"></script>
         <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 5 - 2
Playground/index-local.html

@@ -17,10 +17,13 @@
         <script src="../dist/preview%20release/cannon.js"></script>
         <script src="../dist/preview%20release/Oimo.js"></script>
         <script src="../dist/preview%20release/earcut.min.js"></script>
-        <!--Monaco-->
+        <!-- Draco -->
+        <script src="../dist/preview%20release/draco_decoder.js" type="text/x-draco-decoder"></script>
+        <script src="../dist/preview%20release/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+        <script src="../dist/preview%20release/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
+        <!-- Monaco -->
         <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
         <!-- Babylon.js -->
-        <script src="../dist/preview%20release/draco_decoder.js" type="text/x-draco-decoder"></script>
         <script src="../tools/DevLoader/BabylonLoader.js"></script>
 
         <link href="css/index.css" rel="stylesheet" />

+ 5 - 2
Playground/index.html

@@ -37,10 +37,13 @@
         <script src="https://preview.babylonjs.com/cannon.js"></script>
         <script src="https://preview.babylonjs.com/Oimo.js"></script>
         <script src="https://preview.babylonjs.com/earcut.min.js"></script>
-        <!--Monaco-->
+        <!-- Draco -->
+        <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
+        <script src="https://preview.babylonjs.com/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+        <script src="https://preview.babylonjs.com/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
+        <!-- Monaco -->
         <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
         <!-- Babylon.js -->
-        <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
         <script src="https://preview.babylonjs.com/babylon.js"></script>
         <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
 

+ 5 - 2
Playground/indexStable.html

@@ -36,10 +36,13 @@
         <!-- Physics -->
         <script src="https://cdn.babylonjs.com/cannon.js"></script>
         <script src="https://cdn.babylonjs.com/Oimo.js"></script>
-        <!--Monaco-->
+        <!-- Draco -->
+        <script src="https://cdn.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
+        <script src="https://cdn.babylonjs.com/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+        <script src="https://cdn.babylonjs.com/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
+        <!-- Monaco -->
         <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
         <!-- Babylon.js -->
-        <script src="https://cdn.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
         <script src="https://cdn.babylonjs.com/babylon.js"></script>
         <script src="https://cdn.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>
 

+ 6 - 2
Playground/zipContent/index.html

@@ -4,14 +4,18 @@
         <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
 
         <title>Babylon.js sample code</title>
+
+        <!-- Draco -->
+        <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
+        <script src="https://preview.babylonjs.com/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+        <script src="https://preview.babylonjs.com/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
         <!-- Babylon.js -->
         <script src="https://www.babylonjs.com/hand.minified-1.2.js"></script>
         <script src="https://preview.babylonjs.com/babylon.js"></script>
         <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>
         <script src="https://preview.babylonjs.com/cannon.js"></script>
-        <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
         <script src="https://preview.babylonjs.com/oimo.js"></script>
-        
+
         <style>
             html, body {
                 overflow: hidden;

文件差異過大導致無法顯示
+ 30 - 31
dist/preview release/draco_decoder.js


二進制
dist/preview release/draco_decoder.wasm


文件差異過大導致無法顯示
+ 115 - 0
dist/preview release/draco_wasm_wrapper.js


+ 14 - 9
loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts

@@ -10,6 +10,10 @@ module BABYLON.GLTF2.Extensions {
         attributes: { [name: string]: number };
     }
 
+    interface ILoaderBufferViewDraco extends ILoaderBufferView {
+        _dracoBabylonGeometry?: Promise<Geometry>;
+    }
+
     export class KHR_draco_mesh_compression extends GLTFLoaderExtension {
         public readonly name = NAME;
 
@@ -19,7 +23,7 @@ module BABYLON.GLTF2.Extensions {
             super(loader);
 
             // Disable extension if decoder is not available.
-            if (!DracoCompression.DecoderUrl) {
+            if (!DracoCompression.DecoderAvailable) {
                 this.enabled = false;
             }
         }
@@ -70,9 +74,9 @@ module BABYLON.GLTF2.Extensions {
                 loadAttribute("WEIGHTS_0", VertexBuffer.MatricesWeightsKind);
                 loadAttribute("COLOR_0", VertexBuffer.ColorKind);
 
-                var bufferView = GLTFLoader._GetProperty(extensionContext, this._loader._gltf.bufferViews, extension.bufferView);
-                return this._loader._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(data => {
-                    try {
+                var bufferView = GLTFLoader._GetProperty(extensionContext, this._loader._gltf.bufferViews, extension.bufferView) as ILoaderBufferViewDraco;
+                if (!bufferView._dracoBabylonGeometry) {
+                    bufferView._dracoBabylonGeometry = this._loader._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(data => {
                         if (!this._dracoCompression) {
                             this._dracoCompression = new DracoCompression();
                         }
@@ -81,12 +85,13 @@ module BABYLON.GLTF2.Extensions {
                             const babylonGeometry = new Geometry(babylonMesh.name, this._loader._babylonScene);
                             babylonVertexData.applyToGeometry(babylonGeometry);
                             return babylonGeometry;
+                        }).catch(error => {
+                            throw new Error(`${context}: ${error.message}`);
                         });
-                    }
-                    catch (e) {
-                        throw new Error(`${context}: ${e.message}`);
-                    }
-                });
+                    });
+                }
+
+                return bufferView._dracoBabylonGeometry;
             });
         }
     }

+ 3 - 1
localDev/index.html

@@ -7,8 +7,10 @@
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.6.2/dat.gui.min.js"></script>
     <script src="../dist/preview%20release/cannon.js"></script>
-    <script src="../dist/preview%20release/draco_decoder.js" type="text/x-draco-decoder"></script>
     <script src="../dist/preview%20release/Oimo.js"></script>
+    <script src="../dist/preview%20release/draco_decoder.js" type="text/x-draco-decoder"></script>
+    <script src="../dist/preview%20release/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+    <script src="../dist/preview%20release/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
     <script src="../Tools/DevLoader/BabylonLoader.js"></script>
     <script src="src/webgl-debug.js"></script>
 

+ 3 - 1
sandbox/index-local.html

@@ -4,8 +4,10 @@
     <title>BabylonJS - Sandbox</title>
     <link href="index.css" rel="stylesheet" />
     <script src="../dist/preview%20release/cannon.js"></script>
-    <script src="../dist/preview%20release/draco_decoder.js" type="text/x-draco-decoder"></script>
     <script src="../dist/preview%20release/Oimo.js"></script>
+    <script src="../dist/preview%20release/draco_decoder.js" type="text/x-draco-decoder"></script>
+    <script src="../dist/preview%20release/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+    <script src="../dist/preview%20release/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
     <script src="../Tools/DevLoader/BabylonLoader.js"></script>
 </head>
 <body>

+ 5 - 1
sandbox/index.html

@@ -26,8 +26,12 @@
     <link href="index.css" rel="stylesheet" />
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
 
-    <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <!-- Draco -->
     <script src="https://preview.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.wasm" type="text/x-draco-decoder-wasm-binary"></script>
+    <script src="https://preview.babylonjs.com/draco_wasm_wrapper.js" type="text/x-draco-decoder-wasm-wrapper"></script>
+
+    <script src="https://preview.babylonjs.com/cannon.js"></script>
     <script src="https://preview.babylonjs.com/Oimo.js"></script>
     <script src="https://preview.babylonjs.com/babylon.js"></script>
     <script src="https://preview.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 4 - 2
sandbox/index.js

@@ -9,14 +9,16 @@ var indexOf = location.href.indexOf("?");
 if (indexOf !== -1) {
     var params = location.href.substr(indexOf + 1).split("&");
     for (var index = 0; index < params.length; index++) {
-        var [name, value] = params[index].split("=");
+        var param = params[index].split("=");
+        var name = param[0];
+        var value = param[1];
         switch (name) {
             case "assetUrl": {
                 assetUrl = value;
                 break;
             }
             case "cameraPosition": {
-                cameraPosition = BABYLON.Vector3.FromArray(value.split(",").map(component => +component));
+                cameraPosition = BABYLON.Vector3.FromArray(value.split(",").map(function (component) { return +component; }));
                 break;
             }
             case "kiosk": {

+ 153 - 105
src/Mesh/Compression/babylon.dracoCompression.ts

@@ -1,43 +1,59 @@
 declare var DracoDecoderModule: any;
+declare var WebAssembly: any;
 
 module BABYLON {
     /**
+     * Configuration for Draco compression
+     */
+    export interface IDracoCompressionConfiguration {
+        /**
+         * Configuration for the JavaScript decoder or null if not available.
+         */
+        decoder: Nullable<{
+            url: string;
+        }>;
+
+        /**
+         * Configuration for the WebAssembly decoder or null if not available.
+         */
+        decoderWasm: Nullable<{
+            binaryUrl: string;
+            wrapperUrl: string;
+        }>;
+    }
+
+    /**
      * Draco compression (https://google.github.io/draco/)
      */
     export class DracoCompression implements IDisposable {
-        private _workerPool: WorkerPool;
+        private static _DecoderModulePromise: Promise<any>;
 
         /**
-         * Gets the url to the draco decoder if available.
+         * Gets the configuration.
          */
-        public static DecoderUrl: Nullable<string> = DracoCompression._GetDefaultDecoderUrl();
+        public static Configuration = DracoCompression._GetDefaultConfig();
 
         /**
-         * Constructor
-         * @param numWorkers The number of workers for async operations
+         * Returns true if the decoder is available.
          */
-        constructor(numWorkers = (navigator.hardwareConcurrency || 4)) {
-            let workerBlobUrl = URL && URL.createObjectURL && URL.createObjectURL(new Blob([`(${DracoCompression._Worker.toString()})()`], { type: "application/javascript" }));
-            if (!workerBlobUrl || !Worker) {
-                Tools.Error("Draco Compression disabled. The current context doesn't support worker creation or URL.createObjectURL");
-                return;
-            }
-            const workers = new Array<Worker>(numWorkers);
-            for (let i = 0; i < workers.length; i++) {
-                const worker = new Worker(workerBlobUrl);
-                worker.postMessage({ id: "initDecoder", url: DracoCompression.DecoderUrl });
-                workers[i] = worker;
-            }
+        public static get DecoderAvailable(): boolean {
+            return (
+                typeof DracoDecoderModule !== "undefined" ||
+                (typeof WebAssembly === "object" && !!DracoCompression.Configuration.decoderWasm) ||
+                !!DracoCompression.Configuration.decoder
+            );
+        }
 
-            this._workerPool = new WorkerPool(workers);
+        /**
+         * Constructor
+         */
+        constructor() {
         }
 
         /**
          * Stop all async operations and release resources.
          */
         public dispose(): void {
-            this._workerPool.dispose();
-            delete this._workerPool;
         }
 
         /**
@@ -47,72 +63,26 @@ module BABYLON {
          * @returns A promise that resolves with the decoded vertex data
          */
         public decodeMeshAsync(data: ArrayBufferView, attributes: { [kind: string]: number }): Promise<VertexData> {
-            return new Promise((resolve, reject) => {
-                this._workerPool.push((worker, onComplete) => {
-                    const vertexData = new VertexData();
-
-                    const onError = (error: ErrorEvent) => {
-                        worker.removeEventListener("error", onError);
-                        worker.removeEventListener("message", onMessage);
-                        reject(error);
-                        onComplete();
-                    };
+            return DracoCompression._GetDecoderModule().then(wrappedModule => {
+                const module = wrappedModule.module;
+                const vertexData = new VertexData();
 
-                    const onMessage = (message: MessageEvent) => {
-                        if (message.data === "done") {
-                            worker.removeEventListener("error", onError);
-                            worker.removeEventListener("message", onMessage);
-                            resolve(vertexData);
-                            onComplete();
-                        }
-                        else if (message.data.id === "indices") {
-                            vertexData.indices = message.data.value;
-                        }
-                        else {
-                            vertexData.set(message.data.value, message.data.id);
-                        }
-                    };
-
-                    worker.addEventListener("error", onError);
-                    worker.addEventListener("message", onMessage);
-
-                    const dataCopy = new Uint8Array(data.byteLength);
-                    dataCopy.set(new Uint8Array(data.buffer, data.byteOffset, data.byteLength));
-
-                    worker.postMessage({ id: "decodeMesh", data: dataCopy, attributes: attributes }, [dataCopy.buffer]);
-                });
-            });
-        }
-
-        /**
-         * The worker function that gets converted to a blob url to pass into a worker.
-         */
-        private static _Worker(): void {
-            // self is actually a DedicatedWorkerGlobalScope
-            const _self = self as any as {
-                onmessage: (event: MessageEvent) => void;
-                postMessage: (message: any, transfer?: any[]) => void;
-                close: () => void;
-            };
-
-            const decodeMesh = (data: ArrayBufferView, attributes: { [kind: string]: number }): void => {
-                const dracoModule = new DracoDecoderModule();
-                const buffer = new dracoModule.DecoderBuffer();
+                const buffer = new module.DecoderBuffer();
                 buffer.Init(data, data.byteLength);
 
-                const decoder = new dracoModule.Decoder();
+                const decoder = new module.Decoder();
                 let geometry: any;
                 let status: any;
 
                 try {
                     const type = decoder.GetEncodedGeometryType(buffer);
                     switch (type) {
-                        case dracoModule.TRIANGULAR_MESH:
-                            geometry = new dracoModule.Mesh();
+                        case module.TRIANGULAR_MESH:
+                            geometry = new module.Mesh();
                             status = decoder.DecodeBufferToMesh(buffer, geometry);
                             break;
-                        case dracoModule.POINT_CLOUD:
-                            geometry = new dracoModule.PointCloud();
+                        case module.POINT_CLOUD:
+                            geometry = new module.PointCloud();
                             status = decoder.DecodeBufferToPointCloud(buffer, geometry);
                             break;
                         default:
@@ -125,9 +95,9 @@ module BABYLON {
 
                     const numPoints = geometry.num_points();
 
-                    if (type === dracoModule.TRIANGULAR_MESH) {
+                    if (type === module.TRIANGULAR_MESH) {
                         const numFaces = geometry.num_faces();
-                        const faceIndices = new dracoModule.DracoInt32Array();
+                        const faceIndices = new module.DracoInt32Array();
                         try {
                             const indices = new Uint32Array(numFaces * 3);
                             for (let i = 0; i < numFaces; i++) {
@@ -137,68 +107,146 @@ module BABYLON {
                                 indices[offset + 1] = faceIndices.GetValue(1);
                                 indices[offset + 2] = faceIndices.GetValue(2);
                             }
-                            _self.postMessage({ id: "indices", value: indices }, [indices.buffer]);
+                            vertexData.indices = indices;
                         }
                         finally {
-                            dracoModule.destroy(faceIndices);
+                            module.destroy(faceIndices);
                         }
                     }
 
                     for (const kind in attributes) {
                         const uniqueId = attributes[kind];
                         const attribute = decoder.GetAttributeByUniqueId(geometry, uniqueId);
-                        const dracoData = new dracoModule.DracoFloat32Array();
+                        const dracoData = new module.DracoFloat32Array();
                         try {
                             decoder.GetAttributeFloatForAllPoints(geometry, attribute, dracoData);
                             const babylonData = new Float32Array(numPoints * attribute.num_components());
                             for (let i = 0; i < babylonData.length; i++) {
                                 babylonData[i] = dracoData.GetValue(i);
                             }
-                            _self.postMessage({ id: kind, value: babylonData }, [babylonData.buffer]);
+                            vertexData.set(babylonData, kind);
                         }
                         finally {
-                            dracoModule.destroy(dracoData);
+                            module.destroy(dracoData);
                         }
                     }
                 }
                 finally {
                     if (geometry) {
-                        dracoModule.destroy(geometry);
+                        module.destroy(geometry);
                     }
 
-                    dracoModule.destroy(decoder);
-                    dracoModule.destroy(buffer);
+                    module.destroy(decoder);
+                    module.destroy(buffer);
                 }
 
-                _self.postMessage("done");
+                return vertexData;
+            });
+        }
+
+        private static _GetDecoderModule(): Promise<any> {
+            if (!DracoCompression._DecoderModulePromise) {
+                let promise: Promise<any>;
+                let config: any = {};
+
+                if (typeof DracoDecoderModule !== "undefined") {
+                    promise = Promise.resolve();
+                }
+                else if (typeof WebAssembly === "object" && DracoCompression.Configuration.decoderWasm) {
+                    promise = Promise.all([
+                        DracoCompression._LoadScriptAsync(DracoCompression.Configuration.decoderWasm.wrapperUrl),
+                        DracoCompression._LoadFileAsync(DracoCompression.Configuration.decoderWasm.binaryUrl).then(data => {
+                            config.wasmBinary = data;
+                        })
+                    ]);
+                }
+                else if (DracoCompression.Configuration.decoder) {
+                    promise = DracoCompression._LoadScriptAsync(DracoCompression.Configuration.decoder.url);
+                }
+                else {
+                    throw new Error("Invalid decoder configuration");
+                }
+
+                DracoCompression._DecoderModulePromise = promise.then(() => {
+                    return new Promise(resolve => {
+                        config.onModuleLoaded = (decoderModule: any) => {
+                            // decoderModule is Promise-like. Wrap before resolving to avoid loop.
+                            resolve({ module: decoderModule });
+                        };
+
+                        DracoDecoderModule(config);
+                    });
+                });
             }
 
-            _self.onmessage = event => {
-                switch (event.data.id) {
-                    case "initDecoder": {
-                        importScripts(event.data.url);
-                        break;
-                    }
-                    case "decodeMesh": {
-                        decodeMesh(event.data.data, event.data.attributes);
-                        break;
+            return DracoCompression._DecoderModulePromise;
+        }
+
+        private static _LoadScriptAsync(url: string): Promise<void> {
+            return new Promise((resolve, reject) => {
+                Tools.LoadScript(url, () => {
+                    resolve();
+                }, message => {
+                    reject(new Error(message));
+                });
+            });
+        }
+
+        private static _LoadFileAsync(url: string): Promise<ArrayBuffer> {
+            return new Promise((resolve, reject) => {
+                Tools.LoadFile(url, data => {
+                    resolve(data as ArrayBuffer);
+                }, undefined, undefined, true, (request, exception) => {
+                    reject(exception);
+                });
+            });
+        }
+
+        private static _GetDefaultConfig(): IDracoCompressionConfiguration {
+            const configuration: IDracoCompressionConfiguration = {
+                decoder: null,
+                decoderWasm: null
+            };
+
+            if (Tools.IsWindowObjectExist()) {
+                let decoderUrl: Nullable<string> = null;
+                let decoderWasmBinaryUrl: Nullable<string> = null;
+                let decoderWasmWrapperUrl: Nullable<string> = null;
+
+                for (let i = 0; i < document.scripts.length; i++) {
+                    const type = document.scripts[i].type;
+                    const src = document.scripts[i].src;
+                    switch (type) {
+                        case "text/x-draco-decoder": {
+                            decoderUrl = src;
+                            break;
+                        }
+                        case "text/x-draco-decoder-wasm-binary": {
+                            decoderWasmBinaryUrl = src;
+                            break;
+                        }
+                        case "text/x-draco-decoder-wasm-wrapper": {
+                            decoderWasmWrapperUrl = src;
+                            break;
+                        }
                     }
                 }
-            };
-        }
 
-        private static _GetDefaultDecoderUrl(): Nullable<string> {
-            if (!Tools.IsWindowObjectExist()) {
-                return null;
-            }
+                if (decoderUrl) {
+                    configuration.decoder = {
+                        url: decoderUrl
+                    };
+                }
 
-            for (let i = 0; i < document.scripts.length; i++) {
-                if (document.scripts[i].type === "text/x-draco-decoder") {
-                    return document.scripts[i].src;
+                if (decoderWasmWrapperUrl && decoderWasmBinaryUrl) {
+                    configuration.decoderWasm = {
+                        binaryUrl: decoderWasmBinaryUrl,
+                        wrapperUrl: decoderWasmWrapperUrl
+                    };
                 }
             }
 
-            return null;
+            return configuration;
         }
     }
 }

+ 1 - 1
src/Tools/babylon.tools.ts

@@ -716,7 +716,7 @@
 
             script.onerror = (e) => {
                 if (onError) {
-                    onError("Unable to load script", e);
+                    onError(`Unable to load script '${scriptUrl}'`, e);
                 }
             };
 

+ 12 - 1
tests/validation/validation.js

@@ -312,7 +312,18 @@ function runTest(index, done) {
 function init() {
     BABYLON.SceneLoader.ShowLoadingScreen = false;
     BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental = true;
-    BABYLON.DracoCompression.DecoderUrl = BABYLON.Tools.GetFolderPath(document.location.href) + "../../dist/preview%20release/draco_decoder.js";
+
+    // Draco configuration
+    var baseUrl = BABYLON.Tools.GetFolderPath(document.location.href);
+    BABYLON.DracoCompression.Configuration = {
+        decoder: {
+            url: baseUrl + "../../dist/preview%20release/draco_decoder.js"
+        },
+        decoderWasm: {
+            binaryUrl: baseUrl + "../../dist/preview%20release/draco_decoder.wasm",
+            wrapperUrl: baseUrl + "../../dist/preview%20release/draco_wasm_wrapper.js"
+        }
+    };
 
     canvas = document.createElement("canvas");
     canvas.className = "renderCanvas";