Browse Source

Added state management to headers (inspector) + image processing

David Catuhe 6 years ago
parent
commit
39456d5194

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


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


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


+ 24 - 6
dist/preview release/babylon.max.js

@@ -4134,6 +4134,14 @@ var BABYLON;
             return this;
         };
         /**
+         * Determines equality between Color4 objects
+         * @param otherColor defines the second operand
+         * @returns true if the rgba values are equal to the given ones
+         */
+        Color4.prototype.equals = function (otherColor) {
+            return otherColor && this.r === otherColor.r && this.g === otherColor.g && this.b === otherColor.b && this.a === otherColor.a;
+        };
+        /**
          * Creates a new Color4 set with the added values of the current Color4 and of the given one
          * @param right defines the second operand
          * @returns a new Color4 object
@@ -66376,7 +66384,6 @@ var BABYLON;
              * @param scene defines the hosting scene
              * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
              * @param renderingGroupId defines the rendering group id to use with the viewer
-             * @param utilityLayerRenderer defines an optional utility layer to render the helper on
              */
             function SkeletonViewer(
             /** defines the skeleton to render */
@@ -66386,21 +66393,21 @@ var BABYLON;
             /** defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)  */
             autoUpdateBonesMatrices, 
             /** defines the rendering group id to use with the viewer */
-            renderingGroupId, 
-            /** defines an optional utility layer to render the helper on */
-            utilityLayerRenderer) {
+            renderingGroupId) {
                 if (autoUpdateBonesMatrices === void 0) { autoUpdateBonesMatrices = true; }
                 if (renderingGroupId === void 0) { renderingGroupId = 1; }
                 this.skeleton = skeleton;
                 this.mesh = mesh;
                 this.autoUpdateBonesMatrices = autoUpdateBonesMatrices;
                 this.renderingGroupId = renderingGroupId;
-                this.utilityLayerRenderer = utilityLayerRenderer;
                 /** Gets or sets the color used to render the skeleton */
                 this.color = BABYLON.Color3.White();
                 this._debugLines = new Array();
                 this._isEnabled = false;
                 this._scene = scene;
+                this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene, false);
+                this._utilityLayer.pickUtilitySceneFirst = false;
+                this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
                 this.update();
                 this._renderFunction = this.update.bind(this);
             }
@@ -66495,6 +66502,9 @@ var BABYLON;
             };
             /** Update the viewer to sync with current skeleton state */
             SkeletonViewer.prototype.update = function () {
+                if (!this._utilityLayer) {
+                    return;
+                }
                 if (this.autoUpdateBonesMatrices) {
                     this.skeleton.computeAbsoluteTransforms();
                 }
@@ -66504,7 +66514,7 @@ var BABYLON;
                 else {
                     this._getLinesForBonesWithLength(this.skeleton.bones, this.mesh.getWorldMatrix());
                 }
-                var targetScene = this.utilityLayerRenderer ? this.utilityLayerRenderer.utilityLayerScene : this._scene;
+                var targetScene = this._utilityLayer.utilityLayerScene;
                 if (!this._debugMesh) {
                     this._debugMesh = BABYLON.MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
                     this._debugMesh.renderingGroupId = this.renderingGroupId;
@@ -66517,11 +66527,16 @@ var BABYLON;
             };
             /** Release associated resources */
             SkeletonViewer.prototype.dispose = function () {
+                this.isEnabled = false;
                 if (this._debugMesh) {
                     this.isEnabled = false;
                     this._debugMesh.dispose();
                     this._debugMesh = null;
                 }
+                if (this._utilityLayer) {
+                    this._utilityLayer.dispose();
+                    this._utilityLayer = null;
+                }
             };
             return SkeletonViewer;
         }());
@@ -90579,6 +90594,9 @@ var BABYLON;
                 case ImageProcessingConfiguration.TONEMAPPING_ACES:
                     defines.TONEMAPPING_ACES = true;
                     break;
+                default:
+                    defines.TONEMAPPING_ACES = false;
+                    break;
             }
             defines.CONTRAST = (this.contrast !== 1.0);
             defines.EXPOSURE = (this.exposure !== 1.0);

+ 24 - 6
dist/preview release/babylon.no-module.max.js

