Browse Source

Merge branch 'master' into mutiCameraDepthRendering

Trevor Baron 7 years ago
parent
commit
b7a5f6823e
65 changed files with 18242 additions and 18262 deletions
  1. 2 0
      .gitignore
  2. 7813 7810
      Playground/babylon.d.txt
  3. 1 1
      Playground/debug.html
  4. 1 1
      Playground/frame.html
  5. 1 1
      Playground/full.html
  6. 1 1
      Playground/index-local.html
  7. 1 1
      Playground/index.html
  8. 1 1
      Playground/indexStable.html
  9. 1 1
      Playground/zipContent/index.html
  10. 11 9
      Tools/Gulp/config.json
  11. 5 1
      Tools/Gulp/gulp-addModuleExports.js
  12. 8 1
      Tools/Gulp/gulpfile.js
  13. 1 0
      Viewer/dist/_redirects
  14. 15 18
      Viewer/src/configuration/loader.ts
  15. 0 26
      Viewer/src/helper.ts
  16. 47 24
      Viewer/src/templateManager.ts
  17. 2 2
      Viewer/src/viewer/defaultViewer.ts
  18. 41 7
      Viewer/src/viewer/viewer.ts
  19. 7999 7991
      dist/preview release/babylon.d.ts
  20. 13 13
      dist/preview release/babylon.js
  21. 50 22
      dist/preview release/babylon.max.js
  22. 13 13
      dist/preview release/babylon.worker.js
  23. 1506 1504
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  24. 6 6
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  25. 10 8
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  26. 10 8
      dist/preview release/customConfigurations/minimalGLTFViewer/es6.js
  27. 50 22
      dist/preview release/es6.js
  28. 1 1
      dist/preview release/gui/package.json
  29. 1 1
      dist/preview release/inspector/package.json
  30. 1 1
      dist/preview release/loaders/package.json
  31. 1 1
      dist/preview release/materialsLibrary/package.json
  32. 1 1
      dist/preview release/postProcessesLibrary/package.json
  33. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  34. 1 1
      dist/preview release/serializers/package.json
  35. 8 24
      dist/preview release/viewer/babylon.viewer.d.ts
  36. 48 48
      dist/preview release/viewer/babylon.viewer.js
  37. 84 83
      dist/preview release/viewer/babylon.viewer.max.js
  38. 4 484
      dist/preview release/viewer/babylon.viewer.module.d.ts
  39. 3 1
      dist/preview release/viewer/package.json
  40. 25 12
      dist/preview release/viewer/readme.md
  41. 5 2
      dist/preview release/what's new.md
  42. 26 5
      loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts
  43. 6 0
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  44. 5 1
      loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts
  45. 3 3
      localDev/index.html
  46. 2 2
      package.json
  47. 1 1
      sandbox/index-local.html
  48. 1 1
      sandbox/index.html
  49. 2 2
      src/Engine/babylon.engine.ts
  50. 3 1
      src/Helpers/babylon.environmentHelper.ts
  51. 5 1
      src/Materials/Textures/babylon.cubeTexture.ts
  52. 163 70
      src/Mesh/Compression/babylon.dracoCompression.ts
  53. 33 10
      src/Particles/babylon.gpuParticleSystem.ts
  54. 23 1
      src/Shaders/default.fragment.fx
  55. 14 1
      src/Shaders/default.vertex.fx
  56. 8 0
      src/Shaders/gpuRenderParticles.fragment.fx
  57. 13 0
      src/Shaders/gpuRenderParticles.vertex.fx
  58. 66 0
      src/Tools/babylon.workerPool.ts
  59. 12 6
      src/babylon.scene.ts
  60. 1 1
      tests/validation/config.json
  61. 0 1
      tests/validation/index.html
  62. 0 1
      tests/validation/karma.conf.browserstack.js
  63. 0 1
      tests/validation/karma.conf.js
  64. 61 0
      tests/validation/validate.html
  65. 1 0
      tests/validation/validation.js

+ 2 - 0
.gitignore

@@ -174,3 +174,5 @@ dist/preview release/package/
 # viewer dist files
 /Viewer/dist/viewer.js
 /Viewer/dist/viewer.min.js
+dist/preview release/viewer/babylon.d.ts
+Viewer/dist/viewer.max.js

File diff suppressed because it is too large
+ 7813 - 7810
Playground/babylon.d.txt


+ 1 - 1
Playground/debug.html

@@ -37,7 +37,7 @@
     <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"></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>

+ 1 - 1
Playground/frame.html

@@ -26,7 +26,7 @@
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
     <!-- Babylon.js -->
     <script src="https://preview.babylonjs.com/cannon.js"></script>
-    <script src="https://preview.babylonjs.com/draco_decoder.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>

+ 1 - 1
Playground/full.html

@@ -26,7 +26,7 @@
         <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
         <!-- Babylon.js -->
         <script src="https://preview.babylonjs.com/cannon.js"></script>
-        <script src="https://preview.babylonjs.com/draco_decoder.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>

+ 1 - 1
Playground/index-local.html

@@ -17,7 +17,7 @@
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
     <script src="../dist/preview%20release/cannon.js"></script>
-    <script src="../dist/preview%20release/draco_decoder.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="../tools/DevLoader/BabylonLoader.js"></script>
 

+ 1 - 1
Playground/index.html

@@ -37,7 +37,7 @@
     <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"></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>

+ 1 - 1
Playground/indexStable.html

@@ -37,7 +37,7 @@
     <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
     <!-- Babylon.js -->
     <script src="https://cdn.babylonjs.com/cannon.js"></script>
-    <script src="https://cdn.babylonjs.com/draco_decoder.js"></script>
+    <script src="https://cdn.babylonjs.com/draco_decoder.js" type="text/x-draco-decoder"></script>
     <script src="https://cdn.babylonjs.com/Oimo.js"></script>
     <script src="https://cdn.babylonjs.com/babylon.js"></script>
     <script src="https://cdn.babylonjs.com/inspector/babylon.inspector.bundle.js"></script>

+ 1 - 1
Playground/zipContent/index.html

@@ -9,7 +9,7 @@
         <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"></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>

+ 11 - 9
Tools/Gulp/config.json

@@ -186,6 +186,7 @@
                 "../../src/Tools/babylon.smartArray.js",
                 "../../src/Tools/babylon.tools.js",
                 "../../src/Tools/babylon.promise.js",
+                "../../src/Tools/babylon.workerPool.js",
                 "../../src/States/babylon.alphaCullingState.js",
                 "../../src/States/babylon.depthCullingState.js",
                 "../../src/States/babylon.stencilState.js",
@@ -274,10 +275,10 @@
             "files": [
                 "../../src/Particles/babylon.particle.js",
                 "../../src/Particles/babylon.particleSystem.js",
+                "../../src/Particles/EmitterTypes/babylon.IParticleEmitterType.js",
                 "../../src/Particles/EmitterTypes/babylon.boxParticleEmitter.js",
                 "../../src/Particles/EmitterTypes/babylon.coneParticleEmitter.js",
-                "../../src/Particles/EmitterTypes/babylon.sphereParticleEmitter.js",
-                "../../src/Particles/EmitterTypes/babylon.IParticleEmitterType.js"
+                "../../src/Particles/EmitterTypes/babylon.sphereParticleEmitter.js"
             ],
             "dependUpon": [
                 "core"
@@ -289,7 +290,7 @@
         },
         "gpuParticles": {
             "files": [
-                "../../src/Particles/babylon.gpuParticleSystem.js"          
+                "../../src/Particles/babylon.gpuParticleSystem.js"
             ],
             "dependUpon": [
                 "core",
@@ -430,8 +431,7 @@
                 "additionalMeshes"
             ]
         },
