Sfoglia il codice sorgente

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js

Guide 6 anni fa
parent
commit
a7970aca97
39 ha cambiato i file con 5534 aggiunte e 5259 eliminazioni
  1. 2382 2358
      Playground/babylon.d.txt
  2. 2375 2351
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 81 13
      dist/preview release/babylon.max.js
  5. 81 13
      dist/preview release/babylon.no-module.max.js
  6. 1 1
      dist/preview release/babylon.worker.js
  7. 81 13
      dist/preview release/es6.js
  8. 7 7
      dist/preview release/inspector/babylon.inspector.bundle.js
  9. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  10. 19 197
      dist/preview release/viewer/babylon.viewer.d.ts
  11. 1 1
      dist/preview release/viewer/babylon.viewer.js
  12. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  13. 24 202
      dist/preview release/viewer/babylon.viewer.module.d.ts
  14. 1 0
      dist/preview release/what's new.md
  15. 3 4
      inspector/src/components/actionTabs/actionTabs.scss
  16. 5 0
      inspector/src/components/actionTabs/lines/floatLineComponent.tsx
  17. 41 13
      inspector/src/components/actionTabs/lines/sliderLineComponent.tsx
  18. 10 2
      inspector/src/components/actionTabs/lines/vector3LineComponent.tsx
  19. 6 1
      inspector/src/components/actionTabs/tabs/debugTabComponent.tsx
  20. 20 1
      inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx
  21. 138 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animationGroupPropertyGridComponent.tsx
  22. 0 10
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/arcRotateCameraPropertyGridComponent.tsx
  23. 0 10
      inspector/src/components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx
  24. 22 9
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  25. 37 1
      inspector/src/components/actionTabs/tabs/propertyGrids/scenePropertyGridComponent.tsx
  26. 30 0
      inspector/src/components/sceneExplorer/entities/animationGroupTreeItemComponent.tsx
  27. 14 0
      inspector/src/components/sceneExplorer/sceneExplorer.scss
  28. 4 0
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  29. 6 1
      inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx
  30. 38 25
      sandbox/index.js
  31. 28 0
      src/Animations/babylon.animationGroup.ts
  32. 5 3
      src/Debug/babylon.physicsViewer.ts
  33. 1 1
      src/Engine/babylon.engine.ts
  34. 2 1
      src/Loading/Plugins/babylon.babylonFileLoader.ts
  35. 33 17
      src/Loading/babylon.sceneLoader.ts
  36. 17 0
      src/Materials/Textures/babylon.cubeTexture.ts
  37. 16 0
      src/Materials/Textures/babylon.hdrCubeTexture.ts
  38. 1 0
      src/Materials/Textures/babylon.texture.ts
  39. 1 1
      src/Tools/babylon.filesInput.ts

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


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


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


+ 81 - 13
dist/preview release/babylon.max.js

@@ -32535,6 +32535,7 @@ var BABYLON;
             if (buffer === void 0) { buffer = null; }
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag);
             }
             this.url = url;
             this._buffer = buffer;
@@ -55125,7 +55126,12 @@ var BABYLON;
              * This observable will notify when all animations have paused.
              */
             this.onAnimationGroupPauseObservable = new BABYLON.Observable();