@@ -4101,6 +4101,14 @@ var BABYLON;
             return this;
         };
         /**
+         * Determines equality between Color4 objects
+         * @param otherColor defines the second operand
+         * @returns true if the rgba values are equal to the given ones
+         */
+        Color4.prototype.equals = function (otherColor) {
+            return otherColor && this.r === otherColor.r && this.g === otherColor.g && this.b === otherColor.b && this.a === otherColor.a;
+        };
+        /**
          * Creates a new Color4 set with the added values of the current Color4 and of the given one
          * @param right defines the second operand
          * @returns a new Color4 object
@@ -66343,7 +66351,6 @@ var BABYLON;
              * @param scene defines the hosting scene
              * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
              * @param renderingGroupId defines the rendering group id to use with the viewer
-             * @param utilityLayerRenderer defines an optional utility layer to render the helper on
              */
             function SkeletonViewer(
             /** defines the skeleton to render */
@@ -66353,21 +66360,21 @@ var BABYLON;
             /** defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)  */
             autoUpdateBonesMatrices, 
             /** defines the rendering group id to use with the viewer */
-            renderingGroupId, 
-            /** defines an optional utility layer to render the helper on */
-            utilityLayerRenderer) {
+            renderingGroupId) {
                 if (autoUpdateBonesMatrices === void 0) { autoUpdateBonesMatrices = true; }
                 if (renderingGroupId === void 0) { renderingGroupId = 1; }
                 this.skeleton = skeleton;
                 this.mesh = mesh;
                 this.autoUpdateBonesMatrices = autoUpdateBonesMatrices;
                 this.renderingGroupId = renderingGroupId;
-                this.utilityLayerRenderer = utilityLayerRenderer;
                 /** Gets or sets the color used to render the skeleton */
                 this.color = BABYLON.Color3.White();
                 this._debugLines = new Array();
                 this._isEnabled = false;
                 this._scene = scene;
+                this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene, false);
+                this._utilityLayer.pickUtilitySceneFirst = false;
+                this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
                 this.update();
                 this._renderFunction = this.update.bind(this);
             }
@@ -66462,6 +66469,9 @@ var BABYLON;
             };
             /** Update the viewer to sync with current skeleton state */
             SkeletonViewer.prototype.update = function () {
+                if (!this._utilityLayer) {
+                    return;
+                }
                 if (this.autoUpdateBonesMatrices) {
                     this.skeleton.computeAbsoluteTransforms();
                 }
@@ -66471,7 +66481,7 @@ var BABYLON;
                 else {
                     this._getLinesForBonesWithLength(this.skeleton.bones, this.mesh.getWorldMatrix());
                 }
-                var targetScene = this.utilityLayerRenderer ? this.utilityLayerRenderer.utilityLayerScene : this._scene;
+                var targetScene = this._utilityLayer.utilityLayerScene;
                 if (!this._debugMesh) {
                     this._debugMesh = BABYLON.MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
                     this._debugMesh.renderingGroupId = this.renderingGroupId;
@@ -66484,11 +66494,16 @@ var BABYLON;
             };
             /** Release associated resources */
             SkeletonViewer.prototype.dispose = function () {
+                this.isEnabled = false;
                 if (this._debugMesh) {
                     this.isEnabled = false;
                     this._debugMesh.dispose();
                     this._debugMesh = null;
                 }
+                if (this._utilityLayer) {
+                    this._utilityLayer.dispose();
+                    this._utilityLayer = null;
+                }
             };
             return SkeletonViewer;
         }());
@@ -90546,6 +90561,9 @@ var BABYLON;
                 case ImageProcessingConfiguration.TONEMAPPING_ACES:
                     defines.TONEMAPPING_ACES = true;
                     break;
+                default:
+                    defines.TONEMAPPING_ACES = false;
+                    break;
             }
             defines.CONTRAST = (this.contrast !== 1.0);
             defines.EXPOSURE = (this.exposure !== 1.0);

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


+ 24 - 6
dist/preview release/es6.js