-        "meshCompression" :
-        {
+        "meshCompression": {
             "files": [
                 "../../src/Mesh/Compression/babylon.dracoCompression.js"
             ]
@@ -1676,7 +1676,8 @@
                 "output": "babylon.viewer.js",
                 "webpack": "../../Viewer/webpack.gulp.config.js",
                 "bundle": "true",
-                "moduleDeclaration": "BabylonViewer"
+                "moduleDeclaration": "BabylonViewer",
+                "babylonIncluded": true
             }
         ],
         "build": {
@@ -1685,12 +1686,13 @@
                 {
                     "destination": [
                         {
-                            "filename": "viewer.min.js",
+                            "filename": "viewer.js",
                             "outputDirectory": "/../../Viewer/dist/"
                         },
                         {
                             "filename": "babylon.viewer.js",
-                            "outputDirectory": "/viewer/"
+                            "outputDirectory": "/viewer/",
+                            "addBabylonDeclaration": true
                         }
                     ],
                     "minified": true
@@ -1698,7 +1700,7 @@
                 {
                     "destination": [
                         {
-                            "filename": "viewer.js",
+                            "filename": "viewer.max.js",
                             "outputDirectory": "/../../Viewer/dist/"
                         },
                         {

+ 5 - 1
Tools/Gulp/gulp-addModuleExports.js

@@ -5,7 +5,7 @@ var through = require('through2');
  * The parameters for this function has grown during development.
  * Eventually, this function will need to be reorganized. 
  */
-module.exports = function (varName, subModule, extendsRoot, externalUsingBabylon) {
+module.exports = function (varName, subModule, extendsRoot, externalUsingBabylon, noBabylonInit) {
     return through.obj(function (file, enc, cb) {
 
         var optionalRequire = `var globalObject = (typeof global !== 'undefined') ? global : ((typeof window !== 'undefined') ? window : this);
@@ -75,6 +75,10 @@ globalObject["${base}"] = f;` : '';
             return;
         }
 
+        if (noBabylonInit) {
+            optionalRequire = '';
+        }
+
         try {
             if (externalUsingBabylon) {
                 //file.contents = new Buffer(optionalRequire.concat(String(file.contents)));

+ 8 - 1
Tools/Gulp/gulpfile.js

@@ -434,7 +434,7 @@ var buildExternalLibrary = function (library, settings, watch) {
             let wpBuild = webpack(require(library.webpack));
             if (settings.build.outputs) {
                 let build = wpBuild
-                    .pipe(addModuleExports(library.moduleDeclaration, false, false, true));
+                    .pipe(addModuleExports(library.moduleDeclaration, false, false, true, library.babylonIncluded));
 
                 let unminifiedOutpus = [];
                 let minifiedOutputs = [];
@@ -455,6 +455,12 @@ var buildExternalLibrary = function (library, settings, watch) {
                     build = build
                         .pipe(rename(dest.filename.replace(".js", library.noBundleInName ? '.js' : ".bundle.js")))
                         .pipe(gulp.dest(outputDirectory));
+
+                    if (library.babylonIncluded && dest.addBabylonDeclaration) {
+                        // include the babylon declaration
+                        sequence.unshift(gulp.src(config.build.outputDirectory + '/' + config.build.declarationFilename)
+                            .pipe(gulp.dest(outputDirectory)))
+                    }
                 }
 
                 unminifiedOutpus.forEach(dest => {
@@ -472,6 +478,7 @@ var buildExternalLibrary = function (library, settings, watch) {
                 });
 
                 sequence.push(build);
+
             } else {
                 sequence.push(
                     wpBuild

+ 1 - 0
Viewer/dist/_redirects

@@ -0,0 +1 @@
+/viewer.min.js /viewer.js

+ 15 - 18
Viewer/src/configuration/loader.ts

@@ -3,13 +3,17 @@ import { ViewerConfiguration } from './configuration';
 import { getConfigurationType } from './types';
 
 import * as deepmerge from '../../assets/deepmerge.min.js';
+import { Tools, IFileRequest } from 'babylonjs';
 
 export class ConfigurationLoader {
 
     private configurationCache: { [url: string]: any };
 
+    private loadRequests: Array<IFileRequest>;
+
     constructor() {
         this.configurationCache = {};
+        this.loadRequests = [];
     }
 
     public loadConfiguration(initConfig: ViewerConfiguration = {}, callback?: (config: ViewerConfiguration) => void): Promise<ViewerConfiguration> {
@@ -65,33 +69,26 @@ export class ConfigurationLoader {
         }
     }
 
+    public dispose() {
+        this.loadRequests.forEach(request => {
+            request.abort();
+        });
+    }
+
     private loadFile(url: string): Promise<any> {
         let cacheReference = this.configurationCache;
         if (cacheReference[url]) {
             return Promise.resolve(cacheReference[url]);
         }
 
-        return new Promise(function (resolve, reject) {
-            var xhr = new XMLHttpRequest();
-            xhr.open('GET', url);
-            xhr.send();
-            xhr.onreadystatechange = function () {
-                var DONE = 4;
-                var OK = 200;
-                if (xhr.readyState === DONE) {
-                    if (xhr.status === OK) {
-                        cacheReference[url] = xhr.responseText;
-                        resolve(xhr.responseText); // 'This is the returned text.'
-                    } else {
-                        console.log('Error: ' + xhr.status, url);
-                        reject('Error: ' + xhr.status); // An error occurred during the request.
-                    }
-                }
-            }
+        return new Promise((resolve, reject) => {
+            let fileRequest = Tools.LoadFile(url, resolve, undefined, undefined, false, (request, error: any) => {
+                reject(error);
+            });
+            this.loadRequests.push(fileRequest);
         });
     }
 
-
 }
 
 export let configurationLoader = new ConfigurationLoader();

+ 0 - 26
Viewer/src/helper.ts

@@ -5,32 +5,6 @@ export function isUrl(urlToCheck: string): boolean {
     return false;
 }
 
-export function loadFile(url: string): Promise<any> {
-    /*let cacheReference = this.configurationCache;
-    if (cacheReference[url]) {
-        return Promise.resolve(cacheReference[url]);
-    }*/
-
-    return new Promise(function (resolve, reject) {
-        var xhr = new XMLHttpRequest();
-        xhr.open('GET', url);
-        xhr.send();
-        xhr.onreadystatechange = function () {
-            var DONE = 4;
-            var OK = 200;
-            if (xhr.readyState === DONE) {
-                if (xhr.status === OK) {
-                    //cacheReference[url] = xhr.responseText;
-                    resolve(xhr.responseText); // 'This is the returned text.'
-                }
-            } else {
-                console.log('Error: ' + xhr.status, url);
-                reject('Error: ' + xhr.status); // An error occurred during the request.
-            }
-        }
-    });
-}
-
 export function kebabToCamel(s) {
     return s.replace(/(\-\w)/g, function (m) { return m[1].toUpperCase(); });
 }

+ 47 - 24
Viewer/src/templateManager.ts

@@ -1,6 +1,6 @@
 
-import { Observable } from 'babylonjs';
-import { isUrl, loadFile, camelToKebab, kebabToCamel } from './helper';
+import { Observable, IFileRequest, Tools } from 'babylonjs';
+import { isUrl, camelToKebab, kebabToCamel } from './helper';
 
 export interface ITemplateConfiguration {
     location?: string; // #template-id OR http://example.com/loading.html
@@ -212,6 +212,8 @@ export class Template {
     private fragment: DocumentFragment;
     private htmlTemplate: string;
 
+    private loadRequests: Array<IFileRequest>;
+
     constructor(public name: string, private _configuration: ITemplateConfiguration) {
         this.onInit = new Observable<Template>();
         this.onLoaded = new Observable<Template>();
@@ -219,6 +221,8 @@ export class Template {
         this.onStateChange = new Observable<Template>();
         this.onEventTriggered = new Observable<EventCallback>();
 
+        this.loadRequests = [];
+
         this.isLoaded = false;
         this.isShown = false;
         /*
@@ -228,7 +232,7 @@ export class Template {
         */
         this.onInit.notifyObservers(this);
 
-        let htmlContentPromise = getTemplateAsHtml(_configuration);
+        let htmlContentPromise = this.getTemplateAsHtml(_configuration);
 
         this.initPromise = htmlContentPromise.then(htmlTemplate => {
             if (htmlTemplate) {
@@ -299,8 +303,12 @@ export class Template {
         });
     }
 
+    private isShowing: boolean;
+    private isHiding: boolean;
     public show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template> {
+        if (this.isHiding) return Promise.resolve(this);
         return Promise.resolve().then(() => {
+            this.isShowing = true;
             if (visibilityFunction) {
                 return visibilityFunction(this);
             } else {
@@ -310,13 +318,16 @@ export class Template {
             }
         }).then(() => {
             this.isShown = true;
+            this.isShowing = false;
             this.onStateChange.notifyObservers(this);
             return this;
         });
     }
 
     public hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template> {
+        if (this.isShowing) return Promise.resolve(this);
         return Promise.resolve().then(() => {
+            this.isHiding = true;
             if (visibilityFunction) {
                 return visibilityFunction(this);
             } else {
@@ -326,6 +337,7 @@ export class Template {
             }
         }).then(() => {
             this.isShown = false;
+            this.isHiding = false;
             this.onStateChange.notifyObservers(this);
             return this;
         });
@@ -340,6 +352,38 @@ export class Template {
         this.isLoaded = false;
         // remove from parent
         this.parent.removeChild(this.fragment);
+
+        this.loadRequests.forEach(request => {
+            request.abort();
+        });
+    }
+
+    private getTemplateAsHtml(templateConfig: ITemplateConfiguration): Promise<string> {
+        if (!templateConfig) {
+            return Promise.reject('No templateConfig provided');
+        } else if (templateConfig.html) {
+            return Promise.resolve(templateConfig.html);
+        } else {
+            let location = getTemplateLocation(templateConfig);
+            if (isUrl(location)) {
+                return new Promise((resolve, reject) => {
+                    let fileRequest = Tools.LoadFile(location, (data: string) => {
+                        resolve(data);
+                    }, undefined, undefined, false, (request, error: any) => {
+                        reject(error);
+                    });
+                    this.loadRequests.push(fileRequest);
+                });
+            } else {
+                location = location.replace('#', '');
+                let element = document.getElementById(location);
+                if (element) {
+                    return Promise.resolve(element.innerHTML);
+                } else {
+                    return Promise.reject('Template ID not found');
+                }
+            }
+        }
     }
 
     private registeredEvents: Array<{ htmlElement: HTMLElement, eventName: string, function: EventListenerOrEventListenerObject }>;
@@ -389,27 +433,6 @@ export class Template {
     }
 }
 
-export function getTemplateAsHtml(templateConfig: ITemplateConfiguration): Promise<string> {
-    if (!templateConfig) {
-        return Promise.reject('No templateConfig provided');
-    } else if (templateConfig.html) {
-        return Promise.resolve(templateConfig.html);
-    } else {
-        let location = getTemplateLocation(templateConfig);
-        if (isUrl(location)) {
-            return loadFile(location);
-        } else {
-            location = location.replace('#', '');
-            let element = document.getElementById(location);
-            if (element) {
-                return Promise.resolve(element.innerHTML);
-            } else {
-                return Promise.reject('Template ID not found');
-            }
-        }
-    }
-}
-
 export function getTemplateLocation(templateConfig): string {
     if (!templateConfig || typeof templateConfig === 'string') {
         return templateConfig;

+ 2 - 2
Viewer/src/viewer/defaultViewer.ts

@@ -103,8 +103,8 @@ export class DefaultViewer extends AbstractViewer {
         this.containerElement.style.display = 'flex';
     }
 
-    protected configureModel(modelConfiguration: Partial<IModelConfiguration>) {
-        super.configureModel(modelConfiguration);
+    protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<AbstractMesh> = this.scene.meshes) {
+        super.configureModel(modelConfiguration, focusMeshes);
 
         let navbar = this.templateManager.getTemplate('navBar');
         if (!navbar) return;

+ 41 - 7
Viewer/src/viewer/viewer.ts

@@ -421,7 +421,9 @@ export abstract class AbstractViewer {
             }
         };
 
-        const sceneExtends = this.scene.getWorldExtends();
+        const sceneExtends = this.scene.getWorldExtends((mesh) => {
+            return !this.environmentHelper || (mesh !== this.environmentHelper.ground && mesh !== this.environmentHelper.rootMesh && mesh !== this.environmentHelper.skybox);
+        });
         const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
         const sceneDiagonalLenght = sceneDiagonal.length();
         if (isFinite(sceneDiagonalLenght))
@@ -744,13 +746,36 @@ export abstract class AbstractViewer {
         return Promise.resolve(this.scene);
     }
 
+    private isLoading: boolean;
+    private nextLoading: Function;
+
     public loadModel(model: any = this.configuration.model, clearScene: boolean = true): Promise<Scene> {
         // no model was provided? Do nothing!
-        if (!model.url) {
+        let modelUrl = (typeof model === 'string') ? model : model.url;
+        if (!modelUrl) {
             return Promise.resolve(this.scene);
         }
-        this.configuration.model = model;
-        let modelUrl = (typeof model === 'string') ? model : model.url;
+        if (this.isLoading) {
+            //another model is being model. Wait for it to finish, trigger the load afterwards
+            this.nextLoading = () => {
+                delete this.nextLoading;
+                this.loadModel(model, clearScene);
+            }
+            return Promise.resolve(this.scene);
+        }
+        this.isLoading = true;
+        if ((typeof model === 'string')) {
+            if (this.configuration.model && typeof this.configuration.model === 'object') {
+                this.configuration.model.url = model;
+            }
+        } else {
+            if (this.configuration.model) {
+                deepmerge(this.configuration.model, model)
+            } else {
+                this.configuration.model = model;
+            }
+        }
+
         let parts = modelUrl.split('/');
         let filename = parts.pop();
         let base = parts.join('/') + '/';
@@ -761,13 +786,18 @@ export abstract class AbstractViewer {
 
             if (clearScene) {
                 scene.meshes.forEach(mesh => {
-                    mesh.dispose();
+                    if (Tags.MatchesQuery(mesh, "viewerMesh")) {
+                        mesh.dispose();
+                    }
                 });
             }
             return scene!;
         }).then(() => {
             return new Promise<Array<AbstractMesh>>((resolve, reject) => {
                 this.lastUsedLoader = SceneLoader.ImportMesh(undefined, base, filename, this.scene, (meshes) => {
+                    meshes.forEach(mesh => {
+                        Tags.AddTagsTo(mesh, "viewerMesh");
+                    });
                     resolve(meshes);
                 }, (progressEvent) => {
                     this.onModelLoadProgressObservable.notifyObserversWithPromise(progressEvent);
@@ -783,7 +813,7 @@ export abstract class AbstractViewer {
             return this.onModelLoadedObservable.notifyObserversWithPromise(meshes)
                 .then(() => {
                     // update the models' configuration
-                    this.configureModel(model, meshes);
+                    this.configureModel(this.configuration.model || model, meshes);
                     this.configureLights(this.configuration.lights);
 
                     if (this.configuration.camera) {
@@ -791,12 +821,16 @@ export abstract class AbstractViewer {
                     }
                     return this.initEnvironment(meshes);
                 }).then(() => {
+                    this.isLoading = false;
+                    if (this.nextLoading) {
+                        return this.nextLoading();
+                    }
                     return this.scene;
                 });
         });
     }
 
-    protected initEnvironment(focusMeshes: Array<AbstractMesh> = []): Promise<Scene> {
+    protected initEnvironment(focusMeshes: Array<AbstractMesh> = this.scene.meshes): Promise<Scene> {
         this.configureEnvironment(this.configuration.skybox, this.configuration.ground);
 
         return Promise.resolve(this.scene);

File diff suppressed because it is too large
+ 7999 - 7991
dist/preview release/babylon.d.ts


File diff suppressed because it is too large
+ 13 - 13
dist/preview release/babylon.js


File diff suppressed because it is too large
+ 50 - 22
dist/preview release/babylon.max.js


File diff suppressed because it is too large
+ 13 - 13
dist/preview release/babylon.worker.js


File diff suppressed because it is too large
+ 1506 - 1504
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


File diff suppressed because it is too large
+ 6 - 6
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


File diff suppressed because it is too large
+ 10 - 8
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


File diff suppressed because it is too large
+ 10 - 8
dist/preview release/customConfigurations/minimalGLTFViewer/es6.js


File diff suppressed because it is too large
+ 50 - 22
dist/preview release/es6.js


+ 1 - 1
dist/preview release/gui/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/inspector/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/loaders/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/materialsLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/postProcessesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/proceduralTexturesLibrary/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
dist/preview release/serializers/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 8 - 24
dist/preview release/viewer/babylon.viewer.d.ts

@@ -1,5 +1,3 @@
-/// <reference path="../babylon.d.ts"/>
-
 declare module BabylonViewer {
 
     export let disableInit: boolean;
@@ -56,7 +54,7 @@ declare module BabylonViewer {
         selector: string;
         payload?: any;
     }
-    class TemplateManager {
+    interface TemplateManager {
         containerElement: HTMLElement;
         onInit: BABYLON.Observable<Template>;
         onLoaded: BABYLON.Observable<Template>;
@@ -64,20 +62,16 @@ declare module BabylonViewer {
         onAllLoaded: BABYLON.Observable<TemplateManager>;
         onEventTriggered: BABYLON.Observable<EventCallback>;
         eventManager: EventManager;
-        private templates;
         constructor(containerElement: HTMLElement);
         initTemplate(templates: {
             [key: string]: ITemplateConfiguration;
         }): void;
-        private buildHTMLTree(templates);
         getCanvas(): HTMLCanvasElement | null;
         getTemplate(name: string): Template | undefined;
-        private checkLoadedState();
     }
 
-    class Template {
+    interface Template {
         name: string;
-        private _configuration;
         onInit: BABYLON.Observable<Template>;
         onLoaded: BABYLON.Observable<Template>;
         onAppended: BABYLON.Observable<Template>;
@@ -87,7 +81,6 @@ declare module BabylonViewer {
         isShown: boolean;
         parent: HTMLElement;
         initPromise: Promise<Template>;
-        private fragment;
         constructor(name: string, _configuration: ITemplateConfiguration);
         readonly configuration: ITemplateConfiguration;
         getChildElements(): Array<string>;
@@ -95,11 +88,9 @@ declare module BabylonViewer {
         show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
         hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
         dispose(): void;
-        private registerEvents();
     }
 
-    class ViewerManager {
-        private viewers;
+    interface ViewerManager {
         onViewerAdded: (viewer: AbstractViewer) => void;
         onViewerAddedObservable: BABYLON.Observable<AbstractViewer>;
         constructor();
@@ -107,7 +98,6 @@ declare module BabylonViewer {
         getViewerById(id: string): AbstractViewer;
         getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
         getViewerPromiseById(id: string): Promise<AbstractViewer>;
-        private _onViewerAdded(viewer);
     }
     export let viewerManager: ViewerManager;
 
@@ -119,37 +109,31 @@ declare module BabylonViewer {
 
     export function InitTags(selector?: string): void;
 
-    class EventManager {
-        private templateManager;
-        private callbacksContainer;
+    interface EventManager {
         constructor(templateManager: TemplateManager);
         registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
         unregisterCallback(templateName: string, callback?: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-        private eventTriggered(data);
     }
 
-    class PromiseObservable<T> extends BABYLON.Observable<T> {
+    interface PromiseObservable<T> extends BABYLON.Observable<T> {
         notifyWithPromise(eventData: T, mask?: number, target?: any, currentTarget?: any): Promise<any>;
     }
 
     export interface IMapper {
         map(rawSource: any): ViewerConfiguration;
     }
-    class MapperManager {
-        private mappers;
-        static DefaultMapper: string;
+    interface MapperManager {
+        DefaultMapper: string;
         constructor();
         getMapper(type: string): IMapper;
         registerMapper(type: string, mapper: IMapper): void;
     }
     export let mapperManager: MapperManager;
 
-    class ConfigurationLoader {
-        private configurationCache;
+    interface ConfigurationLoader {
         constructor();
         loadConfiguration(initConfig?: ViewerConfiguration): Promise<ViewerConfiguration>;
         getConfigurationType(type: string): void;
-        private loadFile(url);
     }
     export let configurationLoader: ConfigurationLoader;
 

File diff suppressed because it is too large
+ 48 - 48
dist/preview release/viewer/babylon.viewer.js


File diff suppressed because it is too large
+ 84 - 83
dist/preview release/viewer/babylon.viewer.max.js


+ 4 - 484
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -1,486 +1,6 @@
-/// <reference types="babylonjs"/>
+/// <reference path="./babylon.d.ts"/>
+/// <reference path="./babylon.viewer.d.ts"/>
 
-declare module BabylonViewer {
-
-    export let disableInit: boolean;
-
-    export interface ITemplateConfiguration {
-        location?: string;
-        html?: string;
-        id?: string;
-        params?: {
-            [key: string]: string | number | boolean | object;
-        };
-        events?: {
-            pointerdown?: boolean | {
-                [id: string]: boolean;
-            };
-            pointerup?: boolean | {
-                [id: string]: boolean;
-            };
-            pointermove?: boolean | {
-                [id: string]: boolean;
-            };
-            pointerover?: boolean | {
-                [id: string]: boolean;
-            };
-            pointerout?: boolean | {
-                [id: string]: boolean;
-            };
-            pointerenter?: boolean | {
-                [id: string]: boolean;
-            };
-            pointerleave?: boolean | {
-                [id: string]: boolean;
-            };
-            pointercancel?: boolean | {
-                [id: string]: boolean;
-            };
-            click?: boolean | {
-                [id: string]: boolean;
-            };
-            dragstart?: boolean | {
-                [id: string]: boolean;
-            };
-            drop?: boolean | {
-                [id: string]: boolean;
-            };
-            [key: string]: boolean | {
-                [id: string]: boolean;
-            } | undefined;
-        };
-    }
-    export interface EventCallback {
-        event: Event;
-        template: Template;
-        selector: string;
-        payload?: any;
-    }
-    class TemplateManager {
-        containerElement: HTMLElement;
-        onInit: BABYLON.Observable<Template>;
-        onLoaded: BABYLON.Observable<Template>;
-        onStateChange: BABYLON.Observable<Template>;
-        onAllLoaded: BABYLON.Observable<TemplateManager>;
-        onEventTriggered: BABYLON.Observable<EventCallback>;
-        eventManager: EventManager;
-        private templates;
-        constructor(containerElement: HTMLElement);
-        initTemplate(templates: {
-            [key: string]: ITemplateConfiguration;
-        }): void;
-        private buildHTMLTree(templates);
-        getCanvas(): HTMLCanvasElement | null;
-        getTemplate(name: string): Template | undefined;
-        private checkLoadedState();
-    }
-
-    class Template {
-        name: string;
-        private _configuration;
-        onInit: BABYLON.Observable<Template>;
-        onLoaded: BABYLON.Observable<Template>;
-        onAppended: BABYLON.Observable<Template>;
-        onStateChange: BABYLON.Observable<Template>;
-        onEventTriggered: BABYLON.Observable<EventCallback>;
-        isLoaded: boolean;
-        isShown: boolean;
-        parent: HTMLElement;
-        initPromise: Promise<Template>;
-        private fragment;
-        constructor(name: string, _configuration: ITemplateConfiguration);
-        readonly configuration: ITemplateConfiguration;
-        getChildElements(): Array<string>;
-        appendTo(parent: HTMLElement): void;
-        show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-        hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-        dispose(): void;
-        private registerEvents();
-    }
-
-    class ViewerManager {
-        private viewers;
-        onViewerAdded: (viewer: AbstractViewer) => void;
-        onViewerAddedObservable: BABYLON.Observable<AbstractViewer>;
-        constructor();
-        addViewer(viewer: AbstractViewer): void;
-        getViewerById(id: string): AbstractViewer;
-        getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
-        getViewerPromiseById(id: string): Promise<AbstractViewer>;
-        private _onViewerAdded(viewer);
-    }
-    export let viewerManager: ViewerManager;
-
-    export const enum CameraBehavior {
-        AUTOROTATION = 0,
-        BOUNCING = 1,
-        FRAMING = 2,
-    }
-
-    export function InitTags(selector?: string): void;
-
-    class EventManager {
-        private templateManager;
-        private callbacksContainer;
-        constructor(templateManager: TemplateManager);
-        registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-        unregisterCallback(templateName: string, callback?: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-        private eventTriggered(data);
-    }
-
-    class PromiseObservable<T> extends BABYLON.Observable<T> {
-        notifyWithPromise(eventData: T, mask?: number, target?: any, currentTarget?: any): Promise<any>;
-    }
-
-    export interface IMapper {
-        map(rawSource: any): ViewerConfiguration;
-    }
-    class MapperManager {
-        private mappers;
-        static DefaultMapper: string;
-        constructor();
-        getMapper(type: string): IMapper;
-        registerMapper(type: string, mapper: IMapper): void;
-    }
-    export let mapperManager: MapperManager;
-
-    class ConfigurationLoader {
-        private configurationCache;
-        constructor();
-        loadConfiguration(initConfig?: ViewerConfiguration): Promise<ViewerConfiguration>;
-        getConfigurationType(type: string): void;
-        private loadFile(url);
-    }
-    export let configurationLoader: ConfigurationLoader;
-
-
-    /////> configuration
-    export interface ViewerConfiguration {
-
-        // configuration version
-        version?: string;
-        extends?: string; // is this configuration extending an existing configuration?
-
-        pageUrl?: string; // will be used for sharing and other fun stuff. This is the page showing the model (not the model's url!)
-
-        configuration?: string | {
-            url?: string;
-            payload?: any;
-            mapper?: string; // json (default), html, yaml, xml, etc'. if not provided, file extension will be used.
-        };
-
-        // names of functions in the window context.
-        observers?: IObserversConfiguration;
-
-        canvasElement?: string; // if there is a need to override the standard implementation - ID of HTMLCanvasElement
-
-        model?: IModelConfiguration | string;
-
-        scene?: ISceneConfiguration;
-        optimizer?: ISceneOptimizerConfiguration | boolean;
-        // at the moment, support only a single camera.
-        camera?: ICameraConfiguration,
-        skybox?: boolean | ISkyboxConfiguration;
-
-        ground?: boolean | IGroundConfiguration;
-        lights?: { [name: string]: boolean | ILightConfiguration },
-        // engine configuration. optional!
-        engine?: {
-            antialiasing?: boolean;
-            disableResize?: boolean;
-            engineOptions?: { [key: string]: any };
-            adaptiveQuality?: boolean;
-        },
-        //templateStructure?: ITemplateStructure,
-        templates?: {
-            main: ITemplateConfiguration,
-            [key: string]: ITemplateConfiguration
-        };
-
-        customShaders?: {
-            shaders?: {
-                [key: string]: string;
-            };
-            includes?: {
-                [key: string]: string;
-            }
-        }
-
-        // features that are being tested.
-        // those features' syntax will change and move out! 
-        // Don't use in production (or be ready to make the changes :) )
-        lab?: {
-            flashlight?: boolean | {
-                exponent?: number;
-                angle?: number;
-                intensity?: number;
-                diffuse?: { r: number, g: number, b: number };
-                specular?: { r: number, g: number, b: number };
-            }
-            hideLoadingDelay?: number;
-        }
-    }
-
-    export interface IModelConfiguration {
-        url?: string;
-        loader?: string; // obj, gltf?
-        position?: { x: number, y: number, z: number };
-        rotation?: { x: number, y: number, z: number, w?: number };
-        scaling?: { x: number, y: number, z: number };
-        parentObjectIndex?: number; // the index of the parent object of the model in the loaded meshes array.
-
-        castShadow?: boolean;
-        normalize?: boolean | {
-            center?: boolean;
-            unitSize?: boolean;
-            parentIndex?: number;
-        }; // shoud the model be scaled to unit-size
-
-        title?: string;
-        subtitle?: string;
-        thumbnail?: string; // URL or data-url
-
-        // [propName: string]: any; // further configuration, like title and creator
-    }
-
-    export interface ISkyboxConfiguration {
-        cubeTexture?: {
-            noMipMap?: boolean;
-            gammaSpace?: boolean;
-            url?: string | Array<string>;
-        };
-        color?: { r: number, g: number, b: number };
-        pbr?: boolean; // deprecated
-        scale?: number;
-        blur?: number; // deprecated
-        material?: {
-            imageProcessingConfiguration?: IImageProcessingConfiguration;
-            [propName: string]: any;
-        };
-        infiniteDIstance?: boolean;
-
-    }
-
-    export interface IGroundConfiguration {
-        size?: number;
-        receiveShadows?: boolean;
-        shadowLevel?: number;
-        shadowOnly?: boolean; // deprecated
-        mirror?: boolean | {
-            sizeRatio?: number;
-            blurKernel?: number;
-            amount?: number;
-            fresnelWeight?: number;
-            fallOffDistance?: number;
-            textureType?: number;
-        };
-        texture?: string;
-        color?: { r: number, g: number, b: number };
-        opacity?: number;
-        material?: { // deprecated!
-            [propName: string]: any;
-        };
-    }
-
-    export interface ISceneConfiguration {
-        debug?: boolean;
-        autoRotate?: boolean; // deprecated
-        rotationSpeed?: number; // deprecated
-        defaultCamera?: boolean; // deprecated
-        defaultLight?: boolean; // deprecated
-        clearColor?: { r: number, g: number, b: number, a: number };
-        imageProcessingConfiguration?: IImageProcessingConfiguration;
-        environmentTexture?: string;
-    }
-
-    export interface ISceneOptimizerConfiguration {
-        targetFrameRate?: number;
-        trackerDuration?: number;
-        autoGeneratePriorities?: boolean;
-        improvementMode?: boolean;
-        degradation?: string; // low, moderate, high
-        types?: {
-            texture?: ISceneOptimizerParameters;
-            hardwareScaling?: ISceneOptimizerParameters;
-            shadow?: ISceneOptimizerParameters;
-            postProcess?: ISceneOptimizerParameters;
-            lensFlare?: ISceneOptimizerParameters;
-            particles?: ISceneOptimizerParameters;
-            renderTarget?: ISceneOptimizerParameters;
-            mergeMeshes?: ISceneOptimizerParameters;
-        }
-    }
-
-    export interface IObserversConfiguration {
-        onEngineInit?: string;
-        onSceneInit?: string;
-        onModelLoaded?: string;
-    }
-
-    export interface ICameraConfiguration {
-        position?: { x: number, y: number, z: number };
-        rotation?: { x: number, y: number, z: number, w: number };
-        fov?: number;
-        fovMode?: number;
-        minZ?: number;
-        maxZ?: number;
-        inertia?: number;
-        behaviors?: {
-            [name: string]: number | {
-                type: number;
-                [propName: string]: any;
-            };
-        };
-
-        [propName: string]: any;
-    }
-
-    export interface ILightConfiguration {
-        type: number;
-        name?: string;
-        disabled?: boolean;
-        position?: { x: number, y: number, z: number };
-        target?: { x: number, y: number, z: number };
-        direction?: { x: number, y: number, z: number };
-        diffuse?: { r: number, g: number, b: number };
-        specular?: { r: number, g: number, b: number };
-        intensity?: number;
-        intensityMode?: number;
-        radius?: number;
-        shadownEnabled?: boolean; // only on specific lights!
-        shadowConfig?: {
-            useBlurExponentialShadowMap?: boolean;
-            useKernelBlur?: boolean;
-            blurKernel?: number;
-            blurScale?: number;
-            minZ?: number;
-            maxZ?: number;
-            frustumSize?: number;
-            angleScale?: number;
-            [propName: string]: any;
-        }
-        [propName: string]: any;
-
-        // no behaviors for light at the moment, but allowing configuration for future reference.
-        behaviors?: {
-            [name: string]: number | {
-                type: number;
-                [propName: string]: any;
-            };
-        };
-    }
-
-    export interface ISceneOptimizerParameters {
-        priority?: number;
-        maximumSize?: number;
-        step?: number;
-    }
-
-    export interface IImageProcessingConfiguration {
-        colorGradingEnabled?: boolean;
-        colorCurvesEnabled?: boolean;
-        colorCurves?: {
-            globalHue?: number;
-            globalDensity?: number;
-            globalSaturation?: number;
-            globalExposure?: number;
-            highlightsHue?: number;
-            highlightsDensity?: number;
-            highlightsSaturation?: number;
-            highlightsExposure?: number;
-            midtonesHue?: number;
-            midtonesDensity?: number;
-            midtonesSaturation?: number;
-            midtonesExposure?: number;
-            shadowsHue?: number;
-            shadowsDensity?: number;
-            shadowsSaturation?: number;
-            shadowsExposure?: number;
-        };
-        colorGradingWithGreenDepth?: boolean;
-        colorGradingBGR?: boolean;
-        exposure?: number;
-        toneMappingEnabled?: boolean;
-        contrast?: number;
-        vignetteEnabled?: boolean;
-        vignetteStretch?: number;
-        vignetteCentreX?: number;
-        vignetteCentreY?: number;
-        vignetteWeight?: number;
-        vignetteColor?: { r: number, g: number, b: number, a?: number };
-        vignetteCameraFov?: number;
-        vignetteBlendMode?: number;
-        vignetteM?: boolean;
-        applyByPostProcess?: boolean;
-
-    }
-    /////>configuration
-
-    /////<viewer
-    export abstract class AbstractViewer {
-        containerElement: HTMLElement;
-        templateManager: TemplateManager;
-        camera: BABYLON.ArcRotateCamera;
-        engine: BABYLON.Engine;
-        scene: BABYLON.Scene;
-        baseId: string;
-        canvas: HTMLCanvasElement;
-        protected configuration: ViewerConfiguration;
-        environmentHelper: BABYLON.EnvironmentHelper;
-        protected defaultHighpTextureType: number;
-        protected shadowGeneratorBias: number;
-        protected defaultPipelineTextureType: number;
-        protected maxShadows: number;
-        onSceneInitObservable: BABYLON.Observable<BABYLON.Scene>;
-        onEngineInitObservable: BABYLON.Observable<BABYLON.Engine>;
-        onModelLoadedObservable: BABYLON.Observable<BABYLON.AbstractMesh[]>;
-        onModelLoadProgressObservable: BABYLON.Observable<BABYLON.SceneLoaderProgressEvent>;
-        onModelLoadErrorObservable: BABYLON.Observable<{ message: string; exception: any }>;
-        onLoaderInitObservable: BABYLON.Observable<BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync>;
-        onInitDoneObservable: BABYLON.Observable<AbstractViewer>;
-        constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
-        getBaseId(): string;
-        protected abstract prepareContainerElement(): any;
-        protected onTemplatesLoaded(): Promise<AbstractViewer>;
-        protected initEngine(): Promise<BABYLON.Engine>;
-        protected initScene(): Promise<BABYLON.Scene>;
-        dispose(): void;
-        loadModel(model?: any, clearScene?: boolean): Promise<BABYLON.Scene>;
-        lastUsedLoader: BABYLON.ISceneLoaderPlugin | BABYLON.ISceneLoaderPluginAsync;
-        sceneOptimizer: BABYLON.SceneOptimizer;
-        protected registeredOnBeforerenderFunctions: Array<() => void>;
-        isCanvasInDOM(): boolean;
-        protected resize: () => void;
-        protected render: () => void;
-        updateConfiguration(newConfiguration: Partial<ViewerConfiguration>): void;
-        protected configureEnvironment(skyboxConifguration?: ISkyboxConfiguration | boolean, groundConfiguration?: IGroundConfiguration | boolean): void;
-        protected configureScene(sceneConfig: ISceneConfiguration, optimizerConfig?: ISceneOptimizerConfiguration): void;
-        protected configureOptimizer(optimizerConfig: ISceneOptimizerConfiguration | boolean): void;
-        protected configureObservers(observersConfiguration: IObserversConfiguration): void;
-        protected configureCamera(cameraConfig: ICameraConfiguration, focusMeshes: Array<BABYLON.AbstractMesh>): void;
-        protected configureLights(lightsConfiguration: { [name: string]: ILightConfiguration | boolean }, focusMeshes: Array<BABYLON.AbstractMesh>): void;
-        protected configureModel(modelConfiguration: Partial<IModelConfiguration>, focusMeshes: Array<BABYLON.AbstractMesh>): void;
-        dispose(): void;
-        protected initEnvironment(focusMeshes: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
-        protected injectCustomShaders(): void;
-        protected extendClassWithConfig(object: any, config: any): void;
-        protected handleHardwareLimitations(): void;
-
-
-    }
-
-    export class DefaultViewer extends AbstractViewer {
-        containerElement: HTMLElement;
-        camera: BABYLON.ArcRotateCamera;
-        constructor(containerElement: HTMLElement, initialConfiguration?: ViewerConfiguration);
-        initScene(): Promise<BABYLON.Scene>;
-        protected onTemplatesLoaded(): Promise<AbstractViewer>;
-        protected prepareContainerElement(): void;
-        loadModel(model?: any): Promise<BABYLON.Scene>;
-        initEnvironment(focusMeshes?: Array<BABYLON.AbstractMesh>): Promise<BABYLON.Scene>;
-        showOverlayScreen(subScreen: string): Promise<Template>;
-        hideOverlayScreen(): Promise<Template>;
-        showLoadingScreen(): Promise<Template>;
-        hideLoadingScreen(): Promise<Template>;
-    }
+declare module "babylonjs-viewer" {
+    export = BabylonViewer;
 }

+ 3 - 1
dist/preview release/viewer/package.json

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-viewer",
     "description": "A simple-to-use viewer based on BabylonJS to display 3D elements natively",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -12,7 +12,9 @@
     "main": "babylon.viewer.js",
     "files": [
         "babylon.viewer.js",
+        "babylon.viewer.d.ts",
         "babylon.viewer.module.d.ts",
+        "babylon.d.ts",
         "readme.md",
         "package.json"
     ],

+ 25 - 12
dist/preview release/viewer/readme.md

@@ -1,5 +1,4 @@
-Babylon.js Viewer
-=====================
+# Babylon.js Viewer
 
 Babylon's viewer is a wrapper around Babylon, that automatically initializes the needed components in order to display a loaded model. It is easy to use, and require no coding at all.
 
@@ -9,13 +8,13 @@ for basic and advanced usage instructions please read the doc at https://doc.bab
 
 The source code can be found at https://github.com/BabylonJS/Babylon.js/tree/master/Viewer
 
-# Basic usage
+## Basic usage
 
-to create a simple viewer add the following code to your HTML>
+to create a simple viewer add the following code to your HTML:
 
 ```HTML
 <babylon model="https://playground.babylonjs.com/scenes/Rabbit.babylon"></babylon>
-<script src="https://viewer.babylonjs.com/viewer.min.js"></script>
+<script src="https://viewer.babylonjs.com/viewer.js"></script>
 ```
 
 Make sure to size the babylon HTML tag. For example:
@@ -29,28 +28,42 @@ babylon {
 }
 ```
 
-# Installation instructions
+## Installation instructions
 
-## CDN
+### CDN
 
-Compiled js files (minified) are offered on our public CDN here:
+Compiled js files are offered on our public CDN here:
 
-* https://viewer.babylonjs.com/serializers/viewer.min.js
+* https://viewer.babylonjs.com/viewer.js (minified)
+* https://viewer.babylonjs.com/viewer.max.js
 
-## NPM
+### Using NPM
 
 To install using npm :
 
-```
+```javascript
 npm install --save babylonjs-viewer
 ```
 
 Afterwards it can be imported to the project using:
 
-```
+```javascript
 import from 'babylonjs-viewer';
 ```
 
 This will enable the BabylonViewer namespace.
 
 Using webpack to package your project will use the minified js file.
+
+## TypeScript
+
+If you use the npm package, starting 3.2.0-alpha8 the babylon viewer has a module declaration file that also includes the BABYLON namespace.
+
+Using TypeScript and NPM you could do the following:
+
+```javascript
+import from 'babylonjs-viewer';
+
+// both namespaces are now available
+console.log(BabylonViewer, BABYLON)
+```

+ 5 - 2
dist/preview release/what's new.md

@@ -71,9 +71,11 @@
 - Add isThumbClamped option on GUI slider control ([JeanPhilippeKernel](https://github.com/JeanPhilippeKernel))
 - Add floating point texture support for RenderTargetCubeTexture ([PeapBoy](https://github.com/NicolasBuecher))
 - Support for mutli-touch when interacting with multiple gui elements simultaneously ([trevordev](https://github.com/trevordev))
-- Support depth maps for multiple active cameras ([trevordev](https://github.com/trevordev))
-- (Viewer) Declaration file published. ([RaananW](https://github.com/RaananW))
+- (Viewer) Declaration file published, ready for npm. ([RaananW](https://github.com/RaananW))
 - Added Draco mesh compression support to glTF 2.0 loader. ([bghgary](https://github.com/bghgary))
+- (Viewer) XHR requests not use Tools.LoadFile and are disposed correctly - [#3671](https://github.com/BabylonJS/Babylon.js/issues/3671) ([RaananW](https://github.com/RaananW))
+- Added `Tools.WorkerPool` class for web worker management. ([bghgary](https://github.com/bghgary))
+- Support depth maps for multiple active cameras ([trevordev](https://github.com/trevordev))
 
 ## Bug fixes
 
@@ -83,6 +85,7 @@
 - SPS internal temporary vector3 instead of Tmp.Vector3 to avoid possible concurrent uses ([jbousquie](https://github.com/jbousquie))
 - Fixed a bug when calling load on an empty assets manager - [#3739](https://github.com/BabylonJS/Babylon.js/issues/3739). ([RaananW](https://github.com/RaananW))
 - Enabling teleportation in the vr helper class caused a redundant post process to be added ([trevordev](https://github.com/trevordev))
+- (Viewer) Fixed a bug where loading another mesh positioned it incorrectly ([RaananW](https://github.com/RaananW))
 
 ## Breaking changes
 

+ 26 - 5
loaders/src/glTF/2.0/Extensions/KHR_draco_mesh_compression.ts

@@ -1,7 +1,7 @@
 /// <reference path="../../../../../dist/preview release/babylon.d.ts"/>
 
 module BABYLON.GLTF2.Extensions {
-    // https://github.com/KhronosGroup/glTF/pull/874
+    // https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_draco_mesh_compression
 
     const NAME = "KHR_draco_mesh_compression";
 
@@ -13,6 +13,25 @@ module BABYLON.GLTF2.Extensions {
     export class KHR_draco_mesh_compression extends GLTFLoaderExtension {
         public readonly name = NAME;
 
+        private _dracoCompression: Nullable<DracoCompression> = null;
+
+        constructor(loader: GLTFLoader) {
+            super(loader);
+
+            // Disable extension if decoder is not available.
+            if (!DracoCompression.DecoderUrl) {
+                this.enabled = false;
+            }
+        }
+
+        public dispose(): void {
+            if (this._dracoCompression) {
+                this._dracoCompression.dispose();
+            }
+
+            super.dispose();
+        }
+
         protected _loadVertexDataAsync(context: string, primitive: ILoaderMeshPrimitive, babylonMesh: Mesh): Nullable<Promise<VertexData>> {
             return this._loadExtensionAsync<IKHRDracoMeshCompression, VertexData>(context, primitive, (extensionContext, extension) => {
                 if (primitive.mode != undefined) {
@@ -54,7 +73,11 @@ module BABYLON.GLTF2.Extensions {
                 var bufferView = GLTFLoader._GetProperty(extensionContext, this._loader._gltf.bufferViews, extension.bufferView);
                 return this._loader._loadBufferViewAsync(`#/bufferViews/${bufferView._index}`, bufferView).then(data => {
                     try {
-                        return DracoCompression.Decode(data, attributes);
+                        if (!this._dracoCompression) {
+                            this._dracoCompression = new DracoCompression();
+                        }
+
+                        return this._dracoCompression.decodeMeshAsync(data, attributes);
                     }
                     catch (e) {
                         throw new Error(`${context}: ${e.message}`);
@@ -64,7 +87,5 @@ module BABYLON.GLTF2.Extensions {
         }
     }
 
-    if (DracoCompression.IsSupported) {
-        GLTFLoader._Register(NAME, loader => new KHR_draco_mesh_compression(loader));
-    }
+    GLTFLoader._Register(NAME, loader => new KHR_draco_mesh_compression(loader));
 }

+ 6 - 0
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -1568,7 +1568,13 @@ module BABYLON.GLTF2 {
             delete this._gltf;
             delete this._babylonScene;
             this._completePromises.length = 0;
+
+            for (const name in this._extensions) {
+                this._extensions[name].dispose();
+            }
+
             this._extensions = {};
+
             delete this._rootBabylonMesh;
             delete this._progressCallback;
 

+ 5 - 1
loaders/src/glTF/2.0/babylon.glTFLoaderExtension.ts

@@ -1,7 +1,7 @@
 /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
 
 module BABYLON.GLTF2 {
-    export abstract class GLTFLoaderExtension implements IGLTFLoaderExtension {
+    export abstract class GLTFLoaderExtension implements IGLTFLoaderExtension, IDisposable {
         public enabled = true;
         public abstract readonly name: string;
 
@@ -11,6 +11,10 @@ module BABYLON.GLTF2 {
             this._loader = loader;
         }
 
+        public dispose(): void {
+            delete this._loader;
+        }
+
         // #region Overridable Methods
 
         /** Override this method to modify the default behavior for loading scenes. */

+ 3 - 3
localDev/index.html

@@ -5,10 +5,10 @@
     <title>Local Development</title>
 
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
-    <script src="https://preview.babylonjs.com/cannon.js"></script>
-    <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
-    <script src="https://preview.babylonjs.com/Oimo.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="../Tools/DevLoader/BabylonLoader.js"></script>
     <script src="src/webgl-debug.js"></script>
 

+ 2 - 2
package.json

@@ -9,7 +9,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "3.2.0-alpha7",
+    "version": "3.2.0-alpha8",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -41,4 +41,4 @@
     },
     "readme": "Babylon.js is a 3D engine based on webgl and javascript",
     "readmeFilename": "README.md"
-}
+}

+ 1 - 1
sandbox/index-local.html

@@ -4,7 +4,7 @@
     <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"></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="../Tools/DevLoader/BabylonLoader.js"></script>
 </head>

+ 1 - 1
sandbox/index.html

@@ -27,7 +27,7 @@
     <script src="https://code.jquery.com/pep/0.4.2/pep.min.js"></script>
 
     <script src="https://preview.babylonjs.com/cannon.js"></script>
-    <script src="https://preview.babylonjs.com/draco_decoder.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>

+ 2 - 2
src/Engine/babylon.engine.ts

@@ -4268,7 +4268,7 @@
                 }, undefined, undefined, true, onerror);
             } else if (isDDS) {
                 if (files && files.length === 6) {
-                    this._cascadeLoadFiles(rootUrl,
+                    this._cascadeLoadFiles(
                         scene,
                         imgs => {
                             var info: DDSInfo | undefined;
@@ -5883,7 +5883,7 @@
             this._loadFile(url, onload, undefined, undefined, true, onerror);
         }
 
-        private _cascadeLoadFiles(rootUrl: string, scene: Nullable<Scene>, onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void> = null): void {
+        private _cascadeLoadFiles(scene: Nullable<Scene>, onfinish: (images: (string | ArrayBuffer)[]) => void, files: string[], onError: Nullable<(message?: string, exception?: any) => void> = null): void {
             var loadedFiles: (string | ArrayBuffer)[] = [];
             (<any>loadedFiles)._internalCount = 0;
 

+ 3 - 1
src/Helpers/babylon.environmentHelper.ts

@@ -476,7 +476,9 @@ module BABYLON {
                 return { groundSize, skyboxSize, rootPosition };
             }
 
-            const sceneExtends = this._scene.getWorldExtends();
+            const sceneExtends = this._scene.getWorldExtends((mesh) => {
+                return (mesh !== this._ground && mesh !== this._rootMesh && mesh !== this._skybox);
+            });
             const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
 
             if (this._options.sizeAuto) {

+ 5 - 1
src/Materials/Textures/babylon.cubeTexture.ts

@@ -39,7 +39,11 @@
         private _prefiltered: boolean;
 
         public static CreateFromImages(files: string[], scene: Scene, noMipmap?: boolean) {
-            return new CubeTexture("", scene, null, noMipmap, files);
+            let rootUrlKey = "";
+            
+            files.forEach(url => rootUrlKey += url);
+
+            return new CubeTexture(rootUrlKey, scene, null, noMipmap, files);
         }
 
         public static CreateFromPrefilteredData(url: string, scene: Scene, forcedExtension: any = null) {

+ 163 - 70
src/Mesh/Compression/babylon.dracoCompression.ts

@@ -6,101 +6,194 @@ module BABYLON {
     /**
      * Draco compression (https://google.github.io/draco/)
      */
-    export class DracoCompression {
+    export class DracoCompression implements IDisposable {
+        private _workerPool: WorkerPool;
+
+        /**
+         * Gets the url to the draco decoder if available.
+         */
+        public static DecoderUrl: Nullable<string> = DracoCompression._GetDefaultDecoderUrl();
+
         /**
-         * Returns whether Draco compression is supported.
+         * Constructor
+         * @param numWorkers The number of workers for async operations
          */
-        public static get IsSupported(): boolean {
-            return Tools.IsWindowObjectExist() && !!window.DracoDecoderModule;
+        constructor(numWorkers = (navigator.hardwareConcurrency || 4)) {
+            const workers = new Array<Worker>(numWorkers);
+            for (let i = 0; i < workers.length; i++) {
+                const worker = new Worker(DracoCompression._WorkerBlobUrl);
+                worker.postMessage({ id: "initDecoder", url: DracoCompression.DecoderUrl });
+                workers[i] = worker;
+            }
+
+            this._workerPool = new WorkerPool(workers);
         }
 
         /**
-         * Decodes Draco compressed data to vertex data.
+         * Stop all async operations and release resources.
+         */
+        public dispose(): void {
+            this._workerPool.dispose();
+            delete this._workerPool;
+        }
+
+        /**
+         * Decode Draco compressed mesh data to vertex data.
          * @param data The array buffer view for the Draco compression data
          * @param attributes A map of attributes from vertex buffer kinds to Draco unique ids
-         * @returns The decoded vertex data
+         * @returns A promise that resolves with the decoded vertex data
          */
-        public static Decode(data: ArrayBufferView, attributes: { [kind: string]: number }): VertexData {
-            const dracoModule = new DracoDecoderModule();
-            const buffer = new dracoModule.DecoderBuffer();
-            buffer.Init(data, data.byteLength);
-
-            const decoder = new dracoModule.Decoder();
-            let geometry: any;
-            let status: any;
-
-            const vertexData = new VertexData();
-
-            try {
-                const type = decoder.GetEncodedGeometryType(buffer);
-                switch (type) {
-                    case dracoModule.TRIANGULAR_MESH:
-                        geometry = new dracoModule.Mesh();
-                        status = decoder.DecodeBufferToMesh(buffer, geometry);
-                        break;
-                    case dracoModule.POINT_CLOUD:
-                        geometry = new dracoModule.PointCloud();
-                        status = decoder.DecodeBufferToPointCloud(buffer, geometry);
-                        break;
-                    default:
-                        throw new Error(`Invalid geometry type ${type}`);
-                }
+        public decodeMeshAsync(data: ArrayBufferView, attributes: { [kind: string]: number }): Promise<VertexData> {
+            return new Promise((resolve, reject) => {
+                this._workerPool.push((worker, onComplete) => {
+                    const vertexData = new VertexData();
 
-                if (!status.ok() || !geometry.ptr) {
-                    throw new Error(status.error_msg());
-                }
+                    const onError = (error: ErrorEvent) => {
+                        worker.removeEventListener("error", onError);
+                        worker.removeEventListener("message", onMessage);
+                        reject(error);
+                        onComplete();
+                    };
 
-                const numPoints = geometry.num_points();
-
-                if (type === dracoModule.TRIANGULAR_MESH) {
-                    const numFaces = geometry.num_faces();
-                    const faceIndices = new dracoModule.DracoInt32Array();
-                    try {
-                        vertexData.indices = new Uint32Array(numFaces * 3);
-                        for (let i = 0; i < numFaces; i++) {
-                            decoder.GetFaceFromMesh(geometry, i, faceIndices);
-                            const offset = i * 3;
-                            vertexData.indices[offset + 0] = faceIndices.GetValue(0);
-                            vertexData.indices[offset + 1] = faceIndices.GetValue(1);
-                            vertexData.indices[offset + 2] = faceIndices.GetValue(2);
+                    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();
+                buffer.Init(data, data.byteLength);
+
+                const decoder = new dracoModule.Decoder();
+                let geometry: any;
+                let status: any;
+
+                try {
+                    const type = decoder.GetEncodedGeometryType(buffer);
+                    switch (type) {
+                        case dracoModule.TRIANGULAR_MESH:
+                            geometry = new dracoModule.Mesh();
+                            status = decoder.DecodeBufferToMesh(buffer, geometry);
+                            break;
+                        case dracoModule.POINT_CLOUD:
+                            geometry = new dracoModule.PointCloud();
+                            status = decoder.DecodeBufferToPointCloud(buffer, geometry);
+                            break;
+                        default:
+                            throw new Error(`Invalid geometry type ${type}`);
                     }
-                    finally {
-                        dracoModule.destroy(faceIndices);
+
+                    if (!status.ok() || !geometry.ptr) {
+                        throw new Error(status.error_msg());
                     }
-                }
 
-                for (const kind in attributes) {
-                    const uniqueId = attributes[kind];
-                    const attribute = decoder.GetAttributeByUniqueId(geometry, uniqueId);
-                    const dracoData = new dracoModule.DracoFloat32Array();
-                    try {
-                        if (attribute.num_components() !== VertexBuffer.DeduceStride(kind)) {
-                            throw new Error(`Unsupported number of components for ${kind}`);
+                    const numPoints = geometry.num_points();
+
+                    if (type === dracoModule.TRIANGULAR_MESH) {
+                        const numFaces = geometry.num_faces();
+                        const faceIndices = new dracoModule.DracoInt32Array();
+                        try {
+                            const indices = new Uint32Array(numFaces * 3);
+                            for (let i = 0; i < numFaces; i++) {
+                                decoder.GetFaceFromMesh(geometry, i, faceIndices);
+                                const offset = i * 3;
+                                indices[offset + 0] = faceIndices.GetValue(0);
+                                indices[offset + 1] = faceIndices.GetValue(1);
+                                indices[offset + 2] = faceIndices.GetValue(2);
+                            }
+                            _self.postMessage({ id: "indices", value: indices }, [indices.buffer]);
+                        }
+                        finally {
+                            dracoModule.destroy(faceIndices);
                         }
+                    }
 
-                        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);
+                    for (const kind in attributes) {
+                        const uniqueId = attributes[kind];
+                        const attribute = decoder.GetAttributeByUniqueId(geometry, uniqueId);
+                        const dracoData = new dracoModule.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]);
+                        }
+                        finally {
+                            dracoModule.destroy(dracoData);
                         }
-                        vertexData.set(babylonData, kind);
                     }
-                    finally {
-                        dracoModule.destroy(dracoData);
+                }
+                finally {
+                    if (geometry) {
+                        dracoModule.destroy(geometry);
                     }
+
+                    dracoModule.destroy(decoder);
+                    dracoModule.destroy(buffer);
                 }
+
+                _self.postMessage("done");
             }
-            finally {
-                if (geometry) {
-                    dracoModule.destroy(geometry);
+
+            _self.onmessage = event => {
+                switch (event.data.id) {
+                    case "initDecoder": {
+                        importScripts(event.data.url);
+                        break;
+                    }
+                    case "decodeMesh": {
+                        decodeMesh(event.data.data, event.data.attributes);
+                        break;
+                    }
                 }
+            };
+        }
 
-                dracoModule.destroy(decoder);
-                dracoModule.destroy(buffer);
+        private static _WorkerBlobUrl = URL.createObjectURL(new Blob([`(${DracoCompression._Worker.toString()})()`], { type: "application/javascript" }));
+
+        private static _GetDefaultDecoderUrl(): Nullable<string> {
+            for (let i = 0; i < document.scripts.length; i++) {
+                if (document.scripts[i].type === "text/x-draco-decoder") {
+                    return document.scripts[i].src;
+                }
             }
 
-            return vertexData;
+            return null;
         }
     }
 }

+ 33 - 10
src/Particles/babylon.gpuParticleSystem.ts

@@ -335,10 +335,6 @@
                 transformFeedbackVaryings: ["outPosition", "outAge", "outLife", "outSeed", "outSize", "outColor", "outDirection"]
             };
 
-            this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());   
-
-            this._renderEffect = new Effect("gpuRenderParticles", ["position", "age", "life", "size", "color", "offset", "uv"], ["view", "projection", "colorDead"], ["textureSampler"], this._scene.getEngine());
-
             // Random data
             var maxTextureSize = Math.min(this._engine.getCaps().maxTextureSize, fullOptions.randomTextureSize);
             var d = [];
@@ -452,14 +448,32 @@
         }
 
         /** @ignore */
-        public _recreateUpdateEffect(defines: string) {
-            if (this._updateEffectOptions.defines === defines) {
+        public _recreateUpdateEffect() {
+            let defines = this.particleEmitterType ? this.particleEmitterType.getEffectDefines() : "";
+            if (this._updateEffect && this._updateEffectOptions.defines === defines) {
                 return;
             }
             this._updateEffectOptions.defines = defines;
             this._updateEffect = new Effect("gpuUpdateParticles", this._updateEffectOptions, this._scene.getEngine());   
         }
 
+        /** @ignore */
+        public _recreateRenderEffect() {
+            let defines = "";
+            if (this._scene.clipPlane) {
+                defines = "\n#define CLIPPLANE";
+            }
+
+            if (this._renderEffect && this._renderEffect.defines === defines) {
+                return;
+            }
+
+            this._renderEffect = new Effect("gpuRenderParticles", 
+                                            ["position", "age", "life", "size", "color", "offset", "uv"], 
+                                            ["view", "projection", "colorDead", "invView", "vClipPlane"], 
+                                            ["textureSampler"], this._scene.getEngine(), defines);
+        }        
+
         /**
          * Animates the particle system for the current frame by emitting new particles and or animating the living ones.
          */
@@ -484,9 +498,8 @@
                 return 0;
             }
 
-            if (this.particleEmitterType) {
-                this._recreateUpdateEffect(this.particleEmitterType.getEffectDefines());
-            }
+            this._recreateUpdateEffect();
+            this._recreateRenderEffect();
 
             if (!this.emitter || !this._updateEffect.isReady() || !this._renderEffect.isReady() ) {
                 return 0;
@@ -547,11 +560,21 @@
 
             // Enable render effect
             this._engine.enableEffect(this._renderEffect);
-            this._renderEffect.setMatrix("view", this._scene.getViewMatrix());
+            let viewMatrix = this._scene.getViewMatrix();
+            this._renderEffect.setMatrix("view", viewMatrix);
             this._renderEffect.setMatrix("projection", this._scene.getProjectionMatrix());
             this._renderEffect.setTexture("textureSampler", this.particleTexture);
             this._renderEffect.setDirectColor4("colorDead", this.colorDead);
 
+
+            if (this._scene.clipPlane) {
+                var clipPlane = this._scene.clipPlane;
+                var invView = viewMatrix.clone();
+                invView.invert();
+                this._renderEffect.setMatrix("invView", invView);
+                this._renderEffect.setFloat4("vClipPlane", clipPlane.normal.x, clipPlane.normal.y, clipPlane.normal.z, clipPlane.d);
+            }            
+
             // Draw order
             if (this.blendMode === ParticleSystem.BLENDMODE_ONEONE) {
                 this._engine.setAlphaMode(Engine.ALPHA_ONEONE);

+ 23 - 1
src/Shaders/default.fragment.fx

@@ -4,6 +4,8 @@
 #extension GL_OES_standard_derivatives : enable
 #endif
 
+#define CUSTOM_FRAGMENT_BEGIN
+
 #ifdef LOGARITHMICDEPTH
 #extension GL_EXT_frag_depth : enable
 #endif
@@ -152,14 +154,23 @@ varying vec3 vDirectionW;
 #include<logDepthDeclaration>
 #include<fogFragmentDeclaration>
 
+#define CUSTOM_FRAGMENT_DEFINITIONS
+
 void main(void) {
+
+#define CUSTOM_FRAGMENT_MAIN_BEGIN
+
 #include<clipPlaneFragment>
 
+
+
 	vec3 viewDirectionW = normalize(vEyePosition - vPositionW);
 
 	// Base color
 	vec4 baseColor = vec4(1., 1., 1., 1.);
 	vec3 diffuseColor = vDiffuseColor.rgb;
+	
+	
 
 	// Alpha
 	float alpha = vDiffuseColor.a;
@@ -188,16 +199,22 @@ void main(void) {
 	#ifdef ALPHAFROMDIFFUSE
 		alpha *= baseColor.a;
 	#endif
+	
+	#define CUSTOM_FRAGMENT_UPDATE_ALPHA
 
 	baseColor.rgb *= vDiffuseInfos.y;
 #endif
 
+
+
 #include<depthPrePass>
 
 #ifdef VERTEXCOLOR
 	baseColor.rgb *= vColor.rgb;
 #endif
 
+#define CUSTOM_FRAGMENT_UPDATE_DIFFUSE
+
 	// Ambient color
 	vec3 baseAmbientColor = vec3(1., 1., 1.);
 
@@ -205,6 +222,8 @@ void main(void) {
 	baseAmbientColor = texture2D(ambientSampler, vAmbientUV + uvOffset).rgb * vAmbientInfos.y;
 #endif
 
+#define CUSTOM_FRAGMENT_BEFORE_LIGHTS
+
 	// Specular map
 #ifdef SPECULARTERM
 	float glossiness = vSpecularColor.a;
@@ -398,6 +417,8 @@ void main(void) {
     #endif
 #endif
 
+#define CUSTOM_FRAGMENT_BEFORE_FOG
+
 #include<logDepthFragment>
 #include<fogFragment>
 
@@ -417,5 +438,6 @@ void main(void) {
 	color.rgb *= color.a;
 #endif
 
+#define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR
 	gl_FragColor = color;
-}
+}

+ 14 - 1
src/Shaders/default.vertex.fx

@@ -1,5 +1,8 @@
 #include<__decl__defaultVertex>
 // Attributes
+
+#define CUSTOM_VERTEX_BEGIN
+
 attribute vec3 position;
 #ifdef NORMAL
 attribute vec3 normal;
@@ -89,8 +92,12 @@ varying vec3 vDirectionW;
 #endif
 
 #include<logDepthDeclaration>
+#define CUSTOM_VERTEX_DEFINITIONS
 
 void main(void) {
+	
+	#define CUSTOM_VERTEX_MAIN_BEGIN
+	
 	vec3 positionUpdated = position;
 #ifdef NORMAL	
 	vec3 normalUpdated = normal;
@@ -105,6 +112,10 @@ void main(void) {
 	vPositionUVW = positionUpdated;
 #endif 
 
+#define CUSTOM_VERTEX_UPDATE_POSITION
+
+#define CUSTOM_VERTEX_UPDATE_NORMAL
+
 #include<instancesVertex>
 #include<bonesVertex>
 
@@ -233,4 +244,6 @@ void main(void) {
 #include<pointCloudVertex>
 #include<logDepthVertex>
 
-}
+#define CUSTOM_VERTEX_MAIN_END
+
+}

+ 8 - 0
src/Shaders/gpuRenderParticles.fragment.fx

@@ -7,6 +7,14 @@ in vec4 vColor;
 
 out vec4 outFragColor;
 
+#ifdef CLIPPLANE
+in float fClipDistance;
+#endif
+
 void main() {
+#ifdef CLIPPLANE
+	if (fClipDistance > 0.0)
+		discard;
+#endif  
   outFragColor = texture(textureSampler, vUV) * vColor;
 }

+ 13 - 0
src/Shaders/gpuRenderParticles.vertex.fx

@@ -16,6 +16,13 @@ in vec2 uv;
 out vec2 vUV;
 out vec4 vColor;
 
+#ifdef CLIPPLANE
+uniform vec4 vClipPlane;
+uniform mat4 invView;
+out float fClipDistance;
+#endif
+
+
 void main() {
   vUV = uv;
   float ratio = age / life;
@@ -24,4 +31,10 @@ void main() {
   // Expand position
   vec4 viewPosition = view * vec4(position, 1.0);
   gl_Position = projection * (viewPosition + vec4(offset * size, 0, 1.0));
+
+	// Clip plane
+#ifdef CLIPPLANE
+	vec4 worldPos = invView * viewPosition;
+	fClipDistance = dot(worldPos, vClipPlane);
+#endif  
 }

+ 66 - 0
src/Tools/babylon.workerPool.ts

@@ -0,0 +1,66 @@
+/// <reference path="../../../dist/preview release/babylon.d.ts" />
+
+module BABYLON {
+    interface WorkerInfo {
+        worker: Worker;
+        active: boolean;
+    }
+
+    /**
+     * Helper class to push actions to a pool of workers.
+     */
+    export class WorkerPool implements IDisposable {
+        private _workerInfos: Array<WorkerInfo>;
+        private _pendingActions = new Array<(worker: Worker, onComplete: () => void) => void>();
+
+        /**
+         * Constructor
+         * @param workers Array of workers to use for actions
+         */
+        constructor(workers: Array<Worker>) {
+            this._workerInfos = workers.map(worker => ({
+                worker: worker,
+                active: false
+            }));
+        }
+
+        /**
+         * Terminates all workers and clears any pending actions.
+         */
+        public dispose(): void {
+            for (const workerInfo of this._workerInfos) {
+                workerInfo.worker.terminate();
+            }
+
+            delete this._workerInfos;
+            delete this._pendingActions;
+        }
+
+        /**
+         * Pushes an action to the worker pool. If all the workers are active, the action will be
+         * pended until a worker has completed its action.
+         * @param action The action to perform. Call onComplete when the action is complete.
+         */
+        public push(action: (worker: Worker, onComplete: () => void) => void): void {
+            for (const workerInfo of this._workerInfos) {
+                if (!workerInfo.active) {
+                    this._execute(workerInfo, action);
+                    return;
+                }
+            }
+
+            this._pendingActions.push(action);
+        }
+
+        private _execute(workerInfo: WorkerInfo, action: (worker: Worker, onComplete: () => void) => void): void {
+            workerInfo.active = true;
+            action(workerInfo.worker, () => {
+                workerInfo.active = false;
+                const nextAction = this._pendingActions.shift();
+                if (nextAction) {
+                    this._execute(workerInfo, nextAction);
+                }
+            });
+        }
+    }
+}

+ 12 - 6
src/babylon.scene.ts

@@ -4242,16 +4242,22 @@
         }
 
         // Octrees
-        public getWorldExtends(): { min: Vector3; max: Vector3 } {
+
+        /**
+         * Get the world extend vectors with an optional filter
+         * 
+         * @param filterPredicate the predicate - which meshes should be included when calculating the world size
+         * @returns {{ min: Vector3; max: Vector3 }} min and max vectors
+         */
+        public getWorldExtends(filterPredicate?: (mesh: AbstractMesh) => boolean): { min: Vector3; max: Vector3 } {
             var min = new Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE);
             var max = new Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE);
-            for (var index = 0; index < this.meshes.length; index++) {
-                var mesh = this.meshes[index];
-
+            filterPredicate = filterPredicate || (() => true);
+            this.meshes.filter(filterPredicate).forEach(mesh => {
                 mesh.computeWorldMatrix(true);
 
                 if (!mesh.subMeshes || mesh.subMeshes.length === 0 || mesh.infiniteDistance) {
-                    continue;
+                    return;
                 }
 
                 let boundingInfo = mesh.getBoundingInfo();
@@ -4261,7 +4267,7 @@
 
                 Tools.CheckExtends(minBox, min, max);
                 Tools.CheckExtends(maxBox, min, max);
-            }
+            })
 
             return {
                 min: min,

+ 1 - 1
tests/validation/config.json

@@ -18,7 +18,7 @@
       "sceneFolder": "/Scenes/Espilit/",
       "sceneFilename": "espilit.babylon",
       "referenceImage": "Espilit.png"
-    },
+    },    
     {
       "title": "The car",
       "sceneFolder": "/Scenes/TheCar/",

+ 0 - 1
tests/validation/index.html

@@ -3,7 +3,6 @@
 <head>
 	<title>BabylonJS - Build validation page</title>
 	<link href="index.css" rel="stylesheet" />
-    <script src="../../dist/preview%20release/draco_decoder.js"></script>
 	<script src="../../Tools/DevLoader/BabylonLoader.js"></script>
 </head>
 <body>

+ 0 - 1
tests/validation/karma.conf.browserstack.js

@@ -14,7 +14,6 @@ module.exports = function (config) {
         frameworks: ['mocha', 'chai', 'sinon'],
 
         files: [
-            './dist/preview release/draco_decoder.js',
             './Tools/DevLoader/BabylonLoader.js',
             './tests/validation/index.css',
             './tests/validation/integration.js',

+ 0 - 1
tests/validation/karma.conf.js

@@ -14,7 +14,6 @@ module.exports = function (config) {
         frameworks: ['mocha', 'chai', 'sinon'],
 
         files: [
-            './dist/preview release/draco_decoder.js',
             './Tools/DevLoader/BabylonLoader.js',
             './tests/validation/index.css',
             './tests/validation/integration.js',

+ 61 - 0
tests/validation/validate.html

@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+<head>
+	<title>BabylonJS - Build validation page</title>
+	<link href="index.css" rel="stylesheet" />
+    <script src="https://preview.babylonjs.com/draco_decoder.js"></script>
+    <script src="https://preview.babylonjs.com/cannon.js"></script>
+    <script src="https://preview.babylonjs.com/draco_decoder.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>
+
+    <script src="https://preview.babylonjs.com/loaders/babylonjs.loaders.min.js"></script>
+	<script src="https://preview.babylonjs.com/materialsLibrary/babylonjs.materials.min.js"></script>
+    <script src="https://preview.babylonjs.com/proceduralTexturesLibrary/babylonjs.proceduralTextures.min.js"></script>
+	<script src="https://preview.babylonjs.com/postProcessesLibrary/babylonjs.postProcess.min.js"></script>
+    <script src="https://preview.babylonjs.com/gui/babylon.gui.min.js"></script>	
+</head>
+<body>
+	<script src="validation.js"></script>
+	<script>
+		// Loading tests
+		var xhr = new XMLHttpRequest();
+
+		xhr.open("GET", "config.json", true);
+
+		xhr.addEventListener("load", function () {
+			if (xhr.status === 200) {
+
+				config = JSON.parse(xhr.responseText);
+
+				// Run tests
+				var index = 0;
+				if (window.location.search) {
+					justOnce = true;
+					var title = window.location.search.replace("?", "").replace(/%20/g, " ");
+					for (var index = 0; index < config.tests.length; index++) {
+						if (config.tests[index].title === title) {
+							break;
+						}
+					}
+				}
+
+				var recursiveRunTest = function(i) {
+					runTest(i, function() {
+						i++;
+						if (justOnce || i >= config.tests.length) {
+							return;
+						}
+						recursiveRunTest(i);
+					});
+				}
+
+				recursiveRunTest(index);
+			}
+		}, false);
+
+		xhr.send();
+    </script>	
+</body>
+</html>

+ 1 - 0
tests/validation/validation.js

@@ -305,6 +305,7 @@ function runTest(index, done) {
 BABYLON.SceneLoader.ShowLoadingScreen = false;
 BABYLON.Database.IDBStorageEnabled = false;
 BABYLON.SceneLoader.ForceFullSceneLoadingForIncremental = true;
+BABYLON.DracoCompression.DecoderUrl = BABYLON.Tools.GetFolderPath(document.location.href) + "../../dist/preview%20release/draco_decoder.js";
 
 canvas = document.createElement("canvas");
 canvas.className = "renderCanvas";