+            /**
+             * This observable will notify when all animations are playing.
+             */
+            this.onAnimationGroupPlayObservable = new BABYLON.Observable();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
+            this.uniqueId = this._scene.getUniqueId();
             this._scene.animationGroups.push(this);
         }
         Object.defineProperty(AnimationGroup.prototype, "from", {
@@ -55158,6 +55164,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "isPlaying", {
+            /**
+             * Gets a value indicating that the current group is playing
+             */
+            get: function () {
+                return this._isStarted && !this._isPaused;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(AnimationGroup.prototype, "speedRatio", {
             /**
              * Gets or sets the speed ratio to use for all animations
@@ -55301,6 +55317,8 @@ var BABYLON;
                 this._speedRatio = -speedRatio;
             }
             this._isStarted = true;
+            this._isPaused = false;
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
             return this;
         };
         /**
@@ -55311,6 +55329,7 @@ var BABYLON;
             if (!this._isStarted) {
                 return this;
             }
+            this._isPaused = true;
             for (var index = 0; index < this._animatables.length; index++) {
                 var animatable = this._animatables[index];
                 animatable.pause();
@@ -55339,6 +55358,7 @@ var BABYLON;
                 this.stop();
                 this.start(loop, this._speedRatio);
             }
+            this._isPaused = false;
             return this;
         };
         /**
@@ -55367,6 +55387,7 @@ var BABYLON;
                 var animatable = this._animatables[index];
                 animatable.restart();
             }
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
             return this;
         };
         /**
@@ -67007,14 +67028,15 @@ var BABYLON;
             /**
              * Renders a specified physic impostor
              * @param impostor defines the impostor to render
+             * @returns the new debug mesh used to render the impostor
              */
             PhysicsViewer.prototype.showImpostor = function (impostor) {
                 if (!this._scene) {
-                    return;
+                    return null;
                 }
                 for (var i = 0; i < this._numMeshes; i++) {
                     if (this._impostors[i] == impostor) {
-                        return;
+                        return null;
                     }
                 }
                 var debugMesh = this._getDebugMesh(impostor, this._scene);
@@ -67027,6 +67049,7 @@ var BABYLON;
                     }
                     this._numMeshes++;
                 }
+                return debugMesh;
             };
             /**
              * Hides a specified physic impostor
@@ -73802,6 +73825,13 @@ var BABYLON;
             return new CubeTexture(url, scene, null, false, null, null, null, undefined, true, forcedExtension, createPolynomials);
         };
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "CubeTexture"
+         */
+        CubeTexture.prototype.getClassName = function () {
+            return "CubeTexture";
+        };
+        /**
          * Update the url (and optional buffer) of this texture if url was null during construction.
          * @param url the url of the texture
          * @param forcedExtension defines the extension to use
@@ -73810,6 +73840,7 @@ var BABYLON;
         CubeTexture.prototype.updateURL = function (url, forcedExtension, onLoad) {
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag);
             }
             this.url = url;
             this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NOTLOADED;
@@ -73854,6 +73885,13 @@ var BABYLON;
          * @param value Reflection texture matrix
          */
         CubeTexture.prototype.setReflectionTextureMatrix = function (value) {
+            var _this = this;
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag, function (mat) { return mat.getActiveTextures().indexOf(_this) !== -1; });
+            }
             this._textureMatrix = value;
         };
         /**
@@ -78716,7 +78754,7 @@ var BABYLON;
                 dataCallback(directLoad);
                 return plugin;
             }
-            var file = BABYLON.FilesInput.FilesToLoad[fileInfo.name.toLowerCase()];
+            var file = fileInfo.file || BABYLON.FilesInput.FilesToLoad[fileInfo.name.toLowerCase()];
             if (fileInfo.rootUrl.indexOf("file:") === -1 || (fileInfo.rootUrl.indexOf("file:") !== -1 && !file)) {
                 var engine = scene.getEngine();
                 var canUseOfflineSupport = engine.enableOfflineSupport;
@@ -78754,11 +78792,17 @@ var BABYLON;
         SceneLoader._getFileInfo = function (rootUrl, sceneFilename) {
             var url;
             var name;
+            var file = null;
             if (!sceneFilename) {
                 url = rootUrl;
                 name = BABYLON.Tools.GetFilename(rootUrl);
                 rootUrl = BABYLON.Tools.GetFolderPath(rootUrl);
             }
+            else if (sceneFilename instanceof File) {
+                url = rootUrl + sceneFilename.name;
+                name = sceneFilename.name;
+                file = sceneFilename;
+            }
             else {
                 if (sceneFilename.substr(0, 1) === "/") {
                     BABYLON.Tools.Error("Wrong sceneFilename parameter");
@@ -78770,7 +78814,8 @@ var BABYLON;
             return {
                 url: url,
                 rootUrl: rootUrl,
-                name: name
+                name: name,
+                file: file
             };
         };
         // Public functions
@@ -78816,7 +78861,7 @@ var BABYLON;
          * Import meshes into a scene
          * @param meshNames an array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene the instance of BABYLON.Scene to append to
          * @param onSuccess a callback with a list of imported meshes, particleSystems, and skeletons when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -78905,7 +78950,7 @@ var BABYLON;
          * Import meshes into a scene
          * @param meshNames an array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene the instance of BABYLON.Scene to append to
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
@@ -78932,7 +78977,7 @@ var BABYLON;
         /**
          * Load a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param engine is the instance of BABYLON.Engine to use to create the scene
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -78941,22 +78986,30 @@ var BABYLON;
          * @returns The loaded plugin
          */
         SceneLoader.Load = function (rootUrl, sceneFilename, engine, onSuccess, onProgress, onError, pluginExtension) {
+            if (sceneFilename === void 0) { sceneFilename = ""; }
+            if (engine === void 0) { engine = BABYLON.Engine.LastCreatedEngine; }
             if (onSuccess === void 0) { onSuccess = null; }
             if (onProgress === void 0) { onProgress = null; }
             if (onError === void 0) { onError = null; }
             if (pluginExtension === void 0) { pluginExtension = null; }
+            if (!engine) {
+                BABYLON.Tools.Error("No engine available");
+                return null;
+            }
             return SceneLoader.Append(rootUrl, sceneFilename, new BABYLON.Scene(engine), onSuccess, onProgress, onError, pluginExtension);
         };
         /**
          * Load a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param engine is the instance of BABYLON.Engine to use to create the scene
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
          * @returns The loaded scene
          */
         SceneLoader.LoadAsync = function (rootUrl, sceneFilename, engine, onProgress, pluginExtension) {
+            if (sceneFilename === void 0) { sceneFilename = ""; }
+            if (engine === void 0) { engine = BABYLON.Engine.LastCreatedEngine; }
             if (onProgress === void 0) { onProgress = null; }
             if (pluginExtension === void 0) { pluginExtension = null; }
             return new Promise(function (resolve, reject) {
@@ -78970,7 +79023,7 @@ var BABYLON;
         /**
          * Append a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -79060,7 +79113,7 @@ var BABYLON;
         /**
          * Append a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
@@ -79082,7 +79135,7 @@ var BABYLON;
         /**
          * Load a scene into an asset container
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to (default: last created scene)
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -79848,7 +79901,8 @@ var BABYLON;
                     if (parsedData.createDefaultSkybox === true) {
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        scene.createDefaultSkybox(undefined, true, skyboxScale, skyboxBlurLevel);
+                        var skyboxIsPBR = parsedData.skyboxIsPBR !== undefined ? parsedData.skyboxIsPBR : true;
+                        scene.createDefaultSkybox(scene.environmentTexture, skyboxIsPBR, skyboxScale, skyboxBlurLevel);
                     }
                 }
                 // Finish
@@ -80092,7 +80146,7 @@ var BABYLON;
                     }
                     this._engine.stopRenderLoop();
                 }
-                BABYLON.SceneLoader.LoadAsync("file:", this._sceneFileToLoad.name, this._engine, function (progress) {
+                BABYLON.SceneLoader.LoadAsync("file:", this._sceneFileToLoad, this._engine, function (progress) {
                     if (_this._progressCallback) {
                         _this._progressCallback(progress);
                     }
@@ -95399,6 +95453,13 @@ var BABYLON;
             configurable: true
         });
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "HDRCubeTexture"
+         */
+        HDRCubeTexture.prototype.getClassName = function () {
+            return "HDRCubeTexture";
+        };
+        /**
          * Occurs when the file is raw .hdr file.
          */
         HDRCubeTexture.prototype.loadTexture = function () {
@@ -95507,7 +95568,14 @@ var BABYLON;
          * @param value Define the reflection matrix to set
          */
         HDRCubeTexture.prototype.setReflectionTextureMatrix = function (value) {
+            var _this = this;
             this._textureMatrix = value;
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag, function (mat) { return mat.getActiveTextures().indexOf(_this) !== -1; });
+            }
         };
         /**
          * Parses a JSON representation of an HDR Texture in order to create the texture

+ 81 - 13
dist/preview release/babylon.no-module.max.js

@@ -32502,6 +32502,7 @@ var BABYLON;
             if (buffer === void 0) { buffer = null; }
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag);
             }
             this.url = url;
             this._buffer = buffer;
@@ -55092,7 +55093,12 @@ var BABYLON;
              * This observable will notify when all animations have paused.
              */
             this.onAnimationGroupPauseObservable = new BABYLON.Observable();
+            /**
+             * This observable will notify when all animations are playing.
+             */
+            this.onAnimationGroupPlayObservable = new BABYLON.Observable();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
+            this.uniqueId = this._scene.getUniqueId();
             this._scene.animationGroups.push(this);
         }
         Object.defineProperty(AnimationGroup.prototype, "from", {
@@ -55125,6 +55131,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "isPlaying", {
+            /**
+             * Gets a value indicating that the current group is playing
+             */
+            get: function () {
+                return this._isStarted && !this._isPaused;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(AnimationGroup.prototype, "speedRatio", {
             /**
              * Gets or sets the speed ratio to use for all animations
@@ -55268,6 +55284,8 @@ var BABYLON;
                 this._speedRatio = -speedRatio;
             }
             this._isStarted = true;
+            this._isPaused = false;
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
             return this;
         };
         /**
@@ -55278,6 +55296,7 @@ var BABYLON;
             if (!this._isStarted) {
                 return this;
             }
+            this._isPaused = true;
             for (var index = 0; index < this._animatables.length; index++) {
                 var animatable = this._animatables[index];
                 animatable.pause();
@@ -55306,6 +55325,7 @@ var BABYLON;
                 this.stop();
                 this.start(loop, this._speedRatio);
             }
+            this._isPaused = false;
             return this;
         };
         /**
@@ -55334,6 +55354,7 @@ var BABYLON;
                 var animatable = this._animatables[index];
                 animatable.restart();
             }
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
             return this;
         };
         /**
@@ -66974,14 +66995,15 @@ var BABYLON;
             /**
              * Renders a specified physic impostor
              * @param impostor defines the impostor to render
+             * @returns the new debug mesh used to render the impostor
              */
             PhysicsViewer.prototype.showImpostor = function (impostor) {
                 if (!this._scene) {
-                    return;
+                    return null;
                 }
                 for (var i = 0; i < this._numMeshes; i++) {
                     if (this._impostors[i] == impostor) {
-                        return;
+                        return null;
                     }
                 }
                 var debugMesh = this._getDebugMesh(impostor, this._scene);
@@ -66994,6 +67016,7 @@ var BABYLON;
                     }
                     this._numMeshes++;
                 }
+                return debugMesh;
             };
             /**
              * Hides a specified physic impostor
@@ -73769,6 +73792,13 @@ var BABYLON;
             return new CubeTexture(url, scene, null, false, null, null, null, undefined, true, forcedExtension, createPolynomials);
         };
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "CubeTexture"
+         */
+        CubeTexture.prototype.getClassName = function () {
+            return "CubeTexture";
+        };
+        /**
          * Update the url (and optional buffer) of this texture if url was null during construction.
          * @param url the url of the texture
          * @param forcedExtension defines the extension to use
@@ -73777,6 +73807,7 @@ var BABYLON;
         CubeTexture.prototype.updateURL = function (url, forcedExtension, onLoad) {
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag);
             }
             this.url = url;
             this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NOTLOADED;
@@ -73821,6 +73852,13 @@ var BABYLON;
          * @param value Reflection texture matrix
          */
         CubeTexture.prototype.setReflectionTextureMatrix = function (value) {
+            var _this = this;
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag, function (mat) { return mat.getActiveTextures().indexOf(_this) !== -1; });
+            }
             this._textureMatrix = value;
         };
         /**
@@ -78683,7 +78721,7 @@ var BABYLON;
                 dataCallback(directLoad);
                 return plugin;
             }
-            var file = BABYLON.FilesInput.FilesToLoad[fileInfo.name.toLowerCase()];
+            var file = fileInfo.file || BABYLON.FilesInput.FilesToLoad[fileInfo.name.toLowerCase()];
             if (fileInfo.rootUrl.indexOf("file:") === -1 || (fileInfo.rootUrl.indexOf("file:") !== -1 && !file)) {
                 var engine = scene.getEngine();
                 var canUseOfflineSupport = engine.enableOfflineSupport;
@@ -78721,11 +78759,17 @@ var BABYLON;
         SceneLoader._getFileInfo = function (rootUrl, sceneFilename) {
             var url;
             var name;
+            var file = null;
             if (!sceneFilename) {
                 url = rootUrl;
                 name = BABYLON.Tools.GetFilename(rootUrl);
                 rootUrl = BABYLON.Tools.GetFolderPath(rootUrl);
             }
+            else if (sceneFilename instanceof File) {
+                url = rootUrl + sceneFilename.name;
+                name = sceneFilename.name;
+                file = sceneFilename;
+            }
             else {
                 if (sceneFilename.substr(0, 1) === "/") {
                     BABYLON.Tools.Error("Wrong sceneFilename parameter");
@@ -78737,7 +78781,8 @@ var BABYLON;
             return {
                 url: url,
                 rootUrl: rootUrl,
-                name: name
+                name: name,
+                file: file
             };
         };
         // Public functions
@@ -78783,7 +78828,7 @@ var BABYLON;
          * Import meshes into a scene
          * @param meshNames an array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene the instance of BABYLON.Scene to append to
          * @param onSuccess a callback with a list of imported meshes, particleSystems, and skeletons when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -78872,7 +78917,7 @@ var BABYLON;
          * Import meshes into a scene
          * @param meshNames an array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene the instance of BABYLON.Scene to append to
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
@@ -78899,7 +78944,7 @@ var BABYLON;
         /**
          * Load a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param engine is the instance of BABYLON.Engine to use to create the scene
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -78908,22 +78953,30 @@ var BABYLON;
          * @returns The loaded plugin
          */
         SceneLoader.Load = function (rootUrl, sceneFilename, engine, onSuccess, onProgress, onError, pluginExtension) {
+            if (sceneFilename === void 0) { sceneFilename = ""; }
+            if (engine === void 0) { engine = BABYLON.Engine.LastCreatedEngine; }
             if (onSuccess === void 0) { onSuccess = null; }
             if (onProgress === void 0) { onProgress = null; }
             if (onError === void 0) { onError = null; }
             if (pluginExtension === void 0) { pluginExtension = null; }
+            if (!engine) {
+                BABYLON.Tools.Error("No engine available");
+                return null;
+            }
             return SceneLoader.Append(rootUrl, sceneFilename, new BABYLON.Scene(engine), onSuccess, onProgress, onError, pluginExtension);
         };
         /**
          * Load a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param engine is the instance of BABYLON.Engine to use to create the scene
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
          * @returns The loaded scene
          */
         SceneLoader.LoadAsync = function (rootUrl, sceneFilename, engine, onProgress, pluginExtension) {
+            if (sceneFilename === void 0) { sceneFilename = ""; }
+            if (engine === void 0) { engine = BABYLON.Engine.LastCreatedEngine; }
             if (onProgress === void 0) { onProgress = null; }
             if (pluginExtension === void 0) { pluginExtension = null; }
             return new Promise(function (resolve, reject) {
@@ -78937,7 +78990,7 @@ var BABYLON;
         /**
          * Append a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -79027,7 +79080,7 @@ var BABYLON;
         /**
          * Append a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
@@ -79049,7 +79102,7 @@ var BABYLON;
         /**
          * Load a scene into an asset container
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to (default: last created scene)
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -79815,7 +79868,8 @@ var BABYLON;
                     if (parsedData.createDefaultSkybox === true) {
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        scene.createDefaultSkybox(undefined, true, skyboxScale, skyboxBlurLevel);
+                        var skyboxIsPBR = parsedData.skyboxIsPBR !== undefined ? parsedData.skyboxIsPBR : true;
+                        scene.createDefaultSkybox(scene.environmentTexture, skyboxIsPBR, skyboxScale, skyboxBlurLevel);
                     }
                 }
                 // Finish
@@ -80059,7 +80113,7 @@ var BABYLON;
                     }
                     this._engine.stopRenderLoop();
                 }
-                BABYLON.SceneLoader.LoadAsync("file:", this._sceneFileToLoad.name, this._engine, function (progress) {
+                BABYLON.SceneLoader.LoadAsync("file:", this._sceneFileToLoad, this._engine, function (progress) {
                     if (_this._progressCallback) {
                         _this._progressCallback(progress);
                     }
@@ -95366,6 +95420,13 @@ var BABYLON;
             configurable: true
         });
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "HDRCubeTexture"
+         */
+        HDRCubeTexture.prototype.getClassName = function () {
+            return "HDRCubeTexture";
+        };
+        /**
          * Occurs when the file is raw .hdr file.
          */
         HDRCubeTexture.prototype.loadTexture = function () {
@@ -95474,7 +95535,14 @@ var BABYLON;
          * @param value Define the reflection matrix to set
          */
         HDRCubeTexture.prototype.setReflectionTextureMatrix = function (value) {
+            var _this = this;
             this._textureMatrix = value;
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag, function (mat) { return mat.getActiveTextures().indexOf(_this) !== -1; });
+            }
         };
         /**
          * Parses a JSON representation of an HDR Texture in order to create the texture

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


+ 81 - 13
dist/preview release/es6.js

@@ -32502,6 +32502,7 @@ var BABYLON;
             if (buffer === void 0) { buffer = null; }
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag);
             }
             this.url = url;
             this._buffer = buffer;
@@ -55092,7 +55093,12 @@ var BABYLON;
              * This observable will notify when all animations have paused.
              */
             this.onAnimationGroupPauseObservable = new BABYLON.Observable();
+            /**
+             * This observable will notify when all animations are playing.
+             */
+            this.onAnimationGroupPlayObservable = new BABYLON.Observable();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
+            this.uniqueId = this._scene.getUniqueId();
             this._scene.animationGroups.push(this);
         }
         Object.defineProperty(AnimationGroup.prototype, "from", {
@@ -55125,6 +55131,16 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(AnimationGroup.prototype, "isPlaying", {
+            /**
+             * Gets a value indicating that the current group is playing
+             */
+            get: function () {
+                return this._isStarted && !this._isPaused;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(AnimationGroup.prototype, "speedRatio", {
             /**
              * Gets or sets the speed ratio to use for all animations
@@ -55268,6 +55284,8 @@ var BABYLON;
                 this._speedRatio = -speedRatio;
             }
             this._isStarted = true;
+            this._isPaused = false;
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
             return this;
         };
         /**
@@ -55278,6 +55296,7 @@ var BABYLON;
             if (!this._isStarted) {
                 return this;
             }
+            this._isPaused = true;
             for (var index = 0; index < this._animatables.length; index++) {
                 var animatable = this._animatables[index];
                 animatable.pause();
@@ -55306,6 +55325,7 @@ var BABYLON;
                 this.stop();
                 this.start(loop, this._speedRatio);
             }
+            this._isPaused = false;
             return this;
         };
         /**
@@ -55334,6 +55354,7 @@ var BABYLON;
                 var animatable = this._animatables[index];
                 animatable.restart();
             }
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
             return this;
         };
         /**
@@ -66974,14 +66995,15 @@ var BABYLON;
             /**
              * Renders a specified physic impostor
              * @param impostor defines the impostor to render
+             * @returns the new debug mesh used to render the impostor
              */
             PhysicsViewer.prototype.showImpostor = function (impostor) {
                 if (!this._scene) {
-                    return;
+                    return null;
                 }
                 for (var i = 0; i < this._numMeshes; i++) {
                     if (this._impostors[i] == impostor) {
-                        return;
+                        return null;
                     }
                 }
                 var debugMesh = this._getDebugMesh(impostor, this._scene);
@@ -66994,6 +67016,7 @@ var BABYLON;
                     }
                     this._numMeshes++;
                 }
+                return debugMesh;
             };
             /**
              * Hides a specified physic impostor
@@ -73769,6 +73792,13 @@ var BABYLON;
             return new CubeTexture(url, scene, null, false, null, null, null, undefined, true, forcedExtension, createPolynomials);
         };
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "CubeTexture"
+         */
+        CubeTexture.prototype.getClassName = function () {
+            return "CubeTexture";
+        };
+        /**
          * Update the url (and optional buffer) of this texture if url was null during construction.
          * @param url the url of the texture
          * @param forcedExtension defines the extension to use
@@ -73777,6 +73807,7 @@ var BABYLON;
         CubeTexture.prototype.updateURL = function (url, forcedExtension, onLoad) {
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag);
             }
             this.url = url;
             this.delayLoadState = BABYLON.Engine.DELAYLOADSTATE_NOTLOADED;
@@ -73821,6 +73852,13 @@ var BABYLON;
          * @param value Reflection texture matrix
          */
         CubeTexture.prototype.setReflectionTextureMatrix = function (value) {
+            var _this = this;
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag, function (mat) { return mat.getActiveTextures().indexOf(_this) !== -1; });
+            }
             this._textureMatrix = value;
         };
         /**
@@ -78683,7 +78721,7 @@ var BABYLON;
                 dataCallback(directLoad);
                 return plugin;
             }
-            var file = BABYLON.FilesInput.FilesToLoad[fileInfo.name.toLowerCase()];
+            var file = fileInfo.file || BABYLON.FilesInput.FilesToLoad[fileInfo.name.toLowerCase()];
             if (fileInfo.rootUrl.indexOf("file:") === -1 || (fileInfo.rootUrl.indexOf("file:") !== -1 && !file)) {
                 var engine = scene.getEngine();
                 var canUseOfflineSupport = engine.enableOfflineSupport;
@@ -78721,11 +78759,17 @@ var BABYLON;
         SceneLoader._getFileInfo = function (rootUrl, sceneFilename) {
             var url;
             var name;
+            var file = null;
             if (!sceneFilename) {
                 url = rootUrl;
                 name = BABYLON.Tools.GetFilename(rootUrl);
                 rootUrl = BABYLON.Tools.GetFolderPath(rootUrl);
             }
+            else if (sceneFilename instanceof File) {
+                url = rootUrl + sceneFilename.name;
+                name = sceneFilename.name;
+                file = sceneFilename;
+            }
             else {
                 if (sceneFilename.substr(0, 1) === "/") {
                     BABYLON.Tools.Error("Wrong sceneFilename parameter");
@@ -78737,7 +78781,8 @@ var BABYLON;
             return {
                 url: url,
                 rootUrl: rootUrl,
-                name: name
+                name: name,
+                file: file
             };
         };
         // Public functions
@@ -78783,7 +78828,7 @@ var BABYLON;
          * Import meshes into a scene
          * @param meshNames an array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene the instance of BABYLON.Scene to append to
          * @param onSuccess a callback with a list of imported meshes, particleSystems, and skeletons when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -78872,7 +78917,7 @@ var BABYLON;
          * Import meshes into a scene
          * @param meshNames an array of mesh names, a single mesh name, or empty string for all meshes that filter what meshes are imported
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene the instance of BABYLON.Scene to append to
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
@@ -78899,7 +78944,7 @@ var BABYLON;
         /**
          * Load a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param engine is the instance of BABYLON.Engine to use to create the scene
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -78908,22 +78953,30 @@ var BABYLON;
          * @returns The loaded plugin
          */
         SceneLoader.Load = function (rootUrl, sceneFilename, engine, onSuccess, onProgress, onError, pluginExtension) {
+            if (sceneFilename === void 0) { sceneFilename = ""; }
+            if (engine === void 0) { engine = BABYLON.Engine.LastCreatedEngine; }
             if (onSuccess === void 0) { onSuccess = null; }
             if (onProgress === void 0) { onProgress = null; }
             if (onError === void 0) { onError = null; }
             if (pluginExtension === void 0) { pluginExtension = null; }
+            if (!engine) {
+                BABYLON.Tools.Error("No engine available");
+                return null;
+            }
             return SceneLoader.Append(rootUrl, sceneFilename, new BABYLON.Scene(engine), onSuccess, onProgress, onError, pluginExtension);
         };
         /**
          * Load a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param engine is the instance of BABYLON.Engine to use to create the scene
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
          * @returns The loaded scene
          */
         SceneLoader.LoadAsync = function (rootUrl, sceneFilename, engine, onProgress, pluginExtension) {
+            if (sceneFilename === void 0) { sceneFilename = ""; }
+            if (engine === void 0) { engine = BABYLON.Engine.LastCreatedEngine; }
             if (onProgress === void 0) { onProgress = null; }
             if (pluginExtension === void 0) { pluginExtension = null; }
             return new Promise(function (resolve, reject) {
@@ -78937,7 +78990,7 @@ var BABYLON;
         /**
          * Append a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -79027,7 +79080,7 @@ var BABYLON;
         /**
          * Append a scene
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to
          * @param onProgress a callback with a progress event for each file being loaded
          * @param pluginExtension the extension used to determine the plugin
@@ -79049,7 +79102,7 @@ var BABYLON;
         /**
          * Load a scene into an asset container
          * @param rootUrl a string that defines the root url for the scene and resources or the concatenation of rootURL and filename (e.g. http://example.com/test.glb)
-         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene (default: empty string)
+         * @param sceneFilename a string that defines the name of the scene file or starts with "data:" following by the stringified version of the scene or a File object (default: empty string)
          * @param scene is the instance of BABYLON.Scene to append to (default: last created scene)
          * @param onSuccess a callback with the scene when import succeeds
          * @param onProgress a callback with a progress event for each file being loaded
@@ -79815,7 +79868,8 @@ var BABYLON;
                     if (parsedData.createDefaultSkybox === true) {
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        scene.createDefaultSkybox(undefined, true, skyboxScale, skyboxBlurLevel);
+                        var skyboxIsPBR = parsedData.skyboxIsPBR !== undefined ? parsedData.skyboxIsPBR : true;
+                        scene.createDefaultSkybox(scene.environmentTexture, skyboxIsPBR, skyboxScale, skyboxBlurLevel);
                     }
                 }
                 // Finish
@@ -80059,7 +80113,7 @@ var BABYLON;
                     }
                     this._engine.stopRenderLoop();
                 }
-                BABYLON.SceneLoader.LoadAsync("file:", this._sceneFileToLoad.name, this._engine, function (progress) {
+                BABYLON.SceneLoader.LoadAsync("file:", this._sceneFileToLoad, this._engine, function (progress) {
                     if (_this._progressCallback) {
                         _this._progressCallback(progress);
                     }
@@ -95366,6 +95420,13 @@ var BABYLON;
             configurable: true
         });
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "HDRCubeTexture"
+         */
+        HDRCubeTexture.prototype.getClassName = function () {
+            return "HDRCubeTexture";
+        };
+        /**
          * Occurs when the file is raw .hdr file.
          */
         HDRCubeTexture.prototype.loadTexture = function () {
@@ -95474,7 +95535,14 @@ var BABYLON;
          * @param value Define the reflection matrix to set
          */
         HDRCubeTexture.prototype.setReflectionTextureMatrix = function (value) {
+            var _this = this;
             this._textureMatrix = value;
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene().markAllMaterialsAsDirty(BABYLON.Material.TextureDirtyFlag, function (mat) { return mat.getActiveTextures().indexOf(_this) !== -1; });
+            }
         };
         /**
          * Parses a JSON representation of an HDR Texture in order to create the texture

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


File diff suppressed because it is too large
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js.map


+ 19 - 197
dist/preview release/viewer/babylon.viewer.d.ts

@@ -2,6 +2,10 @@
 /// <reference path="./babylon.glTF2Interface.d.ts"/>
 /// <reference path="./babylonjs.loaders.d.ts"/>
 declare module "babylonjs-loaders"{ export=BABYLON;}
+/// <reference path="./babylon.d.ts"/>
+/// <reference path="./babylon.glTF2Interface.d.ts"/>
+/// <reference path="./babylonjs.loaders.d.ts"/>
+declare module "babylonjs-loaders"{ export=BABYLON;}
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 //   ../../../../../Tools/gulp/babylonjs
@@ -924,7 +928,7 @@ declare module BabylonViewer {
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
@@ -1038,169 +1042,6 @@ declare module BabylonViewer {
     }
 }
 declare module BabylonViewer {
-    /**
-        * The object sent when an event is triggered
-        */
-    export interface EventCallback {
-            event: Event;
-            template: Template;
-            selector: string;
-            payload?: any;
-    }
-    /**
-        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
-        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
-        */
-    export class TemplateManager {
-            containerElement: Element;
-            /**
-                * Will be triggered when any template is initialized
-                */
-            onTemplateInit: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when any template is fully loaded
-                */
-            onTemplateLoaded: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when a template state changes
-                */
-            onTemplateStateChange: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when all templates finished loading
-                */
-            onAllLoaded: BABYLON.Observable<TemplateManager>;
-            /**
-                * Will be triggered when any event on any template is triggered.
-                */
-            onEventTriggered: BABYLON.Observable<EventCallback>;
-            /**
-                * This template manager's event manager. In charge of callback registrations to native event types
-                */
-            eventManager: EventManager;
-            constructor(containerElement: Element);
-            /**
-                * Initialize the template(s) for the viewer. Called bay the Viewer class
-                * @param templates the templates to be used to initialize the main template
-                */
-            initTemplate(templates: {
-                    [key: string]: ITemplateConfiguration;
-            }): Promise<void>;
-            /**
-                * Get the canvas in the template tree.
-                * There must be one and only one canvas inthe template.
-                */
-            getCanvas(): HTMLCanvasElement | null;
-            /**
-                * Get a specific template from the template tree
-                * @param name the name of the template to load
-                */
-            getTemplate(name: string): Template | undefined;
-            /**
-                * Dispose the template manager
-                */
-            dispose(): void;
-    }
-    /**
-        * This class represents a single template in the viewer's template tree.
-        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
-        * A template is injected using the template manager in the correct position.
-        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
-        *
-        * For further information please refer to the documentation page, https://doc.babylonjs.com
-        */
-    export class Template {
-            name: string;
-            /**
-                * Will be triggered when the template is loaded
-                */
-            onLoaded: BABYLON.Observable<Template>;
-            /**
-                * will be triggered when the template is appended to the tree
-                */
-            onAppended: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when the template's state changed (shown, hidden)
-                */
-            onStateChange: BABYLON.Observable<Template>;
-            /**
-                * Will be triggered when an event is triggered on ths template.
-                * The event is a native browser event (like mouse or pointer events)
-                */
-            onEventTriggered: BABYLON.Observable<EventCallback>;
-            onParamsUpdated: BABYLON.Observable<Template>;
-            onHTMLRendered: BABYLON.Observable<Template>;
-            /**
-                * is the template loaded?
-                */
-            isLoaded: boolean;
-            /**
-                * This is meant to be used to track the show and hide functions.
-                * This is NOT (!!) a flag to check if the element is actually visible to the user.
-                */
-            isShown: boolean;
-            /**
-                * Is this template a part of the HTML tree (the template manager injected it)
-                */
-            isInHtmlTree: boolean;
-            /**
-                * The HTML element containing this template
-                */
-            parent: HTMLElement;
-            /**
-                * A promise that is fulfilled when the template finished loading.
-                */
-            initPromise: Promise<Template>;
-            constructor(name: string, _configuration: ITemplateConfiguration);
-            /**
-                * Some templates have parameters (like background color for example).
-                * The parameters are provided to Handlebars which in turn generates the template.
-                * This function will update the template with the new parameters
-                *
-                * Note that when updating parameters the events will be registered again (after being cleared).
-                *
-                * @param params the new template parameters
-                */
-            updateParams(params: {
-                    [key: string]: string | number | boolean | object;
-            }, append?: boolean): void;
-            redraw(): void;
-            /**
-                * Get the template'S configuration
-                */
-            readonly configuration: ITemplateConfiguration;
-            /**
-                * A template can be a parent element for other templates or HTML elements.
-                * This function will deliver all child HTML elements of this template.
-                */
-            getChildElements(): Array<string>;
-            /**
-                * Appending the template to a parent HTML element.
-                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
-                * @param parent the parent to which the template is added
-                * @param forceRemove if the parent already exists, shoud the template be removed from it?
-                */
-            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
-            /**
-                * Show the template using the provided visibilityFunction, or natively using display: flex.
-                * The provided function returns a promise that should be fullfilled when the element is shown.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Hide the template using the provided visibilityFunction, or natively using display: none.
-                * The provided function returns a promise that should be fullfilled when the element is hidden.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Dispose this template
-                */
-            dispose(): void;
-    }
 }
 declare module BabylonViewer {
     export class ConfigurationContainer {
@@ -1558,6 +1399,20 @@ declare module BabylonViewer {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 declare module BabylonViewer {
+    /**
+        * A custom upgrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedUpgrade(sceneManager: SceneManager): boolean;
+    /**
+        * A custom degrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedDegrade(sceneManager: SceneManager): boolean;
+}
+declare module BabylonViewer {
 }
 declare module BabylonViewer {
     export interface IEnvironmentMapConfiguration {
@@ -1585,39 +1440,6 @@ declare module BabylonViewer {
 }
 declare module BabylonViewer {
     /**
-        * The EventManager is in charge of registering user interctions with the viewer.
-        * It is used in the TemplateManager
-        */
-    export class EventManager {
-            constructor(_templateManager: TemplateManager);
-            /**
-                * Register a new callback to a specific template.
-                * The best example for the usage can be found in the DefaultViewer
-                *
-                * @param templateName the templateName to register the event to
-                * @param callback The callback to be executed
-                * @param eventType the type of event to register
-                * @param selector an optional selector. if not defined the parent object in the template will be selected
-                */
-            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * This will remove a registered event from the defined template.
-                * Each one of the variables apart from the template name are optional, but one must be provided.
-                *
-                * @param templateName the templateName
-                * @param callback the callback to remove (optional)
-                * @param eventType the event type to remove (optional)
-                * @param selector the selector from which to remove the event (optional)
-                */
-            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * Dispose the event manager
-                */
-            dispose(): void;
-    }
-}
-declare module BabylonViewer {
-    /**
         * The ViewerLabs class will hold functions that are not (!) backwards compatible.
         * The APIs in all labs-related classes and configuration  might change.
         * Once stable, lab features will be moved to the publis API and configuration object.

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


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


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

@@ -3,6 +3,11 @@
 /// <reference path="./babylonjs.loaders.d.ts"/>
 declare module "babylonjs-loaders"{ export=BABYLON;}
 
+/// <reference path="./babylon.d.ts"/>
+/// <reference path="./babylon.glTF2Interface.d.ts"/>
+/// <reference path="./babylonjs.loaders.d.ts"/>
+declare module "babylonjs-loaders"{ export=BABYLON;}
+
 // Generated by dts-bundle v0.7.3
 // Dependencies for this module:
 //   ../../../../../Tools/gulp/babylonjs
@@ -985,13 +990,14 @@ declare module 'babylonjs-viewer/templating/viewerTemplatePlugin' {
 }
 
 declare module 'babylonjs-viewer/optimizer/custom' {
+    import { extendedUpgrade } from "babylonjs-viewer/optimizer/custom/extended";
     import { SceneManager } from "babylonjs-viewer/managers/sceneManager";
     /**
       *
       * @param name the name of the custom optimizer configuration
       * @param upgrade set to true if you want to upgrade optimizer and false if you want to degrade
       */
-    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): typeof extendedUpgrade;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 
@@ -1114,172 +1120,7 @@ declare module 'babylonjs-viewer/configuration/configuration' {
 }
 
 declare module 'babylonjs-viewer/templating/templateManager' {
-    import { Observable } from 'babylonjs';
-    import { EventManager } from 'babylonjs-viewer/templating/eventManager';
-    import { ITemplateConfiguration } from 'babylonjs-viewer/configuration/interfaces';
-    /**
-        * The object sent when an event is triggered
-        */
-    export interface EventCallback {
-            event: Event;
-            template: Template;
-            selector: string;
-            payload?: any;
-    }
-    /**
-        * The template manager, a member of the viewer class, will manage the viewer's templates and generate the HTML.
-        * The template manager managers a single viewer and can be seen as the collection of all sub-templates of the viewer.
-        */
-    export class TemplateManager {
-            containerElement: Element;
-            /**
-                * Will be triggered when any template is initialized
-                */
-            onTemplateInit: Observable<Template>;
-            /**
-                * Will be triggered when any template is fully loaded
-                */
-            onTemplateLoaded: Observable<Template>;
-            /**
-                * Will be triggered when a template state changes
-                */
-            onTemplateStateChange: Observable<Template>;
-            /**
-                * Will be triggered when all templates finished loading
-                */
-            onAllLoaded: Observable<TemplateManager>;
-            /**
-                * Will be triggered when any event on any template is triggered.
-                */
-            onEventTriggered: Observable<EventCallback>;
-            /**
-                * This template manager's event manager. In charge of callback registrations to native event types
-                */
-            eventManager: EventManager;
-            constructor(containerElement: Element);
-            /**
-                * Initialize the template(s) for the viewer. Called bay the Viewer class
-                * @param templates the templates to be used to initialize the main template
-                */
-            initTemplate(templates: {
-                    [key: string]: ITemplateConfiguration;
-            }): Promise<void>;
-            /**
-                * Get the canvas in the template tree.
-                * There must be one and only one canvas inthe template.
-                */
-            getCanvas(): HTMLCanvasElement | null;
-            /**
-                * Get a specific template from the template tree
-                * @param name the name of the template to load
-                */
-            getTemplate(name: string): Template | undefined;
-            /**
-                * Dispose the template manager
-                */
-            dispose(): void;
-    }
-    /**
-        * This class represents a single template in the viewer's template tree.
-        * An example for a template is a single canvas, an overlay (containing sub-templates) or the navigation bar.
-        * A template is injected using the template manager in the correct position.
-        * The template is rendered using Handlebars and can use Handlebars' features (such as parameter injection)
-        *
-        * For further information please refer to the documentation page, https://doc.babylonjs.com
-        */
-    export class Template {
-            name: string;
-            /**
-                * Will be triggered when the template is loaded
-                */
-            onLoaded: Observable<Template>;
-            /**
-                * will be triggered when the template is appended to the tree
-                */
-            onAppended: Observable<Template>;
-            /**
-                * Will be triggered when the template's state changed (shown, hidden)
-                */
-            onStateChange: Observable<Template>;
-            /**
-                * Will be triggered when an event is triggered on ths template.
-                * The event is a native browser event (like mouse or pointer events)
-                */
-            onEventTriggered: Observable<EventCallback>;
-            onParamsUpdated: Observable<Template>;
-            onHTMLRendered: Observable<Template>;
-            /**
-                * is the template loaded?
-                */
-            isLoaded: boolean;
-            /**
-                * This is meant to be used to track the show and hide functions.
-                * This is NOT (!!) a flag to check if the element is actually visible to the user.
-                */
-            isShown: boolean;
-            /**
-                * Is this template a part of the HTML tree (the template manager injected it)
-                */
-            isInHtmlTree: boolean;
-            /**
-                * The HTML element containing this template
-                */
-            parent: HTMLElement;
-            /**
-                * A promise that is fulfilled when the template finished loading.
-                */
-            initPromise: Promise<Template>;
-            constructor(name: string, _configuration: ITemplateConfiguration);
-            /**
-                * Some templates have parameters (like background color for example).
-                * The parameters are provided to Handlebars which in turn generates the template.
-                * This function will update the template with the new parameters
-                *
-                * Note that when updating parameters the events will be registered again (after being cleared).
-                *
-                * @param params the new template parameters
-                */
-            updateParams(params: {
-                    [key: string]: string | number | boolean | object;
-            }, append?: boolean): void;
-            redraw(): void;
-            /**
-                * Get the template'S configuration
-                */
-            readonly configuration: ITemplateConfiguration;
-            /**
-                * A template can be a parent element for other templates or HTML elements.
-                * This function will deliver all child HTML elements of this template.
-                */
-            getChildElements(): Array<string>;
-            /**
-                * Appending the template to a parent HTML element.
-                * If a parent is already set and you wish to replace the old HTML with new one, forceRemove should be true.
-                * @param parent the parent to which the template is added
-                * @param forceRemove if the parent already exists, shoud the template be removed from it?
-                */
-            appendTo(parent: HTMLElement, forceRemove?: boolean): void;
-            /**
-                * Show the template using the provided visibilityFunction, or natively using display: flex.
-                * The provided function returns a promise that should be fullfilled when the element is shown.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            show(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Hide the template using the provided visibilityFunction, or natively using display: none.
-                * The provided function returns a promise that should be fullfilled when the element is hidden.
-                * Since it is a promise async operations are more than possible.
-                * See the default viewer for an opacity example.
-                * @param visibilityFunction The function to execute to show the template.
-                */
-            hide(visibilityFunction?: (template: Template) => Promise<Template>): Promise<Template>;
-            /**
-                * Dispose this template
-                */
-            dispose(): void;
-    }
+    
 }
 
 declare module 'babylonjs-viewer/configuration/configurationContainer' {
@@ -1662,6 +1503,22 @@ declare module 'babylonjs-viewer/loader/plugins' {
     export function addLoaderPlugin(name: string, plugin: ILoaderPlugin): void;
 }
 
+declare module 'babylonjs-viewer/optimizer/custom/extended' {
+    import { SceneManager } from 'babylonjs-viewer/managers/sceneManager';
+    /**
+        * A custom upgrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedUpgrade(sceneManager: SceneManager): boolean;
+    /**
+        * A custom degrade-oriented function configuration for the scene optimizer.
+        *
+        * @param viewer the viewer to optimize
+        */
+    export function extendedDegrade(sceneManager: SceneManager): boolean;
+}
+
 declare module 'babylonjs-viewer/configuration/interfaces' {
     export * from 'babylonjs-viewer/configuration/interfaces/cameraConfiguration';
     export * from 'babylonjs-viewer/configuration/interfaces/colorGradingConfiguration';
@@ -1705,41 +1562,6 @@ declare module 'babylonjs-viewer/configuration/interfaces/environmentMapConfigur
     }
 }
 
-declare module 'babylonjs-viewer/templating/eventManager' {
-    import { EventCallback, TemplateManager } from "babylonjs-viewer/templating/templateManager";
-    /**
-        * The EventManager is in charge of registering user interctions with the viewer.
-        * It is used in the TemplateManager
-        */
-    export class EventManager {
-            constructor(_templateManager: TemplateManager);
-            /**
-                * Register a new callback to a specific template.
-                * The best example for the usage can be found in the DefaultViewer
-                *
-                * @param templateName the templateName to register the event to
-                * @param callback The callback to be executed
-                * @param eventType the type of event to register
-                * @param selector an optional selector. if not defined the parent object in the template will be selected
-                */
-            registerCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * This will remove a registered event from the defined template.
-                * Each one of the variables apart from the template name are optional, but one must be provided.
-                *
-                * @param templateName the templateName
-                * @param callback the callback to remove (optional)
-                * @param eventType the event type to remove (optional)
-                * @param selector the selector from which to remove the event (optional)
-                */
-            unregisterCallback(templateName: string, callback: (eventData: EventCallback) => void, eventType?: string, selector?: string): void;
-            /**
-                * Dispose the event manager
-                */
-            dispose(): void;
-    }
-}
-
 declare module 'babylonjs-viewer/labs/viewerLabs' {
     import { PBREnvironment } from "babylonjs-viewer/labs/environmentSerializer";
     import { ShadowLight, Vector3, Scene } from 'babylonjs';

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

@@ -82,6 +82,7 @@
 ### Materials Library
 
 ## Bug fixes
+- Fixed FileLoader's loading of a skybox, & added a parsed value for whether to create with PBR or STDMaterial ([Palmer-JC](https://github.com/Palmer-JC))
 - Removed bones from rootNodes where they should never have been ([Deltakosh](https://github.com/deltakosh))
 - Refocusing on input gui with pointer events ([TrevorDev](https://github.com/TrevorDev))
 - Gizmo scaling not consistent when camera is parented ([TrevorDev](https://github.com/TrevorDev))

+ 3 - 4
inspector/src/components/actionTabs/actionTabs.scss

@@ -293,12 +293,11 @@
 
                     button {
                         background: #222222;
-                        border: 0px;
+                        border: 1px solid rgb(51, 122, 183);
+                        margin: 5px 10px 5px 10px;
                         color:white;
                         padding: 4px 5px;
                         opacity: 0.9;
-                        width: 100%;
-                        height: 28px;
                     }
 
                     button:hover {
@@ -310,7 +309,7 @@
                     }   
                     
                     button:focus {
-                        border: 0px;
+                        border: 1px solid rgb(51, 122, 183);
                         outline: 0px;
                     }  
                 }

+ 5 - 0
inspector/src/components/actionTabs/lines/floatLineComponent.tsx

@@ -6,6 +6,7 @@ interface IFloatLineComponentProps {
     label: string,
     target: any,
     propertyName: string,
+    onChange?: (newValue: number) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>,
     additionalClass?: string
 }
@@ -37,6 +38,10 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
     }
 
     raiseOnPropertyChanged(newValue: number, previousValue: number) {
+        if (this.props.onChange) {
+            this.props.onChange(newValue);
+        }
+
         if (!this.props.onPropertyChangedObservable) {
             return;
         }

+ 41 - 13
inspector/src/components/actionTabs/lines/sliderLineComponent.tsx

@@ -4,11 +4,14 @@ import { PropertyChangedEvent } from "../../propertyChangedEvent";
 
 interface ISliderLineComponentProps {
     label: string,
-    target: any,
-    propertyName: string,
+    target?: any,
+    propertyName?: string,
     minimum: number,
     maximum: number,
     step: number,
+    directValue?: number,
+    onChange?: (value: number) => void,
+    onInput?: (value: number) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -17,11 +20,21 @@ export class SliderLineComponent extends React.Component<ISliderLineComponentPro
     constructor(props: ISliderLineComponentProps) {
         super(props);
 
-        this.state = { value: this.props.target[this.props.propertyName] };
+        if (this.props.directValue !== undefined) {
+            this.state = {
+                value: this.props.directValue
+            }
+        } else {
+            this.state = { value: this.props.target![this.props.propertyName!] };
+        }
     }
 
     shouldComponentUpdate(nextProps: ISliderLineComponentProps, nextState: { value: number }) {
-        const currentState = nextProps.target[nextProps.propertyName];
+        if (nextProps.directValue !== undefined) {
+            nextState.value = nextProps.directValue;
+            return true;
+        }
+        const currentState = nextProps.target![nextProps.propertyName!];
 
         if (currentState !== nextState.value || this._localChange) {
             nextState.value = currentState;
@@ -35,20 +48,33 @@ export class SliderLineComponent extends React.Component<ISliderLineComponentPro
         this._localChange = true;
         const newValue = parseFloat(newValueString);
 
-        if (this.props.onPropertyChangedObservable) {
-            this.props.onPropertyChangedObservable.notifyObservers({
-                object: this.props.target,
-                property: this.props.propertyName,
-                value: newValue,
-                initialValue: this.state.value
-            });
+        if (this.props.target) {
+            if (this.props.onPropertyChangedObservable) {
+                this.props.onPropertyChangedObservable.notifyObservers({
+                    object: this.props.target,
+                    property: this.props.propertyName!,
+                    value: newValue,
+                    initialValue: this.state.value
+                });
+            }
+
+            this.props.target[this.props.propertyName!] = newValue;
         }
 
-        this.props.target[this.props.propertyName] = newValue;
+        if (this.props.onChange) {
+            this.props.onChange(newValue);
+        }
 
         this.setState({ value: newValue });
     }
 
+    onInput(newValueString: any) {
+        const newValue = parseFloat(newValueString);
+        if (this.props.onInput) {
+            this.props.onInput(newValue);
+        }
+    }
+
     render() {
         return (
             <div className="sliderLine">
@@ -56,7 +82,9 @@ export class SliderLineComponent extends React.Component<ISliderLineComponentPro
                     {this.props.label}
                 </div>
                 <div className="slider">
-                    {this.state.value ? this.state.value.toFixed(2) : "0"}&nbsp;<input className="range" type="range" step={this.props.step} min={this.props.minimum} max={this.props.maximum} value={this.state.value} onChange={evt => this.onChange(evt.target.value)} />
+                    {this.state.value ? this.state.value.toFixed(2) : "0"}&nbsp;<input className="range" type="range" step={this.props.step} min={this.props.minimum} max={this.props.maximum} value={this.state.value}
+                        onInput={evt => this.onInput((evt.target as HTMLInputElement).value)}
+                        onChange={evt => this.onChange(evt.target.value)} />
                 </div>
             </div>
         );

+ 10 - 2
inspector/src/components/actionTabs/lines/vector3LineComponent.tsx

@@ -9,6 +9,7 @@ interface IVector3LineComponentProps {
     label: string,
     target: any,
     propertyName: string,
+    onChange?: (newvalue: Vector3) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -18,14 +19,14 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
     constructor(props: IVector3LineComponentProps) {
         super(props);
 
-        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName] }
+        this.state = { isExpanded: false, value: this.props.target[this.props.propertyName].clone() }
     }
 
     shouldComponentUpdate(nextProps: IVector3LineComponentProps, nextState: { isExpanded: boolean, value: Vector3 }) {
         const nextPropsValue = nextProps.target[nextProps.propertyName];
 
         if (!nextPropsValue.equals(nextState.value) || this._localChange) {
-            nextState.value = nextPropsValue;
+            nextState.value = nextPropsValue.clone();
             this._localChange = false;
             return true;
         }
@@ -38,6 +39,10 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
     }
 
     raiseOnPropertyChanged(previousValue: Vector3) {
+        if (this.props.onChange) {
+            this.props.onChange(this.state.value);
+        }
+
         if (!this.props.onPropertyChangedObservable) {
             return;
         }
@@ -53,6 +58,7 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
         this._localChange = true;
 
         const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].x = value;
         this.state.value.x = value;
         this.setState({ value: this.state.value });
 
@@ -63,6 +69,7 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
         this._localChange = true;
 
         const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].y = value;
         this.state.value.y = value;
         this.setState({ value: this.state.value });
 
@@ -73,6 +80,7 @@ export class Vector3LineComponent extends React.Component<IVector3LineComponentP
         this._localChange = true;
 
         const store = this.state.value.clone();
+        this.props.target[this.props.propertyName].z = value;
         this.state.value.z = value;
         this.setState({ value: this.state.value });
 

+ 6 - 1
inspector/src/components/actionTabs/tabs/debugTabComponent.tsx

@@ -83,7 +83,12 @@ export class DebugTabComponent extends PaneComponent {
 
             for (var mesh of scene.meshes) {
                 if (mesh.physicsImpostor) {
-                    physicsViewer.showImpostor(mesh.physicsImpostor);
+                    let debugMesh = physicsViewer.showImpostor(mesh.physicsImpostor);
+
+                    if (debugMesh) {
+                        debugMesh.metadata = { hidden: true };
+                        debugMesh.material!.metadata = { hidden: true };
+                    }
                 }
             }
         } else {

+ 20 - 1
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -1,6 +1,6 @@
 import * as React from "react";
 import { PaneComponent, IPaneComponentProps } from "../paneComponent";
-import { Mesh, TransformNode, Material, StandardMaterial, Texture, PBRMaterial, Scene, FreeCamera, ArcRotateCamera, HemisphericLight, PointLight, BackgroundMaterial } from "babylonjs";
+import { Mesh, TransformNode, Material, StandardMaterial, Texture, PBRMaterial, Scene, FreeCamera, ArcRotateCamera, HemisphericLight, PointLight, BackgroundMaterial, AnimationGroup } from "babylonjs";
 import { MaterialPropertyGridComponent } from "./propertyGrids/materials/materialPropertyGridComponent";
 import { StandardMaterialPropertyGridComponent } from "./propertyGrids/materials/standardMaterialPropertyGridComponent";
 import { TexturePropertyGridComponent } from "./propertyGrids/materials/texturePropertyGridComponent";
@@ -21,12 +21,23 @@ import { InputText } from "babylonjs-gui/2D/controls/inputText";
 import { InputTextPropertyGridComponent } from "./propertyGrids/gui/inputTextPropertyGridComponent";
 import { ColorPicker } from "babylonjs-gui";
 import { ColorPickerPropertyGridComponent } from "./propertyGrids/gui/colorPickerPropertyGridComponent";
+import { AnimationGroupGridComponent } from "./propertyGrids/animationGroupPropertyGridComponent";
 
 export class PropertyGridTabComponent extends PaneComponent {
+    private _timerIntervalId: number;
+
     constructor(props: IPaneComponentProps) {
         super(props);
     }
 
+    componentWillMount() {
+        this._timerIntervalId = window.setInterval(() => this.forceUpdate(), 500);
+    }
+
+    componentWillUnmount() {
+        window.clearInterval(this._timerIntervalId);
+    }
+
     render() {
         const entity = this.props.selectedEntity;
 
@@ -103,6 +114,14 @@ export class PropertyGridTabComponent extends PaneComponent {
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
+            if (className === "AnimationGroup") {
+                const animationGroup = entity as AnimationGroup;
+                return (<AnimationGroupGridComponent
+                    animationGroup={animationGroup}
+                    scene={this.props.scene}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
             if (className.indexOf("Material") !== -1) {
                 const material = entity as Material;
                 return (<MaterialPropertyGridComponent material={material} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);

+ 138 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animationGroupPropertyGridComponent.tsx

@@ -0,0 +1,138 @@
+import * as React from "react";
+import { Observable, AnimationGroup, Scene, Nullable, Observer } from "babylonjs";
+import { PropertyChangedEvent } from "../../../propertyChangedEvent";
+import { ButtonLineComponent } from "../../lines/buttonLineComponent";
+import { LineContainerComponent } from "../../lineContainerComponent";
+import { TextLineComponent } from "../../lines/textLineComponent";
+import { SliderLineComponent } from "../../lines/sliderLineComponent";
+
+interface IAnimationGroupGridComponentProps {
+    animationGroup: AnimationGroup,
+    scene: Scene,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class AnimationGroupGridComponent extends React.Component<IAnimationGroupGridComponentProps, { playButtonText: string, currentFrame: number }> {
+    private _onAnimationGroupPlayObserver: Nullable<Observer<AnimationGroup>>;
+    private _onAnimationGroupPauseObserver: Nullable<Observer<AnimationGroup>>;
+    private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
+
+    constructor(props: IAnimationGroupGridComponentProps) {
+        super(props);
+
+        const animationGroup = this.props.animationGroup;
+        this.state = { playButtonText: animationGroup.isPlaying ? "Pause" : "Play", currentFrame: 0 };
+    }
+
+    disconnect(animationGroup: AnimationGroup) {
+
+        if (this._onAnimationGroupPlayObserver) {
+            animationGroup.onAnimationGroupPlayObservable.remove(this._onAnimationGroupPlayObserver);
+            this._onAnimationGroupPlayObserver = null;
+        }
+
+        if (this._onAnimationGroupPauseObserver) {
+            animationGroup.onAnimationGroupPauseObservable.remove(this._onAnimationGroupPauseObserver);
+            this._onAnimationGroupPauseObserver = null;
+        }
+    }
+
+    connect(animationGroup: AnimationGroup) {
+        this._onAnimationGroupPlayObserver = animationGroup.onAnimationGroupPlayObservable.add(() => {
+            this.forceUpdate();
+        });
+
+        this._onAnimationGroupPauseObserver = animationGroup.onAnimationGroupPauseObservable.add(() => {
+            this.forceUpdate();
+        });
+
+        this.updateCurrentFrame(animationGroup);
+    }
+
+    updateCurrentFrame(animationGroup: AnimationGroup) {
+        var targetedAnimations = animationGroup.targetedAnimations;
+        if (targetedAnimations.length > 0) {
+            var runtimeAnimations = animationGroup.targetedAnimations[0].animation.runtimeAnimations;
+            if (runtimeAnimations.length > 0) {
+                this.setState({ currentFrame: runtimeAnimations[0].currentFrame });
+            } else {
+                this.setState({ currentFrame: 0 });
+            }
+        }
+    }
+
+    shouldComponentUpdate(nextProps: IAnimationGroupGridComponentProps): boolean {
+        if (this.props.animationGroup !== nextProps.animationGroup) {
+            this.disconnect(this.props.animationGroup);
+            this.connect(nextProps.animationGroup);
+        }
+        return true;
+    }
+
+    componentWillMount() {
+        this.connect(this.props.animationGroup);
+
+        this._onBeforeRenderObserver = this.props.scene.onBeforeRenderObservable.add(() => {
+            if (this.props.animationGroup.isPlaying) {
+                this.updateCurrentFrame(this.props.animationGroup);
+            }
+        });
+    }
+
+    componentWillUnmount() {
+        this.disconnect(this.props.animationGroup);
+
+        if (this._onBeforeRenderObserver) {
+            this.props.scene.onBeforeRenderObservable.remove(this._onBeforeRenderObserver);
+            this._onBeforeRenderObserver = null;
+        }
+    }
+
+    playOrPause() {
+        const animationGroup = this.props.animationGroup;
+
+        if (animationGroup.isPlaying) {
+            this.setState({ playButtonText: "Play" });
+            animationGroup.pause();
+        } else {
+            this.setState({ playButtonText: "Pause" });
+            this.props.scene.animationGroups.forEach(grp => grp.pause());
+            animationGroup.play(true);
+        }
+    }
+
+    onCurrentFrameChange(value: number) {
+        const animationGroup = this.props.animationGroup;
+
+        if (!animationGroup.isPlaying) {
+            animationGroup.play(true);
+            animationGroup.goToFrame(value);
+            animationGroup.pause();
+        } else {
+            animationGroup.goToFrame(value);
+        }
+
+        this.setState({ currentFrame: value });
+    }
+
+    render() {
+        const animationGroup = this.props.animationGroup;
+
+        const playButtonText = animationGroup.isPlaying ? "Pause" : "Play"
+
+        return (
+            <div className="pane">
+                <LineContainerComponent title="CONTROLS">
+                    <ButtonLineComponent label={playButtonText} onClick={() => this.playOrPause()} />
+                    <SliderLineComponent label="Speed ratio" minimum={0} maximum={10} step={0.1} target={animationGroup} propertyName="speedRatio" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent ref="timeline" label="Current frame" minimum={animationGroup.from} maximum={animationGroup.to} step={(animationGroup.to - animationGroup.from) / 100.0} directValue={this.state.currentFrame} onInput={value => this.onCurrentFrameChange(value)} />
+                </LineContainerComponent>
+                <LineContainerComponent title="INFOS">
+                    <TextLineComponent label="Animation count" value={animationGroup.targetedAnimations.length.toString()} />
+                    <TextLineComponent label="From" value={animationGroup.from.toFixed(2)} />
+                    <TextLineComponent label="To" value={animationGroup.to.toFixed(2)} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 0 - 10
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/arcRotateCameraPropertyGridComponent.tsx

@@ -14,20 +14,10 @@ interface IArcRotateCameraPropertyGridComponentProps {
 }
 
 export class ArcRotateCameraPropertyGridComponent extends React.Component<IArcRotateCameraPropertyGridComponentProps> {
-    private _timerIntervalId: number;
-
     constructor(props: IArcRotateCameraPropertyGridComponentProps) {
         super(props);
     }
 
-    componentWillMount() {
-        this._timerIntervalId = window.setInterval(() => this.forceUpdate(), 500);
-    }
-
-    componentWillUnmount() {
-        window.clearInterval(this._timerIntervalId);
-    }
-
     render() {
         const camera = this.props.camera;
 

+ 0 - 10
inspector/src/components/actionTabs/tabs/propertyGrids/cameras/freeCameraPropertyGridComponent.tsx

@@ -13,20 +13,10 @@ interface IFreeCameraPropertyGridComponentProps {
 }
 
 export class FreeCameraPropertyGridComponent extends React.Component<IFreeCameraPropertyGridComponentProps> {
-    private _timerIntervalId: number;
-
     constructor(props: IFreeCameraPropertyGridComponentProps) {
         super(props);
     }
 
-    componentWillMount() {
-        this._timerIntervalId = window.setInterval(() => this.forceUpdate(), 500);
-    }
-
-    componentWillUnmount() {
-        window.clearInterval(this._timerIntervalId);
-    }
-
     render() {
         const camera = this.props.camera;
 

+ 22 - 9
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -60,6 +60,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                     <FileButtonLineComponent label="Replace texture" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
                 </LineContainerComponent>
                 <LineContainerComponent title="GENERAL">
+                    <TextLineComponent label="Unique ID" value={texture.uniqueId.toString()} />
+                    <TextLineComponent label="Class" value={texture.getClassName()} />
                     <TextLineComponent label="Has alpha" value={texture.hasAlpha ? "Yes" : "No"} />
                     <TextLineComponent label="Is 3D" value={texture.is3D ? "Yes" : "No"} />
                     <TextLineComponent label="Is cube" value={texture.isCube ? "Yes" : "No"} />
@@ -83,15 +85,26 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                     </LineContainerComponent>
                 }
                 <LineContainerComponent title="TRANSFORM">
-                    <FloatLineComponent label="U offset" target={texture} propertyName="uOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V offset" target={texture} propertyName="vOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V scale" target={texture} propertyName="uScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V scale" target={texture} propertyName="vScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="U angle" target={texture} propertyName="uAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="V angle" target={texture} propertyName="vAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <FloatLineComponent label="W angle" target={texture} propertyName="wAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <CheckBoxLineComponent label="Clamp U" isSelected={() => texture.wrapU === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapU = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
-                    <CheckBoxLineComponent label="Clamp V" isSelected={() => texture.wrapV === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapV = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
+                    {
+                        !texture.isCube &&
+                        <div>
+                            <FloatLineComponent label="U offset" target={texture} propertyName="uOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent label="V offset" target={texture} propertyName="vOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent label="V scale" target={texture} propertyName="uScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent label="V scale" target={texture} propertyName="vScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent label="U angle" target={texture} propertyName="uAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent label="V angle" target={texture} propertyName="vAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <FloatLineComponent label="W angle" target={texture} propertyName="wAng" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Clamp U" isSelected={() => texture.wrapU === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapU = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
+                            <CheckBoxLineComponent label="Clamp V" isSelected={() => texture.wrapV === BABYLON.Texture.CLAMP_ADDRESSMODE} onSelect={(value) => texture.wrapV = value ? BABYLON.Texture.CLAMP_ADDRESSMODE : BABYLON.Texture.WRAP_ADDRESSMODE} />
+                        </div>
+                    }
+                    {
+                        texture.isCube &&
+                        <div>
+                            <SliderLineComponent label="Rotation Y" minimum={0} maximum={2 * Math.PI} step={0.1} target={texture} propertyName="rotationY" />
+                        </div>
+                    }
                 </LineContainerComponent>
             </div>
         );

+ 37 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/scenePropertyGridComponent.tsx

@@ -1,5 +1,5 @@
 import * as React from "react";
-import { Observable, Scene, BaseTexture, Nullable } from "babylonjs";
+import { Observable, Scene, BaseTexture, Nullable, Vector3 } from "babylonjs";
 import { PropertyChangedEvent } from "../../../propertyChangedEvent";
 import { LineContainerComponent } from "../../lineContainerComponent";
 import { RadioButtonLineComponent } from "../../lines/radioLineComponent";
@@ -8,6 +8,8 @@ import { CheckBoxLineComponent } from "../../lines/checkBoxLineComponent";
 import { FogPropertyGridComponent } from "./fogPropertyGridComponent";
 import { FileButtonLineComponent } from "../../lines/fileButtonLineComponent";
 import { TextureLinkLineComponent } from "../../lines/textureLinkLineComponent";
+import { Vector3LineComponent } from "../../lines/vector3LineComponent";
+import { FloatLineComponent } from "../../lines/floatLineComponent";
 
 interface IScenePropertyGridComponentProps {
     scene: Scene,
@@ -71,11 +73,35 @@ export class ScenePropertyGridComponent extends React.Component<IScenePropertyGr
         }, undefined, true);
     }
 
+    updateGravity(newValue: Vector3) {
+        const scene = this.props.scene;
+        const physicsEngine = scene.getPhysicsEngine()!;
+
+        physicsEngine.setGravity(newValue);
+    }
+
+    updateTimeStep(newValue: number) {
+        const scene = this.props.scene;
+        const physicsEngine = scene.getPhysicsEngine()!;
+
+        physicsEngine.setTimeStep(newValue);
+    }
+
     render() {
         const scene = this.props.scene;
 
         const renderingModeGroupObservable = new BABYLON.Observable<RadioButtonLineComponent>();
 
+        const physicsEngine = scene.getPhysicsEngine();
+        let dummy: Nullable<{ gravity: Vector3, timeStep: number }> = null;
+
+        if (physicsEngine) {
+            dummy = {
+                gravity: physicsEngine.gravity,
+                timeStep: physicsEngine.getTimeStep()
+            }
+        }
+
         return (
             <div className="pane">
                 <LineContainerComponent title="RENDERING MODE">
@@ -95,6 +121,16 @@ export class ScenePropertyGridComponent extends React.Component<IScenePropertyGr
                     <FileButtonLineComponent label="Update environment texture" onClick={(file) => this.updateEnvironmentTexture(file)} accept=".dds, .env" />
                     <FogPropertyGridComponent scene={scene} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
+                {
+                    dummy !== null &&
+                    <LineContainerComponent title="PHYSICS" closed={true}>
+                        <FloatLineComponent label="Time step" target={dummy} propertyName="timeStep" onChange={newValue => this.updateTimeStep(newValue)} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <Vector3LineComponent label="Gravity" target={dummy} propertyName="gravity" onChange={newValue => this.updateGravity(newValue)} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    </LineContainerComponent>
+                }
+                <LineContainerComponent title="COLLISIONS" closed={true}>
+                    <Vector3LineComponent label="Gravity" target={scene} propertyName="gravity" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
             </div>
         );
     }

+ 30 - 0
inspector/src/components/sceneExplorer/entities/animationGroupTreeItemComponent.tsx

@@ -0,0 +1,30 @@
+import { IExplorerExtensibilityGroup, AnimationGroup } from "babylonjs";
+import { faFilm } from '@fortawesome/free-solid-svg-icons';
+import { TreeItemLabelComponent } from "../treeItemLabelComponent";
+import { ExtensionsComponent } from "../extensionsComponent";
+import * as React from "react";
+
+interface IAnimationGroupItemComponentProps {
+    animationGroup: AnimationGroup,
+    extensibilityGroups?: IExplorerExtensibilityGroup[],
+    onClick: () => void
+}
+
+export class AnimationGroupItemComponent extends React.Component<IAnimationGroupItemComponentProps> {
+    constructor(props: IAnimationGroupItemComponentProps) {
+        super(props);
+    }
+
+
+    render() {
+        const animationGroup = this.props.animationGroup;
+        return (
+            <div className="animationGroupTools">
+                <TreeItemLabelComponent label={animationGroup.name} onClick={() => this.props.onClick()} icon={faFilm} color="cornflowerblue" />
+                {
+                    <ExtensionsComponent target={animationGroup} extensibilityGroups={this.props.extensibilityGroups} />
+                }
+            </div>
+        )
+    }
+}

+ 14 - 0
inspector/src/components/sceneExplorer/sceneExplorer.scss

@@ -309,6 +309,20 @@
             }
         }
 
+        .animationGroupTools {
+            grid-column: 2;
+            width: 100%;
+            display: grid;
+            grid-template-columns: 1fr auto 5px;
+            align-items: center;
+            min-width: 0;
+           
+            .extensions {
+                width: 20px;
+                grid-column: 2;
+            }
+        }
+
         .meshTools {
             grid-column: 2;
             width: 100%;

+ 4 - 0
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -179,6 +179,10 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                     guiElements && guiElements.length > 0 &&
                     <TreeItemComponent extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.state.selectedEntity} items={guiElements} label="GUI" offset={1} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} filter={this.state.filter} />
                 }
+                {
+                    scene.animationGroups.length > 0 &&
+                    <TreeItemComponent extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.state.selectedEntity} items={scene.animationGroups} label="Animation groups" offset={1} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} filter={this.state.filter} />
+                }
             </div>
         )
     }

+ 6 - 1
inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx

@@ -1,4 +1,4 @@
-import { AbstractMesh, Camera, Light, Material, Texture, TransformNode, IExplorerExtensibilityGroup, Observable } from "babylonjs";
+import { AbstractMesh, Camera, Light, Material, Texture, TransformNode, IExplorerExtensibilityGroup, Observable, AnimationGroup } from "babylonjs";
 import { MeshTreeItemComponent } from "./entities/meshTreeItemComponent";
 import { CameraTreeItemComponent } from "./entities/cameraTreeItemComponent";
 import { LightTreeItemComponent } from "./entities/lightTreeItemComponent";
@@ -11,6 +11,7 @@ import * as React from "react";
 import { ControlTreeItemComponent } from "./entities/gui/controlTreeItemComponent";
 import { Control, AdvancedDynamicTexture } from "babylonjs-gui";
 import { AdvancedDynamicTextureTreeItemComponent } from "./entities/gui/advancedDynamicTextureTreeItemComponent";
+import { AnimationGroupItemComponent } from "./entities/animationGroupTreeItemComponent";
 
 interface ITreeItemSpecializedComponentProps {
     label: string,
@@ -68,6 +69,10 @@ export class TreeItemSpecializedComponent extends React.Component<ITreeItemSpeci
                 return (<AdvancedDynamicTextureTreeItemComponent onSelectionChangedObservable={this.props.onSelectionChangedObservable} extensibilityGroups={this.props.extensibilityGroups} texture={entity as AdvancedDynamicTexture} onClick={() => this.onClick()} />);
             }
 
+            if (className === "AnimationGroup") {
+                return (<AnimationGroupItemComponent extensibilityGroups={this.props.extensibilityGroups} animationGroup={entity as AnimationGroup} onClick={() => this.onClick()} />);
+            }
+
             if (className.indexOf("Texture") !== -1) {
                 return (<TextureTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} texture={entity as Texture} onClick={() => this.onClick()} />);
             }

+ 38 - 25
sandbox/index.js

@@ -18,6 +18,7 @@ var playBtn = document.getElementById("playBtn");
 var slider = document.getElementById("slider");
 var footer = document.getElementById("footer");
 var canvas = document.getElementById("renderCanvas");
+var canvasZone = document.getElementById("canvasZone");
 
 var indexOf = location.href.indexOf("?");
 if (indexOf !== -1) {
@@ -45,7 +46,7 @@ if (indexOf !== -1) {
 
 if (kiosk) {
     footer.style.display = "none";
-    canvas.style.height = "100%";
+    canvasZone.style.height = "100%";
 }
 
 if (BABYLON.Engine.isSupported()) {
@@ -374,6 +375,27 @@ dropdownBtn.addEventListener("click", function() {
     }
 });
 
+function selectCurrentGroup(group, index, animation) {
+    if (currentGroupIndex !== undefined) {
+        document.getElementById(formatId(currentGroup.name + "-" + currentGroupIndex)).classList.remove("active");
+    }
+    playBtn.classList.remove("play");
+    playBtn.classList.add("pause");
+
+    // start the new animation group
+    currentGroup = group;
+    currentGroupIndex = index;
+    animation.classList.add("active");
+    dropdownLabel.innerHTML = currentGroup.name;
+    dropdownLabel.title = currentGroup.name;
+
+    // set the slider
+    slider.setAttribute("min", currentGroup.from);
+    slider.setAttribute("max", currentGroup.to);
+    currentSliderValue = currentGroup.from;
+    slider.value = currentGroup.from;
+}
+
 function createDropdownLink(group, index) {
     var animation = document.createElement("a");
     animation.innerHTML = group.name;
@@ -383,55 +405,46 @@ function createDropdownLink(group, index) {
         // stop the current animation group
         currentGroup.reset();
         currentGroup.stop();
-        document.getElementById(formatId(currentGroup.name + "-" + currentGroupIndex)).classList.remove("active");
-        playBtn.classList.remove("play");
-        playBtn.classList.add("pause");
-
-        // start the new animation group
-        currentGroup = group;
-        currentGroupIndex = index;
-        currentGroup.start(true);
-        this.classList.add("active");
-        dropdownLabel.innerHTML = currentGroup.name;
-        dropdownLabel.title = currentGroup.name;
-
-        // set the slider
-        slider.setAttribute("min", currentGroup.from);
-        slider.setAttribute("max", currentGroup.to);
-        currentSliderValue = currentGroup.from;
-        slider.value = currentGroup.from;
+
+        group.play(true);
 
         // hide the content of the dropdown
         displayDropdownContent(false);
     });
     dropdownContent.appendChild(animation);
+
+    group.onAnimationGroupPlayObservable.add(function(grp) {
+        selectCurrentGroup(grp, index, animation);
+    });
+
+    group.onAnimationGroupPauseObservable.add(function(grp) {
+        playBtn.classList.add("play");
+        playBtn.classList.remove("pause");
+    });
 }
 
 // event on the play/pause button
 playBtn.addEventListener("click", function() {
     // click on the button to run the animation
     if (this.classList.contains("play")) {
-        this.classList.remove("play");
-        this.classList.add("pause");
-        var currentFrame = slider.value;
         currentGroup.play(true);
     }
     // click on the button to pause the animation
     else {
-        this.classList.add("play");
-        this.classList.remove("pause");
         currentGroup.pause();
     }
 });
 
 // event on the slider
 slider.addEventListener("input", function() {
+    var value = parseFloat(this.value);
+
     if (playBtn.classList.contains("play")) {
         currentGroup.play(true);
-        currentGroup.goToFrame(this.value);
+        currentGroup.goToFrame(value);
         currentGroup.pause();
     } else {
-        currentGroup.goToFrame(this.value);
+        currentGroup.goToFrame(value);
     }
 });
 

+ 28 - 0
src/Animations/babylon.animationGroup.ts

@@ -24,9 +24,15 @@ module BABYLON {
         private _from = Number.MAX_VALUE;
         private _to = -Number.MAX_VALUE;
         private _isStarted: boolean;
+        private _isPaused: boolean;
         private _speedRatio = 1;
 
         /**
+         * Gets or sets the unique id of the node
+         */
+        public uniqueId: number;
+
+        /**
          * This observable will notify when one animation have ended.
          */
         public onAnimationEndObservable = new Observable<TargetedAnimation>();
@@ -42,6 +48,11 @@ module BABYLON {
         public onAnimationGroupPauseObservable = new Observable<AnimationGroup>();
 
         /**
+         * This observable will notify when all animations are playing.
+         */
+        public onAnimationGroupPlayObservable = new Observable<AnimationGroup>();
+
+        /**
          * Gets the first frame
          */
         public get from(): number {
@@ -63,6 +74,13 @@ module BABYLON {
         }
 
         /**
+         * Gets a value indicating that the current group is playing
+         */
+        public get isPlaying(): boolean {
+            return this._isStarted && !this._isPaused;
+        }
+
+        /**
          * Gets or sets the speed ratio to use for all animations
          */
         public get speedRatio(): number {
@@ -111,6 +129,7 @@ module BABYLON {
             public name: string,
             scene: Nullable<Scene> = null) {
             this._scene = scene || Engine.LastCreatedScene!;
+            this.uniqueId = this._scene.getUniqueId();
 
             this._scene.animationGroups.push(this);
         }
@@ -216,6 +235,9 @@ module BABYLON {
             }
 
             this._isStarted = true;
+            this._isPaused = false;
+
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
 
             return this;
         }
@@ -229,6 +251,8 @@ module BABYLON {
                 return this;
             }
 
+            this._isPaused = true;
+
             for (var index = 0; index < this._animatables.length; index++) {
                 let animatable = this._animatables[index];
                 animatable.pause();
@@ -260,6 +284,8 @@ module BABYLON {
                 this.start(loop, this._speedRatio);
             }
 
+            this._isPaused = false;
+
             return this;
         }
 
@@ -294,6 +320,8 @@ module BABYLON {
                 animatable.restart();
             }
 
+            this.onAnimationGroupPlayObservable.notifyObservers(this);
+
             return this;
         }
 

+ 5 - 3
src/Debug/babylon.physicsViewer.ts

@@ -62,16 +62,17 @@ module BABYLON.Debug {
         /**
          * Renders a specified physic impostor
          * @param impostor defines the impostor to render
+         * @returns the new debug mesh used to render the impostor
          */
-        public showImpostor(impostor: PhysicsImpostor): void {
+        public showImpostor(impostor: PhysicsImpostor): Nullable<AbstractMesh> {
 
             if (!this._scene) {
-                return;
+                return null;
             }
 
             for (var i = 0; i < this._numMeshes; i++) {
                 if (this._impostors[i] == impostor) {
-                    return;
+                    return null;
                 }
             }
 
@@ -89,6 +90,7 @@ module BABYLON.Debug {
                 this._numMeshes++;
             }
 
+            return debugMesh;
         }
 
         /**

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

@@ -4332,7 +4332,7 @@ module BABYLON {
                     loader!.loadData(data as ArrayBuffer, texture, (width: number, height: number, loadMipmap: boolean, isCompressed: boolean, done: () => void, loadFailed) => {
                         if (loadFailed) {
                             onInternalError("TextureLoader failed to load data");
-                        }else {
+                        } else {
                             this._prepareWebGLTexture(texture, scene, width, height, invertY, !loadMipmap, isCompressed, () => {
                                 done();
                                 return false;

+ 2 - 1
src/Loading/Plugins/babylon.babylonFileLoader.ts

@@ -662,7 +662,8 @@ module BABYLON {
                     if (parsedData.createDefaultSkybox === true) {
                         var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
                         var skyboxBlurLevel = parsedData.skyboxBlurLevel || 0;
-                        scene.createDefaultSkybox(undefined, true, skyboxScale, skyboxBlurLevel);
+                        var skyboxIsPBR = parsedData.skyboxIsPBR !== undefined ? parsedData.skyboxIsPBR : true;
+                        scene.createDefaultSkybox(scene.environmentTexture, skyboxIsPBR, skyboxScale, skyboxBlurLevel);
                     }
                 }
                 // Finish

File diff suppressed because it is too large
+ 33 - 17
src/Loading/babylon.sceneLoader.ts


+ 17 - 0
src/Materials/Textures/babylon.cubeTexture.ts

@@ -199,6 +199,14 @@ module BABYLON {
         }
 
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "CubeTexture"
+         */
+        public getClassName(): string {
+            return "CubeTexture";
+        }
+
+        /**
          * Update the url (and optional buffer) of this texture if url was null during construction.
          * @param url the url of the texture
          * @param forcedExtension defines the extension to use
@@ -207,6 +215,7 @@ module BABYLON {
         public updateURL(url: string, forcedExtension?: string, onLoad?: () => void): void {
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene()!.markAllMaterialsAsDirty(Material.TextureDirtyFlag);
             }
 
             this.url = url;
@@ -260,6 +269,14 @@ module BABYLON {
          * @param value Reflection texture matrix
          */
         public setReflectionTextureMatrix(value: Matrix): void {
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene()!.markAllMaterialsAsDirty(Material.TextureDirtyFlag, (mat) => mat.getActiveTextures().indexOf(this) !== -1);
+            }
+
             this._textureMatrix = value;
         }
 

+ 16 - 0
src/Materials/Textures/babylon.hdrCubeTexture.ts

@@ -133,6 +133,14 @@ module BABYLON {
         }
 
         /**
+         * Get the current class name of the texture useful for serialization or dynamic coding.
+         * @returns "HDRCubeTexture"
+         */
+        public getClassName(): string {
+            return "HDRCubeTexture";
+        }
+
+        /**
          * Occurs when the file is raw .hdr file.
          */
         private loadTexture() {
@@ -271,6 +279,14 @@ module BABYLON {
          */
         public setReflectionTextureMatrix(value: Matrix): void {
             this._textureMatrix = value;
+
+            if (value.updateFlag === this._textureMatrix.updateFlag) {
+                return;
+            }
+
+            if (value.isIdentity() !== this._textureMatrix.isIdentity()) {
+                this.getScene()!.markAllMaterialsAsDirty(Material.TextureDirtyFlag, (mat) => mat.getActiveTextures().indexOf(this) !== -1);
+            }
         }
 
         /**

+ 1 - 0
src/Materials/Textures/babylon.texture.ts

@@ -297,6 +297,7 @@ module BABYLON {
         public updateURL(url: string, buffer: Nullable<string | ArrayBuffer | HTMLImageElement | Blob> = null, onLoad?: () => void): void {
             if (this.url) {
                 this.releaseInternalTexture();
+                this.getScene()!.markAllMaterialsAsDirty(Material.TextureDirtyFlag);
             }
 
             this.url = url;

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

@@ -258,7 +258,7 @@ module BABYLON {
                     this._engine.stopRenderLoop();
                 }
 
-                SceneLoader.LoadAsync("file:", this._sceneFileToLoad.name, this._engine, (progress) => {
+                SceneLoader.LoadAsync("file:", this._sceneFileToLoad, this._engine, (progress) => {
                     if (this._progressCallback) {
                         this._progressCallback(progress);
                     }