@@ -4101,6 +4101,14 @@ var BABYLON;
             return this;
         };
         /**
+         * Determines equality between Color4 objects
+         * @param otherColor defines the second operand
+         * @returns true if the rgba values are equal to the given ones
+         */
+        Color4.prototype.equals = function (otherColor) {
+            return otherColor && this.r === otherColor.r && this.g === otherColor.g && this.b === otherColor.b && this.a === otherColor.a;
+        };
+        /**
          * Creates a new Color4 set with the added values of the current Color4 and of the given one
          * @param right defines the second operand
          * @returns a new Color4 object
@@ -66343,7 +66351,6 @@ var BABYLON;
              * @param scene defines the hosting scene
              * @param autoUpdateBonesMatrices defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)
              * @param renderingGroupId defines the rendering group id to use with the viewer
-             * @param utilityLayerRenderer defines an optional utility layer to render the helper on
              */
             function SkeletonViewer(
             /** defines the skeleton to render */
@@ -66353,21 +66360,21 @@ var BABYLON;
             /** defines a boolean indicating if bones matrices must be forced to update before rendering (true by default)  */
             autoUpdateBonesMatrices, 
             /** defines the rendering group id to use with the viewer */
-            renderingGroupId, 
-            /** defines an optional utility layer to render the helper on */
-            utilityLayerRenderer) {
+            renderingGroupId) {
                 if (autoUpdateBonesMatrices === void 0) { autoUpdateBonesMatrices = true; }
                 if (renderingGroupId === void 0) { renderingGroupId = 1; }
                 this.skeleton = skeleton;
                 this.mesh = mesh;
                 this.autoUpdateBonesMatrices = autoUpdateBonesMatrices;
                 this.renderingGroupId = renderingGroupId;
-                this.utilityLayerRenderer = utilityLayerRenderer;
                 /** Gets or sets the color used to render the skeleton */
                 this.color = BABYLON.Color3.White();
                 this._debugLines = new Array();
                 this._isEnabled = false;
                 this._scene = scene;
+                this._utilityLayer = new BABYLON.UtilityLayerRenderer(this._scene, false);
+                this._utilityLayer.pickUtilitySceneFirst = false;
+                this._utilityLayer.utilityLayerScene.autoClearDepthAndStencil = true;
                 this.update();
                 this._renderFunction = this.update.bind(this);
             }
@@ -66462,6 +66469,9 @@ var BABYLON;
             };
             /** Update the viewer to sync with current skeleton state */
             SkeletonViewer.prototype.update = function () {
+                if (!this._utilityLayer) {
+                    return;
+                }
                 if (this.autoUpdateBonesMatrices) {
                     this.skeleton.computeAbsoluteTransforms();
                 }
@@ -66471,7 +66481,7 @@ var BABYLON;
                 else {
                     this._getLinesForBonesWithLength(this.skeleton.bones, this.mesh.getWorldMatrix());
                 }
-                var targetScene = this.utilityLayerRenderer ? this.utilityLayerRenderer.utilityLayerScene : this._scene;
+                var targetScene = this._utilityLayer.utilityLayerScene;
                 if (!this._debugMesh) {
                     this._debugMesh = BABYLON.MeshBuilder.CreateLineSystem("", { lines: this._debugLines, updatable: true, instance: null }, targetScene);
                     this._debugMesh.renderingGroupId = this.renderingGroupId;
@@ -66484,11 +66494,16 @@ var BABYLON;
             };
             /** Release associated resources */
             SkeletonViewer.prototype.dispose = function () {
+                this.isEnabled = false;
                 if (this._debugMesh) {
                     this.isEnabled = false;
                     this._debugMesh.dispose();
                     this._debugMesh = null;
                 }
+                if (this._utilityLayer) {
+                    this._utilityLayer.dispose();
+                    this._utilityLayer = null;
+                }
             };
             return SkeletonViewer;
         }());
@@ -90546,6 +90561,9 @@ var BABYLON;
                 case ImageProcessingConfiguration.TONEMAPPING_ACES:
                     defines.TONEMAPPING_ACES = true;
                     break;
+                default:
+                    defines.TONEMAPPING_ACES = false;
+                    break;
             }
             defines.CONTRAST = (this.contrast !== 1.0);
             defines.EXPOSURE = (this.exposure !== 1.0);

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


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


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


File diff suppressed because it is too large
+ 2 - 2
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


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

