瀏覽代碼

Merge branch 'master' into FacetData.depthSort

Julian 7 年之前
父節點
當前提交
4998855e2d
共有 100 個文件被更改,包括 18462 次插入14211 次删除
  1. 1 1
      .vscode/tasks.json
  2. 10 2
      Playground/scripts/ssao 2.js
  3. 10 2
      Playground/scripts/ssao rendering pipeline.js
  4. 1 0
      Tools/Gulp/config.json
  5. 4 1
      Viewer/dist/basicExample.html
  6. 67 0
      Viewer/dist/eventsExample.html
  7. 3453 1657
      Viewer/dist/viewer.js
  8. 8 8
      Viewer/package.json
  9. 12 3
      Viewer/src/configuration/configuration.ts
  10. 2 2
      Viewer/src/configuration/types/default.ts
  11. 39 0
      Viewer/src/util/promiseObservable.ts
  12. 7 1
      Viewer/src/viewer/defaultViewer.ts
  13. 40 12
      Viewer/src/viewer/viewer.ts
  14. 23 0
      Viewer/src/viewer/viewerManager.ts
  15. 2112 1968
      dist/preview release/babylon.d.ts
  16. 49 49
      dist/preview release/babylon.js
  17. 1306 840
      dist/preview release/babylon.max.js
  18. 2112 1968
      dist/preview release/babylon.module.d.ts
  19. 49 49
      dist/preview release/babylon.worker.js
  20. 2969 2836
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  21. 49 49
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  22. 1289 846
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  23. 2969 2836
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  24. 5 0
      dist/preview release/gui/babylon.gui.d.ts
  25. 169 3
      dist/preview release/gui/babylon.gui.js
  26. 3 3
      dist/preview release/gui/babylon.gui.min.js
  27. 5 0
      dist/preview release/gui/babylon.gui.module.d.ts
  28. 1 1
      dist/preview release/gui/package.json
  29. 18 18
      dist/preview release/inspector/babylon.inspector.bundle.js
  30. 4 2
      dist/preview release/inspector/babylon.inspector.d.ts
  31. 38 20
      dist/preview release/inspector/babylon.inspector.js
  32. 4 4
      dist/preview release/inspector/babylon.inspector.min.js
  33. 3 3
      dist/preview release/inspector/package.json
  34. 3 1
      dist/preview release/loaders/babylon.glTF1FileLoader.d.ts
  35. 3 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  36. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  37. 5 3
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  38. 12 6
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  39. 2 2
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  40. 5 3
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  41. 12 6
      dist/preview release/loaders/babylon.glTFFileLoader.js
  42. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  43. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  44. 12 6
      dist/preview release/loaders/babylonjs.loaders.js
  45. 3 3
      dist/preview release/loaders/babylonjs.loaders.min.js
  46. 5 3
      dist/preview release/loaders/babylonjs.loaders.module.d.ts
  47. 1 1
      dist/preview release/loaders/package.json
  48. 1 0
      dist/preview release/materialsLibrary/babylon.gridMaterial.d.ts
  49. 3 0
      dist/preview release/materialsLibrary/babylon.gridMaterial.js
  50. 1 1
      dist/preview release/materialsLibrary/babylon.gridMaterial.min.js
  51. 3 0
      dist/preview release/materialsLibrary/babylonjs.materials.js
  52. 3 3
      dist/preview release/materialsLibrary/babylonjs.materials.min.js
  53. 1 0
      dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts
  54. 1 1
      dist/preview release/materialsLibrary/package.json
  55. 1 1
      dist/preview release/postProcessesLibrary/package.json
  56. 1 1
      dist/preview release/proceduralTexturesLibrary/package.json
  57. 1 1
      dist/preview release/serializers/package.json
  58. 2 0
      dist/preview release/what's new.md
  59. 18 1
      gui/src/advancedDynamicTexture.ts
  60. 13 0
      gui/src/controls/checkbox.ts
  61. 16 1
      gui/src/controls/colorpicker.ts
  62. 13 0
      gui/src/controls/container.ts
  63. 29 2
      gui/src/controls/control.ts
  64. 13 0
      gui/src/controls/ellipse.ts
  65. 8 1
      gui/src/controls/image.ts
  66. 13 0
      gui/src/controls/inputText.ts
  67. 7 0
      gui/src/controls/line.ts
  68. 13 0
      gui/src/controls/radioButton.ts
  69. 15 1
      gui/src/controls/rectangle.ts
  70. 32 0
      gui/src/controls/slider.ts
  71. 6 0
      gui/src/controls/textBlock.ts
  72. 5 1
      gui/src/controls/virtualKeyboard.ts
  73. 18 9
      inspector/src/adapters/MeshAdapter.ts
  74. 37 25
      inspector/src/tabs/StatsTab.ts
  75. 11 6
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  76. 2 2
      loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts
  77. 7 1
      loaders/src/glTF/babylon.glTFFileLoader.ts
  78. 24 7
      localDev/index.html
  79. 4 0
      materialsLibrary/src/grid/babylon.gridmaterial.ts
  80. 1 1
      package.json
  81. 32 6
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  82. 7 4
      src/Debug/babylon.debugLayer.ts
  83. 33 31
      src/Engine/babylon.engine.ts
  84. 10 0
      src/Engine/babylon.nullEngine.ts
  85. 86 30
      src/Helpers/babylon.environmentHelper.ts
  86. 15 0
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  87. 32 11
      src/Loading/babylon.sceneLoader.ts
  88. 17 2
      src/Materials/Background/babylon.backgroundMaterial.ts
  89. 17 14
      src/Materials/PBR/babylon.pbrBaseMaterial.ts
  90. 1 1
      src/Materials/babylon.material.ts
  91. 41 777
      src/Mesh/babylon.abstractMesh.ts
  92. 5 4
      src/Mesh/babylon.mesh.ts
  93. 910 0
      src/Mesh/babylon.transformNode.ts
  94. 3 3
      src/Particles/babylon.solidParticleSystem.ts
  95. 3 3
      src/Physics/babylon.physicsImpostor.ts
  96. 2 2
      src/PostProcess/RenderPipeline/Pipelines/babylon.ssao2RenderingPipeline.ts
  97. 13 32
      src/PostProcess/RenderPipeline/Pipelines/babylon.ssaoRenderingPipeline.ts
  98. 1 1
      src/Rendering/babylon.renderingGroup.ts
  99. 11 0
      src/Shaders/ShadersInclude/helperFunctions.fx
  100. 0 0
      src/Shaders/background.fragment.fx

+ 1 - 1
.vscode/tasks.json

@@ -26,7 +26,7 @@
                     "code": 4,
                     "message": 5
                 },