@@ -70,6 +70,57 @@ declare module BabylonViewer {
     export let viewerGlobals: ViewerGlobals;
 }
 declare module BabylonViewer {
+    /**
+        * The viewer manager is the container for all viewers currently registered on this page.
+        * It is possible to have more than one viewer on a single page.
+        */
+    export class ViewerManager {
+            /**
+                * A callback that will be triggered when a new viewer was added
+                */
+            onViewerAdded: (viewer: AbstractViewer) => void;
+            /**
+                * Will notify when a new viewer was added
+                */
+            onViewerAddedObservable: BABYLON.Observable<AbstractViewer>;
+            /**
+                * Will notify when a viewer was removed (disposed)
+                */
+            onViewerRemovedObservable: BABYLON.Observable<string>;
+            constructor();
+            /**
+                * Adding a new viewer to the viewer manager and start tracking it.
+                * @param viewer the viewer to add
+                */
+            addViewer(viewer: AbstractViewer): void;
+            /**
+                * remove a viewer from the viewer manager
+                * @param viewer the viewer to remove
+                */
+            removeViewer(viewer: AbstractViewer): void;
+            /**
+                * Get a viewer by its baseId (if the container element has an ID, it is the this is. if not, a random id was assigned)
+                * @param id the id of the HTMl element (or the viewer's, if none provided)
+                */
+            getViewerById(id: string): AbstractViewer;
+            /**
+                * Get a viewer using a container element
+                * @param element the HTML element to search viewers associated with
+                */
+            getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
+            /**
+                * Get a promise that will fullfil when this viewer was initialized.
+                * Since viewer initialization and template injection is asynchronous, using the promise will guaranty that
+                * you will get the viewer after everything was already configured.
+                * @param id the viewer id to find
+                */
+            getViewerPromiseById(id: string): Promise<AbstractViewer>;
+            /**
+                * dispose the manager and all of its associated viewers
+                */
+            dispose(): void;
+    }
+    export let viewerManager: ViewerManager;
 }
 declare module BabylonViewer {
     /**
@@ -117,11 +168,11 @@ declare module BabylonViewer {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
+            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<Template> | Promise<string>;
+            hideOverlayScreen(): Promise<string> | Promise<Template>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -138,11 +189,11 @@ declare module BabylonViewer {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<Template> | Promise<string>;
+            showLoadingScreen(): Promise<string> | Promise<Template>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<Template> | Promise<string>;
+            hideLoadingScreen(): Promise<string> | Promise<Template>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -873,7 +924,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): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
@@ -1507,20 +1558,6 @@ 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 {

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


+ 58 - 23
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -94,7 +94,59 @@ declare module 'babylonjs-viewer/configuration/globals' {
 }
 
 declare module 'babylonjs-viewer/viewer/viewerManager' {
-    
+    import { Observable } from 'babylonjs';
+    import { AbstractViewer } from 'babylonjs-viewer/viewer/viewer';
+    /**
+        * The viewer manager is the container for all viewers currently registered on this page.
+        * It is possible to have more than one viewer on a single page.
+        */
+    export class ViewerManager {
+            /**
+                * A callback that will be triggered when a new viewer was added
+                */
+            onViewerAdded: (viewer: AbstractViewer) => void;
+            /**
+                * Will notify when a new viewer was added
+                */
+            onViewerAddedObservable: Observable<AbstractViewer>;
+            /**
+                * Will notify when a viewer was removed (disposed)
+                */
+            onViewerRemovedObservable: Observable<string>;
+            constructor();
+            /**
+                * Adding a new viewer to the viewer manager and start tracking it.
+                * @param viewer the viewer to add
+                */
+            addViewer(viewer: AbstractViewer): void;
+            /**
+                * remove a viewer from the viewer manager
+                * @param viewer the viewer to remove
+                */
+            removeViewer(viewer: AbstractViewer): void;
+            /**
+                * Get a viewer by its baseId (if the container element has an ID, it is the this is. if not, a random id was assigned)
+                * @param id the id of the HTMl element (or the viewer's, if none provided)
+                */
+            getViewerById(id: string): AbstractViewer;
+            /**
+                * Get a viewer using a container element
+                * @param element the HTML element to search viewers associated with
+                */
+            getViewerByHTMLElement(element: HTMLElement): AbstractViewer | undefined;
+            /**
+                * Get a promise that will fullfil when this viewer was initialized.
+                * Since viewer initialization and template injection is asynchronous, using the promise will guaranty that
+                * you will get the viewer after everything was already configured.
+                * @param id the viewer id to find
+                */
+            getViewerPromiseById(id: string): Promise<AbstractViewer>;
+            /**
+                * dispose the manager and all of its associated viewers
+                */
+            dispose(): void;
+    }
+    export let viewerManager: ViewerManager;
 }
 
 declare module 'babylonjs-viewer/viewer/defaultViewer' {
@@ -148,11 +200,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Mainly used for help and errors
                 * @param subScreen the name of the subScreen. Those can be defined in the configuration object
                 */
-            showOverlayScreen(subScreen: string): Promise<Template> | Promise<string>;
+            showOverlayScreen(subScreen: string): Promise<string> | Promise<Template>;
             /**
                 * Hide the overlay screen.
                 */
-            hideOverlayScreen(): Promise<Template> | Promise<string>;
+            hideOverlayScreen(): Promise<string> | Promise<Template>;
             /**
                 * show the viewer (in case it was hidden)
                 *
@@ -169,11 +221,11 @@ declare module 'babylonjs-viewer/viewer/defaultViewer' {
                 * Show the loading screen.
                 * The loading screen can be configured using the configuration object
                 */
-            showLoadingScreen(): Promise<Template> | Promise<string>;
+            showLoadingScreen(): Promise<string> | Promise<Template>;
             /**
                 * Hide the loading screen
                 */
-            hideLoadingScreen(): Promise<Template> | Promise<string>;
+            hideLoadingScreen(): Promise<string> | Promise<Template>;
             dispose(): void;
             protected _onConfigurationLoaded(configuration: ViewerConfiguration): void;
     }
@@ -933,14 +985,13 @@ 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): typeof extendedUpgrade;
+    export function getCustomOptimizerByName(name: string, upgrade?: boolean): (sceneManager: SceneManager) => boolean;
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 
@@ -1611,22 +1662,6 @@ 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';

+ 1 - 1
inspector/src/components/actionTabs/actionTabs.scss

@@ -564,7 +564,7 @@
                     }                    
 
                     .command {
-                        border: 0px;
+                        border: 1px solid transparent;
                         background:transparent;
                         color: white;
                     }

+ 12 - 1
inspector/src/components/actionTabs/lineContainerComponent.tsx

@@ -12,10 +12,21 @@ export class LineContainerComponent extends React.Component<ILineContainerCompon
     constructor(props: ILineContainerComponentProps) {
         super(props);
 
-        this.state = { isExpanded: !this.props.closed };
+        let initialState: boolean;
+
+        if (typeof (Storage) !== "undefined" && localStorage.getItem(this.props.title) !== null) {
+            initialState = localStorage.getItem(this.props.title) === "true";
+        } else {
+            initialState = !this.props.closed;
+        }
+
+        this.state = { isExpanded: initialState };
     }
 
     switchExpandedState(): void {
+        if (typeof (Storage) !== "undefined") {
+            localStorage.setItem(this.props.title, !this.state.isExpanded ? "true" : "false");
+        }
         this.setState({ isExpanded: !this.state.isExpanded });
     }
 

+ 1 - 3
inspector/src/components/actionTabs/lines/optionsLineComponent.tsx