-                "watching": {
+                "background": {
                     "activeOnStart": true,
                     "beginsPattern": "Starting \\'watch\\'",
                     "endsPattern": "Finished \\'run\\'"

+ 10 - 2
Playground/scripts/ssao 2.js

@@ -40,19 +40,27 @@ var createScene = function () {
         scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
 
         // Manage SSAO
+        var isAttached = true;
         window.addEventListener("keydown", function (evt) {
             // draw SSAO with scene when pressed "1"
             if (evt.keyCode === 49) {
-                scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+                if (!isAttached) {
+                    isAttached = true;
+                    scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+                }
                 scene.postProcessRenderPipelineManager.enableEffectInPipeline("ssao", ssao.SSAOCombineRenderEffect, camera);
             }
                 // draw without SSAO when pressed "2"
             else if (evt.keyCode === 50) {
+                isAttached = false;
                 scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline("ssao", camera);
             }
                 // draw only SSAO when pressed "2"
             else if (evt.keyCode === 51) {
-                scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+                if (!isAttached) {
+                    isAttached = true;
+                    scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+                }
                 scene.postProcessRenderPipelineManager.disableEffectInPipeline("ssao", ssao.SSAOCombineRenderEffect, camera);
             }
         });

+ 10 - 2
Playground/scripts/ssao rendering pipeline.js

@@ -40,19 +40,27 @@
     scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
 
     // Manage SSAO
+    var isAttached = true;
     window.addEventListener("keydown", function (evt) {
         // draw SSAO with scene when pressed "1"
         if (evt.keyCode === 49) {
-            scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+            if (!isAttached) {
+                isAttached = true;
+                scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+            }
             scene.postProcessRenderPipelineManager.enableEffectInPipeline("ssao", ssao.SSAOCombineRenderEffect, camera);
         }
             // draw without SSAO when pressed "2"
         else if (evt.keyCode === 50) {
+            isAttached = false;
             scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline("ssao", camera);
         }
             // draw only SSAO when pressed "2"
         else if (evt.keyCode === 51) {
-            scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+            if (!isAttached) {
+                isAttached = true;
+                scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline("ssao", camera);
+            }
             scene.postProcessRenderPipelineManager.disableEffectInPipeline("ssao", ssao.SSAOCombineRenderEffect, camera);
         }
     });

+ 1 - 0
Tools/Gulp/config.json

@@ -181,6 +181,7 @@
                 "../../src/Culling/babylon.boundingSphere.js",
                 "../../src/Culling/babylon.boundingBox.js",
                 "../../src/Culling/babylon.boundingInfo.js",
+                "../../src/Mesh/babylon.transformNode.js",
                 "../../src/Mesh/babylon.abstractMesh.js",
                 "../../src/Lights/babylon.light.js",
                 "../../src/Cameras/babylon.camera.js",

+ 4 - 1
Viewer/dist/basicExample.html

@@ -11,7 +11,7 @@
                 max-width: 800px;
                 max-height: 500px;
                 width: 100%;
-                height: 100%;
+                height: 600px;
             }
         </style>
     </head>
@@ -21,6 +21,9 @@
             model.url="https://playground.babylonjs.com/scenes/Rabbit.babylon" camera.behaviors.auto-rotate="0" templates.nav-bar.params.disable-on-fullscreen="true"></babylon>
         <script src="viewer.js"></script>
         <script>
+            // The following lines are redundant. 
+            // They are only here to show how you could achive the tag initialization on your own.
+
             // a simple way of disabling auto init 
             BabylonViewer.disableInit = true;
             // Initializing the viewer on specific HTML tags.

+ 67 - 0
Viewer/dist/eventsExample.html

@@ -0,0 +1,67 @@
+<!DOCTYPE html>
+<html lang="en">
+
+    <head>
+        <meta charset="UTF-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1.0">
+        <meta http-equiv="X-UA-Compatible" content="ie=edge">
+        <title>BabylonJS Viewer - Basic usage</title>
+        <style>
+            babylon {
+                max-width: 800px;
+                max-height: 500px;
+                width: 100%;
+                height: 600px;
+            }
+        </style>
+    </head>
+
+    <body>
+        <babylon id="babylon-viewer" model.title="Amazing Rabbit" model.subtitle="BabylonJS" model.thumbnail="https://www.babylonjs.com/img/favicon/apple-icon-144x144.png"
+            model.url="https://playground.babylonjs.com/scenes/Rabbit.babylon" observers.on-scene-init="globalSceneInitCallback"></babylon>
+        <script src="viewer.js"></script>
+        <script>
+            //get by id ONLY after viewer init
+            var willNotWork = BabylonViewer.viewerManager.getViewerById('babylon-viewer');
+            console.log('viewer not yet initialized');
+
+            // Pomise-based API:
+            BabylonViewer.viewerManager.getViewerPromiseById('babylon-viewer').then(function (viewer) {
+                // this will resolve only after the viewer with this specific ID is initialized
+                console.log('Using promises: ', 'viewer - ' + viewer.getBaseId());
+
+                viewerObservables(viewer);
+            });
+
+            // call back variant:
+            BabylonViewer.viewerManager.onViewerAdded = function (viewer) {
+                console.log('Using viewerManager.onViewerAdded: ', 'viewer - ' + viewer.getBaseId());
+            }
+
+            // using observers:
+            BabylonViewer.viewerManager.onViewerAddedObservable.add(function (viewer) {
+                console.log('Using viewerManager.onViewerAddedObservable: ', 'viewer - ' + viewer.getBaseId());
+            });
+
+            function viewerObservables(viewer) {
+                viewer.onEngineInitObservable.add(function (engine) {
+                    console.log('Engine initialized');
+                });
+
+                viewer.onSceneInitObservable.add(function (scene) {
+                    console.log('Scene initialized');
+                });
+
+                viewer.onModelLoadedObservable.add(function (meshes) {
+                    console.log('Model loaded');
+                });
+            }
+
+            function globalSceneInitCallback(scene) {
+                console.log('scene-init function defined in the configuration');
+            }
+
+        </script>
+    </body>
+
+</html>

文件差異過大導致無法顯示
+ 3453 - 1657
Viewer/dist/viewer.js


+ 8 - 8
Viewer/package.json

@@ -1,6 +1,6 @@
 {
     "name": "babylonjs-viewer",
-    "version": "0.1.0",
+    "version": "0.2.0",
     "description": "A viewer using BabylonJS to display 3D elements natively",
     "scripts": {
         "start:server": "webpack-dev-server",
@@ -23,7 +23,7 @@
     },
     "homepage": "https://github.com/BabylonJS/Babylon.js#readme",
     "devDependencies": {
-        "@types/node": "^8.0.47",
+        "@types/node": "^8.0.51",
         "base64-image-loader": "^1.2.0",
         "html-loader": "^0.5.1",
         "json-loader": "^0.5.7",
@@ -34,15 +34,15 @@
         "webpack-dev-server": "^2.9.4"
     },
     "dependencies": {
-        "babylonjs": "^3.1.0-alpha3.7",
-        "babylonjs-loaders": "^3.1.0-alpha3.7",
-        "babylonjs-materials": "^3.1.0-alpha3.7",
-        "babylonjs-post-process": "^3.1.0-alpha3.7",
-        "babylonjs-procedural-textures": "^3.1.0-alpha3.7",
+        "babylonjs": "^3.1.0-beta1.1.1",
+        "babylonjs-loaders": "^3.1.0-beta1.1",
+        "babylonjs-materials": "^3.1.0-beta1.1",
+        "babylonjs-post-process": "^3.1.0-beta1.1",
+        "babylonjs-procedural-textures": "^3.1.0-beta1.1",
         "es6-promise": "^4.1.1",
         "handlebars": "^4.0.11",
         "lodash": "^4.17.4",
         "lodash.merge": "^4.6.0",
         "promise-polyfill": "^6.0.2"
     }
-}
+}

+ 12 - 3
Viewer/src/configuration/configuration.ts

@@ -13,7 +13,8 @@ export interface ViewerConfiguration {
         mapper?: string; // json (default), html, yaml, xml, etc'. if not provided, file extension will be used.
     };
 
-    // native (!!!) javascript events. Mainly used in the JSON-format.
+    // Deprecated
+    /*// native (!!!) javascript events. Mainly used in the JSON-format.
     // those events will be triggered by the container element (the <babylon> tag);
     events?: {
         load: boolean | string;
@@ -23,8 +24,15 @@ export interface ViewerConfiguration {
         pointerup: boolean | string;
         pointermove: boolean | string;
         // load: 'onViewerLoaded' // will trigger the event prefix-onViewerLoaded instead of prefix-onLoad (and ONLY this event).
-    } | boolean; //events: true - fire all events
-    eventPrefix?: string;
+    } | boolean; //events: true - fire all events*/
+    //eventPrefix?: string;
+
+    // names of functions in the window context.
+    observers?: {
+        onEngineInit?: string;
+        onSceneInit?: string;
+        onModelLoaded?: string;
+    }
 
     canvasElement?: string; // if there is a need to override the standard implementation - ID of HTMLCanvasElement
 
@@ -46,6 +54,7 @@ export interface ViewerConfiguration {
     };
 
     scene?: {
+        debug?: boolean;
         autoRotate?: boolean;
         rotationSpeed?: number;
         defaultCamera?: boolean;

+ 2 - 2
Viewer/src/configuration/types/default.ts

@@ -2,8 +2,8 @@ import { ViewerConfiguration } from './../configuration';
 
 export let defaultConfiguration: ViewerConfiguration = {
     version: "0.1",
-    eventPrefix: 'babylonviewer-',
-    events: true,
+    //eventPrefix: 'babylonviewer-',
+    //events: true,
     templates: {
         main: {
             html: require("../../../assets/templates/default/defaultTemplate.html")

+ 39 - 0
Viewer/src/util/promiseObservable.ts

@@ -0,0 +1,39 @@
+import { Observable, Nullable, Observer } from "babylonjs";
+
+export class PromiseObservable<T> extends Observable<T> {
+
+    public notifyWithPromise(eventData: T, mask: number = -1, target?: any, currentTarget?: any): Promise<any> {
+
+        let p = Promise.resolve();
+
+        if (!this._observers.length) {
+            return p;
+        }
+
+        let state = this['_eventState'];
+        state.mask = mask;
+        state.target = target;
+        state.currentTarget = currentTarget;
+        state.skipNextObservers = false;
+
+        this._observers.forEach(obs => {
+            if (state.skipNextObservers) {
+                return;
+            }
+            if (obs.mask & mask) {
+                if (obs.scope) {
+                    // TODO - I can add the variable from the last function here. Requires changing callback sig
+                    p = p.then(() => {
+                        return obs.callback.apply(obs.scope, [eventData, state]);
+                    });
+                } else {
+                    p = p.then(() => {
+                        return obs.callback(eventData, state);
+                    });
+                }
+            }
+        });
+
+        return p;
+    }
+}

+ 7 - 1
Viewer/src/viewer/defaultViewer.ts

@@ -1,3 +1,4 @@
+import { ViewerConfiguration } from './../configuration/configuration';
 import { Template } from './../templateManager';
 import { AbstractViewer } from './viewer';
 import { Observable, ShadowLight, CubeTexture, BouncingBehavior, FramingBehavior, Behavior, Light, Engine, Scene, AutoRotationBehavior, AbstractMesh, Quaternion, StandardMaterial, ShadowOnlyMaterial, ArcRotateCamera, ImageProcessingConfiguration, Color3, Vector3, SceneLoader, Mesh, HemisphericLight } from 'babylonjs';
@@ -11,6 +12,11 @@ export class DefaultViewer extends AbstractViewer {
 
     public camera: ArcRotateCamera;
 
+    constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = { extends: 'default' }) {
+        super(containerElement, initialConfiguration);
+        this.onModelLoadedObservable.add(this.onModelLoaded);
+    }
+
     public initScene(): Promise<Scene> {
         return super.initScene().then(() => {
             this.extendClassWithConfig(this.scene, this.configuration.scene);
@@ -132,7 +138,7 @@ export class DefaultViewer extends AbstractViewer {
         });
     }
 
-    public onModelLoaded(meshes: Array<AbstractMesh>) {
+    private onModelLoaded = (meshes: Array<AbstractMesh>) => {
 
         // here we could set the navbar's model information:
         this.setModelMetaData();

+ 40 - 12
Viewer/src/viewer/viewer.ts

@@ -1,8 +1,9 @@
 import { viewerManager } from './viewerManager';
 import { TemplateManager } from './../templateManager';
 import configurationLoader from './../configuration/loader';
-import { Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight } from 'babylonjs';
+import { Observable, Engine, Scene, ArcRotateCamera, Vector3, SceneLoader, AbstractMesh, Mesh, HemisphericLight, Database } from 'babylonjs';
 import { ViewerConfiguration } from '../configuration/configuration';
+import { PromiseObservable } from '../util/promiseObservable';
 
 export abstract class AbstractViewer {
 
@@ -14,6 +15,11 @@ export abstract class AbstractViewer {
 
     protected configuration: ViewerConfiguration;
 
+    // observables
+    public onSceneInitObservable: PromiseObservable<Scene>;
+    public onEngineInitObservable: PromiseObservable<Engine>;
+    public onModelLoadedObservable: PromiseObservable<AbstractMesh[]>;
+
     constructor(public containerElement: HTMLElement, initialConfiguration: ViewerConfiguration = {}) {
         // if exists, use the container id. otherwise, generate a random string.
         if (containerElement.id) {
@@ -22,6 +28,10 @@ export abstract class AbstractViewer {
             this.baseId = containerElement.id = 'bjs' + Math.random().toString(32).substr(2, 8);
         }
 
+        this.onSceneInitObservable = new PromiseObservable();
+        this.onEngineInitObservable = new PromiseObservable();
+        this.onModelLoadedObservable = new PromiseObservable();
+
         // add this viewer to the viewer manager
         viewerManager.addViewer(this);
 
@@ -33,6 +43,20 @@ export abstract class AbstractViewer {
         // extend the configuration
         configurationLoader.loadConfiguration(initialConfiguration).then((configuration) => {
             this.configuration = configuration;
+
+            // adding preconfigured functions
+            if (this.configuration.observers) {
+                if (this.configuration.observers.onEngineInit) {
+                    this.onEngineInitObservable.add(window[this.configuration.observers.onEngineInit]);
+                }
+                if (this.configuration.observers.onSceneInit) {
+                    this.onSceneInitObservable.add(window[this.configuration.observers.onSceneInit]);
+                }
+                if (this.configuration.observers.onModelLoaded) {
+                    this.onModelLoadedObservable.add(window[this.configuration.observers.onModelLoaded]);
+                }
+            }
+
             // initialize the templates
             let templateConfiguration = this.configuration.templates || {};
             this.templateManager.initTemplate(templateConfiguration);
@@ -82,6 +106,9 @@ export abstract class AbstractViewer {
         // TDO enable further configuration
         this.engine = new Engine(canvasElement, !!config.antialiasing);
 
+        // Disable manifest checking
+        Database.IDBStorageEnabled = false;
+
         window.addEventListener('resize', () => {
             this.engine.resize();
         });
@@ -93,7 +120,9 @@ export abstract class AbstractViewer {
         var scale = Math.max(0.5, 1 / (window.devicePixelRatio || 2));
         this.engine.setHardwareScalingLevel(scale);
 
-        return Promise.resolve(this.engine);
+        return this.onEngineInitObservable.notifyWithPromise(this.engine).then(() => {
+            return this.engine;
+        });
     }
 
     protected initScene(): Promise<Scene> {
@@ -107,7 +136,12 @@ export abstract class AbstractViewer {
         this.scene = new Scene(this.engine);
         // make sure there is a default camera and light.
         this.scene.createDefaultCameraOrLight(true, true, true);
-        return Promise.resolve(this.scene);
+        if (this.configuration.scene && this.configuration.scene.debug) {
+            this.scene.debugLayer.show();
+        }
+        return this.onSceneInitObservable.notifyWithPromise(this.scene).then(() => {
+            return this.scene;
+        });
     }
 
     public loadModel(model: any = this.configuration.model, clearScene: boolean = true): Promise<Scene> {
@@ -130,15 +164,9 @@ export abstract class AbstractViewer {
                 }, plugin);
             });
         }).then((meshes: Array<AbstractMesh>) => {
-            return this.onModelLoaded(meshes);
+            return this.onModelLoadedObservable.notifyWithPromise(meshes).then(() => {
+                return this.scene;
+            });
         });
     }
-
-    protected onModelLoaded(meshes: Array<AbstractMesh>): Promise<Scene> {
-        console.log("model loaded");
-        return Promise.resolve(this.scene);
-    }
-
-    public abstract initEnvironment(): Promise<Scene>;
-
 }

+ 23 - 0
Viewer/src/viewer/viewerManager.ts

@@ -1,15 +1,21 @@
+import { Observable } from 'babylonjs';
 import { AbstractViewer } from './viewer';
 
 class ViewerManager {
 
     private viewers: { [key: string]: AbstractViewer };
 
+    public onViewerAdded: (viewer: AbstractViewer) => void;
+    public onViewerAddedObservable: Observable<AbstractViewer>;
+
     constructor() {
         this.viewers = {};
+        this.onViewerAddedObservable = new Observable();
     }
 
     public addViewer(viewer: AbstractViewer) {
         this.viewers[viewer.getBaseId()] = viewer;
+        this._onViewerAdded(viewer);
     }
 
     public getViewerById(id: string): AbstractViewer {
@@ -23,6 +29,23 @@ class ViewerManager {
             }
         }
     }
+
+    public getViewerPromiseById(id: string): Promise<AbstractViewer> {
+        return new Promise((resolve, reject) => {
+            let viewerFunction = (viewer: AbstractViewer) => {
+                if (viewer.getBaseId() === id) {
+                    resolve(viewer);
+                    this.onViewerAddedObservable.removeCallback(viewerFunction);
+                }
+            }
+            this.onViewerAddedObservable.add(viewerFunction);
+        });
+    }
+
+    private _onViewerAdded(viewer: AbstractViewer) {
+        this.onViewerAdded && this.onViewerAdded(viewer);
+        this.onViewerAddedObservable.notifyObservers(viewer);
+    }
 }
 
 export let viewerManager = new ViewerManager();

文件差異過大導致無法顯示
+ 2112 - 1968
dist/preview release/babylon.d.ts


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


文件差異過大導致無法顯示
+ 1306 - 840
dist/preview release/babylon.max.js


文件差異過大導致無法顯示
+ 2112 - 1968
dist/preview release/babylon.module.d.ts


文件差異過大導致無法顯示
+ 49 - 49
dist/preview release/babylon.worker.js


文件差異過大導致無法顯示
+ 2969 - 2836
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


文件差異過大導致無法顯示
+ 49 - 49
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


文件差異過大導致無法顯示
+ 1289 - 846
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


文件差異過大導致無法顯示
+ 2969 - 2836
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


+ 5 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -36,6 +36,7 @@ declare module BABYLON.GUI {
         readonly layer: Nullable<Layer>;
         readonly rootContainer: Container;
         focusedControl: Nullable<IFocusableControl>;
+        isForeground: boolean;
         constructor(name: string, width: number | undefined, height: number | undefined, scene: Nullable<Scene>, generateMipMaps?: boolean, samplingMode?: number);
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         markAsDirty(): void;
@@ -177,6 +178,10 @@ declare module BABYLON.GUI {
         isHitTestVisible: boolean;
         isPointerBlocker: boolean;
         isFocusInvisible: boolean;
+        shadowOffsetX: number;
+        shadowOffsetY: number;
+        shadowBlur: number;
+        shadowColor: string;
         protected _linkOffsetX: ValueAndUnit;
         protected _linkOffsetY: ValueAndUnit;
         readonly typeName: string;

+ 169 - 3
dist/preview release/gui/babylon.gui.js

@@ -154,6 +154,25 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            Object.defineProperty(AdvancedDynamicTexture.prototype, "isForeground", {
+                get: function () {
+                    if (!this.layer) {
+                        return true;
+                    }
+                    return (!this.layer.isBackground);
+                },
+                set: function (value) {
+                    if (!this.layer) {
+                        return;
+                    }
+                    if (this.layer.isBackground === !value) {
+                        return;
+                    }
+                    this.layer.isBackground = !value;
+                },
+                enumerable: true,
+                configurable: true
+            });
             AdvancedDynamicTexture.prototype.executeOnAllControls = function (func, container) {
                 if (!container) {
                     container = this._rootContainer;
@@ -800,6 +819,10 @@ var BABYLON;
                 this.isHitTestVisible = true;
                 this.isPointerBlocker = false;
                 this.isFocusInvisible = false;
+                this.shadowOffsetX = 0;
+                this.shadowOffsetY = 0;
+                this.shadowBlur = 0;
+                this.shadowColor = '#000';
                 this._linkOffsetX = new GUI.ValueAndUnit(0);
                 this._linkOffsetY = new GUI.ValueAndUnit(0);
                 /**
@@ -1449,7 +1472,19 @@ var BABYLON;
             };
             Control.prototype._clip = function (context) {
                 context.beginPath();
-                context.rect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    var shadowOffsetX = this.shadowOffsetX;
+                    var shadowOffsetY = this.shadowOffsetY;
+                    var shadowBlur = this.shadowBlur;
+                    var leftShadowOffset = Math.min(Math.min(shadowOffsetX, 0) - shadowBlur * 2, 0);
+                    var rightShadowOffset = Math.max(Math.max(shadowOffsetX, 0) + shadowBlur * 2, 0);
+                    var topShadowOffset = Math.min(Math.min(shadowOffsetY, 0) - shadowBlur * 2, 0);
+                    var bottomShadowOffset = Math.max(Math.max(shadowOffsetY, 0) + shadowBlur * 2, 0);
+                    context.rect(this._currentMeasure.left + leftShadowOffset, this._currentMeasure.top + topShadowOffset, this._currentMeasure.width + rightShadowOffset - leftShadowOffset, this._currentMeasure.height + bottomShadowOffset - topShadowOffset);
+                }
+                else {
+                    context.rect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                }
             };
             Control.prototype._measure = function () {
                 // Width / Height
@@ -1771,6 +1806,10 @@ var BABYLON;
                     panel.addControl(control);
                     header.paddingRight = "5px";
                 }
+                header.shadowBlur = control.shadowBlur;
+                header.shadowColor = control.shadowColor;
+                header.shadowOffsetX = control.shadowOffsetX;
+                header.shadowOffsetY = control.shadowOffsetY;
                 return panel;
             };
             Control.drawEllipse = function (x, y, width, height, context) {
@@ -1904,8 +1943,19 @@ var BABYLON;
             };
             Container.prototype._localDraw = function (context) {
                 if (this._background) {
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowColor = this.shadowColor;
+                        context.shadowBlur = this.shadowBlur;
+                        context.shadowOffsetX = this.shadowOffsetX;
+                        context.shadowOffsetY = this.shadowOffsetY;
+                    }
                     context.fillStyle = this._background;
                     context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
                 }
             };
             Container.prototype._link = function (root, host) {
@@ -2170,6 +2220,12 @@ var BABYLON;
             };
             Rectangle.prototype._localDraw = function (context) {
                 context.save();
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
                 if (this._background) {
                     context.fillStyle = this._background;
                     if (this._cornerRadius) {
@@ -2181,6 +2237,11 @@ var BABYLON;
                     }
                 }
                 if (this._thickness) {
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
                     if (this.color) {
                         context.strokeStyle = this.color;
                     }
@@ -2268,11 +2329,22 @@ var BABYLON;
             };
             Ellipse.prototype._localDraw = function (context) {
                 context.save();
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
                 GUI.Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2, this._currentMeasure.width / 2 - this._thickness / 2, this._currentMeasure.height / 2 - this._thickness / 2, context);
                 if (this._background) {
                     context.fillStyle = this._background;
                     context.fill();
                 }
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
                 if (this._thickness) {
                     if (this.color) {
                         context.strokeStyle = this.color;
@@ -2466,6 +2538,12 @@ var BABYLON;
             };
             Line.prototype._draw = function (parentMeasure, context) {
                 context.save();
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
                 this._applyStates(context);
                 if (this._processMeasures(parentMeasure, context)) {
                     context.strokeStyle = this.color;
@@ -2668,6 +2746,12 @@ var BABYLON;
                     // Main bar
                     var effectiveThumbWidth;
                     var effectiveBarOffset;
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowColor = this.shadowColor;
+                        context.shadowBlur = this.shadowBlur;
+                        context.shadowOffsetX = this.shadowOffsetX;
+                        context.shadowOffsetY = this.shadowOffsetY;
+                    }
                     if (this._thumbWidth.isPixel) {
                         effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.height);
                     }
@@ -2686,18 +2770,39 @@ var BABYLON;
                     // Bar
                     context.fillStyle = this._background;
                     context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
                     context.fillStyle = this.color;
                     context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowColor = this.shadowColor;
+                        context.shadowBlur = this.shadowBlur;
+                        context.shadowOffsetX = this.shadowOffsetX;
+                        context.shadowOffsetY = this.shadowOffsetY;
+                    }
                     // Thumb
                     if (this._isThumbCircle) {
                         context.beginPath();
                         context.arc(left + thumbPosition, this._currentMeasure.top + this._currentMeasure.height / 2, effectiveThumbWidth / 2, 0, 2 * Math.PI);
                         context.fill();
+                        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                            context.shadowBlur = 0;
+                            context.shadowOffsetX = 0;
+                            context.shadowOffsetY = 0;
+                        }
                         context.strokeStyle = this._borderColor;
                         context.stroke();
                     }
                     else {
                         context.fillRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
+                        if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                            context.shadowBlur = 0;
+                            context.shadowOffsetX = 0;
+                            context.shadowOffsetY = 0;
+                        }
                         context.strokeStyle = this._borderColor;
                         context.strokeRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
                     }
@@ -2821,8 +2926,19 @@ var BABYLON;
                 if (this._processMeasures(parentMeasure, context)) {
                     var actualWidth = this._currentMeasure.width - this._thickness;
                     var actualHeight = this._currentMeasure.height - this._thickness;
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowColor = this.shadowColor;
+                        context.shadowBlur = this.shadowBlur;
+                        context.shadowOffsetX = this.shadowOffsetX;
+                        context.shadowOffsetY = this.shadowOffsetY;
+                    }
                     context.fillStyle = this._background;
                     context.fillRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2, actualWidth, actualHeight);
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
                     if (this._isChecked) {
                         context.fillStyle = this.color;
                         var offsetWidth = actualWidth * this._checkSizeRatio;
@@ -2954,10 +3070,21 @@ var BABYLON;
                 if (this._processMeasures(parentMeasure, context)) {
                     var actualWidth = this._currentMeasure.width - this._thickness;
                     var actualHeight = this._currentMeasure.height - this._thickness;
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowColor = this.shadowColor;
+                        context.shadowBlur = this.shadowBlur;
+                        context.shadowOffsetX = this.shadowOffsetX;
+                        context.shadowOffsetY = this.shadowOffsetY;
+                    }
                     // Outer
                     GUI.Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2, this._currentMeasure.width / 2 - this._thickness / 2, this._currentMeasure.height / 2 - this._thickness / 2, context);
                     context.fillStyle = this._background;
                     context.fill();
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
                     context.strokeStyle = this.color;
                     context.lineWidth = this._thickness;
                     context.stroke();
@@ -3101,6 +3228,12 @@ var BABYLON;
                         x = (width - textWidth) / 2;
                         break;
                 }
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
                 context.fillText(text, this._currentMeasure.left + x, y);
             };
             TextBlock.prototype._draw = function (parentMeasure, context) {
@@ -3399,6 +3532,12 @@ var BABYLON;
             };
             Image.prototype._draw = function (parentMeasure, context) {
                 context.save();
+                if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
                 var x, y, width, height;
                 if (this.cellId == -1) {
                     x = this._sourceLeft;
@@ -3438,7 +3577,7 @@ var BABYLON;
                                 if (this._autoScale) {
                                     this.synchronizeSizeWithContent();
                                 }
-                                if (this._root) {
+                                if (this._root && this._root.parent) {
                                     this._root.width = this.width;
                                     this._root.height = this.height;
                                 }
@@ -3871,8 +4010,20 @@ var BABYLON;
                     if (!this._colorWheelCanvas || this._colorWheelCanvas.width != radius * 2) {
                         this._colorWheelCanvas = this._createColorWheelCanvas(radius, wheelThickness);
                     }
-                    context.drawImage(this._colorWheelCanvas, left, top);
                     this._updateSquareProps();
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowColor = this.shadowColor;
+                        context.shadowBlur = this.shadowBlur;
+                        context.shadowOffsetX = this.shadowOffsetX;
+                        context.shadowOffsetY = this.shadowOffsetY;
+                        context.fillRect(this._squareLeft, this._squareTop, this._squareSize, this._squareSize);
+                    }
+                    context.drawImage(this._colorWheelCanvas, left, top);
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
                     this._drawGradientSquare(this._h, this._squareLeft, this._squareTop, this._squareSize, this._squareSize, context);
                     var cx = this._squareLeft + this._squareSize * this._s;
                     var cy = this._squareTop + this._squareSize * (1 - this._v);
@@ -4243,6 +4394,12 @@ var BABYLON;
                 context.save();
                 this._applyStates(context);
                 if (this._processMeasures(parentMeasure, context)) {
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowColor = this.shadowColor;
+                        context.shadowBlur = this.shadowBlur;
+                        context.shadowOffsetX = this.shadowOffsetX;
+                        context.shadowOffsetY = this.shadowOffsetY;
+                    }
                     // Background
                     if (this._isFocused) {
                         if (this._focusedBackground) {
@@ -4254,6 +4411,11 @@ var BABYLON;
                         context.fillStyle = this._background;
                         context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                     }
+                    if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
                     if (!this._fontOffset) {
                         this._fontOffset = GUI.Control._GetFontOffset(context.font);
                     }
@@ -4420,6 +4582,10 @@ var BABYLON;
                 button.paddingBottom = propertySet && propertySet.paddingBottom ? propertySet.paddingBottom : this.defaultButtonPaddingBottom;
                 button.thickness = 0;
                 button.isFocusInvisible = true;
+                button.shadowColor = this.shadowColor;
+                button.shadowBlur = this.shadowBlur;
+                button.shadowOffsetX = this.shadowOffsetX;
+                button.shadowOffsetY = this.shadowOffsetY;
                 button.onPointerUpObservable.add(function () {
                     _this.onKeyPressObservable.notifyObservers(key);
                 });

文件差異過大導致無法顯示
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


+ 5 - 0
dist/preview release/gui/babylon.gui.module.d.ts

@@ -41,6 +41,7 @@ declare module BABYLON.GUI {
         readonly layer: Nullable<Layer>;
         readonly rootContainer: Container;
         focusedControl: Nullable<IFocusableControl>;
+        isForeground: boolean;
         constructor(name: string, width: number | undefined, height: number | undefined, scene: Nullable<Scene>, generateMipMaps?: boolean, samplingMode?: number);
         executeOnAllControls(func: (control: Control) => void, container?: Container): void;
         markAsDirty(): void;
@@ -182,6 +183,10 @@ declare module BABYLON.GUI {
         isHitTestVisible: boolean;
         isPointerBlocker: boolean;
         isFocusInvisible: boolean;
+        shadowOffsetX: number;
+        shadowOffsetY: number;
+        shadowBlur: number;
+        shadowColor: string;
         protected _linkOffsetX: ValueAndUnit;
         protected _linkOffsetY: ValueAndUnit;
         readonly typeName: string;

+ 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.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

文件差異過大導致無法顯示
+ 18 - 18
dist/preview release/inspector/babylon.inspector.bundle.js


+ 4 - 2
dist/preview release/inspector/babylon.inspector.d.ts

@@ -398,7 +398,8 @@ declare module INSPECTOR {
     class MeshAdapter extends Adapter implements IToolVisible, IToolDebug, IToolBoundingBox, IToolInfo {
         /** Keep track of the axis of the actual object */
         private _axesViewer;
-        constructor(obj: BABYLON.AbstractMesh);
+        private onBeforeRenderObserver;
+        constructor(mesh: BABYLON.AbstractMesh);
         /** Returns the name displayed in the tree */
         id(): string;
         /** Returns the type of this object - displayed in the tree */
@@ -410,7 +411,7 @@ declare module INSPECTOR {
         isVisible(): boolean;
         isBoxVisible(): boolean;
         setBoxVisible(b: boolean): boolean;
-        debug(b: boolean): void;
+        debug(enable: boolean): void;
         /** Returns some information about this mesh */
         getInfo(): string;
         /** Draw X, Y and Z axis for the actual object if this adapter.
@@ -986,6 +987,7 @@ declare module INSPECTOR {
         private _updateLoopHandler;
         private _sceneInstrumentation;
         private _engineInstrumentation;
+        private _connectToInstrumentation();
         constructor(tabbar: TabBar, insp: Inspector);
         private _createStatLabel(content, parent);
         /** Update each properties of the stats panel */

+ 38 - 20
dist/preview release/inspector/babylon.inspector.js

@@ -1244,8 +1244,8 @@ var INSPECTOR;
 (function (INSPECTOR) {
     var MeshAdapter = /** @class */ (function (_super) {
         __extends(MeshAdapter, _super);
-        function MeshAdapter(obj) {
-            return _super.call(this, obj) || this;
+        function MeshAdapter(mesh) {
+            return _super.call(this, mesh) || this;
         }
         /** Returns the name displayed in the tree */
         MeshAdapter.prototype.id = function () {
@@ -1286,13 +1286,15 @@ var INSPECTOR;
         MeshAdapter.prototype.setBoxVisible = function (b) {
             return this._obj.showBoundingBox = b;
         };
-        MeshAdapter.prototype.debug = function (b) {
+        MeshAdapter.prototype.debug = function (enable) {
             // Draw axis the first time
             if (!this._axesViewer) {
                 this._drawAxis();
             }
             // Display or hide axis
-            if (!b && this._axesViewer) {
+            if (!enable && this._axesViewer) {
+                var mesh = this._obj;
+                mesh.getScene().onBeforeRenderObservable.remove(this.onBeforeRenderObserver);
                 this._axesViewer.dispose();
                 this._axesViewer = null;
             }
@@ -1305,14 +1307,20 @@ var INSPECTOR;
          * Should be called only one time as it will fill this._axis
          */
         MeshAdapter.prototype._drawAxis = function () {
+            var _this = this;
             this._obj.computeWorldMatrix();
             var mesh = this._obj;
             // Axis
-            var x = new BABYLON.Vector3(8 / Math.abs(mesh.scaling.x), 0, 0);
-            var y = new BABYLON.Vector3(0, 8 / Math.abs(mesh.scaling.y), 0);
-            var z = new BABYLON.Vector3(0, 0, 8 / Math.abs(mesh.scaling.z));
+            var x = new BABYLON.Vector3(1, 0, 0);
+            var y = new BABYLON.Vector3(0, 1, 0);
+            var z = new BABYLON.Vector3(0, 0, 1);
             this._axesViewer = new BABYLON.Debug.AxesViewer(this._obj.getScene());
-            this._axesViewer.update(this._obj.position, x, y, z);
+            this.onBeforeRenderObserver = mesh.getScene().onBeforeRenderObservable.add(function () {
+                var matrix = mesh.getWorldMatrix();
+                var extend = mesh.getBoundingInfo().boundingBox.extendSizeWorld;
+                _this._axesViewer.scaleLines = Math.max(extend.x, extend.y, extend.z) * 2;
+                _this._axesViewer.update(_this._obj.position, BABYLON.Vector3.TransformNormal(x, matrix), BABYLON.Vector3.TransformNormal(y, matrix), BABYLON.Vector3.TransformNormal(z, matrix));
+            });
         };
         return MeshAdapter;
     }(INSPECTOR.Adapter));
@@ -3671,18 +3679,7 @@ var INSPECTOR;
             _this._scene = _this._inspector.scene;
             _this._engine = _this._scene.getEngine();
             _this._glInfo = _this._engine.getGlInfo();
-            _this._sceneInstrumentation = new BABYLON.SceneInstrumentation(_this._scene);
-            _this._sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
-            _this._sceneInstrumentation.captureRenderTargetsRenderTime = true;
-            _this._sceneInstrumentation.captureFrameTime = true;
-            _this._sceneInstrumentation.captureRenderTime = true;
-            _this._sceneInstrumentation.captureInterFrameTime = true;
-            _this._sceneInstrumentation.captureParticlesRenderTime = true;
-            _this._sceneInstrumentation.captureSpritesRenderTime = true;
-            _this._sceneInstrumentation.capturePhysicsTime = true;
-            _this._sceneInstrumentation.captureAnimationsTime = true;
-            _this._engineInstrumentation = new BABYLON.EngineInstrumentation(_this._engine);
-            _this._engineInstrumentation.captureGPUFrameTime = true;
+            _this._connectToInstrumentation();
             // Build the stats panel: a div that will contains all stats
             _this._panel = INSPECTOR.Helpers.CreateDiv('tab-panel');
             _this._panel.classList.add("stats-panel");
@@ -3947,6 +3944,23 @@ var INSPECTOR;
             }
             return _this;
         }
+        StatsTab.prototype._connectToInstrumentation = function () {
+            if (this._sceneInstrumentation) {
+                return;
+            }
+            this._sceneInstrumentation = new BABYLON.SceneInstrumentation(this._scene);
+            this._sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
+            this._sceneInstrumentation.captureRenderTargetsRenderTime = true;
+            this._sceneInstrumentation.captureFrameTime = true;
+            this._sceneInstrumentation.captureRenderTime = true;
+            this._sceneInstrumentation.captureInterFrameTime = true;
+            this._sceneInstrumentation.captureParticlesRenderTime = true;
+            this._sceneInstrumentation.captureSpritesRenderTime = true;
+            this._sceneInstrumentation.capturePhysicsTime = true;
+            this._sceneInstrumentation.captureAnimationsTime = true;
+            this._engineInstrumentation = new BABYLON.EngineInstrumentation(this._engine);
+            this._engineInstrumentation.captureGPUFrameTime = true;
+        };
         StatsTab.prototype._createStatLabel = function (content, parent) {
             var elem = INSPECTOR.Helpers.CreateDiv('stat-label', parent);
             elem.textContent = content;
@@ -3962,10 +3976,14 @@ var INSPECTOR;
         StatsTab.prototype.dispose = function () {
             this._scene.unregisterAfterRender(this._updateLoopHandler);
             this._sceneInstrumentation.dispose();
+            this._sceneInstrumentation = null;
+            this._engineInstrumentation.dispose();
+            this._engineInstrumentation = null;
         };
         StatsTab.prototype.active = function (b) {
             _super.prototype.active.call(this, b);
             if (b) {
+                this._connectToInstrumentation();
                 this._scene.registerAfterRender(this._updateLoopHandler);
             }
         };

文件差異過大導致無法顯示
+ 4 - 4
dist/preview release/inspector/babylon.inspector.min.js


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

@@ -4,14 +4,14 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "3.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
-    "main": "babylonjs.inspector.bundle.js",
+    "main": "babylon.inspector.bundle.js",
     "files": [
-        "babylonjs.inspector.bundle.js",
+        "babylon.inspector.bundle.js",
         "readme.md",
         "package.json"
     ],

+ 3 - 1
dist/preview release/loaders/babylon.glTF1FileLoader.d.ts

@@ -13,7 +13,7 @@ declare module BABYLON {
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
         loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
     }
-    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync {
+    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
         onParsed: (data: IGLTFLoaderData) => void;
@@ -39,6 +39,8 @@ declare module BABYLON {
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         canDirectLoad(data: string): boolean;
+        rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
+        createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         private static _parse(data);
         private _getLoader(loaderData);
         private static _parseBinary(data);

+ 3 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -55,6 +55,9 @@ var BABYLON;
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };
+        GLTFFileLoader.prototype.createPlugin = function () {
+            return new GLTFFileLoader();
+        };
         GLTFFileLoader._parse = function (data) {
             if (data instanceof ArrayBuffer) {
                 return GLTFFileLoader._parseBinary(data);

文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


+ 5 - 3
dist/preview release/loaders/babylon.glTF2FileLoader.d.ts

@@ -13,7 +13,7 @@ declare module BABYLON {
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
         loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
     }
-    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync {
+    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
         onParsed: (data: IGLTFLoaderData) => void;
@@ -39,6 +39,8 @@ declare module BABYLON {
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         canDirectLoad(data: string): boolean;
+        rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
+        createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         private static _parse(data);
         private _getLoader(loaderData);
         private static _parseBinary(data);
@@ -245,10 +247,10 @@ declare module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
-        babylonAnimationTargets: Node[];
+        babylonAnimationTargets?: Node[];
     }
     interface IGLTFSampler extends IGLTFChildRootProperty {
         magFilter?: ETextureMagFilter;

+ 12 - 6
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -55,6 +55,9 @@ var BABYLON;
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };
+        GLTFFileLoader.prototype.createPlugin = function () {
+            return new GLTFFileLoader();
+        };
         GLTFFileLoader._parse = function (data) {
             if (data instanceof ArrayBuffer) {
                 return GLTFFileLoader._parseBinary(data);
@@ -542,8 +545,6 @@ var BABYLON;
                 this._rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (context, node) {
-                node.babylonBones = {};
-                node.babylonAnimationTargets = [];
                 if (GLTF2.GLTFLoaderExtension.LoadNode(this, context, node)) {
                     return;
                 }
@@ -558,6 +559,7 @@ var BABYLON;
                     this._loadMesh("#/meshes/" + node.mesh, node, mesh);
                 }
                 node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin != null) {
                     var skin = GLTFLoader._GetProperty(this._gltf.skins, node.skin);
@@ -986,7 +988,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
                 var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
                 node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(babylonBone);
                 return babylonBone;
             };
@@ -1200,10 +1204,12 @@ var BABYLON;
                         var animationName = animation.name || "anim" + animation.index;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
-                            var target = _a[_i];
-                            target.animations.push(babylonAnimation.clone());
-                            animation.targets.push(target);
+                        if (targetNode.babylonAnimationTargets) {
+                            for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
+                                var target = _a[_i];
+                                target.animations.push(babylonAnimation.clone());
+                                animation.targets.push(target);
+                            }
                         }
                     }
                 };

文件差異過大導致無法顯示
+ 2 - 2
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


+ 5 - 3
dist/preview release/loaders/babylon.glTFFileLoader.d.ts

@@ -13,7 +13,7 @@ declare module BABYLON {
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
         loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
     }
-    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync {
+    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
         onParsed: (data: IGLTFLoaderData) => void;
@@ -39,6 +39,8 @@ declare module BABYLON {
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         canDirectLoad(data: string): boolean;
+        rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
+        createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         private static _parse(data);
         private _getLoader(loaderData);
         private static _parseBinary(data);
@@ -792,10 +794,10 @@ declare module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
-        babylonAnimationTargets: Node[];
+        babylonAnimationTargets?: Node[];
     }
     interface IGLTFSampler extends IGLTFChildRootProperty {
         magFilter?: ETextureMagFilter;

+ 12 - 6
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -55,6 +55,9 @@ var BABYLON;
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };
+        GLTFFileLoader.prototype.createPlugin = function () {
+            return new GLTFFileLoader();
+        };
         GLTFFileLoader._parse = function (data) {
             if (data instanceof ArrayBuffer) {
                 return GLTFFileLoader._parseBinary(data);
@@ -2687,8 +2690,6 @@ var BABYLON;
                 this._rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (context, node) {
-                node.babylonBones = {};
-                node.babylonAnimationTargets = [];
                 if (GLTF2.GLTFLoaderExtension.LoadNode(this, context, node)) {
                     return;
                 }
@@ -2703,6 +2704,7 @@ var BABYLON;
                     this._loadMesh("#/meshes/" + node.mesh, node, mesh);
                 }
                 node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin != null) {
                     var skin = GLTFLoader._GetProperty(this._gltf.skins, node.skin);
@@ -3131,7 +3133,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
                 var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
                 node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(babylonBone);
                 return babylonBone;
             };
@@ -3345,10 +3349,12 @@ var BABYLON;
                         var animationName = animation.name || "anim" + animation.index;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
-                            var target = _a[_i];
-                            target.animations.push(babylonAnimation.clone());
-                            animation.targets.push(target);
+                        if (targetNode.babylonAnimationTargets) {
+                            for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
+                                var target = _a[_i];
+                                target.animations.push(babylonAnimation.clone());
+                                animation.targets.push(target);
+                            }
                         }
                     }
                 };

文件差異過大導致無法顯示
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 12 - 6
dist/preview release/loaders/babylonjs.loaders.js

@@ -1029,6 +1029,9 @@ var BABYLON;
         GLTFFileLoader.prototype.canDirectLoad = function (data) {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         };
+        GLTFFileLoader.prototype.createPlugin = function () {
+            return new GLTFFileLoader();
+        };
         GLTFFileLoader._parse = function (data) {
             if (data instanceof ArrayBuffer) {
                 return GLTFFileLoader._parseBinary(data);
@@ -3643,8 +3646,6 @@ var BABYLON;
                 this._rootNode.babylonMesh.setEnabled(false);
             };
             GLTFLoader.prototype._loadNode = function (context, node) {
-                node.babylonBones = {};
-                node.babylonAnimationTargets = [];
                 if (GLTF2.GLTFLoaderExtension.LoadNode(this, context, node)) {
                     return;
                 }
@@ -3659,6 +3660,7 @@ var BABYLON;
                     this._loadMesh("#/meshes/" + node.mesh, node, mesh);
                 }
                 node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(node.babylonMesh);
                 if (node.skin != null) {
                     var skin = GLTFLoader._GetProperty(this._gltf.skins, node.skin);
@@ -4087,7 +4089,9 @@ var BABYLON;
             };
             GLTFLoader.prototype._createBone = function (node, skin, parent, localMatrix, baseMatrix, index) {
                 var babylonBone = new BABYLON.Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+                node.babylonBones = node.babylonBones || {};
                 node.babylonBones[skin.index] = babylonBone;
+                node.babylonAnimationTargets = node.babylonAnimationTargets || [];
                 node.babylonAnimationTargets.push(babylonBone);
                 return babylonBone;
             };
@@ -4301,10 +4305,12 @@ var BABYLON;
                         var animationName = animation.name || "anim" + animation.index;
                         var babylonAnimation = new BABYLON.Animation(animationName, targetPath, 1, animationType);
                         babylonAnimation.setKeys(keys);
-                        for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
-                            var target = _a[_i];
-                            target.animations.push(babylonAnimation.clone());
-                            animation.targets.push(target);
+                        if (targetNode.babylonAnimationTargets) {
+                            for (var _i = 0, _a = targetNode.babylonAnimationTargets; _i < _a.length; _i++) {
+                                var target = _a[_i];
+                                target.animations.push(babylonAnimation.clone());
+                                animation.targets.push(target);
+                            }
                         }
                     }
                 };

文件差異過大導致無法顯示
+ 3 - 3
dist/preview release/loaders/babylonjs.loaders.min.js


+ 5 - 3
dist/preview release/loaders/babylonjs.loaders.module.d.ts

@@ -111,7 +111,7 @@ declare module BABYLON {
         importMeshAsync: (meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
         loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
     }
-    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync {
+    class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
         onParsed: (data: IGLTFLoaderData) => void;
@@ -137,6 +137,8 @@ declare module BABYLON {
         importMeshAsync(meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         loadAsync(scene: Scene, data: string | ArrayBuffer, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void): void;
         canDirectLoad(data: string): boolean;
+        rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
+        createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
         private static _parse(data);
         private _getLoader(loaderData);
         private static _parseBinary(data);
@@ -890,10 +892,10 @@ declare module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: {
+        babylonBones?: {
             [skin: number]: Bone;
         };
-        babylonAnimationTargets: Node[];
+        babylonAnimationTargets?: Node[];
     }
     interface IGLTFSampler extends IGLTFChildRootProperty {
         magFilter?: ETextureMagFilter;

+ 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.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 0
dist/preview release/materialsLibrary/babylon.gridMaterial.d.ts

@@ -49,6 +49,7 @@ declare module BABYLON {
          * Returns wehter or not the grid requires alpha blending.
          */
         needAlphaBlending(): boolean;
+        needAlphaBlendingForMesh(mesh: AbstractMesh): boolean;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         dispose(forceDisposeEffect?: boolean): void;

+ 3 - 0
dist/preview release/materialsLibrary/babylon.gridMaterial.js

@@ -83,6 +83,9 @@ var BABYLON;
         GridMaterial.prototype.needAlphaBlending = function () {
             return this.opacity < 1.0;
         };
+        GridMaterial.prototype.needAlphaBlendingForMesh = function (mesh) {
+            return this.needAlphaBlending();
+        };
         GridMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
             if (this.isFrozen) {
                 if (this._wasPreviouslyReady && subMesh.effect) {

文件差異過大導致無法顯示
+ 1 - 1
dist/preview release/materialsLibrary/babylon.gridMaterial.min.js


+ 3 - 0
dist/preview release/materialsLibrary/babylonjs.materials.js

@@ -3863,6 +3863,9 @@ var BABYLON;
         GridMaterial.prototype.needAlphaBlending = function () {
             return this.opacity < 1.0;
         };
+        GridMaterial.prototype.needAlphaBlendingForMesh = function (mesh) {
+            return this.needAlphaBlending();
+        };
         GridMaterial.prototype.isReadyForSubMesh = function (mesh, subMesh, useInstances) {
             if (this.isFrozen) {
                 if (this._wasPreviouslyReady && subMesh.effect) {

文件差異過大導致無法顯示
+ 3 - 3
dist/preview release/materialsLibrary/babylonjs.materials.min.js


+ 1 - 0
dist/preview release/materialsLibrary/babylonjs.materials.module.d.ts

@@ -491,6 +491,7 @@ declare module BABYLON {
          * Returns wehter or not the grid requires alpha blending.
          */
         needAlphaBlending(): boolean;
+        needAlphaBlendingForMesh(mesh: AbstractMesh): boolean;
         isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean;
         bindForSubMesh(world: Matrix, mesh: Mesh, subMesh: SubMesh): void;
         dispose(forceDisposeEffect?: boolean): void;

+ 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.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "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.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "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.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "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.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -22,6 +22,7 @@
 - Complete rework of Unity3D exporter. [Doc here](http://doc.babylonjs.com/resources/intro) ([MackeyK24](https://github.com/MackeyK24))
 
 ## Updates
+- Introduced `TransformNode` class as a parent of `AbstractMesh`. This class was extensively asked by the community to hold only tranformation for a node ([deltakosh](https://github.com/deltakosh))
 - Added `boundingInfo.centerOn` to recreate the bounding info to be centered around a specific point given a specific extend ([deltakosh](https://github.com/deltakosh))
 - Added `mesh.normalizeToUnitCube` to uniformly scales the mesh to fit inside of a unit cube (1 X 1 X 1 units) ([deltakosh](https://github.com/deltakosh))
 - Added `scene.onDataLoadedObservable` which is raised when SceneLoader.Append or SceneLoader.Load or SceneLoader.ImportMesh were successfully executed ([deltakosh](https://github.com/deltakosh))
@@ -45,6 +46,7 @@
 - Improved the SPS perfs for dead or invisible solid particles ([jerome](https://github.com/jbousquie))  
 - Added `enableDepthSort` parameter to the SPS in order to sort the particles from the camera position ([jerome](https://github.com/jbousquie)) 
 - Added `pivot` property to the SPS solid particles ([jerome](https://github.com/jbousquie)) 
+- Added the mesh facet depth sort to FacetData  ([jerome](https://github.com/jbousquie)) 
 
 ## Bug fixes
 - Fixed a bug with PBR on iOS ([sebavan](https://github.com/sebavan))

+ 18 - 1
gui/src/advancedDynamicTexture.ts

@@ -113,6 +113,23 @@ module BABYLON.GUI {
 
             this._focusedControl = control;
         }
+        
+        public get isForeground(): boolean {
+            if (!this.layer) {
+                return true;
+            }
+            return (!this.layer.isBackground);
+        }
+
+        public set isForeground(value: boolean) {
+            if (!this.layer) {
+                return;
+            }            
+            if (this.layer.isBackground === !value) {
+                return;
+            }
+            this.layer.isBackground = !value;
+        }   
        
         constructor(name: string, width = 0, height = 0, scene: Nullable<Scene>, generateMipMaps = false, samplingMode = Texture.NEAREST_SAMPLINGMODE) {
             super(name, {width: width, height: height}, scene, generateMipMaps, samplingMode, Engine.TEXTUREFORMAT_RGBA);
@@ -507,4 +524,4 @@ module BABYLON.GUI {
             return result;
         }
     }    
-}
+}

+ 13 - 0
gui/src/controls/checkbox.ts

@@ -81,10 +81,23 @@ module BABYLON.GUI {
             if (this._processMeasures(parentMeasure, context)) {
                 let actualWidth = this._currentMeasure.width - this._thickness;
                 let actualHeight = this._currentMeasure.height - this._thickness;
+
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
                 
                 context.fillStyle = this._background;
                 context.fillRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2, actualWidth, actualHeight);   
 
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
+
                 if (this._isChecked) {
                     context.fillStyle = this.color;
                     let offsetWidth = actualWidth * this._checkSizeRatio;

+ 16 - 1
gui/src/controls/colorpicker.ts

@@ -274,9 +274,24 @@ module BABYLON.GUI {
                     this._colorWheelCanvas = this._createColorWheelCanvas(radius, wheelThickness);
                 }
 
+                this._updateSquareProps();
+
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){                
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY; 
+                    
+                    context.fillRect(this._squareLeft, this._squareTop, this._squareSize, this._squareSize);
+                }
+
                 context.drawImage(this._colorWheelCanvas, left, top);
 
-                this._updateSquareProps();
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
 
                 this._drawGradientSquare(this._h, 
                                         this._squareLeft,

+ 13 - 0
gui/src/controls/container.ts

@@ -119,8 +119,21 @@ module BABYLON.GUI {
 
         protected _localDraw(context: CanvasRenderingContext2D): void {
             if (this._background) {
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
+                
                 context.fillStyle = this._background;
                 context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+                
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
             }
         }
 

+ 29 - 2
gui/src/controls/control.ts

@@ -49,6 +49,11 @@ module BABYLON.GUI {
         public isHitTestVisible = true;
         public isPointerBlocker = false;
         public isFocusInvisible = false;
+ 
+        public shadowOffsetX = 0;
+        public shadowOffsetY = 0;
+        public shadowBlur = 0;
+        public shadowColor = '#000'; 
 
         protected _linkOffsetX = new ValueAndUnit(0);
         protected _linkOffsetY = new ValueAndUnit(0);
@@ -683,7 +688,24 @@ module BABYLON.GUI {
 
         protected _clip( context: CanvasRenderingContext2D) {
             context.beginPath();
-            context.rect(this._currentMeasure.left ,this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+            
+            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                var shadowOffsetX = this.shadowOffsetX;
+                var shadowOffsetY = this.shadowOffsetY;
+                var shadowBlur = this.shadowBlur;
+                
+                var leftShadowOffset = Math.min(Math.min(shadowOffsetX, 0) - shadowBlur*2, 0);
+                var rightShadowOffset = Math.max(Math.max(shadowOffsetX, 0) + shadowBlur*2, 0);
+                var topShadowOffset = Math.min(Math.min(shadowOffsetY, 0) - shadowBlur*2, 0);
+                var bottomShadowOffset = Math.max(Math.max(shadowOffsetY, 0) + shadowBlur*2, 0);
+
+                context.rect(this._currentMeasure.left + leftShadowOffset, 
+                            this._currentMeasure.top + topShadowOffset, 
+                            this._currentMeasure.width + rightShadowOffset - leftShadowOffset, 
+                            this._currentMeasure.height + bottomShadowOffset - topShadowOffset);
+            } else {
+                context.rect(this._currentMeasure.left ,this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
+            }
         }
 
         public _measure(): void {  
@@ -1032,7 +1054,7 @@ module BABYLON.GUI {
             let panel = new BABYLON.GUI.StackPanel("panel");        
             let isHorizontal = options ? options.isHorizontal : true;
             let controlFirst = options ? options.controlFirst : true;
-
+            
             panel.isVertical = !isHorizontal;
 
             let header = new BABYLON.GUI.TextBlock("header");
@@ -1053,6 +1075,11 @@ module BABYLON.GUI {
                 panel.addControl(control);
                 header.paddingRight = "5px";
             }
+            
+            header.shadowBlur = control.shadowBlur;
+            header.shadowColor = control.shadowColor;
+            header.shadowOffsetX = control.shadowOffsetX;
+            header.shadowOffsetY = control.shadowOffsetY;
 
             return panel;
         }

+ 13 - 0
gui/src/controls/ellipse.ts

@@ -28,6 +28,13 @@ module BABYLON.GUI {
         protected _localDraw(context: CanvasRenderingContext2D): void {
             context.save();
 
+            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                context.shadowColor = this.shadowColor;
+                context.shadowBlur = this.shadowBlur;
+                context.shadowOffsetX = this.shadowOffsetX;
+                context.shadowOffsetY = this.shadowOffsetY;
+            }
+
             Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2, 
                             this._currentMeasure.width / 2 - this._thickness / 2, this._currentMeasure.height / 2 - this._thickness / 2, context);
 
@@ -37,6 +44,12 @@ module BABYLON.GUI {
                 context.fill();
             }
 
+            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                context.shadowBlur = 0;
+                context.shadowOffsetX = 0;
+                context.shadowOffsetY = 0;
+            }
+
             if (this._thickness) {
                 if (this.color) {
                     context.strokeStyle = this.color;

+ 8 - 1
gui/src/controls/image.ts

@@ -198,6 +198,13 @@ module BABYLON.GUI {
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
 
+            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                context.shadowColor = this.shadowColor;
+                context.shadowBlur = this.shadowBlur;
+                context.shadowOffsetX = this.shadowOffsetX;
+                context.shadowOffsetY = this.shadowOffsetY;
+            }
+
             let x, y, width, height;
             if (this.cellId == -1) {
                 x = this._sourceLeft;
@@ -245,7 +252,7 @@ module BABYLON.GUI {
                             if (this._autoScale) {
                                 this.synchronizeSizeWithContent();
                             }
-                            if (this._root) {
+                            if (this._root && this._root.parent) { // Will update root size if root is not the top root
                                 this._root.width = this.width;
                                 this._root.height = this.height;
                             }

+ 13 - 0
gui/src/controls/inputText.ts

@@ -274,6 +274,13 @@ module BABYLON.GUI {
             this._applyStates(context);
             if (this._processMeasures(parentMeasure, context)) {
                 
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
+
                 // Background
                 if (this._isFocused) {
                     if (this._focusedBackground) {
@@ -286,6 +293,12 @@ module BABYLON.GUI {
 
                     context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                 }
+                
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
 
                 if (!this._fontOffset) {
                     this._fontOffset = Control._GetFontOffset(context.font);

+ 7 - 0
gui/src/controls/line.ts

@@ -146,6 +146,13 @@ module BABYLON.GUI {
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
 
+            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                context.shadowColor = this.shadowColor;
+                context.shadowBlur = this.shadowBlur;
+                context.shadowOffsetX = this.shadowOffsetX;
+                context.shadowOffsetY = this.shadowOffsetY;
+            }
+
             this._applyStates(context);
             if (this._processMeasures(parentMeasure, context)) {
                 context.strokeStyle = this.color;

+ 13 - 0
gui/src/controls/radioButton.ts

@@ -102,6 +102,13 @@ module BABYLON.GUI {
                 let actualWidth = this._currentMeasure.width - this._thickness;
                 let actualHeight = this._currentMeasure.height - this._thickness;
 
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
+                
                 // Outer
                 Control.drawEllipse(this._currentMeasure.left + this._currentMeasure.width / 2, this._currentMeasure.top + this._currentMeasure.height / 2, 
                             this._currentMeasure.width / 2 - this._thickness / 2, this._currentMeasure.height / 2 - this._thickness / 2, context);
@@ -109,6 +116,12 @@ module BABYLON.GUI {
                 context.fillStyle = this._background;
                 context.fill();
 
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
+
                 context.strokeStyle = this.color;
                 context.lineWidth = this._thickness;
 

+ 15 - 1
gui/src/controls/rectangle.ts

@@ -45,6 +45,13 @@ module BABYLON.GUI {
 
         protected _localDraw(context: CanvasRenderingContext2D): void {
             context.save();
+            
+            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                context.shadowColor = this.shadowColor;
+                context.shadowBlur = this.shadowBlur;
+                context.shadowOffsetX = this.shadowOffsetX;
+                context.shadowOffsetY = this.shadowOffsetY;
+            }
 
             if (this._background) {
                 context.fillStyle = this._background;
@@ -58,6 +65,13 @@ module BABYLON.GUI {
             }
 
             if (this._thickness) {
+
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
+
                 if (this.color) {
                     context.strokeStyle = this.color;
                 }
@@ -71,7 +85,7 @@ module BABYLON.GUI {
                                        this._currentMeasure.width - this._thickness, this._currentMeasure.height - this._thickness);
                 }
             }
-        
+
             context.restore();
         }
 

+ 32 - 0
gui/src/controls/slider.ts

@@ -153,6 +153,13 @@ module BABYLON.GUI {
                 var effectiveThumbWidth;
                 var effectiveBarOffset;
 
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
+
                 if (this._thumbWidth.isPixel) {
                     effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.height);
                 } else {
@@ -174,21 +181,46 @@ module BABYLON.GUI {
                 context.fillStyle = this._background;
                 context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
 
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowBlur = 0;
+                    context.shadowOffsetX = 0;
+                    context.shadowOffsetY = 0;
+                }
+
                 context.fillStyle = this.color;
                 context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
 
+                if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                    context.shadowColor = this.shadowColor;
+                    context.shadowBlur = this.shadowBlur;
+                    context.shadowOffsetX = this.shadowOffsetX;
+                    context.shadowOffsetY = this.shadowOffsetY;
+                }
+
                 // Thumb
                 if (this._isThumbCircle) {
                     context.beginPath();
                     context.arc(left + thumbPosition, this._currentMeasure.top + this._currentMeasure.height / 2, effectiveThumbWidth / 2, 0, 2 * Math.PI);
                     context.fill();
 
+                    if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
+
                     context.strokeStyle = this._borderColor;
                     context.stroke();
                 }
                 else {
                     context.fillRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
                     
+                    if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                        context.shadowBlur = 0;
+                        context.shadowOffsetX = 0;
+                        context.shadowOffsetY = 0;
+                    }
+
                     context.strokeStyle = this._borderColor;
                     context.strokeRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
                 }

+ 6 - 0
gui/src/controls/textBlock.ts

@@ -106,6 +106,12 @@ module BABYLON.GUI {
                     break;
             }
 
+            if(this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY){
+                context.shadowColor = this.shadowColor;
+                context.shadowBlur = this.shadowBlur;
+                context.shadowOffsetX = this.shadowOffsetX;
+                context.shadowOffsetY = this.shadowOffsetY;
+            }
 
             context.fillText(text, this._currentMeasure.left + x, y);
         }

+ 5 - 1
gui/src/controls/virtualKeyboard.ts

@@ -33,7 +33,6 @@ module BABYLON.GUI {
 
         private _createKey(key: string, propertySet: Nullable<KeyPropertySet>) {
             var button = Button.CreateSimpleButton(key, key);
-            
            
             button.width = propertySet && propertySet.width ? propertySet.width : this.defaultButtonWidth;
             button.height = propertySet && propertySet.height ? propertySet.height : this.defaultButtonHeight;
@@ -46,6 +45,11 @@ module BABYLON.GUI {
         
             button.thickness = 0;
             button.isFocusInvisible = true;
+
+            button.shadowColor = this.shadowColor;
+            button.shadowBlur = this.shadowBlur;
+            button.shadowOffsetX = this.shadowOffsetX;
+            button.shadowOffsetY = this.shadowOffsetY;
         
             button.onPointerUpObservable.add(() => {
                 this.onKeyPressObservable.notifyObservers(key);

+ 18 - 9
inspector/src/adapters/MeshAdapter.ts

@@ -6,9 +6,10 @@ module INSPECTOR {
 
         /** Keep track of the axis of the actual object */
         private _axesViewer: BABYLON.Nullable<BABYLON.Debug.AxesViewer>;
+        private onBeforeRenderObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Scene>>;
 
-        constructor(obj: BABYLON.AbstractMesh) {
-            super(obj);
+        constructor(mesh: BABYLON.AbstractMesh) {
+            super(mesh);
         }
 
         /** Returns the name displayed in the tree */
@@ -56,13 +57,15 @@ module INSPECTOR {
             return (this._obj as BABYLON.AbstractMesh).showBoundingBox = b;
         }
 
-        public debug(b: boolean) {
+        public debug(enable: boolean) {
             // Draw axis the first time
             if (!this._axesViewer) {
                  this._drawAxis();
             }
             // Display or hide axis
-            if (!b && this._axesViewer) {
+            if (!enable && this._axesViewer) {
+                let mesh = this._obj as BABYLON.AbstractMesh;
+                mesh.getScene().onBeforeRenderObservable.remove(this.onBeforeRenderObserver);
                 this._axesViewer.dispose();
                 this._axesViewer = null;
             }
@@ -79,15 +82,21 @@ module INSPECTOR {
         private _drawAxis() {
             this._obj.computeWorldMatrix();
 
-            let mesh = this._obj as BABYLON.AbstractMesh;
+            let mesh = this._obj as BABYLON.Mesh;
 
             // Axis
-            var x = new BABYLON.Vector3(8 / Math.abs(mesh.scaling.x), 0, 0);
-            var y = new BABYLON.Vector3(0, 8 / Math.abs(mesh.scaling.y), 0);
-            var z = new BABYLON.Vector3(0, 0, 8 / Math.abs(mesh.scaling.z));
+            var x = new BABYLON.Vector3(1, 0, 0);
+            var y = new BABYLON.Vector3(0, 1, 0);
+            var z = new BABYLON.Vector3(0, 0, 1);
 
             this._axesViewer = new BABYLON.Debug.AxesViewer(this._obj.getScene());
-            this._axesViewer.update(this._obj.position, x, y, z);
+
+            this.onBeforeRenderObserver = mesh.getScene().onBeforeRenderObservable.add(() => {
+                let matrix = mesh.getWorldMatrix();
+                let extend = mesh.getBoundingInfo()!.boundingBox.extendSizeWorld;
+                this._axesViewer!.scaleLines = Math.max(extend.x, extend.y, extend.z) * 2;
+                this._axesViewer!.update(this._obj.position, BABYLON.Vector3.TransformNormal(x, matrix), BABYLON.Vector3.TransformNormal(y, matrix), BABYLON.Vector3.TransformNormal(z, matrix));
+            });
         }
     }
 }

+ 37 - 25
inspector/src/tabs/StatsTab.ts

@@ -16,17 +16,13 @@ module INSPECTOR {
 
         private _updateLoopHandler : any;
 
-        private _sceneInstrumentation: BABYLON.SceneInstrumentation;
-        private _engineInstrumentation: BABYLON.EngineInstrumentation;
+        private _sceneInstrumentation: BABYLON.Nullable<BABYLON.SceneInstrumentation>;
+        private _engineInstrumentation: BABYLON.Nullable<BABYLON.EngineInstrumentation>;
 
-        constructor(tabbar:TabBar, insp:Inspector) {
-            super(tabbar, 'Stats');        
-
-            this._inspector         = insp;  
-
-            this._scene             = this._inspector.scene;
-            this._engine            = this._scene.getEngine();
-            this._glInfo            = this._engine.getGlInfo();
+        private _connectToInstrumentation() {
+            if (this._sceneInstrumentation) {
+                return;
+            }
 
             this._sceneInstrumentation = new BABYLON.SceneInstrumentation(this._scene);
             this._sceneInstrumentation.captureActiveMeshesEvaluationTime = true;
@@ -41,6 +37,18 @@ module INSPECTOR {
 
             this._engineInstrumentation = new BABYLON.EngineInstrumentation(this._engine);
             this._engineInstrumentation.captureGPUFrameTime = true;
+        }
+
+        constructor(tabbar:TabBar, insp:Inspector) {
+            super(tabbar, 'Stats');        
+
+            this._inspector         = insp;  
+
+            this._scene             = this._inspector.scene;
+            this._engine            = this._scene.getEngine();
+            this._glInfo            = this._engine.getGlInfo();
+
+            this._connectToInstrumentation();
 
             // Build the stats panel: a div that will contains all stats
             this._panel             = Helpers.CreateDiv('tab-panel') as HTMLDivElement; 
@@ -75,7 +83,7 @@ module INSPECTOR {
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return this._sceneInstrumentation.drawCallsCounter.current.toString()}
+                    updateFct:() => { return this._sceneInstrumentation!.drawCallsCounter.current.toString()}
                 });
 
                 elemLabel = this._createStatLabel("Total lights", this._panel);
@@ -142,73 +150,73 @@ module INSPECTOR {
                 let elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.activeMeshesEvaluationTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.activeMeshesEvaluationTimeCounter.current)}
                 });
                 elemLabel = this._createStatLabel("Render targets", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.renderTargetsRenderTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.renderTargetsRenderTimeCounter.current)}
                 });
                 elemLabel = this._createStatLabel("Particles", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.particlesRenderTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.particlesRenderTimeCounter.current)}
                 });
                 elemLabel = this._createStatLabel("Sprites", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.spritesRenderTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.spritesRenderTimeCounter.current)}
                 });
                 elemLabel = this._createStatLabel("Animations", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.animationsTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.animationsTimeCounter.current)}
                 });                       
                 elemLabel = this._createStatLabel("Physics", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.physicsTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.physicsTimeCounter.current)}
                 });                
                 elemLabel = this._createStatLabel("Render", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.renderTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.renderTimeCounter.current)}
                 });
                 elemLabel = this._createStatLabel("Frame", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.frameTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.frameTimeCounter.current)}
                 });                
                 elemLabel = this._createStatLabel("Inter-frame", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation.interFrameTimeCounter.current)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._sceneInstrumentation!.interFrameTimeCounter.current)}
                 });       
                 elemLabel = this._createStatLabel("GPU Frame time", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._engineInstrumentation.gpuFrameTimeCounter.current * 0.000001)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._engineInstrumentation!.gpuFrameTimeCounter.current * 0.000001)}
                 });                  
                 elemLabel = this._createStatLabel("GPU Frame time (average)", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(this._engineInstrumentation.gpuFrameTimeCounter.average * 0.000001)}
+                    updateFct:() => { return BABYLON.Tools.Format(this._engineInstrumentation!.gpuFrameTimeCounter.average * 0.000001)}
                 });                                 
                 elemLabel = this._createStatLabel("Potential FPS", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
                 this._updatableProperties.push({ 
                     elem:elemValue, 
-                    updateFct:() => { return BABYLON.Tools.Format(1000.0 / this._sceneInstrumentation.frameTimeCounter.current, 0)}
+                    updateFct:() => { return BABYLON.Tools.Format(1000.0 / this._sceneInstrumentation!.frameTimeCounter.current, 0)}
                 });
                 elemLabel = this._createStatLabel("Resolution", this._panel);
                 elemValue = Helpers.CreateDiv('stat-value', this._panel);
@@ -337,12 +345,16 @@ module INSPECTOR {
 
         public dispose() {
             this._scene.unregisterAfterRender(this._updateLoopHandler);
-            this._sceneInstrumentation.dispose();
+            this._sceneInstrumentation!.dispose();
+            this._sceneInstrumentation = null;
+            this._engineInstrumentation!.dispose();
+            this._engineInstrumentation = null;
         }
 
         public active(b: boolean){
             super.active(b);
-            if(b){
+            if (b){
+                this._connectToInstrumentation();
                 this._scene.registerAfterRender(this._updateLoopHandler);
             }
         }

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

@@ -315,9 +315,6 @@ module BABYLON.GLTF2 {
         }
 
         public _loadNode(context: string, node: IGLTFNode): void {
-            node.babylonBones = {};
-            node.babylonAnimationTargets = [];
-
             if (GLTFLoaderExtension.LoadNode(this, context, node)) {
                 return;
             }
@@ -338,6 +335,7 @@ module BABYLON.GLTF2 {
 
             node.babylonMesh.parent = node.parent ? node.parent.babylonMesh : null;
 
+            node.babylonAnimationTargets = node.babylonAnimationTargets || [];
             node.babylonAnimationTargets.push(node.babylonMesh);
 
             if (node.skin != null) {
@@ -785,8 +783,13 @@ module BABYLON.GLTF2 {
 
         private _createBone(node: IGLTFNode, skin: IGLTFSkin, parent: Nullable<Bone>, localMatrix: Matrix, baseMatrix: Matrix, index: number): Bone {
             const babylonBone = new Bone(node.name || "bone" + node.index, skin.babylonSkeleton, parent, localMatrix, null, baseMatrix, index);
+
+            node.babylonBones = node.babylonBones || {};
             node.babylonBones[skin.index] = babylonBone;
+
+            node.babylonAnimationTargets = node.babylonAnimationTargets || [];
             node.babylonAnimationTargets.push(babylonBone);
+
             return babylonBone;
         }
 
@@ -1027,9 +1030,11 @@ module BABYLON.GLTF2 {
                     const babylonAnimation = new Animation(animationName, targetPath, 1, animationType);
                     babylonAnimation.setKeys(keys);
 
-                    for (const target of targetNode.babylonAnimationTargets) {
-                        target.animations.push(babylonAnimation.clone());
-                        animation.targets.push(target);
+                    if (targetNode.babylonAnimationTargets) {
+                        for (const target of targetNode.babylonAnimationTargets) {
+                            target.animations.push(babylonAnimation.clone());
+                            animation.targets.push(target);
+                        }
                     }
                 }
             };

+ 2 - 2
loaders/src/glTF/2.0/babylon.glTFLoaderInterfaces.ts

@@ -234,8 +234,8 @@ module BABYLON.GLTF2 {
         index: number;
         parent?: IGLTFNode;
         babylonMesh: Mesh;
-        babylonBones: { [skin: number]: Bone };
-        babylonAnimationTargets: Node[];
+        babylonBones?: { [skin: number]: Bone };
+        babylonAnimationTargets?: Node[];
     }
 
     export interface IGLTFSampler extends IGLTFChildRootProperty {

+ 7 - 1
loaders/src/glTF/babylon.glTFFileLoader.ts

@@ -23,7 +23,7 @@ module BABYLON {
         loadAsync: (scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
     }
 
-    export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync {
+    export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISceneLoaderPluginFactory {
         public static CreateGLTFLoaderV1: (parent: GLTFFileLoader) => IGLTFLoader;
         public static CreateGLTFLoaderV2: (parent: GLTFFileLoader) => IGLTFLoader;
 
@@ -102,6 +102,12 @@ module BABYLON {
             return ((data.indexOf("scene") !== -1) && (data.indexOf("node") !== -1));
         }
 
+        public rewriteRootURL: (rootUrl: string, responseURL?: string) => string;
+
+        public createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync {
+            return new GLTFFileLoader();
+        }
+
         private static _parse(data: string | ArrayBuffer): IGLTFLoaderData {
             if (data instanceof ArrayBuffer) {
                 return GLTFFileLoader._parseBinary(data);

+ 24 - 7
localDev/index.html

@@ -69,14 +69,31 @@
 					BABYLONDEVTOOLS.Loader.debugShortcut(engine);
 
 					// call the scene creation from the js.
-					var scene = createScene();
+					if (typeof delayCreateScene !== "undefined") {
+						var scene = delayCreateScene();
 
-					if (scene) {
-						// Register a render loop to repeatedly render the scene
-						engine.runRenderLoop(function () {
-							scene.render();
-							divFps.innerHTML = engine.getFps().toFixed() + " fps";
-						});
+						if (scene) {
+							// Register a render loop to repeatedly render the scene
+
+							engine.runRenderLoop(function () {
+								if (scene.activeCamera) {
+									scene.render();
+								}
+								divFps.innerHTML = engine.getFps().toFixed() + " fps";
+							});
+						}
+					}
+					else {
+						var scene = createScene();
+
+						if (scene) {
+							// Register a render loop to repeatedly render the scene
+
+							engine.runRenderLoop(function () {
+								scene.render();
+								divFps.innerHTML = engine.getFps().toFixed() + " fps";
+							});
+						}
 					}
 
 					// Resize

+ 4 - 0
materialsLibrary/src/grid/babylon.gridmaterial.ts

@@ -86,6 +86,10 @@ module BABYLON {
             return this.opacity < 1.0;
         }
 
+        public needAlphaBlendingForMesh(mesh: AbstractMesh): boolean {
+            return this.needAlphaBlending();
+        }
+
         public isReadyForSubMesh(mesh: AbstractMesh, subMesh: SubMesh, useInstances?: boolean): boolean {   
             if (this.isFrozen) {
                 if (this._wasPreviouslyReady && subMesh.effect) {

+ 1 - 1
package.json

@@ -8,7 +8,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "3.1.0-beta1.0",
+    "version": "3.1.0-beta3",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 32 - 6
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -30,23 +30,43 @@ module BABYLON {
         public onEnteringVR: () => void;
         public onExitingVR: () => void;
         public onControllerMeshLoaded: (controller: WebVRController) => void;
+
+        public get deviceOrientationCamera(): DeviceOrientationCamera {
+            return this._deviceOrientationCamera;
+        }
+
+        // Based on the current WebVR support, returns the current VR camera used
+        public get currentVRCamera(): FreeCamera {
+            if (this._webVRready) {
+                return this._webVRCamera;
+            }
+            else {
+                return this._vrDeviceOrientationCamera;
+            }
+        }
+
+        public get webVRCamera(): WebVRFreeCamera {
+            return this._webVRCamera;
+        }
+
+        public get vrDeviceOrientationCamera(): VRDeviceOrientationFreeCamera {
+            return this._vrDeviceOrientationCamera;
+        }
                 
         constructor(scene: Scene, public webVROptions: WebVROptions = {}) {
             this._scene = scene;
 
             if (!this._scene.activeCamera || isNaN(this._scene.activeCamera.position.x)) {
+                this._position = new BABYLON.Vector3(0, 2, 0);
                 this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", new BABYLON.Vector3(0, 2, 0), scene);
             }
             else {
-                this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._scene.activeCamera.position, scene);
-                if ((<FreeCamera>scene.activeCamera).rotation) {
-                    this._deviceOrientationCamera.rotation = (<FreeCamera>scene.activeCamera).rotation.clone();
-                }
+                this._position = this._scene.activeCamera.position.clone();
+                this._deviceOrientationCamera = new BABYLON.DeviceOrientationCamera("deviceOrientationVRHelper", this._position, scene);
                 this._deviceOrientationCamera.minZ = this._scene.activeCamera.minZ;
                 this._deviceOrientationCamera.maxZ = this._scene.activeCamera.maxZ;
             }
             this._scene.activeCamera = this._deviceOrientationCamera;
-            this._position = this._scene.activeCamera.position;
             this._canvas = scene.getEngine().getRenderingCanvas();
             if (this._canvas) {
                 this._scene.activeCamera.attachControl(this._canvas);
@@ -136,6 +156,7 @@ module BABYLON {
             this.updateButtonVisibility();
         }
 
+        // Raised when one of the controller has loaded successfully its associated default mesh
         private _onDefaultMeshLoaded(webVRController: WebVRController) {
             if (this.onControllerMeshLoaded) {
                 this.onControllerMeshLoaded(webVRController);
@@ -207,6 +228,10 @@ module BABYLON {
          * Otherwise, will use the fullscreen API.
          */
         public enterVR() {
+            if (this._scene.activeCamera) {
+                this._position = this._scene.activeCamera.position.clone();
+            }
+
             if (this.onEnteringVR) {
                 this.onEnteringVR();
             }
@@ -243,7 +268,8 @@ module BABYLON {
                 this._scene.getEngine().disableVR();
             }
             if (this._scene.activeCamera) {
-                this._position = this._scene.activeCamera.position;
+                this._position = this._scene.activeCamera.position.clone();
+                
             }
             this._deviceOrientationCamera.position = this._position;
             this._scene.activeCamera = this._deviceOrientationCamera;

+ 7 - 4
src/Debug/babylon.debugLayer.ts

@@ -1,7 +1,6 @@
 module BABYLON {
 
     // declare INSPECTOR namespace for compilation issue
-    declare var require: any;
     declare var INSPECTOR: any;
     // load the inspector using require, if not present in the global namespace.
 
@@ -11,10 +10,12 @@ module BABYLON {
         // The inspector instance
         private _inspector: any;
 
+        private BJSINSPECTOR = typeof INSPECTOR !== 'undefined' ? INSPECTOR : undefined;
+
         constructor(scene: Scene) {
             this._scene = scene;
             // load inspector using require, if it doesn't exist on the global namespace.
-            INSPECTOR = typeof INSPECTOR !== 'undefined' ? INSPECTOR : (typeof require !== 'undefined' ? require('INSPECTOR') : undefined);
+
         }
 
         /** Creates the inspector window. */
@@ -36,7 +37,9 @@ module BABYLON {
             let initialTab = config.initialTab || 0;
             let parentElement = config.parentElement || null;
             if (!this._inspector) {
-                this._inspector = new INSPECTOR.Inspector(this._scene, popup, initialTab, parentElement, config.newColors);
+                this.BJSINSPECTOR = this.BJSINSPECTOR || typeof INSPECTOR !== 'undefined' ? INSPECTOR : undefined;
+
+                this._inspector = new this.BJSINSPECTOR.Inspector(this._scene, popup, initialTab, parentElement, config.newColors);
             } // else nothing to do,; instance is already existing
         }
 
@@ -72,7 +75,7 @@ module BABYLON {
                 colorBot?: string
             }
         } = {}) {
-            if (typeof INSPECTOR == 'undefined') {
+            if (typeof this.BJSINSPECTOR == 'undefined') {
                 // Load inspector and add it to the DOM
                 Tools.LoadScript(DebugLayer.InspectorURL, this._createInspector.bind(this, config));
             } else {

+ 33 - 31
src/Engine/babylon.engine.ts

@@ -531,7 +531,7 @@
         }
 
         public static get Version(): string {
-            return "3.1-beta-2";
+            return "3.1-beta-3";
         }
 
         // Updatable statics so stick with vars here
@@ -4827,40 +4827,42 @@
             this.disableVR();
 
             // Events
-            window.removeEventListener("blur", this._onBlur);
-            window.removeEventListener("focus", this._onFocus);
-            window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
-            window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
-            if (this._renderingCanvas) {
-                this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
-                this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
-                this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
+            if (Tools.IsWindowObjectExist()) {
+                window.removeEventListener("blur", this._onBlur);
+                window.removeEventListener("focus", this._onFocus);
+                window.removeEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted);
+                window.removeEventListener('vrdisplaypointerunrestricted', this._onVRDisplayPointerUnrestricted);
+                if (this._renderingCanvas) {
+                    this._renderingCanvas.removeEventListener("focus", this._onCanvasFocus);
+                    this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
+                    this._renderingCanvas.removeEventListener("pointerout", this._onCanvasBlur);
 
-                if (!this._doNotHandleContextLost) {
-                    this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
-                    this._renderingCanvas.removeEventListener("webglcontextrestored", this._onContextRestored);
-                }
-            }
-            document.removeEventListener("fullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("msfullscreenchange", this._onFullscreenChange);
-            document.removeEventListener("pointerlockchange", this._onPointerLockChange);
-            document.removeEventListener("mspointerlockchange", this._onPointerLockChange);
-            document.removeEventListener("mozpointerlockchange", this._onPointerLockChange);
-            document.removeEventListener("webkitpointerlockchange", this._onPointerLockChange);
-
-            if (this._onVrDisplayConnect) {
-                window.removeEventListener('vrdisplayconnect', this._onVrDisplayConnect);
-                if (this._onVrDisplayDisconnect) {
-                    window.removeEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
+                    if (!this._doNotHandleContextLost) {
+                        this._renderingCanvas.removeEventListener("webglcontextlost", this._onContextLost);
+                        this._renderingCanvas.removeEventListener("webglcontextrestored", this._onContextRestored);
+                    }
                 }
+                document.removeEventListener("fullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("msfullscreenchange", this._onFullscreenChange);
+                document.removeEventListener("pointerlockchange", this._onPointerLockChange);
+                document.removeEventListener("mspointerlockchange", this._onPointerLockChange);
+                document.removeEventListener("mozpointerlockchange", this._onPointerLockChange);
+                document.removeEventListener("webkitpointerlockchange", this._onPointerLockChange);
+
+                if (this._onVrDisplayConnect) {
+                    window.removeEventListener('vrdisplayconnect', this._onVrDisplayConnect);
+                    if (this._onVrDisplayDisconnect) {
+                        window.removeEventListener('vrdisplaydisconnect', this._onVrDisplayDisconnect);
+                    }
 
-                if (this._onVrDisplayPresentChange) {
-                    window.removeEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
+                    if (this._onVrDisplayPresentChange) {
+                        window.removeEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
+                    }
+                    this._onVrDisplayConnect = null;
+                    this._onVrDisplayDisconnect = null;
                 }
-                this._onVrDisplayConnect = null;
-                this._onVrDisplayDisconnect = null;
             }
 
             // Remove from Instances

+ 10 - 0
src/Engine/babylon.nullEngine.ts

@@ -380,5 +380,15 @@
 
             this._bindTextureDirectly(0, texture);
         }
+
+        public _releaseBuffer(buffer: WebGLBuffer): boolean {
+            buffer.references--;
+
+            if (buffer.references === 0) {
+                return true;
+            }
+
+            return false;
+        }        
     }
 }

+ 86 - 30
src/Helpers/babylon.environmentHelper.ts

@@ -118,6 +118,11 @@ namespace BABYLON {
         sizeAuto: boolean;
 
         /**
+         * Default position of the rootMesh if autoSize is not true.
+         */
+        rootPosition: Vector3;
+
+        /**
          * Sets up the inmage processing in the scene.
          * true by default.
          */
@@ -150,6 +155,12 @@ namespace BABYLON {
         toneMappingEnabled: boolean;
     }
 
+    interface ISceneSize {
+        groundSize: number,
+        skyboxSize: number,
+        rootPosition: Vector3
+    }
+
     /**
      * The Environment helper class can be used to add a fully featuread none expensive background to your scene.
      * It includes by default a skybox and a ground relying on the BackgroundMaterial.
@@ -180,8 +191,8 @@ namespace BABYLON {
                 createGround: true,
                 groundSize: 15,
                 groundTexture: this._groundTextureCDNUrl,
-                groundColor: new BABYLON.Color3(0.2, 0.2, 0.3),
-                groundOpacity: 1,
+                groundColor: new BABYLON.Color3(0.2, 0.2, 0.3).toLinearSpace().scale(3),
+                groundOpacity: 0.9,
                 enableGroundShadow: true,
                 groundShadowLevel: 0.5,
 
@@ -196,15 +207,16 @@ namespace BABYLON {
                 createSkybox: true,
                 skyboxSize: 20,
                 skyboxTexture: this._skyboxTextureCDNUrl,
-                skyboxColor: new BABYLON.Color3(0.2, 0.2, 0.3),
+                skyboxColor: new BABYLON.Color3(0.2, 0.2, 0.3).toLinearSpace().scale(3),
 
                 backgroundYRotation: 0,
                 sizeAuto: true,
+                rootPosition: Vector3.Zero(),
 
                 setupImageProcessing: true,
                 environmentTexture: this._environmentTextureCDNUrl,
                 cameraExposure: 0.8,
-                cameraContrast: 1.6,
+                cameraContrast: 1.2,
                 toneMappingEnabled: true,
             };
         }
@@ -270,7 +282,10 @@ namespace BABYLON {
          * you wish in the ground reflection.
          */
         public get groundMirrorRenderList(): Nullable<AbstractMesh[]> {
-            return this._groundMirror!.renderList;
+            if (this._groundMirror) {
+                return this._groundMirror.renderList;
+            }
+            return null;
         }
 
         private _groundMaterial: Nullable<BackgroundMaterial>;
@@ -413,42 +428,76 @@ namespace BABYLON {
             }
             this._rootMesh.rotation.y = this._options.backgroundYRotation;
 
+            const sceneSize = this._getSceneSize();
             if (this._options.createGround) {
-                this._setupGround();
+                this._setupGround(sceneSize);
                 this._setupGroundMaterial();
                 this._setupGroundDiffuseTexture();
 
                 if (this._options.enableGroundMirror) {
-                    this._setupGroundMirrorTexture();
+                    this._setupGroundMirrorTexture(sceneSize);
                     this._setupMirrorInGroundMaterial();
                 }
             }
 
             if (this._options.createSkybox) {
-                this._setupSkybox();
+                this._setupSkybox(sceneSize);
                 this._setupSkyboxMaterial();
                 this._setupSkyboxReflectionTexture();
             }
+
+            this._rootMesh.position.x = sceneSize.rootPosition.x;
+            this._rootMesh.position.z = sceneSize.rootPosition.z;
+            this._rootMesh.position.y = sceneSize.rootPosition.y;
+        }
+
+        /**
+         * Get the scene sizes according to the setup.
+         */
+        private _getSceneSize(): ISceneSize {
+            let groundSize = this._options.groundSize;
+            let skyboxSize = this._options.skyboxSize;
+            let rootPosition = this._options.rootPosition;
+            const sceneExtends = this._scene.getWorldExtends();
+            const sceneDiagonal = sceneExtends.max.subtract(sceneExtends.min);
+            let bias = 0.0001;
+
+            if (this._options.sizeAuto) {
+                if (this._scene.activeCamera instanceof ArcRotateCamera &&
+                    this._scene.activeCamera.upperRadiusLimit) {
+                    groundSize = this._scene.activeCamera.upperRadiusLimit * 2;
+                }
+
+                if (this._scene.activeCamera) {
+                    bias = (this._scene.activeCamera.maxZ - this._scene.activeCamera.minZ) / 10000;
+                }
+
+                const sceneDiagonalLenght = sceneDiagonal.length();
+                if (sceneDiagonalLenght > groundSize) {
+                    groundSize = sceneDiagonalLenght * 2;
+                }
+
+                // 10 % bigger.
+                groundSize *= 1.1;
+                skyboxSize *= 1.5;
+                rootPosition = sceneExtends.min.add(sceneDiagonal.scale(0.5));
+                rootPosition.y = sceneExtends.min.y - bias;
+            }
+
+            return { groundSize, skyboxSize, rootPosition };
         }
 
         /**
          * Setup the ground according to the specified options.
          */
-        private _setupGround(): void {
+        private _setupGround(sceneSize: ISceneSize): void {
             if (!this._ground) {
-                let size = this._options.groundSize;
-                if (this._options.sizeAuto) {
-                    if (this._scene.activeCamera instanceof ArcRotateCamera &&
-                        this._scene.activeCamera.upperRadiusLimit) {
-                        size = this._scene.activeCamera.upperRadiusLimit * 0.9;
-                    }
-                }
-                this._ground = Mesh.CreatePlane("BackgroundPlane", size, this._scene);
+                this._ground = Mesh.CreatePlane("BackgroundPlane", sceneSize.groundSize, this._scene);
+                this._ground.rotation.x = Math.PI / 2; // Face up by default.
+                this._ground.parent = this._rootMesh;
             }
             
-            this._ground.rotation.x = Math.PI / 2; // Face up by default.
             this._ground.receiveShadows = this._options.enableGroundShadow;
-            this._ground.parent = this._rootMesh;
         }
 
         /**
@@ -466,6 +515,7 @@ namespace BABYLON {
             this._groundMaterial.secondaryLevel = 0;
             this._groundMaterial.tertiaryLevel = 0;
             this._groundMaterial.useRGBColor = false;
+            this._groundMaterial.enableNoise = true;
             
             if (this._ground) {
                 this._ground.material = this._groundMaterial;
@@ -498,7 +548,7 @@ namespace BABYLON {
         /**
          * Setup the ground mirror texture according to the specified options.
          */
-        private _setupGroundMirrorTexture(): void {
+        private _setupGroundMirrorTexture(sceneSize: ISceneSize): void {
             let wrapping = BABYLON.Texture.CLAMP_ADDRESSMODE;
             if (!this._groundMirror) {
                 this._groundMirror = new BABYLON.MirrorTexture("BackgroundPlaneMirrorTexture", 
@@ -508,10 +558,22 @@ namespace BABYLON {
                     this._options.groundMirrorTextureType,
                     BABYLON.Texture.BILINEAR_SAMPLINGMODE,
                     true);
-                this._groundMirror.mirrorPlane = new BABYLON.Plane(0, -1, 0, 0);
+                this._groundMirror.mirrorPlane = new BABYLON.Plane(0, -1, 0, sceneSize.rootPosition.y);
                 this._groundMirror.anisotropicFilteringLevel = 1;
                 this._groundMirror.wrapU = wrapping;
                 this._groundMirror.wrapV = wrapping;
+                this._groundMirror.gammaSpace = false;
+
+                if (this._groundMirror.renderList) {
+                    for (let i = 0; i < this._scene.meshes.length; i++) {
+                        const mesh = this._scene.meshes[i];
+                        if (mesh !== this._ground && 
+                            mesh !== this._skybox &&
+                            mesh !== this._rootMesh) {
+                            this._groundMirror.renderList.push(mesh);
+                        }
+                    }
+                }
             }
             
             this._groundMirror.clearColor = new BABYLON.Color4(
@@ -538,16 +600,9 @@ namespace BABYLON {
         /**
          * Setup the skybox according to the specified options.
          */
-        private _setupSkybox(): void {
+        private _setupSkybox(sceneSize: ISceneSize): void {
             if (!this._skybox) {
-                let size = this._options.skyboxSize;
-                if (this._options.sizeAuto) {
-                    if (this._scene.activeCamera instanceof ArcRotateCamera &&
-                        this._scene.activeCamera.upperRadiusLimit) {
-                        size = this._scene.activeCamera.upperRadiusLimit;
-                    }
-                }
-                this._skybox = Mesh.CreateBox("BackgroundSkybox", size, this._scene, undefined, BABYLON.Mesh.BACKSIDE);
+                this._skybox = Mesh.CreateBox("BackgroundSkybox", sceneSize.skyboxSize, this._scene, undefined, BABYLON.Mesh.BACKSIDE);
             }
             this._skybox.parent = this._rootMesh;
         }
@@ -568,6 +623,7 @@ namespace BABYLON {
             this._skyboxMaterial.primaryColor = this._options.skyboxColor;
             this._skyboxMaterial.secondaryLevel = 0;
             this._skyboxMaterial.tertiaryLevel = 0;
+            this._skyboxMaterial.enableNoise = true;
 
             this._skybox.material = this._skyboxMaterial;
         }

+ 15 - 0
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -434,6 +434,14 @@
                     }
                 }
 
+                // Transform nodes
+                if (parsedData.transformNodes !== undefined && parsedData.transformNodes !== null) {
+                    for (index = 0, cache = parsedData.transformNodes.length; index < cache; index++) {
+                        var parsedTransformNode = parsedData.transformNodes[index];
+                        TransformNode.Parse(parsedTransformNode, scene, rootUrl);
+                    }
+                }                
+
                 // Meshes
                 if (parsedData.meshes !== undefined && parsedData.meshes !== null) {
                     for (index = 0, cache = parsedData.meshes.length; index < cache; index++) {
@@ -498,6 +506,13 @@
                 loadedSounds = [];
 
                 // Connect parents & children and parse actions
+                for (index = 0, cache = scene.transformNodes.length; index < cache; index++) {
+                    var transformNode = scene.transformNodes[index];
+                    if (transformNode._waitingParentId) {
+                        transformNode.parent = scene.getLastEntryByID(transformNode._waitingParentId);
+                        transformNode._waitingParentId = null;
+                    }
+                }                
                 for (index = 0, cache = scene.meshes.length; index < cache; index++) {
                     var mesh = scene.meshes[index];
                     if (mesh._waitingParentId) {

+ 32 - 11
src/Loading/babylon.sceneLoader.ts

@@ -5,12 +5,19 @@
         };
     }
 
+    export interface ISceneLoaderPluginFactory {
+        name: string;
+        createPlugin(): ISceneLoaderPlugin | ISceneLoaderPluginAsync;
+        canDirectLoad?: (data: string) => boolean;
+    }
+
     export interface ISceneLoaderPlugin {
         name: string;
         extensions: string | ISceneLoaderPluginExtensions;
         importMesh: (meshesNames: any, scene: Scene, data: any, rootUrl: string, meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], onError?: (message: string, exception?: any) => void) => boolean;
         load: (scene: Scene, data: string, rootUrl: string, onError?: (message: string, exception?: any) => void) => boolean;
         canDirectLoad?: (data: string) => boolean;
+        rewriteRootURL?: (rootUrl: string, responseURL?: string) => string;
     }
 
     export interface ISceneLoaderPluginAsync {
@@ -19,10 +26,11 @@
         importMeshAsync: (meshesNames: any, scene: Scene, data: any, rootUrl: string, onSuccess: (meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[]) => void, onProgress: (event: ProgressEvent) => void, onError: (message: string) => void) => void;
         loadAsync: (scene: Scene, data: string, rootUrl: string, onSuccess: () => void, onProgress: (event: ProgressEvent) => void, onError: (message: string, exception?: any) => void) => void;
         canDirectLoad?: (data: string) => boolean;
+        rewriteRootURL?: (rootUrl: string, responseURL?: string) => string;
     }
 
     interface IRegisteredPlugin {
-        plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
+        plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync | ISceneLoaderPluginFactory;
         isBinary: boolean;
     }
 
@@ -138,16 +146,24 @@
             return null;
         }
 
-        private static _loadData(rootUrl: string, sceneFilename: string, scene: Scene, onSuccess: (plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync, data: any) => void, onProgress: (event: ProgressEvent) => void, onError: (message: Nullable<string>, exception?: any) => void, pluginExtension?: string): ISceneLoaderPlugin | ISceneLoaderPluginAsync {
+        private static _loadData(rootUrl: string, sceneFilename: string, scene: Scene, onSuccess: (plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync, data: any, responseURL?: string) => void, onProgress: (event: ProgressEvent) => void, onError: (message: Nullable<string>, exception?: any) => void, pluginExtension?: string): ISceneLoaderPlugin | ISceneLoaderPluginAsync {
             var directLoad = SceneLoader._getDirectLoad(sceneFilename);
             var registeredPlugin = pluginExtension ? SceneLoader._getPluginForExtension(pluginExtension) : (directLoad ? SceneLoader._getPluginForDirectLoad(sceneFilename) : SceneLoader._getPluginForFilename(sceneFilename));
-            var plugin = registeredPlugin.plugin;
+
+            let plugin: ISceneLoaderPlugin | ISceneLoaderPluginAsync;
+            if ((registeredPlugin.plugin as ISceneLoaderPluginFactory).createPlugin) {
+                plugin = (registeredPlugin.plugin as ISceneLoaderPluginFactory).createPlugin();
+            }
+            else {
+                plugin = <any>registeredPlugin.plugin;
+            }
+
             var useArrayBuffer = registeredPlugin.isBinary;
             var database: Database;
 
-            SceneLoader.OnPluginActivatedObservable.notifyObservers(registeredPlugin.plugin);
+            SceneLoader.OnPluginActivatedObservable.notifyObservers(plugin);
 
-            var dataCallback = (data: any) => {
+            var dataCallback = (data: any, responseURL?: string) => {
                 if (scene.isDisposed) {
                     onError("Scene has been disposed");
                     return;
@@ -156,7 +172,7 @@
                 scene.database = database;
 
                 try {
-                    onSuccess(plugin, data);
+                    onSuccess(plugin, data, responseURL);
                 }
                 catch (e) {
                     onError(null, e);
@@ -173,7 +189,7 @@
 
             if (directLoad) {
                 dataCallback(directLoad);
-                return registeredPlugin.plugin;
+                return plugin;
             }
 
             if (rootUrl.indexOf("file:") === -1) {
@@ -197,11 +213,11 @@
                     onError("Unable to find file named " + sceneFilename);
                 }
             }
-            return registeredPlugin.plugin;
+            return plugin;
         }
 
         // Public functions
-        public static GetPluginForExtension(extension: string): ISceneLoaderPlugin | ISceneLoaderPluginAsync {
+        public static GetPluginForExtension(extension: string): ISceneLoaderPlugin | ISceneLoaderPluginAsync | ISceneLoaderPluginFactory {
             return SceneLoader._getPluginForExtension(extension).plugin;
         }
 
@@ -260,12 +276,17 @@
                 }
             };
 
-            return SceneLoader._loadData(rootUrl, sceneFilename, scene, (plugin, data) => {
+            return SceneLoader._loadData(rootUrl, sceneFilename, scene, (plugin, data, responseURL) => {
+                if (plugin.rewriteRootURL) {
+                    rootUrl = plugin.rewriteRootURL(rootUrl, responseURL);
+                }
+
                 if ((<any>plugin).importMesh) {
                     var syncedPlugin = <ISceneLoaderPlugin>plugin;
                     var meshes = new Array<AbstractMesh>();
                     var particleSystems = new Array<ParticleSystem>();
                     var skeletons = new Array<Skeleton>();
+
                     if (!syncedPlugin.importMesh(meshNames, scene, data, rootUrl, meshes, particleSystems, skeletons, errorHandler)) {
                         return;
                     }
@@ -353,7 +374,7 @@
                 }
             };
 
-            return SceneLoader._loadData(rootUrl, sceneFilename, scene, (plugin, data) => {
+            return SceneLoader._loadData(rootUrl, sceneFilename, scene, (plugin, data, responseURL) => {
                 if ((<any>plugin).load) {
                     var syncedPlugin = <ISceneLoaderPlugin>plugin;
                     if (!syncedPlugin.load(scene, data, rootUrl, errorHandler)) {

+ 17 - 2
src/Materials/Background/babylon.backgroundMaterial.ts

@@ -58,6 +58,11 @@
          */
         public USERGBCOLOR = false;
 
+        /**
+         * True to add noise in order to reduce the banding effect.
+         */
+        public NOISE = false;
+
         // Image Processing Configuration.
         public IMAGEPROCESSING = false;
         public VIGNETTE = false;
@@ -317,6 +322,15 @@
         public useRGBColor: boolean = true;
 
         /**
+         * This helps reducing the banding effect that could occur on the background.
+         */
+        @serialize()
+        protected _enableNoise: boolean;
+        @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+        public enableNoise: boolean = false;
+
+
+        /**
          * Number of Simultaneous lights allowed on the material.
          */
         @serialize()
@@ -519,7 +533,7 @@
          * @returns false
          */
         public needAlphaTesting(): boolean {
-            return false;
+            return true;
         }
 
         /**
@@ -641,7 +655,7 @@
                             this._reflectionControls.x = this.reflectionAmount;
                             this._reflectionControls.y = this.reflectionReflectance0;
                             this._reflectionControls.z = this.reflectionReflectance90;
-                            this._reflectionControls.w = this.reflectionFalloffDistance;
+                            this._reflectionControls.w = 1 / this.reflectionFalloffDistance;
                         }
                         else {
                             defines.REFLECTIONFRESNEL = false;
@@ -670,6 +684,7 @@
 
                 defines.PREMULTIPLYALPHA = (this.alphaMode === Engine.ALPHA_PREMULTIPLIED || this.alphaMode === Engine.ALPHA_PREMULTIPLIED_PORTERDUFF);
                 defines.USERGBCOLOR = this._useRGBColor;
+                defines.NOISE = this._enableNoise;
             }
 
             if (defines._areImageProcessingDirty) {

+ 17 - 14
src/Materials/PBR/babylon.pbrBaseMaterial.ts

@@ -516,35 +516,38 @@
         }
 
         /**
-         * Specifies whether or not the meshes using this material should be rendered in alpha blend mode.
+         * Returns true if alpha blending should be disabled.
+         */
+        private get _disableAlphaBlending(): boolean {
+            return (this._linkRefractionWithTransparency ||
+                this._transparencyMode === PBRMaterial.PBRMATERIAL_OPAQUE ||
+                this._transparencyMode === PBRMaterial.PBRMATERIAL_ALPHATEST);
+        }
+
+        /**
+         * Specifies whether or not this material should be rendered in alpha blend mode.
          */
         public needAlphaBlending(): boolean {
-            if (this._transparencyMode === PBRMaterial.PBRMATERIAL_OPAQUE ||
-                this._transparencyMode === PBRMaterial.PBRMATERIAL_ALPHATEST) {
+            if (this._disableAlphaBlending) {
                 return false;
             }
 
-            return super.needAlphaBlending();
+            return (this.alpha < 1.0) || (this._opacityTexture != null) || this._shouldUseAlphaFromAlbedoTexture();
         }
 
         /**
-         * Specifies whether or not the meshes using this material should be rendered in alpha blend mode.
+         * Specifies whether or not this material should be rendered in alpha blend mode for the given mesh.
          */
         public needAlphaBlendingForMesh(mesh: AbstractMesh): boolean {
-            if (this._linkRefractionWithTransparency) {
-                return false;
-            }
-
-            if (this._transparencyMode === PBRMaterial.PBRMATERIAL_OPAQUE ||
-                this._transparencyMode === PBRMaterial.PBRMATERIAL_ALPHATEST) {
+            if (this._disableAlphaBlending) {
                 return false;
             }
 
-            return super.needAlphaBlendingForMesh(mesh) || (this._opacityTexture != null) || this._shouldUseAlphaFromAlbedoTexture();
+            return super.needAlphaBlendingForMesh(mesh);
         }
 
         /**
-         * Specifies whether or not the meshes using this material should be rendered in alpha test mode.
+         * Specifies whether or not this material should be rendered in alpha test mode.
          */
         public needAlphaTesting(): boolean {
             if (this._forceAlphaTest) {
@@ -1465,7 +1468,7 @@
                     this._reflectionTexture.dispose();
                 }
 
-                if (this._environmentBRDFTexture) {
+                if (this._environmentBRDFTexture && this.getScene()._environmentBRDFTexture !== this._environmentBRDFTexture) {
                     this._environmentBRDFTexture.dispose();
                 }
 

+ 1 - 1
src/Materials/babylon.material.ts

@@ -451,7 +451,7 @@
         }
 
         public needAlphaBlendingForMesh(mesh: AbstractMesh): boolean {
-            return (mesh.visibility < 1.0) || mesh.hasVertexAlpha;
+            return this.needAlphaBlending() || (mesh.visibility < 1.0) || mesh.hasVertexAlpha;
         }
 
         public needAlphaTesting(): boolean {

文件差異過大導致無法顯示
+ 41 - 777
src/Mesh/babylon.abstractMesh.ts


+ 5 - 4
src/Mesh/babylon.mesh.ts

@@ -912,6 +912,7 @@
         /**
          * Sets the mesh indices.  
          * Expects an array populated with integers or a typed array (Int32Array, Uint32Array, Uint16Array).
+         * Type is Uint16Array by default unless the mesh has more than 65536 vertices.
          * If the mesh has no geometry, a new Geometry object is created and set to the mesh. 
          * This method creates a new index buffer each call.  
          * Returns the Mesh.  
@@ -1588,7 +1589,7 @@
          * Disposes the mesh.
          * This also frees the memory allocated under the hood to all the buffers used by WebGL.
          */
-        public dispose(doNotRecurse?: boolean): void {
+        public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures: boolean = false): void {
             this.morphTargetManager = null;
 
             if (this._geometry) {
@@ -1623,7 +1624,7 @@
                     highlightLayer.removeExcludedMesh(this);
                 }
             }
-            super.dispose(doNotRecurse);
+            super.dispose(doNotRecurse, disposeMaterialAndTextures);
         }
 
         /**
@@ -2221,8 +2222,8 @@
 
         // Statics
         /**
-         * Returns a new Mesh object what is a deep copy of the passed mesh.   
-         * The parameter `parsedMesh` is the mesh to be copied.   
+         * Returns a new Mesh object parsed from the source provided.   
+         * The parameter `parsedMesh` is the source.   
          * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
          */
         public static Parse(parsedMesh: any, scene: Scene, rootUrl: string): Mesh {

+ 910 - 0
src/Mesh/babylon.transformNode.ts

@@ -0,0 +1,910 @@
+module BABYLON {
+    export class TransformNode extends Node {
+        // Statics
+        public static BILLBOARDMODE_NONE = 0;
+        public static BILLBOARDMODE_X = 1;
+        public static BILLBOARDMODE_Y = 2;
+        public static BILLBOARDMODE_Z = 4;
+        public static BILLBOARDMODE_ALL = 7;
+        
+        // Properties
+        private _rotation = Vector3.Zero();
+        private _rotationQuaternion: Nullable<Quaternion>;
+        protected _scaling = Vector3.One();
+        protected _isDirty = false;
+        private _transformToBoneReferal: Nullable<TransformNode>;
+        
+        public billboardMode = AbstractMesh.BILLBOARDMODE_NONE;
+        public scalingDeterminant = 1;
+        public infiniteDistance = false;
+        public position = Vector3.Zero();
+                        
+        // Cache        
+        public _poseMatrix: Matrix;
+        private _localWorld = Matrix.Zero();
+        public _worldMatrix = Matrix.Zero();
+        private _absolutePosition = Vector3.Zero();
+        private _pivotMatrix = Matrix.Identity();
+        private _pivotMatrixInverse: Matrix;
+        private _postMultiplyPivotMatrix = false;        
+        
+        protected _isWorldMatrixFrozen = false;
+
+        /**
+        * An event triggered after the world matrix is updated
+        * @type {BABYLON.Observable}
+        */
+        public onAfterWorldMatrixUpdateObservable = new Observable<TransformNode>();        
+
+        constructor(name: string, scene: Nullable<Scene> = null, isPure = true) {
+            super(name, scene);
+
+            if (isPure) {
+                this.getScene().addTransformNode(this);
+            }
+        }        
+                
+       /**
+         * Rotation property : a Vector3 depicting the rotation value in radians around each local axis X, Y, Z. 
+         * If rotation quaternion is set, this Vector3 will (almost always) be the Zero vector!
+         * Default : (0.0, 0.0, 0.0)
+         */
+        public get rotation(): Vector3 {
+            return this._rotation;
+        }
+
+        public set rotation(newRotation: Vector3) {
+            this._rotation = newRotation;
+        }
+
+        /**
+         * Scaling property : a Vector3 depicting the mesh scaling along each local axis X, Y, Z.  
+         * Default : (1.0, 1.0, 1.0)
+         */
+        public get scaling(): Vector3 {
+            return this._scaling;
+        }
+
+        /**
+         * Scaling property : a Vector3 depicting the mesh scaling along each local axis X, Y, Z.  
+         * Default : (1.0, 1.0, 1.0)
+        */
+        public set scaling(newScaling: Vector3) {
+            this._scaling = newScaling;
+        }
+
+        /**
+         * Rotation Quaternion property : this a Quaternion object depicting the mesh rotation by using a unit quaternion. 
+         * It's null by default.  
+         * If set, only the rotationQuaternion is then used to compute the mesh rotation and its property `.rotation\ is then ignored and set to (0.0, 0.0, 0.0)
+         */
+        public get rotationQuaternion(): Nullable<Quaternion> {
+            return this._rotationQuaternion;
+        }
+
+        public set rotationQuaternion(quaternion: Nullable<Quaternion>) {
+            this._rotationQuaternion = quaternion;
+            //reset the rotation vector. 
+            if (quaternion && this.rotation.length()) {
+                this.rotation.copyFromFloats(0.0, 0.0, 0.0);
+            }
+        }
+
+        /**
+         * Returns the latest update of the World matrix
+         * Returns a Matrix.  
+         */
+        public getWorldMatrix(): Matrix {
+            if (this._currentRenderId !== this.getScene().getRenderId()) {
+                this.computeWorldMatrix();
+            }
+            return this._worldMatrix;
+        }
+
+        /**
+         * Returns directly the latest state of the mesh World matrix. 
+         * A Matrix is returned.    
+         */
+        public get worldMatrixFromCache(): Matrix {
+            return this._worldMatrix;
+        }
+
+        /**
+         * Copies the paramater passed Matrix into the mesh Pose matrix.  
+         * Returns the AbstractMesh.  
+         */
+        public updatePoseMatrix(matrix: Matrix): TransformNode {
+            this._poseMatrix.copyFrom(matrix);
+            return this;
+        }
+
+        /**
+         * Returns the mesh Pose matrix.  
+         * Returned object : Matrix
+         */
+        public getPoseMatrix(): Matrix {
+            return this._poseMatrix;
+        }
+        
+        public _isSynchronized(): boolean {
+            if (this._isDirty) {
+                return false;
+            }
+
+            if (this.billboardMode !== this._cache.billboardMode || this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE)
+                return false;
+
+            if (this._cache.pivotMatrixUpdated) {
+                return false;
+            }
+
+            if (this.infiniteDistance) {
+                return false;
+            }
+
+            if (!this._cache.position.equals(this.position))
+                return false;
+
+            if (this.rotationQuaternion) {
+                if (!this._cache.rotationQuaternion.equals(this.rotationQuaternion))
+                    return false;
+            }
+
+            if (!this._cache.rotation.equals(this.rotation))
+                return false;
+
+            if (!this._cache.scaling.equals(this.scaling))
+                return false;
+
+            return true;
+        }
+
+        public _initCache() {
+            super._initCache();
+
+            this._cache.localMatrixUpdated = false;
+            this._cache.position = Vector3.Zero();
+            this._cache.scaling = Vector3.Zero();
+            this._cache.rotation = Vector3.Zero();
+            this._cache.rotationQuaternion = new Quaternion(0, 0, 0, 0);
+            this._cache.billboardMode = -1;
+        }
+
+        public markAsDirty(property: string): TransformNode {
+            if (property === "rotation") {
+                this.rotationQuaternion = null;
+            }
+            this._currentRenderId = Number.MAX_VALUE;
+            this._isDirty = true;
+            return this;
+        }        
+
+        /**
+         * Returns the current mesh absolute position.
+         * Retuns a Vector3.
+         */
+        public get absolutePosition(): Vector3 {
+            return this._absolutePosition;
+        }
+
+        /**
+         * Sets a new pivot matrix to the mesh.  
+         * Returns the AbstractMesh.
+        */
+        public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = false): TransformNode {
+            this._pivotMatrix = matrix.clone();
+            this._cache.pivotMatrixUpdated = true;
+            this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
+
+            if (this._postMultiplyPivotMatrix) {
+                this._pivotMatrixInverse = Matrix.Invert(matrix);
+            }
+            return this;
+        }
+
+        /**
+         * Returns the mesh pivot matrix.
+         * Default : Identity.  
+         * A Matrix is returned.  
+         */
+        public getPivotMatrix(): Matrix {
+            return this._pivotMatrix;
+        }
+
+        /**
+         * Prevents the World matrix to be computed any longer.
+         * Returns the AbstractMesh.  
+         */
+        public freezeWorldMatrix(): TransformNode {
+            this._isWorldMatrixFrozen = false;  // no guarantee world is not already frozen, switch off temporarily
+            this.computeWorldMatrix(true);
+            this._isWorldMatrixFrozen = true;
+            return this;
+        }
+
+        /**
+         * Allows back the World matrix computation. 
+         * Returns the AbstractMesh.  
+         */
+        public unfreezeWorldMatrix() {
+            this._isWorldMatrixFrozen = false;
+            this.computeWorldMatrix(true);
+            return this;
+        }
+
+        /**
+         * True if the World matrix has been frozen.  
+         * Returns a boolean.  
+         */
+        public get isWorldMatrixFrozen(): boolean {
+            return this._isWorldMatrixFrozen;
+        }
+
+     /**
+         * Retuns the mesh absolute position in the World.  
+         * Returns a Vector3.
+         */
+        public getAbsolutePosition(): Vector3 {
+            this.computeWorldMatrix();
+            return this._absolutePosition;
+        }
+
+        /**
+         * Sets the mesh absolute position in the World from a Vector3 or an Array(3).
+         * Returns the AbstractMesh.  
+         */
+        public setAbsolutePosition(absolutePosition: Vector3): TransformNode {
+            if (!absolutePosition) {
+                return this;
+            }
+            var absolutePositionX;
+            var absolutePositionY;
+            var absolutePositionZ;
+            if (absolutePosition.x === undefined) {
+                if (arguments.length < 3) {
+                    return this;
+                }
+                absolutePositionX = arguments[0];
+                absolutePositionY = arguments[1];
+                absolutePositionZ = arguments[2];
+            }
+            else {
+                absolutePositionX = absolutePosition.x;
+                absolutePositionY = absolutePosition.y;
+                absolutePositionZ = absolutePosition.z;
+            }
+            if (this.parent) {
+                var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
+                invertParentWorldMatrix.invert();
+                var worldPosition = new Vector3(absolutePositionX, absolutePositionY, absolutePositionZ);
+                this.position = Vector3.TransformCoordinates(worldPosition, invertParentWorldMatrix);
+            } else {
+                this.position.x = absolutePositionX;
+                this.position.y = absolutePositionY;
+                this.position.z = absolutePositionZ;
+            }
+            return this;
+        }   
+
+      /**
+         * Sets the mesh position in its local space.  
+         * Returns the AbstractMesh.  
+         */
+        public setPositionWithLocalVector(vector3: Vector3): TransformNode {
+            this.computeWorldMatrix();
+            this.position = Vector3.TransformNormal(vector3, this._localWorld);
+            return this;
+        }
+
+        /**
+         * Returns the mesh position in the local space from the current World matrix values.
+         * Returns a new Vector3.
+         */
+        public getPositionExpressedInLocalSpace(): Vector3 {
+            this.computeWorldMatrix();
+            var invLocalWorldMatrix = this._localWorld.clone();
+            invLocalWorldMatrix.invert();
+
+            return Vector3.TransformNormal(this.position, invLocalWorldMatrix);
+        }
+
+        /**
+         * Translates the mesh along the passed Vector3 in its local space.  
+         * Returns the AbstractMesh. 
+         */
+        public locallyTranslate(vector3: Vector3): TransformNode {
+            this.computeWorldMatrix(true);
+            this.position = Vector3.TransformCoordinates(vector3, this._localWorld);
+            return this;
+        }
+
+        private static _lookAtVectorCache = new Vector3(0, 0, 0);
+        public lookAt(targetPoint: Vector3, yawCor: number = 0, pitchCor: number = 0, rollCor: number = 0, space: Space = Space.LOCAL): TransformNode {
+            /// <summary>Orients a mesh towards a target point. Mesh must be drawn facing user.</summary>
+            /// <param name="targetPoint" type="Vector3">The position (must be in same space as current mesh) to look at</param>
+            /// <param name="yawCor" type="Number">optional yaw (y-axis) correction in radians</param>
+            /// <param name="pitchCor" type="Number">optional pitch (x-axis) correction in radians</param>
+            /// <param name="rollCor" type="Number">optional roll (z-axis) correction in radians</param>
+            /// <returns>Mesh oriented towards targetMesh</returns>
+
+            var dv = AbstractMesh._lookAtVectorCache;
+            var pos = space === Space.LOCAL ? this.position : this.getAbsolutePosition();
+            targetPoint.subtractToRef(pos, dv);
+            var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
+            var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
+            var pitch = Math.atan2(dv.y, len);
+            this.rotationQuaternion = this.rotationQuaternion || new Quaternion();
+            Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
+            return this;
+        }        
+
+       /**
+         * Returns a new Vector3 what is the localAxis, expressed in the mesh local space, rotated like the mesh.  
+         * This Vector3 is expressed in the World space.  
+         */
+        public getDirection(localAxis: Vector3): Vector3 {
+            var result = Vector3.Zero();
+
+            this.getDirectionToRef(localAxis, result);
+
+            return result;
+        }
+
+        /**
+         * Sets the Vector3 "result" as the rotated Vector3 "localAxis" in the same rotation than the mesh.
+         * localAxis is expressed in the mesh local space.
+         * result is computed in the Wordl space from the mesh World matrix.  
+         * Returns the AbstractMesh.  
+         */
+        public getDirectionToRef(localAxis: Vector3, result: Vector3): TransformNode {
+            Vector3.TransformNormalToRef(localAxis, this.getWorldMatrix(), result);
+            return this;
+        }
+
+        public setPivotPoint(point: Vector3, space: Space = Space.LOCAL): TransformNode {
+            if (this.getScene().getRenderId() == 0) {
+                this.computeWorldMatrix(true);
+            }
+
+            var wm = this.getWorldMatrix();
+
+            if (space == Space.WORLD) {
+                var tmat = Tmp.Matrix[0];
+                wm.invertToRef(tmat);
+                point = Vector3.TransformCoordinates(point, tmat);
+            }
+
+            Vector3.TransformCoordinatesToRef(point, wm, this.position);
+            this._pivotMatrix.m[12] = -point.x;
+            this._pivotMatrix.m[13] = -point.y;
+            this._pivotMatrix.m[14] = -point.z;
+            this._cache.pivotMatrixUpdated = true;
+            return this;
+        }
+
+        /**
+         * Returns a new Vector3 set with the mesh pivot point coordinates in the local space.  
+         */
+        public getPivotPoint(): Vector3 {
+            var point = Vector3.Zero();
+            this.getPivotPointToRef(point);
+            return point;
+        }
+
+        /**
+         * Sets the passed Vector3 "result" with the coordinates of the mesh pivot point in the local space.   
+         * Returns the AbstractMesh.   
+         */
+        public getPivotPointToRef(result: Vector3): TransformNode {
+            result.x = -this._pivotMatrix.m[12];
+            result.y = -this._pivotMatrix.m[13];
+            result.z = -this._pivotMatrix.m[14];
+            return this;
+        }
+
+        /**
+         * Returns a new Vector3 set with the mesh pivot point World coordinates.  
+         */
+        public getAbsolutePivotPoint(): Vector3 {
+            var point = Vector3.Zero();
+            this.getAbsolutePivotPointToRef(point);
+            return point;
+        }
+
+        /**
+         * Sets the Vector3 "result" coordinates with the mesh pivot point World coordinates.  
+         * Returns the AbstractMesh.  
+         */
+        public getAbsolutePivotPointToRef(result: Vector3): TransformNode {
+            result.x = this._pivotMatrix.m[12];
+            result.y = this._pivotMatrix.m[13];
+            result.z = this._pivotMatrix.m[14];
+            this.getPivotPointToRef(result);
+            Vector3.TransformCoordinatesToRef(result, this.getWorldMatrix(), result);
+            return this;
+        }        
+
+        /**
+         * Defines the passed mesh as the parent of the current mesh.  
+         * Returns the AbstractMesh.  
+         */
+        public setParent(mesh: Nullable<AbstractMesh>): TransformNode {
+            var parent = (<AbstractMesh>mesh);
+
+            if (mesh == null) {
+
+                var rotation = Tmp.Quaternion[0];
+                var position = Tmp.Vector3[0];
+                var scale = Tmp.Vector3[1];
+
+                this.getWorldMatrix().decompose(scale, rotation, position);
+
+                if (this.rotationQuaternion) {
+                    this.rotationQuaternion.copyFrom(rotation);
+                } else {
+                    rotation.toEulerAnglesToRef(this.rotation);
+                }
+
+                this.position.x = position.x;
+                this.position.y = position.y;
+                this.position.z = position.z;
+
+            } else {
+
+                var position = Tmp.Vector3[0];
+                var m1 = Tmp.Matrix[0];
+
+                parent.getWorldMatrix().invertToRef(m1);
+                Vector3.TransformCoordinatesToRef(this.position, m1, position);
+
+                this.position.copyFrom(position);
+            }
+            this.parent = parent;
+            return this;
+        }       
+        
+        private _nonUniformScaling = false;
+        public get nonUniformScaling(): boolean {
+            return this._nonUniformScaling;
+        }
+
+        public _updateNonUniformScalingState(value: boolean): boolean {
+            if (this._nonUniformScaling === value) {
+                return false;
+            }
+
+            this._nonUniformScaling = true;
+            return true;
+        }        
+
+        /**
+         * Attach the current TransformNode to another TransformNode associated with a bone
+         * @param bone Bone affecting the TransformNode
+         * @param affectedTransformNode TransformNode associated with the bone 
+         */
+        public attachToBone(bone: Bone, affectedTransformNode: TransformNode): TransformNode {
+            this._transformToBoneReferal = affectedTransformNode;
+            this.parent = bone;
+
+            if (bone.getWorldMatrix().determinant() < 0) {
+                this.scalingDeterminant *= -1;
+            }
+            return this;
+        }
+
+        public detachFromBone(): TransformNode {
+            if (!this.parent) {
+                return this;
+            }
+
+            if (this.parent.getWorldMatrix().determinant() < 0) {
+                this.scalingDeterminant *= -1;
+            }
+            this._transformToBoneReferal = null;
+            this.parent = null;
+            return this;
+        }        
+
+        private static _rotationAxisCache = new Quaternion();
+        /**
+         * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in the given space.  
+         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD.
+         * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.  
+         * The passed axis is also normalized.  
+         * Returns the AbstractMesh.
+         */
+        public rotate(axis: Vector3, amount: number, space?: Space): TransformNode {
+            axis.normalize();
+            if (!this.rotationQuaternion) {
+                this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
+                this.rotation = Vector3.Zero();
+            }
+            var rotationQuaternion: Quaternion;
+            if (!space || (space as any) === Space.LOCAL) {
+                rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
+                this.rotationQuaternion.multiplyToRef(rotationQuaternion, this.rotationQuaternion);
+            }
+            else {
+                if (this.parent) {
+                    var invertParentWorldMatrix = this.parent.getWorldMatrix().clone();
+                    invertParentWorldMatrix.invert();
+                    axis = Vector3.TransformNormal(axis, invertParentWorldMatrix);
+                }
+                rotationQuaternion = Quaternion.RotationAxisToRef(axis, amount, AbstractMesh._rotationAxisCache);
+                rotationQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
+            }
+            return this;
+        }
+
+        /**
+         * Rotates the mesh around the axis vector for the passed angle (amount) expressed in radians, in world space.  
+         * Note that the property `rotationQuaternion` is then automatically updated and the property `rotation` is set to (0,0,0) and no longer used.  
+         * The passed axis is also normalized.  
+         * Returns the AbstractMesh.
+         * Method is based on http://www.euclideanspace.com/maths/geometry/affine/aroundPoint/index.htm
+         */
+        public rotateAround(point: Vector3, axis: Vector3, amount: number): TransformNode {
+            axis.normalize();
+            if (!this.rotationQuaternion) {
+                this.rotationQuaternion = Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z);
+                this.rotation.copyFromFloats(0, 0, 0);
+            }
+            point.subtractToRef(this.position, Tmp.Vector3[0]);
+            Matrix.TranslationToRef(Tmp.Vector3[0].x, Tmp.Vector3[0].y, Tmp.Vector3[0].z, Tmp.Matrix[0]);
+            Tmp.Matrix[0].invertToRef(Tmp.Matrix[2]);
+            Matrix.RotationAxisToRef(axis, amount, Tmp.Matrix[1]);
+            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[2]);
+            Tmp.Matrix[2].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[2]);
+
+            Tmp.Matrix[2].decompose(Tmp.Vector3[0], Tmp.Quaternion[0], Tmp.Vector3[1]);
+
+            this.position.addInPlace(Tmp.Vector3[1]);
+            Tmp.Quaternion[0].multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
+
+            return this;
+        }
+
+        /**
+         * Translates the mesh along the axis vector for the passed distance in the given space.  
+         * space (default LOCAL) can be either BABYLON.Space.LOCAL, either BABYLON.Space.WORLD.
+         * Returns the AbstractMesh.
+         */
+        public translate(axis: Vector3, distance: number, space?: Space): TransformNode {
+            var displacementVector = axis.scale(distance);
+            if (!space || (space as any) === Space.LOCAL) {
+                var tempV3 = this.getPositionExpressedInLocalSpace().add(displacementVector);
+                this.setPositionWithLocalVector(tempV3);
+            }
+            else {
+                this.setAbsolutePosition(this.getAbsolutePosition().add(displacementVector));
+            }
+            return this;
+        }
+
+        /**
+         * Adds a rotation step to the mesh current rotation.  
+         * x, y, z are Euler angles expressed in radians.  
+         * This methods updates the current mesh rotation, either mesh.rotation, either mesh.rotationQuaternion if it's set.  
+         * This means this rotation is made in the mesh local space only.   
+         * It's useful to set a custom rotation order different from the BJS standard one YXZ.  
+         * Example : this rotates the mesh first around its local X axis, then around its local Z axis, finally around its local Y axis.  
+         * ```javascript
+         * mesh.addRotation(x1, 0, 0).addRotation(0, 0, z2).addRotation(0, 0, y3);
+         * ```
+         * Note that `addRotation()` accumulates the passed rotation values to the current ones and computes the .rotation or .rotationQuaternion updated values.  
+         * Under the hood, only quaternions are used. So it's a little faster is you use .rotationQuaternion because it doesn't need to translate them back to Euler angles.   
+         * Returns the AbstractMesh.  
+         */
+        public addRotation(x: number, y: number, z: number): TransformNode {
+            var rotationQuaternion;
+            if (this.rotationQuaternion) {
+                rotationQuaternion = this.rotationQuaternion;
+            }
+            else {
+                rotationQuaternion = Tmp.Quaternion[1];
+                Quaternion.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, rotationQuaternion);
+            }
+            var accumulation = BABYLON.Tmp.Quaternion[0];
+            Quaternion.RotationYawPitchRollToRef(y, x, z, accumulation);
+            rotationQuaternion.multiplyInPlace(accumulation);
+            if (!this.rotationQuaternion) {
+                rotationQuaternion.toEulerAnglesToRef(this.rotation);
+            }
+            return this;
+        }        
+        
+        /**
+         * Computes the mesh World matrix and returns it.  
+         * If the mesh world matrix is frozen, this computation does nothing more than returning the last frozen values.  
+         * If the parameter `force` is let to `false` (default), the current cached World matrix is returned. 
+         * If the parameter `force`is set to `true`, the actual computation is done.  
+         * Returns the mesh World Matrix.
+         */
+        public computeWorldMatrix(force?: boolean): Matrix {
+            if (this._isWorldMatrixFrozen) {
+                return this._worldMatrix;
+            }
+
+            if (!force && this.isSynchronized(true)) {
+                return this._worldMatrix;
+            }
+
+            this._cache.position.copyFrom(this.position);
+            this._cache.scaling.copyFrom(this.scaling);
+            this._cache.pivotMatrixUpdated = false;
+            this._cache.billboardMode = this.billboardMode;
+            this._currentRenderId = this.getScene().getRenderId();
+            this._isDirty = false;
+
+            // Scaling
+            Matrix.ScalingToRef(this.scaling.x * this.scalingDeterminant, this.scaling.y * this.scalingDeterminant, this.scaling.z * this.scalingDeterminant, Tmp.Matrix[1]);
+
+            // Rotation
+
+            //rotate, if quaternion is set and rotation was used
+            if (this.rotationQuaternion) {
+                var len = this.rotation.length();
+                if (len) {
+                    this.rotationQuaternion.multiplyInPlace(BABYLON.Quaternion.RotationYawPitchRoll(this.rotation.y, this.rotation.x, this.rotation.z))
+                    this.rotation.copyFromFloats(0, 0, 0);
+                }
+            }
+
+            if (this.rotationQuaternion) {
+                this.rotationQuaternion.toRotationMatrix(Tmp.Matrix[0]);
+                this._cache.rotationQuaternion.copyFrom(this.rotationQuaternion);
+            } else {
+                Matrix.RotationYawPitchRollToRef(this.rotation.y, this.rotation.x, this.rotation.z, Tmp.Matrix[0]);
+                this._cache.rotation.copyFrom(this.rotation);
+            }
+
+            // Translation
+            let camera = (<Camera>this.getScene().activeCamera);
+            
+            if (this.infiniteDistance && !this.parent && camera) {
+
+                var cameraWorldMatrix = camera.getWorldMatrix();
+
+                var cameraGlobalPosition = new Vector3(cameraWorldMatrix.m[12], cameraWorldMatrix.m[13], cameraWorldMatrix.m[14]);
+
+                Matrix.TranslationToRef(this.position.x + cameraGlobalPosition.x, this.position.y + cameraGlobalPosition.y,
+                    this.position.z + cameraGlobalPosition.z, Tmp.Matrix[2]);
+            } else {
+                Matrix.TranslationToRef(this.position.x, this.position.y, this.position.z, Tmp.Matrix[2]);
+            }
+
+            // Composing transformations
+            this._pivotMatrix.multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
+            Tmp.Matrix[4].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
+
+            // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
+            if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE && camera) {
+                if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_ALL) !== AbstractMesh.BILLBOARDMODE_ALL) {
+                    // Need to decompose each rotation here
+                    var currentPosition = Tmp.Vector3[3];
+
+                    if (this.parent && this.parent.getWorldMatrix) {
+                        if (this._transformToBoneReferal) {
+                            this.parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
+                            Vector3.TransformCoordinatesToRef(this.position, Tmp.Matrix[6], currentPosition);
+                        } else {
+                            Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), currentPosition);
+                        }
+                    } else {
+                        currentPosition.copyFrom(this.position);
+                    }
+
+                    currentPosition.subtractInPlace(camera.globalPosition);
+
+                    var finalEuler = Tmp.Vector3[4].copyFromFloats(0, 0, 0);
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_X) === AbstractMesh.BILLBOARDMODE_X) {
+                        finalEuler.x = Math.atan2(-currentPosition.y, currentPosition.z);
+                    }
+
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Y) === AbstractMesh.BILLBOARDMODE_Y) {
+                        finalEuler.y = Math.atan2(currentPosition.x, currentPosition.z);
+                    }
+
+                    if ((this.billboardMode & AbstractMesh.BILLBOARDMODE_Z) === AbstractMesh.BILLBOARDMODE_Z) {
+                        finalEuler.z = Math.atan2(currentPosition.y, currentPosition.x);
+                    }
+
+                    Matrix.RotationYawPitchRollToRef(finalEuler.y, finalEuler.x, finalEuler.z, Tmp.Matrix[0]);
+                } else {
+                    Tmp.Matrix[1].copyFrom(camera.getViewMatrix());
+
+                    Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
+                    Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
+                }
+
+                Tmp.Matrix[1].copyFrom(Tmp.Matrix[5]);
+                Tmp.Matrix[1].multiplyToRef(Tmp.Matrix[0], Tmp.Matrix[5]);
+            }
+
+            // Local world
+            Tmp.Matrix[5].multiplyToRef(Tmp.Matrix[2], this._localWorld);
+
+            // Parent
+            if (this.parent && this.parent.getWorldMatrix) {
+                if (this.billboardMode !== AbstractMesh.BILLBOARDMODE_NONE) {
+                    if (this._transformToBoneReferal) {
+                        this.parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
+                        Tmp.Matrix[5].copyFrom(Tmp.Matrix[6]);
+                    } else {
+                        Tmp.Matrix[5].copyFrom(this.parent.getWorldMatrix());
+                    }
+
+                    this._localWorld.getTranslationToRef(Tmp.Vector3[5]);
+                    Vector3.TransformCoordinatesToRef(Tmp.Vector3[5], Tmp.Matrix[5], Tmp.Vector3[5]);
+                    this._worldMatrix.copyFrom(this._localWorld);
+                    this._worldMatrix.setTranslation(Tmp.Vector3[5]);
+
+                } else {
+                    if (this._transformToBoneReferal) {
+                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
+                        Tmp.Matrix[6].multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), this._worldMatrix);
+                    } else {
+                        this._localWorld.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
+                    }
+                }
+                this._markSyncedWithParent();
+            } else {
+                this._worldMatrix.copyFrom(this._localWorld);
+            }
+
+            // Post multiply inverse of pivotMatrix
+            if (this._postMultiplyPivotMatrix) {
+                this._worldMatrix.multiplyToRef(this._pivotMatrixInverse, this._worldMatrix);
+            }
+
+            // Normal matrix
+            if (this.scaling.isNonUniform) {
+                this._updateNonUniformScalingState(true);
+            } else if (this.parent && (<TransformNode>this.parent)._nonUniformScaling) {
+                this._updateNonUniformScalingState((<TransformNode>this.parent)._nonUniformScaling);
+            } else {
+                this._updateNonUniformScalingState(false);
+            }
+
+            this._afterComputeWorldMatrix();
+
+            // Absolute position
+            this._absolutePosition.copyFromFloats(this._worldMatrix.m[12], this._worldMatrix.m[13], this._worldMatrix.m[14]);
+
+            // Callbacks
+            this.onAfterWorldMatrixUpdateObservable.notifyObservers(this);
+
+            if (!this._poseMatrix) {
+                this._poseMatrix = Matrix.Invert(this._worldMatrix);
+            }
+
+            return this._worldMatrix;
+        }   
+
+        protected _afterComputeWorldMatrix(): void {
+        }
+
+        /**
+        * If you'd like to be called back after the mesh position, rotation or scaling has been updated.  
+        * @param func: callback function to add
+        *
+        * Returns the TransformNode. 
+        */
+        public registerAfterWorldMatrixUpdate(func: (mesh: TransformNode) => void): TransformNode {
+            this.onAfterWorldMatrixUpdateObservable.add(func);
+            return this;
+        }
+
+        /**
+         * Removes a registered callback function.  
+         * Returns the TransformNode.
+         */
+        public unregisterAfterWorldMatrixUpdate(func: (mesh: TransformNode) => void): TransformNode {
+            this.onAfterWorldMatrixUpdateObservable.removeCallback(func);
+            return this;
+        }        
+
+        public clone(name: string, newParent: Node): Nullable<TransformNode> {
+            var result = SerializationHelper.Clone(() => new TransformNode(name, this.getScene()), this);
+
+            result.name = name;
+            result.id = name;
+
+            if (newParent) {
+                result.parent = newParent;
+            }
+
+            return result;
+        }        
+
+        public serialize(serializationObject: any = null): any {
+            if (!serializationObject) {
+                serializationObject = {};
+            }
+
+            serializationObject.name = this.name;
+            serializationObject.id = this.id;
+            serializationObject.type = this.getClassName();
+
+            if (Tags && Tags.HasTags(this)) {
+                serializationObject.tags = Tags.GetTags(this);
+            }
+
+            serializationObject.position = this.position.asArray();
+
+            if (this.rotationQuaternion) {
+                serializationObject.rotationQuaternion = this.rotationQuaternion.asArray();
+            } else if (this.rotation) {
+                serializationObject.rotation = this.rotation.asArray();
+            }
+
+            serializationObject.scaling = this.scaling.asArray();
+            serializationObject.localMatrix = this.getPivotMatrix().asArray();
+
+            serializationObject.isEnabled = this.isEnabled();
+            serializationObject.infiniteDistance = this.infiniteDistance;
+
+            serializationObject.billboardMode = this.billboardMode;
+
+            // Parent
+            if (this.parent) {
+                serializationObject.parentId = this.parent.id;
+            }
+
+            // Metadata
+            if (this.metadata) {
+                serializationObject.metadata = this.metadata;
+            }
+
+            return serializationObject;
+        }
+
+        // Statics
+        /**
+         * Returns a new TransformNode object parsed from the source provided.   
+         * The parameter `parsedMesh` is the source.   
+         * The parameter `rootUrl` is a string, it's the root URL to prefix the `delayLoadingFile` property with
+         */
+        public static Parse(parsedTransformNode: any, scene: Scene, rootUrl: string): TransformNode {
+            var transformNode = new TransformNode(parsedTransformNode.name, scene);
+
+            transformNode.id = parsedTransformNode.id;
+
+            if (Tags) {
+                Tags.AddTagsTo(transformNode, parsedTransformNode.tags);
+            }
+
+            transformNode.position = Vector3.FromArray(parsedTransformNode.position);
+
+            if (parsedTransformNode.metadata !== undefined) {
+                transformNode.metadata = parsedTransformNode.metadata;
+            }
+
+            if (parsedTransformNode.rotationQuaternion) {
+                transformNode.rotationQuaternion = Quaternion.FromArray(parsedTransformNode.rotationQuaternion);
+            } else if (parsedTransformNode.rotation) {
+                transformNode.rotation = Vector3.FromArray(parsedTransformNode.rotation);
+            }
+
+            transformNode.scaling = Vector3.FromArray(parsedTransformNode.scaling);
+
+            if (parsedTransformNode.localMatrix) {
+                transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.localMatrix));
+            } else if (parsedTransformNode.pivotMatrix) {
+                transformNode.setPivotMatrix(Matrix.FromArray(parsedTransformNode.pivotMatrix));
+            }
+
+            transformNode.setEnabled(parsedTransformNode.isEnabled);
+            transformNode.infiniteDistance = parsedTransformNode.infiniteDistance;
+
+            transformNode.billboardMode = parsedTransformNode.billboardMode;
+
+            // Parent
+            if (parsedTransformNode.parentId) {
+                transformNode._waitingParentId = parsedTransformNode.parentId;
+            }
+         
+            return transformNode;
+        }        
+    }
+}

+ 3 - 3
src/Particles/babylon.solidParticleSystem.ts

@@ -61,8 +61,8 @@
             private _normals: number[] = new Array<number>();
             private _colors: number[] = new Array<number>();
             private _uvs: number[] = new Array<number>();
-            private _indices32: IndicesArray;
-            private _positions32: Float32Array;
+            private _indices32: IndicesArray;           // used as depth sorted array if depth sort enabled, else used as typed indices
+            private _positions32: Float32Array;         // updated positions for the VBO
             private _normals32: Float32Array;           // updated normals for the VBO
             private _fixedNormal32: Float32Array;       // initial normal references
             private _colors32: Float32Array;
@@ -183,7 +183,7 @@
                 }
 
                 var vertexData = new VertexData();
-                vertexData.indices = this._indices32;
+                vertexData.indices = (this._depthSort) ? this._indices : this._indices32;
                 vertexData.set(this._positions32, VertexBuffer.PositionKind);
                 vertexData.set(this._normals32, VertexBuffer.NormalKind);
                 if (this._uvs32) {

+ 3 - 3
src/Physics/babylon.physicsImpostor.ts

@@ -24,9 +24,9 @@ module BABYLON {
         getScene?(): Scene;
         getAbsolutePosition(): Vector3;
         getAbsolutePivotPoint(): Vector3;
-        rotate(axis: Vector3, amount: number, space?: Space): IPhysicsEnabledObject;
-        translate(axis: Vector3, distance: number, space?: Space): IPhysicsEnabledObject;
-        setAbsolutePosition(absolutePosition: Vector3): IPhysicsEnabledObject;
+        rotate(axis: Vector3, amount: number, space?: Space): TransformNode;
+        translate(axis: Vector3, distance: number, space?: Space): TransformNode;
+        setAbsolutePosition(absolutePosition: Vector3): TransformNode;
     }
 
     export class PhysicsImpostor {

+ 2 - 2
src/PostProcess/RenderPipeline/Pipelines/babylon.ssao2RenderingPipeline.ts

@@ -232,7 +232,7 @@
                     return;
                 }
 
-                effect.setFloat("outSize", this._ssaoCombinePostProcess.width);
+                effect.setFloat("outSize", this._ssaoCombinePostProcess.width > 0 ? this._ssaoCombinePostProcess.width : this._originalColorPostProcess.width);
                 effect.setFloat("near", this._scene.activeCamera.minZ);
                 effect.setFloat("far", this._scene.activeCamera.maxZ);
                 effect.setFloat("radius", this.radius);
@@ -249,7 +249,7 @@
                     return;
                 }
 
-                effect.setFloat("outSize", this._ssaoCombinePostProcess.height);
+                effect.setFloat("outSize", this._ssaoCombinePostProcess.height > 0 ? this._ssaoCombinePostProcess.height : this._originalColorPostProcess.height);
                 effect.setFloat("near", this._scene.activeCamera.minZ);
                 effect.setFloat("far", this._scene.activeCamera.maxZ);
                 effect.setFloat("radius", this.radius);

+ 13 - 32
src/PostProcess/RenderPipeline/Pipelines/babylon.ssaoRenderingPipeline.ts

@@ -74,8 +74,8 @@
 
         private _originalColorPostProcess: PassPostProcess;
         private _ssaoPostProcess: PostProcess;
-        private _blurHPostProcess: PostProcess;
-        private _blurVPostProcess: PostProcess;
+        private _blurHPostProcess: BlurPostProcess;
+        private _blurVPostProcess: BlurPostProcess;
         private _ssaoCombinePostProcess: PostProcess;
 
         private _firstUpdate: boolean = true;
@@ -153,39 +153,20 @@
 
         // Private Methods
         private _createBlurPostProcess(ratio: number): void {
-            /*
-            var samplerOffsets = [
-                -8.0, -6.0, -4.0, -2.0,
-                0.0,
-                2.0, 4.0, 6.0, 8.0
-            ];
-            */
-            var samplerOffsets = new Array<number>();
-
-            for (var i = -8; i < 8; i++) {
-                samplerOffsets.push(i * 2);
-            }
+            var size = 16;
 
-            this._blurHPostProcess = new PostProcess("BlurH", "ssao", ["outSize", "samplerOffsets"], ["depthSampler"], ratio, null, Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define BILATERAL_BLUR_H\n#define SAMPLES 16");
-            this._blurHPostProcess.onApply = (effect: Effect) => {
-                effect.setFloat("outSize", this._ssaoCombinePostProcess.width);
-                effect.setTexture("depthSampler", this._depthTexture);
-
-                if (this._firstUpdate) {
-                    effect.setArray("samplerOffsets", samplerOffsets);
-                }
-            };
+            this._blurHPostProcess = new BlurPostProcess("BlurH", new Vector2(1, 0), size, ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_UNSIGNED_INT);
+            this._blurVPostProcess = new BlurPostProcess("BlurV", new Vector2(0, 1), size, ratio, null, BABYLON.Texture.BILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, Engine.TEXTURETYPE_UNSIGNED_INT);
 
-            this._blurVPostProcess = new PostProcess("BlurV", "ssao", ["outSize", "samplerOffsets"], ["depthSampler"], ratio, null, Texture.TRILINEAR_SAMPLINGMODE, this._scene.getEngine(), false, "#define BILATERAL_BLUR\n#define SAMPLES 16");
-            this._blurVPostProcess.onApply = (effect: Effect) => {
-                effect.setFloat("outSize", this._ssaoCombinePostProcess.height);
-                effect.setTexture("depthSampler", this._depthTexture);
+            this._blurHPostProcess.onActivateObservable.add(() => {
+                let dw = this._blurHPostProcess.width / this._scene.getEngine().getRenderWidth();
+                this._blurHPostProcess.kernel = size * dw;
+            });
 
-                if (this._firstUpdate) {
-                    effect.setArray("samplerOffsets", samplerOffsets);
-                    this._firstUpdate = false;
-                }
-            };
+            this._blurVPostProcess.onActivateObservable.add(() => {
+                let dw = this._blurVPostProcess.height / this._scene.getEngine().getRenderHeight();
+                this._blurVPostProcess.kernel = size * dw;
+            });
         }
 
         public _rebuild() {

+ 1 - 1
src/Rendering/babylon.renderingGroup.ts

@@ -324,7 +324,7 @@
                 return;
             }
 
-            if (material.needAlphaBlending() || material.needAlphaBlendingForMesh(mesh)) { // Transparent
+            if (material.needAlphaBlendingForMesh(mesh)) { // Transparent
                 this._transparentSubMeshes.push(subMesh);
             } else if (material.needAlphaTesting()) { // Alpha test
                 if (material.needDepthPrePass) {

+ 11 - 0
src/Shaders/ShadersInclude/helperFunctions.fx

@@ -63,4 +63,15 @@ float square(float value)
 float getLuminance(vec3 color)
 {
     return clamp(dot(color, LuminanceEncodeApprox), 0., 1.);
+}
+
+// https://stackoverflow.com/questions/4200224/random-noise-functions-for-glsl
+float getRand(vec2 seed) {
+	return fract(sin(dot(seed.xy ,vec2(12.9898,78.233))) * 43758.5453);
+}
+
+vec3 dither(vec2 seed, vec3 color) {
+	float rand = getRand(seed);
+	color.rgb += mix(-0.5/255.0, 0.5/255.0, rand);
+	return color;
 }

+ 0 - 0
src/Shaders/background.fragment.fx


部分文件因文件數量過多而無法顯示