@@ -71,8 +71,6 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
     }
 
     render() {
-        var currentValue = this.props.target[this.props.propertyName];
-
         return (
             <div className="listLine">
                 <div className="label">
@@ -80,7 +78,7 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
 
                 </div>
                 <div className="options">
-                    <select onChange={evt => this.updateValue(evt.target.value)} defaultValue={currentValue}>
+                    <select onChange={evt => this.updateValue(evt.target.value)} value={this.state.value}>
                         {
                             this.props.options.map(option => {
                                 return (

+ 1 - 0
inspector/src/components/actionTabs/lines/radioLineComponent.tsx

@@ -26,6 +26,7 @@ export class RadioButtonLineComponent extends React.Component<IRadioButtonLineCo
     componentWillUnmount() {
         if (this._onSelectionChangedObserver) {
             this.props.onSelectionChangedObservable.remove(this._onSelectionChangedObserver);
+            this._onSelectionChangedObserver = null;
         }
     }
 

+ 19 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/scenePropertyGridComponent.tsx

@@ -10,6 +10,8 @@ import { FileButtonLineComponent } from "../../lines/fileButtonLineComponent";
 import { TextureLinkLineComponent } from "../../lines/textureLinkLineComponent";
 import { Vector3LineComponent } from "../../lines/vector3LineComponent";
 import { FloatLineComponent } from "../../lines/floatLineComponent";
+import { SliderLineComponent } from "../../lines/sliderLineComponent";
+import { OptionsLineComponent } from "../../lines/optionsLineComponent";
 
 interface IScenePropertyGridComponentProps {
     scene: Scene,
@@ -19,6 +21,7 @@ interface IScenePropertyGridComponentProps {
 
 export class ScenePropertyGridComponent extends React.Component<IScenePropertyGridComponentProps> {
     private _storedEnvironmentTexture: Nullable<BaseTexture>;
+    private _renderingModeGroupObservable = new BABYLON.Observable<RadioButtonLineComponent>();
 
     constructor(props: IScenePropertyGridComponentProps) {
         super(props);
@@ -90,7 +93,6 @@ export class ScenePropertyGridComponent extends React.Component<IScenePropertyGr
     render() {
         const scene = this.props.scene;
 
-        const renderingModeGroupObservable = new BABYLON.Observable<RadioButtonLineComponent>();
 
         const physicsEngine = scene.getPhysicsEngine();
         let dummy: Nullable<{ gravity: Vector3, timeStep: number }> = null;
@@ -102,12 +104,19 @@ export class ScenePropertyGridComponent extends React.Component<IScenePropertyGr
             }
         }
 
+        const imageProcessing = scene.imageProcessingConfiguration;
+
+        var toneMappingOptions = [
+            { label: "Standard", value: BABYLON.ImageProcessingConfiguration.TONEMAPPING_STANDARD },
+            { label: "ACES", value: BABYLON.ImageProcessingConfiguration.TONEMAPPING_ACES }
+        ]
+
         return (
             <div className="pane">
                 <LineContainerComponent title="RENDERING MODE">
-                    <RadioButtonLineComponent onSelectionChangedObservable={renderingModeGroupObservable} label="Point" isSelected={() => scene.forcePointsCloud} onSelect={() => this.setRenderingModes(true, false)} />
-                    <RadioButtonLineComponent onSelectionChangedObservable={renderingModeGroupObservable} label="Wireframe" isSelected={() => scene.forceWireframe} onSelect={() => this.setRenderingModes(false, true)} />
-                    <RadioButtonLineComponent onSelectionChangedObservable={renderingModeGroupObservable} label="Solid" isSelected={() => !scene.forcePointsCloud && !scene.forceWireframe} onSelect={() => this.setRenderingModes(false, false)} />
+                    <RadioButtonLineComponent onSelectionChangedObservable={this._renderingModeGroupObservable} label="Point" isSelected={() => scene.forcePointsCloud} onSelect={() => this.setRenderingModes(true, false)} />
+                    <RadioButtonLineComponent onSelectionChangedObservable={this._renderingModeGroupObservable} label="Wireframe" isSelected={() => scene.forceWireframe} onSelect={() => this.setRenderingModes(false, true)} />
+                    <RadioButtonLineComponent onSelectionChangedObservable={this._renderingModeGroupObservable} label="Solid" isSelected={() => !scene.forcePointsCloud && !scene.forceWireframe} onSelect={() => this.setRenderingModes(false, false)} />
                 </LineContainerComponent>
                 <LineContainerComponent title="ENVIRONMENT">
                     <Color3LineComponent label="Clear color" target={scene} propertyName="clearColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -121,6 +130,12 @@ 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>
+                <LineContainerComponent title="IMAGE PROCESSING">
+                    <SliderLineComponent minimum={0} maximum={4} step={0.1} label="Contrast" target={imageProcessing} propertyName="contrast" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent minimum={0} maximum={4} step={0.1} label="Exposure" target={imageProcessing} propertyName="exposure" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Tone mapping" target={imageProcessing} propertyName="toneMappingEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Tone mapping type" options={toneMappingOptions} target={imageProcessing} propertyName="toneMappingType" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => this.setState({ mode: value })} />
+                </LineContainerComponent>
                 {
                     dummy !== null &&
                     <LineContainerComponent title="PHYSICS" closed={true}>

+ 7 - 3
inspector/src/components/sceneExplorer/entities/cameraTreeItemComponent.tsx

@@ -57,13 +57,17 @@ export class CameraTreeItemComponent extends React.Component<ICameraTreeItemComp
 
     render() {
         const isActiveElement = this.state.isActive ? <FontAwesomeIcon icon={faVideo} /> : <FontAwesomeIcon icon={faVideo} className="isNotActive" />;
+        const scene = this.props.camera.getScene()!;
 
         return (
             <div className="cameraTools">
                 <TreeItemLabelComponent label={this.props.camera.name} onClick={() => this.props.onClick()} icon={faCamera} color="green" />
-                <div className="activeCamera icon" onClick={() => this.setActive()} title="Set as main camera">
-                    {isActiveElement}
-                </div>
+                {
+                    (!scene.activeCameras || scene.activeCameras.length === 0) &&
+                    <div className="activeCamera icon" onClick={() => this.setActive()} title="Set as main camera">
+                        {isActiveElement}
+                    </div>
+                }
                 <ExtensionsComponent target={this.props.camera} extensibilityGroups={this.props.extensibilityGroups} />
             </div>
         )

+ 3 - 0
src/Materials/babylon.imageProcessingConfiguration.ts

@@ -453,6 +453,9 @@ module BABYLON {
                 case ImageProcessingConfiguration.TONEMAPPING_ACES:
                     defines.TONEMAPPING_ACES = true;
                     break;
+                default:
+                    defines.TONEMAPPING_ACES = false;
+                    break;
             }
 
             defines.CONTRAST = (this.contrast !== 1.0);

+ 20 - 11
src/Math/babylon.math.ts

@@ -539,6 +539,15 @@ module BABYLON {
         }
 
         /**
+         * Determines equality between Color4 objects
+         * @param otherColor defines the second operand
+         * @returns true if the rgba values are equal to the given ones
+         */
+        public equals(otherColor: DeepImmutable<Color4>): boolean {
+            return otherColor && this.r === otherColor.r && this.g === otherColor.g && this.b === otherColor.b && this.a === otherColor.a;
+        }
+
+        /**
          * Creates a new Color4 set with the added values of the current Color4 and of the given one
          * @param right defines the second operand
          * @returns a new Color4 object
@@ -2624,17 +2633,17 @@ module BABYLON {
             Vector3.UnprojectFromInvertedMatrixToRef(screenSource, matrix, result);
         }
 
-       /**
-         * Unproject a ray from screen space to object space
-         * @param sourceX defines the screen space x coordinate to use
-         * @param sourceY defines the screen space y coordinate to use
-         * @param viewportWidth defines the current width of the viewport
-         * @param viewportHeight defines the current height of the viewport
-         * @param world defines the world matrix to use (can be set to Identity to go to world space)
-         * @param view defines the view matrix to use
-         * @param projection defines the projection matrix to use
-         * @param ray defines the Ray where to store the result
-         */
+        /**
+          * Unproject a ray from screen space to object space
+          * @param sourceX defines the screen space x coordinate to use
+          * @param sourceY defines the screen space y coordinate to use
+          * @param viewportWidth defines the current width of the viewport
+          * @param viewportHeight defines the current height of the viewport
+          * @param world defines the world matrix to use (can be set to Identity to go to world space)
+          * @param view defines the view matrix to use
+          * @param projection defines the projection matrix to use
+          * @param ray defines the Ray where to store the result
+          */
         public static UnprojectRayToRef(sourceX: float, sourceY: float, viewportWidth: number, viewportHeight: number, world: DeepImmutable<Matrix>, view: DeepImmutable<Matrix>, projection: DeepImmutable<Matrix>, ray: Ray): void {
             var matrix = MathTmp.Matrix[0];
             world.multiplyToRef(view, matrix);