Browse Source

Merge branch 'master' into master

Michael Smith 5 years ago
parent
commit
8622ac06e9
77 changed files with 2802 additions and 932 deletions
  1. 11 11
      dist/preview release/ammo.js
  2. 1 1
      dist/preview release/ammo.wasm.js
  3. BIN
      dist/preview release/ammo.wasm.wasm
  4. 130 1
      dist/preview release/babylon.d.ts
  5. 2 2
      dist/preview release/babylon.js
  6. 369 144
      dist/preview release/babylon.max.js
  7. 1 1
      dist/preview release/babylon.max.js.map
  8. 261 2
      dist/preview release/babylon.module.d.ts
  9. 130 1
      dist/preview release/documentation.d.ts
  10. 48 48
      dist/preview release/gui/babylon.gui.js
  11. 1 1
      dist/preview release/gui/babylon.gui.js.map
  12. 2 2
      dist/preview release/inspector/babylon.inspector.bundle.js
  13. 47 62
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  14. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  15. 30 10
      dist/preview release/inspector/babylon.inspector.d.ts
  16. 66 20
      dist/preview release/inspector/babylon.inspector.module.d.ts
  17. 27 13
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  18. 20 6
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  19. 389 215
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  20. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  21. 54 26
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  22. 1 1
      dist/preview release/packagesSizeBaseLine.json
  23. 11 11
      dist/preview release/serializers/babylon.glTF2Serializer.js
  24. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  25. 4 4
      dist/preview release/serializers/babylon.objSerializer.js
  26. 1 1
      dist/preview release/serializers/babylon.objSerializer.js.map
  27. 13 13
      dist/preview release/serializers/babylonjs.serializers.js
  28. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  29. 261 2
      dist/preview release/viewer/babylon.module.d.ts
  30. 27 23
      dist/preview release/viewer/babylon.viewer.js
  31. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  32. 12 0
      dist/preview release/what's new.md
  33. 4 33
      inspector/src/components/actionTabs/lineContainerComponent.tsx
  34. 4 1
      inspector/src/components/actionTabs/lines/fileButtonLineComponent.tsx
  35. 4 1
      inspector/src/components/actionTabs/lines/fileMultipleButtonLineComponent.tsx
  36. 6 2
      inspector/src/components/actionTabs/lines/textureLineComponent.tsx
  37. 5 2
      inspector/src/components/actionTabs/tabs/propertyGrids/animationGroupPropertyGridComponent.tsx
  38. 5 1
      inspector/src/components/actionTabs/tabs/propertyGrids/animationPropertyGridComponent.tsx
  39. 6 2
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  40. 40 0
      inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx
  41. 14 7
      inspector/src/components/embedHost/embedHostComponent.tsx
  42. 5 5
      inspector/src/components/globalState.ts
  43. 3 1
      inspector/src/components/sceneExplorer/extensionsComponent.tsx
  44. 20 2
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  45. 0 14
      inspector/src/tools.ts
  46. 6 6
      nodeEditor/src/components/preview/previewAreaComponent.tsx
  47. 9 7
      nodeEditor/src/components/preview/previewMeshControlComponent.tsx
  48. 4 4
      nodeEditor/src/components/propertyTab/propertyTabComponent.tsx
  49. 0 65
      nodeEditor/src/dataStorage.ts
  50. 74 0
      nodeEditor/src/diagram/graphCanvas.scss
  51. 1 1
      nodeEditor/src/diagram/graphCanvas.tsx
  52. 317 44
      nodeEditor/src/diagram/graphFrame.ts
  53. 3 3
      nodeEditor/src/globalState.ts
  54. 3 3
      nodeEditor/src/graphEditor.tsx
  55. 1 1
      nodeEditor/src/serializationTools.ts
  56. 5 1
      nodeEditor/src/sharedComponents/fileButtonLineComponent.tsx
  57. 2 2
      nodeEditor/src/sharedComponents/lineContainerComponent.tsx
  58. 11 7
      nodeEditor/src/sharedComponents/textureLineComponent.tsx
  59. 21 1
      src/Audio/audioEngine.ts
  60. 4 0
      src/Audio/sound.ts
  61. 6 0
      src/Engines/IPipelineContext.ts
  62. 8 0
      src/Engines/WebGL/webGLPipelineContext.ts
  63. 8 0
      src/Engines/nativeEngine.ts
  64. 6 2
      src/Engines/thinEngine.ts
  65. 22 18
      src/Layers/effectLayer.ts
  66. 2 2
      src/Layers/glowLayer.ts
  67. 18 13
      src/Lights/Shadows/shadowGenerator.ts
  68. 3 1
      src/Materials/Textures/renderTargetTexture.ts
  69. 46 0
      src/Materials/effect.ts
  70. 16 1
      src/Meshes/meshSimplification.ts
  71. 91 0
      src/Misc/dataStorage.ts
  72. 1 0
      src/Misc/index.ts
  73. 6 0
      src/Particles/IParticleSystem.ts
  74. 17 13
      src/PostProcesses/volumetricLightScatteringPostProcess.ts
  75. 13 9
      src/Rendering/depthRenderer.ts
  76. 25 21
      src/Rendering/geometryBufferRenderer.ts
  77. 13 10
      src/Rendering/outlineRenderer.ts

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


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


BIN
dist/preview release/ammo.wasm.wasm


+ 130 - 1
dist/preview release/babylon.d.ts

@@ -1078,6 +1078,10 @@ declare module BABYLON {
          */
         isReady: boolean;
         /** @hidden */
+        _getVertexShaderCode(): string | null;
+        /** @hidden */
+        _getFragmentShaderCode(): string | null;
+        /** @hidden */
         _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
     }
 }
@@ -7155,6 +7159,8 @@ declare module BABYLON {
         get isAsync(): boolean;
         get isReady(): boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
+        _getVertexShaderCode(): string | null;
+        _getFragmentShaderCode(): string | null;
     }
 }
 declare module BABYLON {
@@ -13154,6 +13160,11 @@ declare module BABYLON {
          */
         isReady(): boolean;
         /**
+         * Returns the string "ParticleSystem"
+         * @returns a string containing the class name
+         */
+        getClassName(): string;
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color1 defines the color to affect to the specified gradient
@@ -30002,6 +30013,10 @@ declare module BABYLON {
          */
         static ShadersRepository: string;
         /**
+         * Enable logging of the shader code when a compilation error occurs
+         */
+        static LogShaderCodeOnCompilationError: boolean;
+        /**
          * Name of the effect.
          */
         name: any;
@@ -30185,6 +30200,7 @@ declare module BABYLON {
          * @hidden
          */
         _prepareEffect(): void;
+        private _getShaderCodeAndErrorLine;
         private _processCompilationErrors;
         /**
          * Checks if the effect is supported. (Must be called after compilation)
@@ -31530,6 +31546,8 @@ declare module BABYLON {
         protected static _ConcatenateShader(source: string, defines: Nullable<string>, shaderVersion?: string): string;
         private _compileShader;
         private _compileRawShader;
+        /** @hidden */
+        _getShaderSource(shader: WebGLShader): Nullable<string>;
         /**
          * Directly creates a webGL program
          * @param pipelineContext  defines the pipeline context to attach to
@@ -32620,6 +32638,23 @@ declare module BABYLON {
          * This is helpful to resume play once browser policies have been satisfied.
          */
         unlock(): void;
+        /**
+         * Gets the global volume sets on the master gain.
+         * @returns the global volume if set or -1 otherwise
+         */
+        getGlobalVolume(): number;
+        /**
+         * Sets the global volume of your experience (sets on the master gain).
+         * @param newVolume Defines the new global volume of the application
+         */
+        setGlobalVolume(newVolume: number): void;
+        /**
+         * Connect the audio engine to an audio analyser allowing some amazing
+         * synchornization between the sounds/music and your visualization (VuMeter for instance).
+         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+         * @param analyser The analyser to connect to the engine
+         */
+        connectToAnalyser(analyser: Analyser): void;
     }
     /**
      * This represents the default audio engine used in babylon.
@@ -33978,7 +34013,8 @@ declare module BABYLON {
          * It can be a string if the expected behavior is identical in the entire app.
          * Or a callback to be able to set it per url or on a group of them (in case of Video source for instance)
          */
-        static CorsBehavior: string | ((url: string | string[]) => string);
+        static get CorsBehavior(): string | ((url: string | string[]) => string);
+        static set CorsBehavior(value: string | ((url: string | string[]) => string));
         /**
          * Gets or sets a global variable indicating if fallback texture must be used when a texture cannot be loaded
          * @ignorenaming
@@ -61764,6 +61800,51 @@ declare module BABYLON {
         /** Quadratic error decimation */
         QUADRATIC = 0
     }
+    /**
+     * An implementation of the Quadratic Error simplification algorithm.
+     * Original paper : http://www1.cs.columbia.edu/~cs4162/html05s/garland97.pdf
+     * Ported mostly from QSlim and http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html to babylon JS
+     * @author RaananW
+     * @see http://doc.babylonjs.com/how_to/in-browser_mesh_simplification
+     */
+    export class QuadraticErrorSimplification implements ISimplifier {
+        private _mesh;
+        private triangles;
+        private vertices;
+        private references;
+        private _reconstructedMesh;
+        /** Gets or sets the number pf sync interations */
+        syncIterations: number;
+        /** Gets or sets the aggressiveness of the simplifier */
+        aggressiveness: number;
+        /** Gets or sets the number of allowed iterations for decimation */
+        decimationIterations: number;
+        /** Gets or sets the espilon to use for bounding box computation */
+        boundingBoxEpsilon: number;
+        /**
+         * Creates a new QuadraticErrorSimplification
+         * @param _mesh defines the target mesh
+         */
+        constructor(_mesh: Mesh);
+        /**
+         * Simplification of a given mesh according to the given settings.
+         * Since this requires computation, it is assumed that the function runs async.
+         * @param settings The settings of the simplification, including quality and distance
+         * @param successCallback A callback that will be called after the mesh was simplified.
+         */
+        simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void): void;
+        private runDecimation;
+        private initWithMesh;
+        private init;
+        private reconstructMesh;
+        private initDecimatedMesh;
+        private isFlipped;
+        private updateTriangles;
+        private identifyBorder;
+        private updateMesh;
+        private vertexError;
+        private calculateError;
+    }
 }
 declare module BABYLON {
         interface Scene {
@@ -68625,6 +68706,54 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Class for storing data to local storage if available or in-memory storage otherwise
+     */
+    export class DataStorage {
+        private static _Storage;
+        private static _GetStorage;
+        /**
+         * Reads a string from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The string value
+         */
+        static ReadString(key: string, defaultValue: string): string;
+        /**
+         * Writes a string to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteString(key: string, value: string): void;
+        /**
+         * Reads a boolean from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The boolean value
+         */
+        static ReadBoolean(key: string, defaultValue: boolean): boolean;
+        /**
+         * Writes a boolean to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteBoolean(key: string, value: boolean): void;
+        /**
+         * Reads a number from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The number value
+         */
+        static ReadNumber(key: string, defaultValue: number): number;
+        /**
+         * Writes a number to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteNumber(key: string, value: number): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Options used for hit testing
      */
     export interface IWebXRHitTestOptions {

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


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


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


+ 261 - 2
dist/preview release/babylon.module.d.ts

@@ -1084,6 +1084,10 @@ declare module "babylonjs/Engines/IPipelineContext" {
          */
         isReady: boolean;
         /** @hidden */
+        _getVertexShaderCode(): string | null;
+        /** @hidden */
+        _getFragmentShaderCode(): string | null;
+        /** @hidden */
         _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
     }
 }
@@ -7223,6 +7227,8 @@ declare module "babylonjs/Engines/WebGL/webGLPipelineContext" {
         get isAsync(): boolean;
         get isReady(): boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
+        _getVertexShaderCode(): string | null;
+        _getFragmentShaderCode(): string | null;
     }
 }
 declare module "babylonjs/Engines/Extensions/engine.uniformBuffer" {
@@ -13480,6 +13486,11 @@ declare module "babylonjs/Particles/IParticleSystem" {
          */
         isReady(): boolean;
         /**
+         * Returns the string "ParticleSystem"
+         * @returns a string containing the class name
+         */
+        getClassName(): string;
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color1 defines the color to affect to the specified gradient
@@ -30943,6 +30954,10 @@ declare module "babylonjs/Materials/effect" {
          */
         static ShadersRepository: string;
         /**
+         * Enable logging of the shader code when a compilation error occurs
+         */
+        static LogShaderCodeOnCompilationError: boolean;
+        /**
          * Name of the effect.
          */
         name: any;
@@ -31126,6 +31141,7 @@ declare module "babylonjs/Materials/effect" {
          * @hidden
          */
         _prepareEffect(): void;
+        private _getShaderCodeAndErrorLine;
         private _processCompilationErrors;
         /**
          * Checks if the effect is supported. (Must be called after compilation)
@@ -32506,6 +32522,8 @@ declare module "babylonjs/Engines/thinEngine" {
         protected static _ConcatenateShader(source: string, defines: Nullable<string>, shaderVersion?: string): string;
         private _compileShader;
         private _compileRawShader;
+        /** @hidden */
+        _getShaderSource(shader: WebGLShader): Nullable<string>;
         /**
          * Directly creates a webGL program
          * @param pipelineContext  defines the pipeline context to attach to
@@ -33608,6 +33626,23 @@ declare module "babylonjs/Audio/audioEngine" {
          * This is helpful to resume play once browser policies have been satisfied.
          */
         unlock(): void;
+        /**
+         * Gets the global volume sets on the master gain.
+         * @returns the global volume if set or -1 otherwise
+         */
+        getGlobalVolume(): number;
+        /**
+         * Sets the global volume of your experience (sets on the master gain).
+         * @param newVolume Defines the new global volume of the application
+         */
+        setGlobalVolume(newVolume: number): void;
+        /**
+         * Connect the audio engine to an audio analyser allowing some amazing
+         * synchornization between the sounds/music and your visualization (VuMeter for instance).
+         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+         * @param analyser The analyser to connect to the engine
+         */
+        connectToAnalyser(analyser: Analyser): void;
     }
     /**
      * This represents the default audio engine used in babylon.
@@ -34999,7 +35034,8 @@ declare module "babylonjs/Misc/tools" {
          * It can be a string if the expected behavior is identical in the entire app.
          * Or a callback to be able to set it per url or on a group of them (in case of Video source for instance)
          */
-        static CorsBehavior: string | ((url: string | string[]) => string);
+        static get CorsBehavior(): string | ((url: string | string[]) => string);
+        static set CorsBehavior(value: string | ((url: string | string[]) => string));
         /**
          * Gets or sets a global variable indicating if fallback texture must be used when a texture cannot be loaded
          * @ignorenaming
@@ -64876,6 +64912,51 @@ declare module "babylonjs/Meshes/meshSimplification" {
         /** Quadratic error decimation */
         QUADRATIC = 0
     }
+    /**
+     * An implementation of the Quadratic Error simplification algorithm.
+     * Original paper : http://www1.cs.columbia.edu/~cs4162/html05s/garland97.pdf
+     * Ported mostly from QSlim and http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html to babylon JS
+     * @author RaananW
+     * @see http://doc.babylonjs.com/how_to/in-browser_mesh_simplification
+     */
+    export class QuadraticErrorSimplification implements ISimplifier {
+        private _mesh;
+        private triangles;
+        private vertices;
+        private references;
+        private _reconstructedMesh;
+        /** Gets or sets the number pf sync interations */
+        syncIterations: number;
+        /** Gets or sets the aggressiveness of the simplifier */
+        aggressiveness: number;
+        /** Gets or sets the number of allowed iterations for decimation */
+        decimationIterations: number;
+        /** Gets or sets the espilon to use for bounding box computation */
+        boundingBoxEpsilon: number;
+        /**
+         * Creates a new QuadraticErrorSimplification
+         * @param _mesh defines the target mesh
+         */
+        constructor(_mesh: Mesh);
+        /**
+         * Simplification of a given mesh according to the given settings.
+         * Since this requires computation, it is assumed that the function runs async.
+         * @param settings The settings of the simplification, including quality and distance
+         * @param successCallback A callback that will be called after the mesh was simplified.
+         */
+        simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void): void;
+        private runDecimation;
+        private initWithMesh;
+        private init;
+        private reconstructMesh;
+        private initDecimatedMesh;
+        private isFlipped;
+        private updateTriangles;
+        private identifyBorder;
+        private updateMesh;
+        private vertexError;
+        private calculateError;
+    }
 }
 declare module "babylonjs/Meshes/meshSimplificationSceneComponent" {
     import { Scene } from "babylonjs/scene";
@@ -72298,6 +72379,54 @@ declare module "babylonjs/Misc/dataReader" {
         skipBytes(byteLength: number): void;
     }
 }
+declare module "babylonjs/Misc/dataStorage" {
+    /**
+     * Class for storing data to local storage if available or in-memory storage otherwise
+     */
+    export class DataStorage {
+        private static _Storage;
+        private static _GetStorage;
+        /**
+         * Reads a string from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The string value
+         */
+        static ReadString(key: string, defaultValue: string): string;
+        /**
+         * Writes a string to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteString(key: string, value: string): void;
+        /**
+         * Reads a boolean from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The boolean value
+         */
+        static ReadBoolean(key: string, defaultValue: boolean): boolean;
+        /**
+         * Writes a boolean to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteBoolean(key: string, value: boolean): void;
+        /**
+         * Reads a number from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The number value
+         */
+        static ReadNumber(key: string, defaultValue: number): number;
+        /**
+         * Writes a number to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteNumber(key: string, value: number): void;
+    }
+}
 declare module "babylonjs/Misc/index" {
     export * from "babylonjs/Misc/andOrNotEvaluator";
     export * from "babylonjs/Misc/assetsManager";
@@ -72348,6 +72477,7 @@ declare module "babylonjs/Misc/index" {
     export * from "babylonjs/Misc/dataReader";
     export * from "babylonjs/Misc/minMaxReducer";
     export * from "babylonjs/Misc/depthReducer";
+    export * from "babylonjs/Misc/dataStorage";
 }
 declare module "babylonjs/XR/features/WebXRHitTestLegacy" {
     import { WebXRSessionManager } from "babylonjs/XR/webXRSessionManager";
@@ -74361,6 +74491,10 @@ declare module BABYLON {
          */
         isReady: boolean;
         /** @hidden */
+        _getVertexShaderCode(): string | null;
+        /** @hidden */
+        _getFragmentShaderCode(): string | null;
+        /** @hidden */
         _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
     }
 }
@@ -80438,6 +80572,8 @@ declare module BABYLON {
         get isAsync(): boolean;
         get isReady(): boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
+        _getVertexShaderCode(): string | null;
+        _getFragmentShaderCode(): string | null;
     }
 }
 declare module BABYLON {
@@ -86437,6 +86573,11 @@ declare module BABYLON {
          */
         isReady(): boolean;
         /**
+         * Returns the string "ParticleSystem"
+         * @returns a string containing the class name
+         */
+        getClassName(): string;
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color1 defines the color to affect to the specified gradient
@@ -103285,6 +103426,10 @@ declare module BABYLON {
          */
         static ShadersRepository: string;
         /**
+         * Enable logging of the shader code when a compilation error occurs
+         */
+        static LogShaderCodeOnCompilationError: boolean;
+        /**
          * Name of the effect.
          */
         name: any;
@@ -103468,6 +103613,7 @@ declare module BABYLON {
          * @hidden
          */
         _prepareEffect(): void;
+        private _getShaderCodeAndErrorLine;
         private _processCompilationErrors;
         /**
          * Checks if the effect is supported. (Must be called after compilation)
@@ -104813,6 +104959,8 @@ declare module BABYLON {
         protected static _ConcatenateShader(source: string, defines: Nullable<string>, shaderVersion?: string): string;
         private _compileShader;
         private _compileRawShader;
+        /** @hidden */
+        _getShaderSource(shader: WebGLShader): Nullable<string>;
         /**
          * Directly creates a webGL program
          * @param pipelineContext  defines the pipeline context to attach to
@@ -105903,6 +106051,23 @@ declare module BABYLON {
          * This is helpful to resume play once browser policies have been satisfied.
          */
         unlock(): void;
+        /**
+         * Gets the global volume sets on the master gain.
+         * @returns the global volume if set or -1 otherwise
+         */
+        getGlobalVolume(): number;
+        /**
+         * Sets the global volume of your experience (sets on the master gain).
+         * @param newVolume Defines the new global volume of the application
+         */
+        setGlobalVolume(newVolume: number): void;
+        /**
+         * Connect the audio engine to an audio analyser allowing some amazing
+         * synchornization between the sounds/music and your visualization (VuMeter for instance).
+         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+         * @param analyser The analyser to connect to the engine
+         */
+        connectToAnalyser(analyser: Analyser): void;
     }
     /**
      * This represents the default audio engine used in babylon.
@@ -107261,7 +107426,8 @@ declare module BABYLON {
          * It can be a string if the expected behavior is identical in the entire app.
          * Or a callback to be able to set it per url or on a group of them (in case of Video source for instance)
          */
-        static CorsBehavior: string | ((url: string | string[]) => string);
+        static get CorsBehavior(): string | ((url: string | string[]) => string);
+        static set CorsBehavior(value: string | ((url: string | string[]) => string));
         /**
          * Gets or sets a global variable indicating if fallback texture must be used when a texture cannot be loaded
          * @ignorenaming
@@ -135047,6 +135213,51 @@ declare module BABYLON {
         /** Quadratic error decimation */
         QUADRATIC = 0
     }
+    /**
+     * An implementation of the Quadratic Error simplification algorithm.
+     * Original paper : http://www1.cs.columbia.edu/~cs4162/html05s/garland97.pdf
+     * Ported mostly from QSlim and http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html to babylon JS
+     * @author RaananW
+     * @see http://doc.babylonjs.com/how_to/in-browser_mesh_simplification
+     */
+    export class QuadraticErrorSimplification implements ISimplifier {
+        private _mesh;
+        private triangles;
+        private vertices;
+        private references;
+        private _reconstructedMesh;
+        /** Gets or sets the number pf sync interations */
+        syncIterations: number;
+        /** Gets or sets the aggressiveness of the simplifier */
+        aggressiveness: number;
+        /** Gets or sets the number of allowed iterations for decimation */
+        decimationIterations: number;
+        /** Gets or sets the espilon to use for bounding box computation */
+        boundingBoxEpsilon: number;
+        /**
+         * Creates a new QuadraticErrorSimplification
+         * @param _mesh defines the target mesh
+         */
+        constructor(_mesh: Mesh);
+        /**
+         * Simplification of a given mesh according to the given settings.
+         * Since this requires computation, it is assumed that the function runs async.
+         * @param settings The settings of the simplification, including quality and distance
+         * @param successCallback A callback that will be called after the mesh was simplified.
+         */
+        simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void): void;
+        private runDecimation;
+        private initWithMesh;
+        private init;
+        private reconstructMesh;
+        private initDecimatedMesh;
+        private isFlipped;
+        private updateTriangles;
+        private identifyBorder;
+        private updateMesh;
+        private vertexError;
+        private calculateError;
+    }
 }
 declare module BABYLON {
         interface Scene {
@@ -141908,6 +142119,54 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Class for storing data to local storage if available or in-memory storage otherwise
+     */
+    export class DataStorage {
+        private static _Storage;
+        private static _GetStorage;
+        /**
+         * Reads a string from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The string value
+         */
+        static ReadString(key: string, defaultValue: string): string;
+        /**
+         * Writes a string to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteString(key: string, value: string): void;
+        /**
+         * Reads a boolean from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The boolean value
+         */
+        static ReadBoolean(key: string, defaultValue: boolean): boolean;
+        /**
+         * Writes a boolean to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteBoolean(key: string, value: boolean): void;
+        /**
+         * Reads a number from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The number value
+         */
+        static ReadNumber(key: string, defaultValue: number): number;
+        /**
+         * Writes a number to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteNumber(key: string, value: number): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Options used for hit testing
      */
     export interface IWebXRHitTestOptions {

+ 130 - 1
dist/preview release/documentation.d.ts

@@ -1078,6 +1078,10 @@ declare module BABYLON {
          */
         isReady: boolean;
         /** @hidden */
+        _getVertexShaderCode(): string | null;
+        /** @hidden */
+        _getFragmentShaderCode(): string | null;
+        /** @hidden */
         _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
     }
 }
@@ -7155,6 +7159,8 @@ declare module BABYLON {
         get isAsync(): boolean;
         get isReady(): boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
+        _getVertexShaderCode(): string | null;
+        _getFragmentShaderCode(): string | null;
     }
 }
 declare module BABYLON {
@@ -13154,6 +13160,11 @@ declare module BABYLON {
          */
         isReady(): boolean;
         /**
+         * Returns the string "ParticleSystem"
+         * @returns a string containing the class name
+         */
+        getClassName(): string;
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color1 defines the color to affect to the specified gradient
@@ -30002,6 +30013,10 @@ declare module BABYLON {
          */
         static ShadersRepository: string;
         /**
+         * Enable logging of the shader code when a compilation error occurs
+         */
+        static LogShaderCodeOnCompilationError: boolean;
+        /**
          * Name of the effect.
          */
         name: any;
@@ -30185,6 +30200,7 @@ declare module BABYLON {
          * @hidden
          */
         _prepareEffect(): void;
+        private _getShaderCodeAndErrorLine;
         private _processCompilationErrors;
         /**
          * Checks if the effect is supported. (Must be called after compilation)
@@ -31530,6 +31546,8 @@ declare module BABYLON {
         protected static _ConcatenateShader(source: string, defines: Nullable<string>, shaderVersion?: string): string;
         private _compileShader;
         private _compileRawShader;
+        /** @hidden */
+        _getShaderSource(shader: WebGLShader): Nullable<string>;
         /**
          * Directly creates a webGL program
          * @param pipelineContext  defines the pipeline context to attach to
@@ -32620,6 +32638,23 @@ declare module BABYLON {
          * This is helpful to resume play once browser policies have been satisfied.
          */
         unlock(): void;
+        /**
+         * Gets the global volume sets on the master gain.
+         * @returns the global volume if set or -1 otherwise
+         */
+        getGlobalVolume(): number;
+        /**
+         * Sets the global volume of your experience (sets on the master gain).
+         * @param newVolume Defines the new global volume of the application
+         */
+        setGlobalVolume(newVolume: number): void;
+        /**
+         * Connect the audio engine to an audio analyser allowing some amazing
+         * synchornization between the sounds/music and your visualization (VuMeter for instance).
+         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+         * @param analyser The analyser to connect to the engine
+         */
+        connectToAnalyser(analyser: Analyser): void;
     }
     /**
      * This represents the default audio engine used in babylon.
@@ -33978,7 +34013,8 @@ declare module BABYLON {
          * It can be a string if the expected behavior is identical in the entire app.
          * Or a callback to be able to set it per url or on a group of them (in case of Video source for instance)
          */
-        static CorsBehavior: string | ((url: string | string[]) => string);
+        static get CorsBehavior(): string | ((url: string | string[]) => string);
+        static set CorsBehavior(value: string | ((url: string | string[]) => string));
         /**
          * Gets or sets a global variable indicating if fallback texture must be used when a texture cannot be loaded
          * @ignorenaming
@@ -61764,6 +61800,51 @@ declare module BABYLON {
         /** Quadratic error decimation */
         QUADRATIC = 0
     }
+    /**
+     * An implementation of the Quadratic Error simplification algorithm.
+     * Original paper : http://www1.cs.columbia.edu/~cs4162/html05s/garland97.pdf
+     * Ported mostly from QSlim and http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html to babylon JS
+     * @author RaananW
+     * @see http://doc.babylonjs.com/how_to/in-browser_mesh_simplification
+     */
+    export class QuadraticErrorSimplification implements ISimplifier {
+        private _mesh;
+        private triangles;
+        private vertices;
+        private references;
+        private _reconstructedMesh;
+        /** Gets or sets the number pf sync interations */
+        syncIterations: number;
+        /** Gets or sets the aggressiveness of the simplifier */
+        aggressiveness: number;
+        /** Gets or sets the number of allowed iterations for decimation */
+        decimationIterations: number;
+        /** Gets or sets the espilon to use for bounding box computation */
+        boundingBoxEpsilon: number;
+        /**
+         * Creates a new QuadraticErrorSimplification
+         * @param _mesh defines the target mesh
+         */
+        constructor(_mesh: Mesh);
+        /**
+         * Simplification of a given mesh according to the given settings.
+         * Since this requires computation, it is assumed that the function runs async.
+         * @param settings The settings of the simplification, including quality and distance
+         * @param successCallback A callback that will be called after the mesh was simplified.
+         */
+        simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void): void;
+        private runDecimation;
+        private initWithMesh;
+        private init;
+        private reconstructMesh;
+        private initDecimatedMesh;
+        private isFlipped;
+        private updateTriangles;
+        private identifyBorder;
+        private updateMesh;
+        private vertexError;
+        private calculateError;
+    }
 }
 declare module BABYLON {
         interface Scene {
@@ -68625,6 +68706,54 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Class for storing data to local storage if available or in-memory storage otherwise
+     */
+    export class DataStorage {
+        private static _Storage;
+        private static _GetStorage;
+        /**
+         * Reads a string from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The string value
+         */
+        static ReadString(key: string, defaultValue: string): string;
+        /**
+         * Writes a string to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteString(key: string, value: string): void;
+        /**
+         * Reads a boolean from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The boolean value
+         */
+        static ReadBoolean(key: string, defaultValue: boolean): boolean;
+        /**
+         * Writes a boolean to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteBoolean(key: string, value: boolean): void;
+        /**
+         * Reads a number from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The number value
+         */
+        static ReadNumber(key: string, defaultValue: number): number;
+        /**
+         * Writes a number to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteNumber(key: string, value: number): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Options used for hit testing
      */
     export interface IWebXRHitTestOptions {

+ 48 - 48
dist/preview release/gui/babylon.gui.js

@@ -7,7 +7,7 @@
 		exports["babylonjs-gui"] = factory(require("babylonjs"));
 	else
 		root["BABYLON"] = root["BABYLON"] || {}, root["BABYLON"]["GUI"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -366,7 +366,7 @@ module.exports = g;
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTextureInstrumentation", function() { return AdvancedDynamicTextureInstrumentation; });
-/* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/perfCounter */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/perfCounter */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_perfCounter__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -509,7 +509,7 @@ var AdvancedDynamicTextureInstrumentation = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTexture", function() { return AdvancedDynamicTexture; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _controls_container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./controls/container */ "./2D/controls/container.ts");
 /* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./style */ "./2D/style.ts");
@@ -1477,7 +1477,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _textBlock__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./textBlock */ "./2D/controls/textBlock.ts");
 /* harmony import */ var _image__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./image */ "./2D/controls/image.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__);
 
 
@@ -1709,7 +1709,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Checkbox", function() { return Checkbox; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -1892,7 +1892,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ColorPicker", function() { return ColorPicker; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
@@ -3281,7 +3281,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container", function() { return Container; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -3696,7 +3696,7 @@ babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredTypes
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control", function() { return Control; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -5615,7 +5615,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DisplayGrid", function() { return DisplayGrid; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -5848,7 +5848,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -5945,7 +5945,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__);
 
 
@@ -6403,7 +6403,7 @@ babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__["_TypeStore"].RegisteredTypes[
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Image", function() { return Image; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 
@@ -7330,7 +7330,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputPassword", function() { return InputPassword; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -7369,7 +7369,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputText", function() { return InputText; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -8382,7 +8382,7 @@ babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].RegisteredT
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Line", function() { return Line; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -8653,7 +8653,7 @@ babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].Registere
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLine", function() { return MultiLine; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _multiLinePoint__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../multiLinePoint */ "./2D/multiLinePoint.ts");
@@ -8923,7 +8923,7 @@ babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__["_TypeStore"].Registe
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RadioButton", function() { return RadioButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -9130,7 +9130,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Rectangle", function() { return Rectangle; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -9280,7 +9280,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var _scrollViewerWindow__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ./scrollViewerWindow */ "./2D/controls/scrollViewers/scrollViewerWindow.ts");
 /* harmony import */ var _sliders_scrollBar__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! ../sliders/scrollBar */ "./2D/controls/sliders/scrollBar.ts");
 /* harmony import */ var _sliders_imageScrollBar__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../sliders/imageScrollBar */ "./2D/controls/sliders/imageScrollBar.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__);
 
 
@@ -10873,7 +10873,7 @@ var SelectionPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSlider", function() { return BaseSlider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -11203,7 +11203,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _baseSlider__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./baseSlider */ "./2D/controls/sliders/baseSlider.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../../measure */ "./2D/measure.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -11796,7 +11796,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Slider", function() { return Slider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _baseSlider__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./baseSlider */ "./2D/controls/sliders/baseSlider.ts");
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -12051,7 +12051,7 @@ babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__["_TypeStore"].RegisteredTy
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel", function() { return StackPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -12319,7 +12319,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextWrapping", function() { return TextWrapping; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextBlock", function() { return TextBlock; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -12782,7 +12782,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KeyPropertySet", function() { return KeyPropertySet; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VirtualKeyboard", function() { return VirtualKeyboard; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
 /* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./button */ "./2D/controls/button.ts");
@@ -13171,7 +13171,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector2WithInfo", function() { return Vector2WithInfo; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Matrix2D", function() { return Matrix2D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -13396,7 +13396,7 @@ var Matrix2D = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Measure", function() { return Measure; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 var tmpRect = [
@@ -13545,7 +13545,7 @@ var Measure = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLinePoint", function() { return MultiLinePoint; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -13688,7 +13688,7 @@ var MultiLinePoint = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Style", function() { return Style; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -13994,7 +13994,7 @@ var ValueAndUnit = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "XmlLoader", function() { return XmlLoader; });
-/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/typeStore */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -14313,7 +14313,7 @@ var XmlLoader = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AbstractButton3D", function() { return AbstractButton3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -14356,7 +14356,7 @@ var AbstractButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Button3D", function() { return Button3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _abstractButton3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./abstractButton3D */ "./3D/controls/abstractButton3D.ts");
 /* harmony import */ var _2D_advancedDynamicTexture__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../2D/advancedDynamicTexture */ "./2D/advancedDynamicTexture.ts");
@@ -14537,7 +14537,7 @@ var Button3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container3D", function() { return Container3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -14694,7 +14694,7 @@ var Container3D = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control3D", function() { return Control3D; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _vector3WithInfo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../vector3WithInfo */ "./3D/vector3WithInfo.ts");
 
@@ -15100,7 +15100,7 @@ var Control3D = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CylinderPanel", function() { return CylinderPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -15186,7 +15186,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HolographicButton", function() { return HolographicButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _button3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./button3D */ "./3D/controls/button3D.ts");
-/* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Materials/standardMaterial */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Materials/standardMaterial */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_standardMaterial__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _materials_fluentMaterial__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../materials/fluentMaterial */ "./3D/materials/fluentMaterial.ts");
 /* harmony import */ var _2D_controls_stackPanel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../2D/controls/stackPanel */ "./2D/controls/stackPanel.ts");
@@ -15680,7 +15680,7 @@ var MeshButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlanePanel", function() { return PlanePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
@@ -15735,7 +15735,7 @@ var PlanePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScatterPanel", function() { return ScatterPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -15862,7 +15862,7 @@ var ScatterPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SpherePanel", function() { return SpherePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -15948,7 +15948,7 @@ var SpherePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel3D", function() { return StackPanel3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -16073,7 +16073,7 @@ var StackPanel3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VolumeBasedPanel", function() { return VolumeBasedPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -16264,7 +16264,7 @@ var VolumeBasedPanel = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GUI3DManager", function() { return GUI3DManager; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _controls_container3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./controls/container3D */ "./3D/controls/container3D.ts");
 
@@ -16531,7 +16531,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterialDefines", function() { return FluentMaterialDefines; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterial", function() { return FluentMaterial; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _shaders_fluent_vertex__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./shaders/fluent.vertex */ "./3D/materials/shaders/fluent.vertex.ts");
 /* harmony import */ var _shaders_fluent_fragment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./shaders/fluent.fragment */ "./3D/materials/shaders/fluent.fragment.ts");
@@ -16854,7 +16854,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentPixelShader", function() { return fluentPixelShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentPixelShader';
@@ -16876,7 +16876,7 @@ var fluentPixelShader = { name: name, shader: shader };
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentVertexShader", function() { return fluentVertexShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentVertexShader';
@@ -16899,7 +16899,7 @@ var fluentVertexShader = { name: name, shader: shader };
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector3WithInfo", function() { return Vector3WithInfo; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Misc/perfCounter");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -17201,14 +17201,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Maths/math.vector":
+/***/ "babylonjs/Misc/perfCounter":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__;
 
 /***/ })
 

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


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


+ 47 - 62
dist/preview release/inspector/babylon.inspector.bundle.max.js

@@ -40688,6 +40688,9 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _fortawesome_react_fontawesome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! @fortawesome/react-fontawesome */ "../../node_modules/@fortawesome/react-fontawesome/index.es.js");
 /* harmony import */ var _fortawesome_free_solid_svg_icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! @fortawesome/free-solid-svg-icons */ "../../node_modules/@fortawesome/free-solid-svg-icons/index.es.js");
+/* harmony import */ var babylonjs_Misc_dataStorage__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! babylonjs/Misc/dataStorage */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_dataStorage__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_dataStorage__WEBPACK_IMPORTED_MODULE_4__);
+
 
 
 
@@ -40696,40 +40699,13 @@ var LineContainerComponent = /** @class */ (function (_super) {
     Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(LineContainerComponent, _super);
     function LineContainerComponent(props) {
         var _this = _super.call(this, props) || this;
-        var initialState;
-        try {
-            if (LineContainerComponent._InMemoryStorage && LineContainerComponent._InMemoryStorage[_this.props.title] !== undefined) {
-                initialState = LineContainerComponent._InMemoryStorage[_this.props.title];
-            }
-            else if (typeof (Storage) !== "undefined" && localStorage.getItem(_this.props.title) !== null) {
-                initialState = localStorage.getItem(_this.props.title) === "true";
-            }
-            else {
-                initialState = !_this.props.closed;
-            }
-        }
-        catch (e) {
-            LineContainerComponent._InMemoryStorage = {};
-            LineContainerComponent._InMemoryStorage[_this.props.title] = !_this.props.closed;
-            initialState = !_this.props.closed;
-        }
+        var initialState = babylonjs_Misc_dataStorage__WEBPACK_IMPORTED_MODULE_4__["DataStorage"].ReadBoolean(_this.props.title, !_this.props.closed);
         _this.state = { isExpanded: initialState, isHighlighted: false };
         return _this;
     }
     LineContainerComponent.prototype.switchExpandedState = function () {
         var newState = !this.state.isExpanded;
-        try {
-            if (LineContainerComponent._InMemoryStorage) {
-                LineContainerComponent._InMemoryStorage[this.props.title] = newState;
-            }
-            else if (typeof (Storage) !== "undefined") {
-                localStorage.setItem(this.props.title, newState ? "true" : "false");
-            }
-        }
-        catch (e) {
-            LineContainerComponent._InMemoryStorage = {};
-            LineContainerComponent._InMemoryStorage[this.props.title] = newState;
-        }
+        babylonjs_Misc_dataStorage__WEBPACK_IMPORTED_MODULE_4__["DataStorage"].WriteBoolean(this.props.title, newState);
         this.setState({ isExpanded: newState });
     };
     LineContainerComponent.prototype.componentDidMount = function () {
@@ -41246,6 +41222,7 @@ var FileButtonLineComponent = /** @class */ (function (_super) {
     function FileButtonLineComponent(props) {
         var _this = _super.call(this, props) || this;
         _this._id = FileButtonLineComponent._IDGenerator++;
+        _this.uploadInputRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
         return _this;
     }
     FileButtonLineComponent.prototype.onChange = function (evt) {
@@ -41259,7 +41236,7 @@ var FileButtonLineComponent = /** @class */ (function (_super) {
         var _this = this;
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "buttonLine" },
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("label", { htmlFor: "file-upload" + this._id, className: "file-upload" }, this.props.label),
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("input", { ref: "upload", id: "file-upload" + this._id, type: "file", accept: this.props.accept, onChange: function (evt) { return _this.onChange(evt); } })));
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("input", { ref: this.uploadInputRef, id: "file-upload" + this._id, type: "file", accept: this.props.accept, onChange: function (evt) { return _this.onChange(evt); } })));
     };
     FileButtonLineComponent._IDGenerator = 0;
     return FileButtonLineComponent;
@@ -41289,6 +41266,7 @@ var FileMultipleButtonLineComponent = /** @class */ (function (_super) {
     function FileMultipleButtonLineComponent(props) {
         var _this = _super.call(this, props) || this;
         _this._id = FileMultipleButtonLineComponent._IDGenerator++;
+        _this.uploadInputRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
         return _this;
     }
     FileMultipleButtonLineComponent.prototype.onChange = function (evt) {
@@ -41302,7 +41280,7 @@ var FileMultipleButtonLineComponent = /** @class */ (function (_super) {
         var _this = this;
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "buttonLine" },
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("label", { htmlFor: "file-upload" + this._id, className: "file-upload" }, this.props.label),
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("input", { ref: "upload", id: "file-upload" + this._id, type: "file", accept: this.props.accept, onChange: function (evt) { return _this.onChange(evt); }, multiple: true })));
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("input", { ref: this.uploadInputRef, id: "file-upload" + this._id, type: "file", accept: this.props.accept, onChange: function (evt) { return _this.onChange(evt); }, multiple: true })));
     };
     FileMultipleButtonLineComponent._IDGenerator = 0;
     return FileMultipleButtonLineComponent;
@@ -42073,6 +42051,7 @@ var TextureLineComponent = /** @class */ (function (_super) {
             channel: ChannelToDisplay.All,
             face: 0
         };
+        _this.canvasRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
         return _this;
     }
     TextureLineComponent.prototype.shouldComponentUpdate = function (nextProps, nextState) {
@@ -42113,7 +42092,7 @@ var TextureLineComponent = /** @class */ (function (_super) {
             setTimeout(function () { return _this.updatePreview(); }, 250);
             return;
         }
-        var previewCanvas = this.refs.canvas;
+        var previewCanvas = this.canvasRef.current;
         if (this.props.globalState) {
             this.props.globalState.blockMutationUpdates = true;
         }
@@ -42211,7 +42190,7 @@ var TextureLineComponent = /** @class */ (function (_super) {
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("button", { className: this.state.channel === ChannelToDisplay.B ? "blue command selected" : "blue command", onClick: function () { return _this.setState({ channel: ChannelToDisplay.B }); } }, "B"),
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("button", { className: this.state.channel === ChannelToDisplay.A ? "alpha command selected" : "alpha command", onClick: function () { return _this.setState({ channel: ChannelToDisplay.A }); } }, "A"),
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("button", { className: this.state.channel === ChannelToDisplay.All ? "all command selected" : "all command", onClick: function () { return _this.setState({ channel: ChannelToDisplay.All }); } }, "ALL")),
-                react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("canvas", { ref: "canvas", className: "preview" })),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("canvas", { ref: this.canvasRef, className: "preview" })),
             texture.isRenderTarget &&
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_buttonLineComponent__WEBPACK_IMPORTED_MODULE_3__["ButtonLineComponent"], { label: "Refresh", onClick: function () {
                         _this.updatePreview();
@@ -43225,6 +43204,7 @@ var AnimationGroupGridComponent = /** @class */ (function (_super) {
         _this._onBeforeRenderObserver = _this.props.scene.onBeforeRenderObservable.add(function () {
             _this.updateCurrentFrame(_this.props.animationGroup);
         });
+        _this.timelineRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
         return _this;
     }
     AnimationGroupGridComponent.prototype.disconnect = function (animationGroup) {
@@ -43305,7 +43285,7 @@ var AnimationGroupGridComponent = /** @class */ (function (_super) {
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "CONTROLS" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_buttonLineComponent__WEBPACK_IMPORTED_MODULE_2__["ButtonLineComponent"], { label: playButtonText, onClick: function () { return _this.playOrPause(); } }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_5__["SliderLineComponent"], { label: "Speed ratio", minimum: 0, maximum: 10, step: 0.1, target: animationGroup, propertyName: "speedRatio", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
-                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_5__["SliderLineComponent"], { ref: "timeline", label: "Current frame", minimum: animationGroup.from, maximum: animationGroup.to, step: (animationGroup.to - animationGroup.from) / 1000.0, directValue: this.state.currentFrame, onInput: function (value) { return _this.onCurrentFrameChange(value); } })),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_5__["SliderLineComponent"], { ref: this.timelineRef, label: "Current frame", minimum: animationGroup.from, maximum: animationGroup.to, step: (animationGroup.to - animationGroup.from) / 1000.0, directValue: this.state.currentFrame, onInput: function (value) { return _this.onCurrentFrameChange(value); } })),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "INFOS" },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "Animation count", value: animationGroup.targetedAnimations.length.toString() }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textLineComponent__WEBPACK_IMPORTED_MODULE_4__["TextLineComponent"], { label: "From", value: animationGroup.from.toFixed(2) }),
@@ -43388,6 +43368,7 @@ var AnimationGridComponent = /** @class */ (function (_super) {
                 });
             }
         }
+        _this.timelineRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
         return _this;
     }
     AnimationGridComponent.prototype.playOrPause = function () {
@@ -43470,7 +43451,7 @@ var AnimationGridComponent = /** @class */ (function (_super) {
                     react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_6__["CheckBoxLineComponent"], { label: "Loop", onSelect: function (value) { return _this._animationControl.loop = value; }, isSelected: function () { return _this._animationControl.loop; } }),
                     react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_buttonLineComponent__WEBPACK_IMPORTED_MODULE_2__["ButtonLineComponent"], { label: this._isPlaying ? "Stop" : "Play", onClick: function () { return _this.playOrPause(); } }),
                     this._isPlaying &&
-                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_4__["SliderLineComponent"], { ref: "timeline", label: "Current frame", minimum: this._animationControl.from, maximum: this._animationControl.to, step: (this._animationControl.to - this._animationControl.from) / 1000.0, directValue: this.state.currentFrame, onInput: function (value) { return _this.onCurrentFrameChange(value); } }))));
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_4__["SliderLineComponent"], { ref: this.timelineRef, label: "Current frame", minimum: this._animationControl.from, maximum: this._animationControl.to, step: (this._animationControl.to - this._animationControl.from) / 1000.0, directValue: this.state.currentFrame, onInput: function (value) { return _this.onCurrentFrameChange(value); } }))));
     };
     return AnimationGridComponent;
 }(react__WEBPACK_IMPORTED_MODULE_1__["Component"]));
@@ -46066,6 +46047,7 @@ var TexturePropertyGridComponent = /** @class */ (function (_super) {
         _this._adtInstrumentation = new babylonjs_gui_2D_adtInstrumentation__WEBPACK_IMPORTED_MODULE_12__["AdvancedDynamicTextureInstrumentation"](adt);
         _this._adtInstrumentation.captureRenderTime = true;
         _this._adtInstrumentation.captureLayoutTime = true;
+        _this.textureLineRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
         return _this;
     }
     TexturePropertyGridComponent.prototype.componentWillUnmount = function () {
@@ -46097,7 +46079,7 @@ var TexturePropertyGridComponent = /** @class */ (function (_super) {
     };
     TexturePropertyGridComponent.prototype.foreceRefresh = function () {
         this.forceUpdate();
-        this.refs["textureLine"].updatePreview();
+        this.textureLineRef.current.updatePreview();
     };
     TexturePropertyGridComponent.prototype.render = function () {
         var _this = this;
@@ -46135,7 +46117,7 @@ var TexturePropertyGridComponent = /** @class */ (function (_super) {
         var textureUrl = (url.substring(0, 4) === "data" || url.substring(0, 4) === "blob") ? "" : url;
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "pane" },
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "PREVIEW" },
-                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textureLineComponent__WEBPACK_IMPORTED_MODULE_7__["TextureLineComponent"], { ref: "textureLine", texture: texture, width: 256, height: 256, globalState: this.props.globalState }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textureLineComponent__WEBPACK_IMPORTED_MODULE_7__["TextureLineComponent"], { ref: this.textureLineRef, texture: texture, width: 256, height: 256, globalState: this.props.globalState }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_fileButtonLineComponent__WEBPACK_IMPORTED_MODULE_10__["FileButtonLineComponent"], { label: "Load texture from file", onClick: function (file) { return _this.updateTexture(file); }, accept: ".jpg, .png, .tga, .dds, .env" }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textInputLineComponent__WEBPACK_IMPORTED_MODULE_15__["TextInputLineComponent"], { label: "URL", value: textureUrl, lockObject: this.props.lockObject, onChange: function (url) {
                         texture.updateURL(url);
@@ -48227,15 +48209,18 @@ var EmbedHostComponent = /** @class */ (function (_super) {
         return _this;
     }
     EmbedHostComponent.prototype.componentDidMount = function () {
-        var container = this.refs.split;
+        var container = this.splitRef.current;
         if (!container) {
             return;
         }
-        Split([this.refs.topPart, this.refs.bottomPart], {
+        Split([this.topPartRef.current, this.bottomPartRef.current], {
             direction: "vertical",
             minSize: [200, 200],
             gutterSize: 4
         });
+        this.splitRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
+        this.topPartRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
+        this.bottomPartRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
     };
     EmbedHostComponent.prototype.renderContent = function () {
         if (this.props.popupMode) {
@@ -48246,10 +48231,10 @@ var EmbedHostComponent = /** @class */ (function (_super) {
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { id: "bottomPart", style: { marginTop: "4px", overflow: "hidden" } },
                     react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_actionTabs_actionTabsComponent__WEBPACK_IMPORTED_MODULE_5__["ActionTabsComponent"], { scene: this.props.scene, popupMode: true, globalState: this.props.globalState, noHeader: true, initialTab: this.props.initialTab }))));
         }
-        return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { ref: "split", id: "split", className: "noPopup" },
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { id: "topPart", ref: "topPart" },
+        return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { ref: this.splitRef, id: "split", className: "noPopup" },
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { id: "topPart", ref: this.topPartRef },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_sceneExplorer_sceneExplorerComponent__WEBPACK_IMPORTED_MODULE_4__["SceneExplorerComponent"], { scene: this.props.scene, extensibilityGroups: this.props.extensibilityGroups, globalState: this.props.globalState, popupMode: true, noHeader: true })),
-            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { id: "bottomPart", ref: "bottomPart", style: { marginTop: "4px", overflow: "hidden" } },
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { id: "bottomPart", ref: this.bottomPartRef, style: { marginTop: "4px", overflow: "hidden" } },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_actionTabs_actionTabsComponent__WEBPACK_IMPORTED_MODULE_5__["ActionTabsComponent"], { scene: this.props.scene, globalState: this.props.globalState, popupMode: true, noHeader: true, initialTab: this.props.initialTab }))));
     };
     EmbedHostComponent.prototype.render = function () {
@@ -48294,7 +48279,6 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _replayRecorder__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./replayRecorder */ "./components/replayRecorder.ts");
-/* harmony import */ var _tools__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../tools */ "./tools.ts");
 
 
 
@@ -48319,13 +48303,13 @@ var GlobalState = /** @class */ (function () {
     Object.defineProperty(GlobalState.prototype, "onlyUseEulers", {
         get: function () {
             if (this._onlyUseEulers === null) {
-                this._onlyUseEulers = _tools__WEBPACK_IMPORTED_MODULE_2__["Tools"].ReadLocalBooleanSettings("settings_onlyUseEulers", true);
+                this._onlyUseEulers = babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["DataStorage"].ReadBoolean("settings_onlyUseEulers", true);
             }
             return this._onlyUseEulers;
         },
         set: function (value) {
             this._onlyUseEulers = value;
-            _tools__WEBPACK_IMPORTED_MODULE_2__["Tools"].StoreLocalBooleanSettings("settings_onlyUseEulers", value);
+            babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["DataStorage"].WriteBoolean("settings_onlyUseEulers", value);
         },
         enumerable: true,
         configurable: true
@@ -48333,13 +48317,13 @@ var GlobalState = /** @class */ (function () {
     Object.defineProperty(GlobalState.prototype, "ignoreBackfacesForPicking", {
         get: function () {
             if (this._ignoreBackfacesForPicking === null) {
-                this._ignoreBackfacesForPicking = _tools__WEBPACK_IMPORTED_MODULE_2__["Tools"].ReadLocalBooleanSettings("settings_ignoreBackfacesForPicking", false);
+                this._ignoreBackfacesForPicking = babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["DataStorage"].ReadBoolean("settings_ignoreBackfacesForPicking", false);
             }
             return this._ignoreBackfacesForPicking;
         },
         set: function (value) {
             this._ignoreBackfacesForPicking = value;
-            _tools__WEBPACK_IMPORTED_MODULE_2__["Tools"].StoreLocalBooleanSettings("settings_ignoreBackfacesForPicking", value);
+            babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["DataStorage"].WriteBoolean("settings_ignoreBackfacesForPicking", value);
         },
         enumerable: true,
         configurable: true
@@ -49602,6 +49586,7 @@ var ExtensionsComponent = /** @class */ (function (_super) {
     function ExtensionsComponent(props) {
         var _this = _super.call(this, props) || this;
         _this.state = { popupVisible: false };
+        _this.extensionRef = react__WEBPACK_IMPORTED_MODULE_3__["createRef"]();
         return _this;
     }
     ExtensionsComponent.prototype.showPopup = function () {
@@ -49634,7 +49619,7 @@ var ExtensionsComponent = /** @class */ (function (_super) {
         if (options.length === 0) {
             return null;
         }
-        return (react__WEBPACK_IMPORTED_MODULE_3__["createElement"]("div", { ref: "extensions", className: "extensions", onClick: function () { return _this.showPopup(); } },
+        return (react__WEBPACK_IMPORTED_MODULE_3__["createElement"]("div", { ref: this.extensionRef, className: "extensions", onClick: function () { return _this.showPopup(); } },
             react__WEBPACK_IMPORTED_MODULE_3__["createElement"]("div", { title: "Additional options", className: "icon" },
                 react__WEBPACK_IMPORTED_MODULE_3__["createElement"](_fortawesome_react_fontawesome__WEBPACK_IMPORTED_MODULE_1__["FontAwesomeIcon"], { icon: _fortawesome_free_solid_svg_icons__WEBPACK_IMPORTED_MODULE_2__["faEllipsisH"] })),
             react__WEBPACK_IMPORTED_MODULE_3__["createElement"]("div", { ref: function (input) { _this._popup = input; }, tabIndex: -1, className: this.state.popupVisible ? "popup show" : "popup", onBlur: function () { return _this.setState({ popupVisible: false }); } }, options.map(function (extensibility) {
@@ -49715,6 +49700,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 __webpack_require__(/*! ./sceneExplorer.scss */ "./components/sceneExplorer/sceneExplorer.scss");
 var SceneExplorerFilterComponent = /** @class */ (function (_super) {
     Object(tslib__WEBPACK_IMPORTED_MODULE_0__["__extends"])(SceneExplorerFilterComponent, _super);
@@ -49737,6 +49723,7 @@ var SceneExplorerComponent = /** @class */ (function (_super) {
         _this._hooked = false;
         _this.state = { filter: null, selectedEntity: null, scene: _this.props.scene };
         _this.sceneMutationFunc = _this.processMutation.bind(_this);
+        _this.sceneExplorerRef = react__WEBPACK_IMPORTED_MODULE_1__["createRef"]();
         return _this;
     }
     SceneExplorerComponent.prototype.processMutation = function () {
@@ -49936,6 +49923,7 @@ var SceneExplorerComponent = /** @class */ (function (_super) {
                 _this.props.globalState.onSelectionChangedObservable.notifyObservers(newFreeCamera);
             }
         });
+        // Materials
         var materialsContextMenus = [];
         materialsContextMenus.push({
             label: "Add new node material",
@@ -49951,6 +49939,15 @@ var SceneExplorerComponent = /** @class */ (function (_super) {
         if (scene.multiMaterials && scene.multiMaterials.length) {
             materials.push.apply(materials, scene.multiMaterials);
         }
+        // Particle systems
+        var particleSystemsContextMenus = [];
+        particleSystemsContextMenus.push({
+            label: "Add new particle system",
+            action: function () {
+                var newSystem = babylonjs_Engines_engineStore__WEBPACK_IMPORTED_MODULE_2__["ParticleHelper"].CreateDefault(babylonjs_Engines_engineStore__WEBPACK_IMPORTED_MODULE_2__["Vector3"].Zero(), 1000, scene);
+                _this.props.globalState.onSelectionChangedObservable.notifyObservers(newSystem);
+            }
+        });
         return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { id: "tree", onContextMenu: function (e) { return e.preventDefault(); } },
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](SceneExplorerFilterComponent, { onFilter: function (filter) { return _this.filterContent(filter); } }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_entities_sceneTreeItemComponent__WEBPACK_IMPORTED_MODULE_6__["SceneTreeItemComponent"], { globalState: this.props.globalState, extensibilityGroups: this.props.extensibilityGroups, selectedEntity: this.state.selectedEntity, scene: scene, onRefresh: function () { return _this.forceUpdate(); }, onSelectionChangedObservable: this.props.globalState.onSelectionChangedObservable }),
@@ -49962,6 +49959,7 @@ var SceneExplorerComponent = /** @class */ (function (_super) {
             postProcessses.length > 0 &&
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_treeItemComponent__WEBPACK_IMPORTED_MODULE_3__["TreeItemComponent"], { globalState: this.props.globalState, extensibilityGroups: this.props.extensibilityGroups, selectedEntity: this.state.selectedEntity, items: postProcessses, label: "Post-processes", offset: 1, filter: this.state.filter }),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_treeItemComponent__WEBPACK_IMPORTED_MODULE_3__["TreeItemComponent"], { globalState: this.props.globalState, extensibilityGroups: this.props.extensibilityGroups, contextMenuItems: pipelineContextMenus, selectedEntity: this.state.selectedEntity, items: pipelines, label: "Rendering pipelines", offset: 1, filter: this.state.filter }),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_treeItemComponent__WEBPACK_IMPORTED_MODULE_3__["TreeItemComponent"], { globalState: this.props.globalState, contextMenuItems: particleSystemsContextMenus, extensibilityGroups: this.props.extensibilityGroups, selectedEntity: this.state.selectedEntity, items: scene.particleSystems, label: "Particle systems", offset: 1, filter: this.state.filter }),
             guiElements && guiElements.length > 0 &&
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_treeItemComponent__WEBPACK_IMPORTED_MODULE_3__["TreeItemComponent"], { globalState: this.props.globalState, extensibilityGroups: this.props.extensibilityGroups, selectedEntity: this.state.selectedEntity, items: guiElements, label: "GUI", offset: 1, filter: this.state.filter }),
             scene.animationGroups.length > 0 &&
@@ -49998,7 +49996,7 @@ var SceneExplorerComponent = /** @class */ (function (_super) {
                 element.style.width = "300px";
             }, 150);
         }
-        return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](re_resizable__WEBPACK_IMPORTED_MODULE_4___default.a, { tabIndex: -1, id: "sceneExplorer", ref: "sceneExplorer", size: { height: "100%" }, minWidth: 300, maxWidth: 600, minHeight: "100%", enable: { top: false, right: true, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }, onKeyDown: function (keyEvent) { return _this.processKeys(keyEvent); } },
+        return (react__WEBPACK_IMPORTED_MODULE_1__["createElement"](re_resizable__WEBPACK_IMPORTED_MODULE_4___default.a, { tabIndex: -1, id: "sceneExplorer", ref: this.sceneExplorerRef, size: { height: "100%" }, minWidth: 300, maxWidth: 600, minHeight: "100%", enable: { top: false, right: true, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }, onKeyDown: function (keyEvent) { return _this.processKeys(keyEvent); } },
             !this.props.noHeader &&
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_headerComponent__WEBPACK_IMPORTED_MODULE_5__["HeaderComponent"], { title: "SCENE EXPLORER", noClose: this.props.noClose, noExpand: this.props.noExpand, noCommands: this.props.noCommands, onClose: function () { return _this.onClose(); }, onPopup: function () { return _this.onPopup(); } }),
             this.renderContent()));
@@ -50939,19 +50937,6 @@ __webpack_require__.r(__webpack_exports__);
 var Tools = /** @class */ (function () {
     function Tools() {
     }
-    Tools.StoreLocalBooleanSettings = function (key, value) {
-        if (typeof (Storage) !== "undefined") {
-            localStorage.setItem(key, value ? "true" : "false");
-        }
-    };
-    Tools.ReadLocalBooleanSettings = function (key, defaultValue) {
-        if (typeof (Storage) !== "undefined" && localStorage.getItem(key) !== null) {
-            return localStorage.getItem(key) === "true";
-        }
-        else {
-            return defaultValue;
-        }
-    };
     Tools.LookForItem = function (item, selectedEntity) {
         if (item === selectedEntity) {
             return true;

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


+ 30 - 10
dist/preview release/inspector/babylon.inspector.d.ts

@@ -18,15 +18,6 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    export class Tools {
-        static StoreLocalBooleanSettings(key: string, value: boolean): void;
-        static ReadLocalBooleanSettings(key: string, defaultValue: boolean): boolean;
-        static LookForItem(item: any, selectedEntity: any): boolean;
-        private static _RecursiveRemoveHiddenMeshesAndHoistChildren;
-        static SortAndFilter(parent: any, items: any[]): any[];
-    }
-}
-declare module INSPECTOR {
     export class GlobalState {
         onSelectionChangedObservable: BABYLON.Observable<any>;
         onPropertyChangedObservable: BABYLON.Observable<PropertyChangedEvent>;
@@ -118,7 +109,6 @@ declare module INSPECTOR {
         isExpanded: boolean;
         isHighlighted: boolean;
     }> {
-        private static _InMemoryStorage;
         constructor(props: ILineContainerComponentProps);
         switchExpandedState(): void;
         componentDidMount(): void;
@@ -471,6 +461,7 @@ declare module INSPECTOR {
     export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
         private static _IDGenerator;
         private _id;
+        private uploadInputRef;
         constructor(props: IFileButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -534,6 +525,7 @@ declare module INSPECTOR {
         channel: ChannelToDisplay;
         face: number;
     }> {
+        private canvasRef;
         constructor(props: ITextureLineComponentProps);
         shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
             channel: ChannelToDisplay;
@@ -585,6 +577,7 @@ declare module INSPECTOR {
     }
     export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps> {
         private _adtInstrumentation;
+        private textureLineRef;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
         updateTexture(file: File): void;
@@ -908,6 +901,7 @@ declare module INSPECTOR {
         private _onAnimationGroupPlayObserver;
         private _onAnimationGroupPauseObserver;
         private _onBeforeRenderObserver;
+        private timelineRef;
         constructor(props: IAnimationGroupGridComponentProps);
         disconnect(animationGroup: BABYLON.AnimationGroup): void;
         connect(animationGroup: BABYLON.AnimationGroup): void;
@@ -1185,6 +1179,7 @@ declare module INSPECTOR {
         private _runningAnimatable;
         private _onBeforeRenderObserver;
         private _isPlaying;
+        private timelineRef;
         constructor(props: IAnimationGridComponentProps);
         playOrPause(): void;
         componentDidMount(): void;
@@ -1425,6 +1420,7 @@ declare module INSPECTOR {
     export class FileMultipleButtonLineComponent extends React.Component<IFileMultipleButtonLineComponentProps> {
         private static _IDGenerator;
         private _id;
+        private uploadInputRef;
         constructor(props: IFileMultipleButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -1509,6 +1505,7 @@ declare module INSPECTOR {
         popupVisible: boolean;
     }> {
         private _popup;
+        private extensionRef;
         constructor(props: IExtensionsComponentProps);
         showPopup(): void;
         componentDidMount(): void;
@@ -1703,6 +1700,13 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    export class Tools {
+        static LookForItem(item: any, selectedEntity: any): boolean;
+        private static _RecursiveRemoveHiddenMeshesAndHoistChildren;
+        static SortAndFilter(parent: any, items: any[]): any[];
+    }
+}
+declare module INSPECTOR {
     export interface ITreeItemSelectableComponentProps {
         entity: any;
         selectedEntity?: any;
@@ -1822,6 +1826,7 @@ declare module INSPECTOR {
     }> {
         private _onSelectionChangeObserver;
         private _onNewSceneAddedObserver;
+        private sceneExplorerRef;
         private _once;
         private _hooked;
         private sceneMutationFunc;
@@ -1855,6 +1860,9 @@ declare module INSPECTOR {
     }
     export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps> {
         private _once;
+        private splitRef;
+        private topPartRef;
+        private bottomPartRef;
         constructor(props: IEmbedHostComponentProps);
         componentDidMount(): void;
         renderContent(): JSX.Element;
@@ -1891,4 +1899,16 @@ declare module INSPECTOR {
         private static _RemoveElementFromDOM;
         static Hide(): void;
     }
+}
+declare module INSPECTOR {
+    interface IParticleSystemPropertyGridComponentProps {
+        globalState: GlobalState;
+        system: BABYLON.IParticleSystem;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class ParticleSystemPropertyGridComponent extends React.Component<IParticleSystemPropertyGridComponentProps> {
+        constructor(props: IParticleSystemPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
 }

+ 66 - 20
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -18,15 +18,6 @@ declare module "babylonjs-inspector/components/replayRecorder" {
         export(): void;
     }
 }
-declare module "babylonjs-inspector/tools" {
-    export class Tools {
-        static StoreLocalBooleanSettings(key: string, value: boolean): void;
-        static ReadLocalBooleanSettings(key: string, defaultValue: boolean): boolean;
-        static LookForItem(item: any, selectedEntity: any): boolean;
-        private static _RecursiveRemoveHiddenMeshesAndHoistChildren;
-        static SortAndFilter(parent: any, items: any[]): any[];
-    }
-}
 declare module "babylonjs-inspector/components/globalState" {
     import { GLTFFileLoader, IGLTFLoaderExtension } from "babylonjs-loaders/glTF/index";
     import { IGLTFValidationResults } from "babylonjs-gltf2interface";
@@ -139,7 +130,6 @@ declare module "babylonjs-inspector/components/actionTabs/lineContainerComponent
         isExpanded: boolean;
         isHighlighted: boolean;
     }> {
-        private static _InMemoryStorage;
         constructor(props: ILineContainerComponentProps);
         switchExpandedState(): void;
         componentDidMount(): void;
@@ -545,6 +535,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/fileButtonLineCo
     export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
         private static _IDGenerator;
         private _id;
+        private uploadInputRef;
         constructor(props: IFileButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -622,6 +613,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/textureLineCompo
         channel: ChannelToDisplay;
         face: number;
     }> {
+        private canvasRef;
         constructor(props: ITextureLineComponentProps);
         shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
             channel: ChannelToDisplay;
@@ -683,6 +675,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/mat
     }
     export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps> {
         private _adtInstrumentation;
+        private textureLineRef;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
         updateTexture(file: File): void;
@@ -1129,6 +1122,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         private _onAnimationGroupPlayObserver;
         private _onAnimationGroupPauseObserver;
         private _onBeforeRenderObserver;
+        private timelineRef;
         constructor(props: IAnimationGroupGridComponentProps);
         disconnect(animationGroup: AnimationGroup): void;
         connect(animationGroup: AnimationGroup): void;
@@ -1533,6 +1527,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         private _runningAnimatable;
         private _onBeforeRenderObserver;
         private _isPlaying;
+        private timelineRef;
         constructor(props: IAnimationGridComponentProps);
         playOrPause(): void;
         componentDidMount(): void;
@@ -1833,6 +1828,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/fileMultipleButt
     export class FileMultipleButtonLineComponent extends React.Component<IFileMultipleButtonLineComponentProps> {
         private static _IDGenerator;
         private _id;
+        private uploadInputRef;
         constructor(props: IFileMultipleButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -1927,6 +1923,7 @@ declare module "babylonjs-inspector/components/sceneExplorer/extensionsComponent
         popupVisible: boolean;
     }> {
         private _popup;
+        private extensionRef;
         constructor(props: IExtensionsComponentProps);
         showPopup(): void;
         componentDidMount(): void;
@@ -2165,6 +2162,13 @@ declare module "babylonjs-inspector/components/sceneExplorer/treeItemSpecialized
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/tools" {
+    export class Tools {
+        static LookForItem(item: any, selectedEntity: any): boolean;
+        private static _RecursiveRemoveHiddenMeshesAndHoistChildren;
+        static SortAndFilter(parent: any, items: any[]): any[];
+    }
+}
 declare module "babylonjs-inspector/components/sceneExplorer/treeItemSelectableComponent" {
     import { Nullable } from "babylonjs/types";
     import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
@@ -2303,6 +2307,7 @@ declare module "babylonjs-inspector/components/sceneExplorer/sceneExplorerCompon
     }> {
         private _onSelectionChangeObserver;
         private _onNewSceneAddedObserver;
+        private sceneExplorerRef;
         private _once;
         private _hooked;
         private sceneMutationFunc;
@@ -2340,6 +2345,9 @@ declare module "babylonjs-inspector/components/embedHost/embedHostComponent" {
     }
     export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps> {
         private _once;
+        private splitRef;
+        private topPartRef;
+        private bottomPartRef;
         constructor(props: IEmbedHostComponentProps);
         componentDidMount(): void;
         renderContent(): JSX.Element;
@@ -2385,6 +2393,24 @@ declare module "babylonjs-inspector/inspector" {
 declare module "babylonjs-inspector/index" {
     export * from "babylonjs-inspector/inspector";
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent" {
+    import * as React from "react";
+    import { Observable } from "babylonjs/Misc/observable";
+    import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
+    import { LockObject } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/lockObject";
+    import { GlobalState } from "babylonjs-inspector/components/globalState";
+    import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
+    interface IParticleSystemPropertyGridComponentProps {
+        globalState: GlobalState;
+        system: IParticleSystem;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    }
+    export class ParticleSystemPropertyGridComponent extends React.Component<IParticleSystemPropertyGridComponentProps> {
+        constructor(props: IParticleSystemPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/legacy/legacy" {
     export * from "babylonjs-inspector/index";
 }
@@ -2411,15 +2437,6 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
-    export class Tools {
-        static StoreLocalBooleanSettings(key: string, value: boolean): void;
-        static ReadLocalBooleanSettings(key: string, defaultValue: boolean): boolean;
-        static LookForItem(item: any, selectedEntity: any): boolean;
-        private static _RecursiveRemoveHiddenMeshesAndHoistChildren;
-        static SortAndFilter(parent: any, items: any[]): any[];
-    }
-}
-declare module INSPECTOR {
     export class GlobalState {
         onSelectionChangedObservable: BABYLON.Observable<any>;
         onPropertyChangedObservable: BABYLON.Observable<PropertyChangedEvent>;
@@ -2511,7 +2528,6 @@ declare module INSPECTOR {
         isExpanded: boolean;
         isHighlighted: boolean;
     }> {
-        private static _InMemoryStorage;
         constructor(props: ILineContainerComponentProps);
         switchExpandedState(): void;
         componentDidMount(): void;
@@ -2864,6 +2880,7 @@ declare module INSPECTOR {
     export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
         private static _IDGenerator;
         private _id;
+        private uploadInputRef;
         constructor(props: IFileButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -2927,6 +2944,7 @@ declare module INSPECTOR {
         channel: ChannelToDisplay;
         face: number;
     }> {
+        private canvasRef;
         constructor(props: ITextureLineComponentProps);
         shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
             channel: ChannelToDisplay;
@@ -2978,6 +2996,7 @@ declare module INSPECTOR {
     }
     export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps> {
         private _adtInstrumentation;
+        private textureLineRef;
         constructor(props: ITexturePropertyGridComponentProps);
         componentWillUnmount(): void;
         updateTexture(file: File): void;
@@ -3301,6 +3320,7 @@ declare module INSPECTOR {
         private _onAnimationGroupPlayObserver;
         private _onAnimationGroupPauseObserver;
         private _onBeforeRenderObserver;
+        private timelineRef;
         constructor(props: IAnimationGroupGridComponentProps);
         disconnect(animationGroup: BABYLON.AnimationGroup): void;
         connect(animationGroup: BABYLON.AnimationGroup): void;
@@ -3578,6 +3598,7 @@ declare module INSPECTOR {
         private _runningAnimatable;
         private _onBeforeRenderObserver;
         private _isPlaying;
+        private timelineRef;
         constructor(props: IAnimationGridComponentProps);
         playOrPause(): void;
         componentDidMount(): void;
@@ -3818,6 +3839,7 @@ declare module INSPECTOR {
     export class FileMultipleButtonLineComponent extends React.Component<IFileMultipleButtonLineComponentProps> {
         private static _IDGenerator;
         private _id;
+        private uploadInputRef;
         constructor(props: IFileMultipleButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -3902,6 +3924,7 @@ declare module INSPECTOR {
         popupVisible: boolean;
     }> {
         private _popup;
+        private extensionRef;
         constructor(props: IExtensionsComponentProps);
         showPopup(): void;
         componentDidMount(): void;
@@ -4096,6 +4119,13 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    export class Tools {
+        static LookForItem(item: any, selectedEntity: any): boolean;
+        private static _RecursiveRemoveHiddenMeshesAndHoistChildren;
+        static SortAndFilter(parent: any, items: any[]): any[];
+    }
+}
+declare module INSPECTOR {
     export interface ITreeItemSelectableComponentProps {
         entity: any;
         selectedEntity?: any;
@@ -4215,6 +4245,7 @@ declare module INSPECTOR {
     }> {
         private _onSelectionChangeObserver;
         private _onNewSceneAddedObserver;
+        private sceneExplorerRef;
         private _once;
         private _hooked;
         private sceneMutationFunc;
@@ -4248,6 +4279,9 @@ declare module INSPECTOR {
     }
     export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps> {
         private _once;
+        private splitRef;
+        private topPartRef;
+        private bottomPartRef;
         constructor(props: IEmbedHostComponentProps);
         componentDidMount(): void;
         renderContent(): JSX.Element;
@@ -4284,4 +4318,16 @@ declare module INSPECTOR {
         private static _RemoveElementFromDOM;
         static Hide(): void;
     }
+}
+declare module INSPECTOR {
+    interface IParticleSystemPropertyGridComponentProps {
+        globalState: GlobalState;
+        system: BABYLON.IParticleSystem;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class ParticleSystemPropertyGridComponent extends React.Component<IParticleSystemPropertyGridComponentProps> {
+        constructor(props: IParticleSystemPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
 }

+ 27 - 13
dist/preview release/nodeEditor/babylon.nodeEditor.d.ts

@@ -8,15 +8,6 @@ declare module NODEEDITOR {
     }
 }
 declare module NODEEDITOR {
-    export class DataStorage {
-        private static _InMemoryStorage;
-        static ReadBoolean(key: string, defaultValue: boolean): boolean;
-        static StoreBoolean(key: string, value: boolean): void;
-        static ReadNumber(key: string, defaultValue: number): number;
-        static StoreNumber(key: string, value: number): void;
-    }
-}
-declare module NODEEDITOR {
     interface ILogComponentProps {
         globalState: GlobalState;
     }
@@ -195,26 +186,46 @@ declare module NODEEDITOR {
         private initResizing;
         private cleanUpResizing;
         private updateMinHeightWithComments;
+        private _isResizingTop;
+        private _isResizingRight;
+        private _isResizingBottom;
+        private _isResizingLeft;
         private _onRightHandlePointerDown;
         private _onRightHandlePointerMove;
         private _moveRightHandle;
-        private _expandRight;
         private _onRightHandlePointerUp;
         private _onBottomHandlePointerDown;
         private _onBottomHandlePointerMove;
         private _moveBottomHandle;
-        private _expandBottom;
         private _onBottomHandlePointerUp;
         private _onLeftHandlePointerDown;
         private _onLeftHandlePointerMove;
         private _moveLeftHandle;
-        private _expandLeft;
         private _onLeftHandlePointerUp;
         private _onTopHandlePointerDown;
         private _onTopHandlePointerMove;
         private _moveTopHandle;
-        private _expandTop;
         private _onTopHandlePointerUp;
+        private _onTopRightHandlePointerDown;
+        private _onTopRightHandlePointerMove;
+        private _moveTopRightHandle;
+        private _onTopRightHandlePointerUp;
+        private _onBottomRightHandlePointerDown;
+        private _onBottomRightHandlePointerMove;
+        private _moveBottomRightHandle;
+        private _onBottomRightHandlePointerUp;
+        private _onBottomLeftHandlePointerDown;
+        private _onBottomLeftHandlePointerMove;
+        private _moveBottomLeftHandle;
+        private _onBottomLeftHandlePointerUp;
+        private _onTopLeftHandlePointerDown;
+        private _onTopLeftHandlePointerMove;
+        private _moveTopLeftHandle;
+        private _onTopLeftHandlePointerUp;
+        private _expandLeft;
+        private _expandTop;
+        private _expandRight;
+        private _expandBottom;
         dispose(): void;
         serialize(): IFrameData;
         export(): void;
@@ -881,6 +892,7 @@ declare module NODEEDITOR {
         accept: string;
     }
     export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        private uploadRef;
         constructor(props: IFileButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -994,6 +1006,7 @@ declare module NODEEDITOR {
         face: number;
     }
     export class TextureLineComponent extends React.Component<ITextureLineComponentProps, ITextureLineComponentState> {
+        private canvasRef;
         constructor(props: ITextureLineComponentProps);
         shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
             displayRed: boolean;
@@ -1256,6 +1269,7 @@ declare module NODEEDITOR {
     }
     export class PreviewMeshControlComponent extends React.Component<IPreviewMeshControlComponent> {
         private colorInputRef;
+        private filePickerRef;
         constructor(props: IPreviewMeshControlComponent);
         changeMeshType(newOne: PreviewMeshType): void;
         useCustomMesh(evt: any): void;

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


File diff suppressed because it is too large
+ 389 - 215
dist/preview release/nodeEditor/babylon.nodeEditor.max.js


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


+ 54 - 26
dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts

@@ -69,15 +69,6 @@ declare module "babylonjs-node-editor/blockTools" {
         static GetStringFromConnectionNodeType(type: NodeMaterialBlockConnectionPointTypes): "Float" | "Vector2" | "Vector3" | "Vector4" | "Matrix" | "Color3" | "Color4" | "";
     }
 }
-declare module "babylonjs-node-editor/dataStorage" {
-    export class DataStorage {
-        private static _InMemoryStorage;
-        static ReadBoolean(key: string, defaultValue: boolean): boolean;
-        static StoreBoolean(key: string, value: boolean): void;
-        static ReadNumber(key: string, defaultValue: number): number;
-        static StoreNumber(key: string, value: number): void;
-    }
-}
 declare module "babylonjs-node-editor/components/log/logComponent" {
     import * as React from "react";
     import { GlobalState } from "babylonjs-node-editor/globalState";
@@ -275,26 +266,46 @@ declare module "babylonjs-node-editor/diagram/graphFrame" {
         private initResizing;
         private cleanUpResizing;
         private updateMinHeightWithComments;
+        private _isResizingTop;
+        private _isResizingRight;
+        private _isResizingBottom;
+        private _isResizingLeft;
         private _onRightHandlePointerDown;
         private _onRightHandlePointerMove;
         private _moveRightHandle;
-        private _expandRight;
         private _onRightHandlePointerUp;
         private _onBottomHandlePointerDown;
         private _onBottomHandlePointerMove;
         private _moveBottomHandle;
-        private _expandBottom;
         private _onBottomHandlePointerUp;
         private _onLeftHandlePointerDown;
         private _onLeftHandlePointerMove;
         private _moveLeftHandle;
-        private _expandLeft;
         private _onLeftHandlePointerUp;
         private _onTopHandlePointerDown;
         private _onTopHandlePointerMove;
         private _moveTopHandle;
-        private _expandTop;
         private _onTopHandlePointerUp;
+        private _onTopRightHandlePointerDown;
+        private _onTopRightHandlePointerMove;
+        private _moveTopRightHandle;
+        private _onTopRightHandlePointerUp;
+        private _onBottomRightHandlePointerDown;
+        private _onBottomRightHandlePointerMove;
+        private _moveBottomRightHandle;
+        private _onBottomRightHandlePointerUp;
+        private _onBottomLeftHandlePointerDown;
+        private _onBottomLeftHandlePointerMove;
+        private _moveBottomLeftHandle;
+        private _onBottomLeftHandlePointerUp;
+        private _onTopLeftHandlePointerDown;
+        private _onTopLeftHandlePointerMove;
+        private _moveTopLeftHandle;
+        private _onTopLeftHandlePointerUp;
+        private _expandLeft;
+        private _expandTop;
+        private _expandRight;
+        private _expandBottom;
         dispose(): void;
         serialize(): IFrameData;
         export(): void;
@@ -1075,6 +1086,7 @@ declare module "babylonjs-node-editor/sharedComponents/fileButtonLineComponent"
         accept: string;
     }
     export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        private uploadRef;
         constructor(props: IFileButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -1210,6 +1222,7 @@ declare module "babylonjs-node-editor/sharedComponents/textureLineComponent" {
         face: number;
     }
     export class TextureLineComponent extends React.Component<ITextureLineComponentProps, ITextureLineComponentState> {
+        private canvasRef;
         constructor(props: ITextureLineComponentProps);
         shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
             displayRed: boolean;
@@ -1516,6 +1529,7 @@ declare module "babylonjs-node-editor/components/preview/previewMeshControlCompo
     }
     export class PreviewMeshControlComponent extends React.Component<IPreviewMeshControlComponent> {
         private colorInputRef;
+        private filePickerRef;
         constructor(props: IPreviewMeshControlComponent);
         changeMeshType(newOne: PreviewMeshType): void;
         useCustomMesh(evt: any): void;
@@ -1667,15 +1681,6 @@ declare module NODEEDITOR {
     }
 }
 declare module NODEEDITOR {
-    export class DataStorage {
-        private static _InMemoryStorage;
-        static ReadBoolean(key: string, defaultValue: boolean): boolean;
-        static StoreBoolean(key: string, value: boolean): void;
-        static ReadNumber(key: string, defaultValue: number): number;
-        static StoreNumber(key: string, value: number): void;
-    }
-}
-declare module NODEEDITOR {
     interface ILogComponentProps {
         globalState: GlobalState;
     }
@@ -1854,26 +1859,46 @@ declare module NODEEDITOR {
         private initResizing;
         private cleanUpResizing;
         private updateMinHeightWithComments;
+        private _isResizingTop;
+        private _isResizingRight;
+        private _isResizingBottom;
+        private _isResizingLeft;
         private _onRightHandlePointerDown;
         private _onRightHandlePointerMove;
         private _moveRightHandle;
-        private _expandRight;
         private _onRightHandlePointerUp;
         private _onBottomHandlePointerDown;
         private _onBottomHandlePointerMove;
         private _moveBottomHandle;
-        private _expandBottom;
         private _onBottomHandlePointerUp;
         private _onLeftHandlePointerDown;
         private _onLeftHandlePointerMove;
         private _moveLeftHandle;
-        private _expandLeft;
         private _onLeftHandlePointerUp;
         private _onTopHandlePointerDown;
         private _onTopHandlePointerMove;
         private _moveTopHandle;
-        private _expandTop;
         private _onTopHandlePointerUp;
+        private _onTopRightHandlePointerDown;
+        private _onTopRightHandlePointerMove;
+        private _moveTopRightHandle;
+        private _onTopRightHandlePointerUp;
+        private _onBottomRightHandlePointerDown;
+        private _onBottomRightHandlePointerMove;
+        private _moveBottomRightHandle;
+        private _onBottomRightHandlePointerUp;
+        private _onBottomLeftHandlePointerDown;
+        private _onBottomLeftHandlePointerMove;
+        private _moveBottomLeftHandle;
+        private _onBottomLeftHandlePointerUp;
+        private _onTopLeftHandlePointerDown;
+        private _onTopLeftHandlePointerMove;
+        private _moveTopLeftHandle;
+        private _onTopLeftHandlePointerUp;
+        private _expandLeft;
+        private _expandTop;
+        private _expandRight;
+        private _expandBottom;
         dispose(): void;
         serialize(): IFrameData;
         export(): void;
@@ -2540,6 +2565,7 @@ declare module NODEEDITOR {
         accept: string;
     }
     export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+        private uploadRef;
         constructor(props: IFileButtonLineComponentProps);
         onChange(evt: any): void;
         render(): JSX.Element;
@@ -2653,6 +2679,7 @@ declare module NODEEDITOR {
         face: number;
     }
     export class TextureLineComponent extends React.Component<ITextureLineComponentProps, ITextureLineComponentState> {
+        private canvasRef;
         constructor(props: ITextureLineComponentProps);
         shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: {
             displayRed: boolean;
@@ -2915,6 +2942,7 @@ declare module NODEEDITOR {
     }
     export class PreviewMeshControlComponent extends React.Component<IPreviewMeshControlComponent> {
         private colorInputRef;
+        private filePickerRef;
         constructor(props: IPreviewMeshControlComponent);
         changeMeshType(newOne: PreviewMeshType): void;
         useCustomMesh(evt: any): void;

+ 1 - 1
dist/preview release/packagesSizeBaseLine.json

@@ -1 +1 @@
-{"thinEngineOnly":112271,"engineOnly":148874,"sceneOnly":504363,"minGridMaterial":635054,"minStandardMaterial":775014}
+{"thinEngineOnly":113669,"engineOnly":150272,"sceneOnly":505908,"minGridMaterial":636668,"minStandardMaterial":776628}

+ 11 - 11
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -7,7 +7,7 @@
 		exports["babylonjs-serializers"] = factory(require("babylonjs"));
 	else
 		root["SERIALIZERS"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_PBR_pbrMaterial__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -366,7 +366,7 @@ module.exports = g;
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KHR_lights_punctual", function() { return KHR_lights_punctual; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _glTFExporter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../glTFExporter */ "./glTF/2.0/glTFExporter.ts");
 /* harmony import */ var _glTFUtilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../glTFUtilities */ "./glTF/2.0/glTFUtilities.ts");
@@ -564,7 +564,7 @@ _glTFExporter__WEBPACK_IMPORTED_MODULE_1__["_Exporter"].RegisterExtension(NAME,
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KHR_materials_sheen", function() { return KHR_materials_sheen; });
 /* harmony import */ var _glTFExporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../glTFExporter */ "./glTF/2.0/glTFExporter.ts");
-/* harmony import */ var babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Materials/PBR/pbrMaterial */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Materials/PBR/pbrMaterial */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -664,7 +664,7 @@ _glTFExporter__WEBPACK_IMPORTED_MODULE_0__["_Exporter"].RegisterExtension(NAME,
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KHR_texture_transform", function() { return KHR_texture_transform; });
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _glTFExporter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../glTFExporter */ "./glTF/2.0/glTFExporter.ts");
 /* harmony import */ var _shaders_textureTransform_fragment__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../shaders/textureTransform.fragment */ "./glTF/2.0/shaders/textureTransform.fragment.ts");
@@ -848,7 +848,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_GLTFAnimation", function() { return _GLTFAnimation; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _glTFUtilities__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./glTFUtilities */ "./glTF/2.0/glTFUtilities.ts");
 
@@ -1591,7 +1591,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Exporter", function() { return _Exporter; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_BinaryWriter", function() { return _BinaryWriter; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _glTFMaterialExporter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./glTFMaterialExporter */ "./glTF/2.0/glTFMaterialExporter.ts");
 /* harmony import */ var _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./glTFUtilities */ "./glTF/2.0/glTFUtilities.ts");
@@ -3132,7 +3132,7 @@ var __IGLTFExporterExtensionV2 = 0; // I am here to allow dts to be created
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_GLTFMaterialExporter", function() { return _GLTFMaterialExporter; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -4331,7 +4331,7 @@ var GLTF2Export = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_GLTFUtilities", function() { return _GLTFUtilities; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -4575,7 +4575,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "textureTransformPixelShader", function() { return textureTransformPixelShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'textureTransformPixelShader';
@@ -4689,14 +4689,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Maths/math.vector":
+/***/ "babylonjs/Materials/PBR/pbrMaterial":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_PBR_pbrMaterial__;
 
 /***/ })
 

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


+ 4 - 4
dist/preview release/serializers/babylon.objSerializer.js

@@ -7,7 +7,7 @@
 		exports["babylonjs-serializers"] = factory(require("babylonjs"));
 	else
 		root["SERIALIZERS"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_PBR_pbrMaterial__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -154,7 +154,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "OBJExport", function() { return OBJExport; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -334,14 +334,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Maths/math.vector":
+/***/ "babylonjs/Materials/PBR/pbrMaterial":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_PBR_pbrMaterial__;
 
 /***/ })
 

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


+ 13 - 13
dist/preview release/serializers/babylonjs.serializers.js

@@ -7,7 +7,7 @@
 		exports["babylonjs-serializers"] = factory(require("babylonjs"));
 	else
 		root["SERIALIZERS"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_PBR_pbrMaterial__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -383,7 +383,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "OBJExport", function() { return OBJExport; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -544,7 +544,7 @@ var OBJExport = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KHR_lights_punctual", function() { return KHR_lights_punctual; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _glTFExporter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../glTFExporter */ "./glTF/2.0/glTFExporter.ts");
 /* harmony import */ var _glTFUtilities__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../glTFUtilities */ "./glTF/2.0/glTFUtilities.ts");
@@ -742,7 +742,7 @@ _glTFExporter__WEBPACK_IMPORTED_MODULE_1__["_Exporter"].RegisterExtension(NAME,
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KHR_materials_sheen", function() { return KHR_materials_sheen; });
 /* harmony import */ var _glTFExporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../glTFExporter */ "./glTF/2.0/glTFExporter.ts");
-/* harmony import */ var babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Materials/PBR/pbrMaterial */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Materials/PBR/pbrMaterial */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_PBR_pbrMaterial__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -842,7 +842,7 @@ _glTFExporter__WEBPACK_IMPORTED_MODULE_0__["_Exporter"].RegisterExtension(NAME,
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KHR_texture_transform", function() { return KHR_texture_transform; });
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _glTFExporter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../glTFExporter */ "./glTF/2.0/glTFExporter.ts");
 /* harmony import */ var _shaders_textureTransform_fragment__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../shaders/textureTransform.fragment */ "./glTF/2.0/shaders/textureTransform.fragment.ts");
@@ -1026,7 +1026,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_GLTFAnimation", function() { return _GLTFAnimation; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _glTFUtilities__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./glTFUtilities */ "./glTF/2.0/glTFUtilities.ts");
 
@@ -1769,7 +1769,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_Exporter", function() { return _Exporter; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_BinaryWriter", function() { return _BinaryWriter; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _glTFMaterialExporter__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./glTFMaterialExporter */ "./glTF/2.0/glTFMaterialExporter.ts");
 /* harmony import */ var _glTFUtilities__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./glTFUtilities */ "./glTF/2.0/glTFUtilities.ts");
@@ -3310,7 +3310,7 @@ var __IGLTFExporterExtensionV2 = 0; // I am here to allow dts to be created
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_GLTFMaterialExporter", function() { return _GLTFMaterialExporter; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -4509,7 +4509,7 @@ var GLTF2Export = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "_GLTFUtilities", function() { return _GLTFUtilities; });
-/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math.vector */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -4753,7 +4753,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "textureTransformPixelShader", function() { return textureTransformPixelShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'textureTransformPixelShader';
@@ -5089,7 +5089,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "STLExport", function() { return STLExport; });
-/* harmony import */ var babylonjs_Meshes_buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Meshes/buffer */ "babylonjs/Maths/math.vector");
+/* harmony import */ var babylonjs_Meshes_buffer__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Meshes/buffer */ "babylonjs/Materials/PBR/pbrMaterial");
 /* harmony import */ var babylonjs_Meshes_buffer__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_buffer__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -5201,14 +5201,14 @@ var STLExport = /** @class */ (function () {
 
 /***/ }),
 
-/***/ "babylonjs/Maths/math.vector":
+/***/ "babylonjs/Materials/PBR/pbrMaterial":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Materials_PBR_pbrMaterial__;
 
 /***/ })
 

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


+ 261 - 2
dist/preview release/viewer/babylon.module.d.ts

@@ -1084,6 +1084,10 @@ declare module "babylonjs/Engines/IPipelineContext" {
          */
         isReady: boolean;
         /** @hidden */
+        _getVertexShaderCode(): string | null;
+        /** @hidden */
+        _getFragmentShaderCode(): string | null;
+        /** @hidden */
         _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
     }
 }
@@ -7223,6 +7227,8 @@ declare module "babylonjs/Engines/WebGL/webGLPipelineContext" {
         get isAsync(): boolean;
         get isReady(): boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
+        _getVertexShaderCode(): string | null;
+        _getFragmentShaderCode(): string | null;
     }
 }
 declare module "babylonjs/Engines/Extensions/engine.uniformBuffer" {
@@ -13480,6 +13486,11 @@ declare module "babylonjs/Particles/IParticleSystem" {
          */
         isReady(): boolean;
         /**
+         * Returns the string "ParticleSystem"
+         * @returns a string containing the class name
+         */
+        getClassName(): string;
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color1 defines the color to affect to the specified gradient
@@ -30943,6 +30954,10 @@ declare module "babylonjs/Materials/effect" {
          */
         static ShadersRepository: string;
         /**
+         * Enable logging of the shader code when a compilation error occurs
+         */
+        static LogShaderCodeOnCompilationError: boolean;
+        /**
          * Name of the effect.
          */
         name: any;
@@ -31126,6 +31141,7 @@ declare module "babylonjs/Materials/effect" {
          * @hidden
          */
         _prepareEffect(): void;
+        private _getShaderCodeAndErrorLine;
         private _processCompilationErrors;
         /**
          * Checks if the effect is supported. (Must be called after compilation)
@@ -32506,6 +32522,8 @@ declare module "babylonjs/Engines/thinEngine" {
         protected static _ConcatenateShader(source: string, defines: Nullable<string>, shaderVersion?: string): string;
         private _compileShader;
         private _compileRawShader;
+        /** @hidden */
+        _getShaderSource(shader: WebGLShader): Nullable<string>;
         /**
          * Directly creates a webGL program
          * @param pipelineContext  defines the pipeline context to attach to
@@ -33608,6 +33626,23 @@ declare module "babylonjs/Audio/audioEngine" {
          * This is helpful to resume play once browser policies have been satisfied.
          */
         unlock(): void;
+        /**
+         * Gets the global volume sets on the master gain.
+         * @returns the global volume if set or -1 otherwise
+         */
+        getGlobalVolume(): number;
+        /**
+         * Sets the global volume of your experience (sets on the master gain).
+         * @param newVolume Defines the new global volume of the application
+         */
+        setGlobalVolume(newVolume: number): void;
+        /**
+         * Connect the audio engine to an audio analyser allowing some amazing
+         * synchornization between the sounds/music and your visualization (VuMeter for instance).
+         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+         * @param analyser The analyser to connect to the engine
+         */
+        connectToAnalyser(analyser: Analyser): void;
     }
     /**
      * This represents the default audio engine used in babylon.
@@ -34999,7 +35034,8 @@ declare module "babylonjs/Misc/tools" {
          * It can be a string if the expected behavior is identical in the entire app.
          * Or a callback to be able to set it per url or on a group of them (in case of Video source for instance)
          */
-        static CorsBehavior: string | ((url: string | string[]) => string);
+        static get CorsBehavior(): string | ((url: string | string[]) => string);
+        static set CorsBehavior(value: string | ((url: string | string[]) => string));
         /**
          * Gets or sets a global variable indicating if fallback texture must be used when a texture cannot be loaded
          * @ignorenaming
@@ -64876,6 +64912,51 @@ declare module "babylonjs/Meshes/meshSimplification" {
         /** Quadratic error decimation */
         QUADRATIC = 0
     }
+    /**
+     * An implementation of the Quadratic Error simplification algorithm.
+     * Original paper : http://www1.cs.columbia.edu/~cs4162/html05s/garland97.pdf
+     * Ported mostly from QSlim and http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html to babylon JS
+     * @author RaananW
+     * @see http://doc.babylonjs.com/how_to/in-browser_mesh_simplification
+     */
+    export class QuadraticErrorSimplification implements ISimplifier {
+        private _mesh;
+        private triangles;
+        private vertices;
+        private references;
+        private _reconstructedMesh;
+        /** Gets or sets the number pf sync interations */
+        syncIterations: number;
+        /** Gets or sets the aggressiveness of the simplifier */
+        aggressiveness: number;
+        /** Gets or sets the number of allowed iterations for decimation */
+        decimationIterations: number;
+        /** Gets or sets the espilon to use for bounding box computation */
+        boundingBoxEpsilon: number;
+        /**
+         * Creates a new QuadraticErrorSimplification
+         * @param _mesh defines the target mesh
+         */
+        constructor(_mesh: Mesh);
+        /**
+         * Simplification of a given mesh according to the given settings.
+         * Since this requires computation, it is assumed that the function runs async.
+         * @param settings The settings of the simplification, including quality and distance
+         * @param successCallback A callback that will be called after the mesh was simplified.
+         */
+        simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void): void;
+        private runDecimation;
+        private initWithMesh;
+        private init;
+        private reconstructMesh;
+        private initDecimatedMesh;
+        private isFlipped;
+        private updateTriangles;
+        private identifyBorder;
+        private updateMesh;
+        private vertexError;
+        private calculateError;
+    }
 }
 declare module "babylonjs/Meshes/meshSimplificationSceneComponent" {
     import { Scene } from "babylonjs/scene";
@@ -72298,6 +72379,54 @@ declare module "babylonjs/Misc/dataReader" {
         skipBytes(byteLength: number): void;
     }
 }
+declare module "babylonjs/Misc/dataStorage" {
+    /**
+     * Class for storing data to local storage if available or in-memory storage otherwise
+     */
+    export class DataStorage {
+        private static _Storage;
+        private static _GetStorage;
+        /**
+         * Reads a string from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The string value
+         */
+        static ReadString(key: string, defaultValue: string): string;
+        /**
+         * Writes a string to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteString(key: string, value: string): void;
+        /**
+         * Reads a boolean from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The boolean value
+         */
+        static ReadBoolean(key: string, defaultValue: boolean): boolean;
+        /**
+         * Writes a boolean to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteBoolean(key: string, value: boolean): void;
+        /**
+         * Reads a number from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The number value
+         */
+        static ReadNumber(key: string, defaultValue: number): number;
+        /**
+         * Writes a number to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteNumber(key: string, value: number): void;
+    }
+}
 declare module "babylonjs/Misc/index" {
     export * from "babylonjs/Misc/andOrNotEvaluator";
     export * from "babylonjs/Misc/assetsManager";
@@ -72348,6 +72477,7 @@ declare module "babylonjs/Misc/index" {
     export * from "babylonjs/Misc/dataReader";
     export * from "babylonjs/Misc/minMaxReducer";
     export * from "babylonjs/Misc/depthReducer";
+    export * from "babylonjs/Misc/dataStorage";
 }
 declare module "babylonjs/XR/features/WebXRHitTestLegacy" {
     import { WebXRSessionManager } from "babylonjs/XR/webXRSessionManager";
@@ -74361,6 +74491,10 @@ declare module BABYLON {
          */
         isReady: boolean;
         /** @hidden */
+        _getVertexShaderCode(): string | null;
+        /** @hidden */
+        _getFragmentShaderCode(): string | null;
+        /** @hidden */
         _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
     }
 }
@@ -80438,6 +80572,8 @@ declare module BABYLON {
         get isAsync(): boolean;
         get isReady(): boolean;
         _handlesSpectorRebuildCallback(onCompiled: (program: WebGLProgram) => void): void;
+        _getVertexShaderCode(): string | null;
+        _getFragmentShaderCode(): string | null;
     }
 }
 declare module BABYLON {
@@ -86437,6 +86573,11 @@ declare module BABYLON {
          */
         isReady(): boolean;
         /**
+         * Returns the string "ParticleSystem"
+         * @returns a string containing the class name
+         */
+        getClassName(): string;
+        /**
          * Adds a new color gradient
          * @param gradient defines the gradient to use (between 0 and 1)
          * @param color1 defines the color to affect to the specified gradient
@@ -103285,6 +103426,10 @@ declare module BABYLON {
          */
         static ShadersRepository: string;
         /**
+         * Enable logging of the shader code when a compilation error occurs
+         */
+        static LogShaderCodeOnCompilationError: boolean;
+        /**
          * Name of the effect.
          */
         name: any;
@@ -103468,6 +103613,7 @@ declare module BABYLON {
          * @hidden
          */
         _prepareEffect(): void;
+        private _getShaderCodeAndErrorLine;
         private _processCompilationErrors;
         /**
          * Checks if the effect is supported. (Must be called after compilation)
@@ -104813,6 +104959,8 @@ declare module BABYLON {
         protected static _ConcatenateShader(source: string, defines: Nullable<string>, shaderVersion?: string): string;
         private _compileShader;
         private _compileRawShader;
+        /** @hidden */
+        _getShaderSource(shader: WebGLShader): Nullable<string>;
         /**
          * Directly creates a webGL program
          * @param pipelineContext  defines the pipeline context to attach to
@@ -105903,6 +106051,23 @@ declare module BABYLON {
          * This is helpful to resume play once browser policies have been satisfied.
          */
         unlock(): void;
+        /**
+         * Gets the global volume sets on the master gain.
+         * @returns the global volume if set or -1 otherwise
+         */
+        getGlobalVolume(): number;
+        /**
+         * Sets the global volume of your experience (sets on the master gain).
+         * @param newVolume Defines the new global volume of the application
+         */
+        setGlobalVolume(newVolume: number): void;
+        /**
+         * Connect the audio engine to an audio analyser allowing some amazing
+         * synchornization between the sounds/music and your visualization (VuMeter for instance).
+         * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+         * @param analyser The analyser to connect to the engine
+         */
+        connectToAnalyser(analyser: Analyser): void;
     }
     /**
      * This represents the default audio engine used in babylon.
@@ -107261,7 +107426,8 @@ declare module BABYLON {
          * It can be a string if the expected behavior is identical in the entire app.
          * Or a callback to be able to set it per url or on a group of them (in case of Video source for instance)
          */
-        static CorsBehavior: string | ((url: string | string[]) => string);
+        static get CorsBehavior(): string | ((url: string | string[]) => string);
+        static set CorsBehavior(value: string | ((url: string | string[]) => string));
         /**
          * Gets or sets a global variable indicating if fallback texture must be used when a texture cannot be loaded
          * @ignorenaming
@@ -135047,6 +135213,51 @@ declare module BABYLON {
         /** Quadratic error decimation */
         QUADRATIC = 0
     }
+    /**
+     * An implementation of the Quadratic Error simplification algorithm.
+     * Original paper : http://www1.cs.columbia.edu/~cs4162/html05s/garland97.pdf
+     * Ported mostly from QSlim and http://voxels.blogspot.de/2014/05/quadric-mesh-simplification-with-source.html to babylon JS
+     * @author RaananW
+     * @see http://doc.babylonjs.com/how_to/in-browser_mesh_simplification
+     */
+    export class QuadraticErrorSimplification implements ISimplifier {
+        private _mesh;
+        private triangles;
+        private vertices;
+        private references;
+        private _reconstructedMesh;
+        /** Gets or sets the number pf sync interations */
+        syncIterations: number;
+        /** Gets or sets the aggressiveness of the simplifier */
+        aggressiveness: number;
+        /** Gets or sets the number of allowed iterations for decimation */
+        decimationIterations: number;
+        /** Gets or sets the espilon to use for bounding box computation */
+        boundingBoxEpsilon: number;
+        /**
+         * Creates a new QuadraticErrorSimplification
+         * @param _mesh defines the target mesh
+         */
+        constructor(_mesh: Mesh);
+        /**
+         * Simplification of a given mesh according to the given settings.
+         * Since this requires computation, it is assumed that the function runs async.
+         * @param settings The settings of the simplification, including quality and distance
+         * @param successCallback A callback that will be called after the mesh was simplified.
+         */
+        simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void): void;
+        private runDecimation;
+        private initWithMesh;
+        private init;
+        private reconstructMesh;
+        private initDecimatedMesh;
+        private isFlipped;
+        private updateTriangles;
+        private identifyBorder;
+        private updateMesh;
+        private vertexError;
+        private calculateError;
+    }
 }
 declare module BABYLON {
         interface Scene {
@@ -141908,6 +142119,54 @@ declare module BABYLON {
 }
 declare module BABYLON {
     /**
+     * Class for storing data to local storage if available or in-memory storage otherwise
+     */
+    export class DataStorage {
+        private static _Storage;
+        private static _GetStorage;
+        /**
+         * Reads a string from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The string value
+         */
+        static ReadString(key: string, defaultValue: string): string;
+        /**
+         * Writes a string to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteString(key: string, value: string): void;
+        /**
+         * Reads a boolean from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The boolean value
+         */
+        static ReadBoolean(key: string, defaultValue: boolean): boolean;
+        /**
+         * Writes a boolean to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteBoolean(key: string, value: boolean): void;
+        /**
+         * Reads a number from the data storage
+         * @param key The key to read
+         * @param defaultValue The value if the key doesn't exist
+         * @returns The number value
+         */
+        static ReadNumber(key: string, defaultValue: number): number;
+        /**
+         * Writes a number to the data storage
+         * @param key The key to write
+         * @param value The value to write
+         */
+        static WriteNumber(key: string, value: number): void;
+    }
+}
+declare module BABYLON {
+    /**
      * Options used for hit testing
      */
     export interface IWebXRHitTestOptions {

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


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

@@ -5,12 +5,24 @@
 ## Updates
 
 ### General
+- NME Frames are now resizable from the corners ([Kyle Belfort](https://github.com/belfortk)
+- Refactored React refs from old string API to React.createRef() API ([Kyle Belfort](https://github.com/belfortk)
 
 ### Engine
 
+- Allow logging of shader code when a compilation error occurs ([Popov72](https://github.com/Popov72))
+
 ### Cameras
+
 - Added flag to TargetCamera to invert rotation direction and multiplier to adjust speed ([Exolun](https://github.com/Exolun))
 
+### Physics
+
+- Ammo.js IDL exposed property update and raycast vehicle stablization support ([MackeyK24](https://github.com/MackeyK24))
+
 ## Bugs
 
+- Fix infinite loop in `GlowLayer.unReferenceMeshFromUsingItsOwnMaterial` ([Popov72](https://github.com/Popov72)
+- `QuadraticErrorSimplification` was not exported ([RaananW](https://github.com/Raananw)
+
 ## Breaking changes

+ 4 - 33
inspector/src/components/actionTabs/lineContainerComponent.tsx

@@ -2,6 +2,7 @@ import * as React from "react";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
 import { GlobalState } from '../../components/globalState';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 
 interface ILineContainerComponentProps {
     globalState?: GlobalState;
@@ -11,46 +12,16 @@ interface ILineContainerComponentProps {
 }
 
 export class LineContainerComponent extends React.Component<ILineContainerComponentProps, { isExpanded: boolean, isHighlighted: boolean }> {
-    private static _InMemoryStorage: { [key: string]: boolean };
-
     constructor(props: ILineContainerComponentProps) {
         super(props);
 
-        let initialState: boolean;
-
-        try {
-            if (LineContainerComponent._InMemoryStorage && LineContainerComponent._InMemoryStorage[this.props.title] !== undefined) {
-                initialState = LineContainerComponent._InMemoryStorage[this.props.title];
-            } else if (typeof (Storage) !== "undefined" && localStorage.getItem(this.props.title) !== null) {
-                initialState = localStorage.getItem(this.props.title) === "true";
-            } else {
-                initialState = !this.props.closed;
-            }
-        }
-        catch (e) {
-            LineContainerComponent._InMemoryStorage = {};
-            LineContainerComponent._InMemoryStorage[this.props.title] = !this.props.closed
-            initialState = !this.props.closed;
-        }
-
+        const initialState = DataStorage.ReadBoolean(this.props.title, !this.props.closed);
         this.state = { isExpanded: initialState, isHighlighted: false };
     }
 
     switchExpandedState(): void {
         const newState = !this.state.isExpanded;
-
-        try {
-            if (LineContainerComponent._InMemoryStorage) {
-                LineContainerComponent._InMemoryStorage[this.props.title] = newState;
-            } else if (typeof (Storage) !== "undefined") {
-                localStorage.setItem(this.props.title, newState ? "true" : "false");
-            }
-        }
-        catch (e) {
-            LineContainerComponent._InMemoryStorage = {};
-            LineContainerComponent._InMemoryStorage[this.props.title] = newState;
-        }
-
+        DataStorage.WriteBoolean(this.props.title, newState);
         this.setState({ isExpanded: newState });
     }
 
@@ -71,7 +42,7 @@ export class LineContainerComponent extends React.Component<ILineContainerCompon
             }, 5000);
         } else {
             this.setState({isExpanded: false});
-        }        
+        }
     }
 
     renderHeader() {

+ 4 - 1
inspector/src/components/actionTabs/lines/fileButtonLineComponent.tsx

@@ -9,9 +9,12 @@ interface IFileButtonLineComponentProps {
 export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
     private static _IDGenerator = 0;
     private _id = FileButtonLineComponent._IDGenerator++;
+    private uploadInputRef: React.RefObject<HTMLInputElement>;
+
 
     constructor(props: IFileButtonLineComponentProps) {
         super(props);
+        this.uploadInputRef = React.createRef();
     }
 
     onChange(evt: any) {
@@ -29,7 +32,7 @@ export class FileButtonLineComponent extends React.Component<IFileButtonLineComp
                 <label htmlFor={"file-upload" + this._id} className="file-upload">
                     {this.props.label}
                 </label>
-                <input ref="upload" id={"file-upload" + this._id} type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} />
+                <input ref={this.uploadInputRef} id={"file-upload" + this._id} type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} />
             </div>
         );
     }

+ 4 - 1
inspector/src/components/actionTabs/lines/fileMultipleButtonLineComponent.tsx

@@ -9,9 +9,12 @@ interface IFileMultipleButtonLineComponentProps {
 export class FileMultipleButtonLineComponent extends React.Component<IFileMultipleButtonLineComponentProps> {
     private static _IDGenerator = 0;
     private _id = FileMultipleButtonLineComponent._IDGenerator++;
+    private uploadInputRef: React.RefObject<HTMLInputElement>;
+
 
     constructor(props: IFileMultipleButtonLineComponentProps) {
         super(props);
+        this.uploadInputRef = React.createRef();
     }
 
     onChange(evt: any) {
@@ -29,7 +32,7 @@ export class FileMultipleButtonLineComponent extends React.Component<IFileMultip
                 <label htmlFor={"file-upload" + this._id} className="file-upload">
                     {this.props.label}
                 </label>
-                <input ref="upload" id={"file-upload" + this._id} type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} multiple />
+                <input ref={this.uploadInputRef} id={"file-upload" + this._id} type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} multiple />
             </div>
         );
     }

+ 6 - 2
inspector/src/components/actionTabs/lines/textureLineComponent.tsx

@@ -27,6 +27,8 @@ enum ChannelToDisplay {
 }
 
 export class TextureLineComponent extends React.Component<ITextureLineComponentProps, { channel: ChannelToDisplay, face: number }> {
+    private canvasRef: React.RefObject<HTMLCanvasElement>;
+
     constructor(props: ITextureLineComponentProps) {
         super(props);
 
@@ -34,6 +36,8 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
             channel: ChannelToDisplay.All,
             face: 0
         };
+
+        this.canvasRef = React.createRef();
     }
 
     shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: { channel: ChannelToDisplay, face: number }): boolean {
@@ -82,7 +86,7 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
             return;
         }
 
-        const previewCanvas = this.refs.canvas as HTMLCanvasElement;
+        const previewCanvas = this.canvasRef.current as HTMLCanvasElement;
 
         if (this.props.globalState) {
             this.props.globalState.blockMutationUpdates = true;
@@ -208,7 +212,7 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
                             <button className={this.state.channel === ChannelToDisplay.All ? "all command selected" : "all command"} onClick={() => this.setState({ channel: ChannelToDisplay.All })}>ALL</button>
                         </div>
                     }
-                    <canvas ref="canvas" className="preview" />
+                    <canvas ref={this.canvasRef} className="preview" />
                 </div>
                 {
                     texture.isRenderTarget &&

+ 5 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/animationGroupPropertyGridComponent.tsx

@@ -25,6 +25,7 @@ export class AnimationGroupGridComponent extends React.Component<IAnimationGroup
     private _onAnimationGroupPlayObserver: Nullable<Observer<AnimationGroup>>;
     private _onAnimationGroupPauseObserver: Nullable<Observer<AnimationGroup>>;
     private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
+    private timelineRef: React.RefObject<SliderLineComponent>;
 
     constructor(props: IAnimationGroupGridComponentProps) {
         super(props);
@@ -36,7 +37,9 @@ export class AnimationGroupGridComponent extends React.Component<IAnimationGroup
 
         this._onBeforeRenderObserver = this.props.scene.onBeforeRenderObservable.add(() => {
             this.updateCurrentFrame(this.props.animationGroup);
-        });        
+        });
+
+        this.timelineRef = React.createRef();
     }
 
     disconnect(animationGroup: AnimationGroup) {
@@ -130,7 +133,7 @@ export class AnimationGroupGridComponent extends React.Component<IAnimationGroup
                 <LineContainerComponent globalState={this.props.globalState} 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) / 1000.0} directValue={this.state.currentFrame} onInput={value => this.onCurrentFrameChange(value)} />
+                    <SliderLineComponent ref={this.timelineRef} label="Current frame" minimum={animationGroup.from} maximum={animationGroup.to} step={(animationGroup.to - animationGroup.from) / 1000.0} directValue={this.state.currentFrame} onInput={value => this.onCurrentFrameChange(value)} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="INFOS">
                     <TextLineComponent label="Animation count" value={animationGroup.targetedAnimations.length.toString()} />

+ 5 - 1
inspector/src/components/actionTabs/tabs/propertyGrids/animationPropertyGridComponent.tsx

@@ -38,6 +38,8 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
     private _runningAnimatable: Nullable<Animatable>;
     private _onBeforeRenderObserver: Nullable<Observer<Scene>>;
     private _isPlaying = false;
+    private timelineRef: React.RefObject<SliderLineComponent>;
+
 
     constructor(props: IAnimationGridComponentProps) {
         super(props);
@@ -74,6 +76,8 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                 });
             }
         }
+
+        this.timelineRef = React.createRef();
     }
 
     playOrPause() {
@@ -182,7 +186,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                         <ButtonLineComponent label={this._isPlaying ? "Stop" : "Play"} onClick={() => this.playOrPause()} />
                         {
                             this._isPlaying &&
-                            <SliderLineComponent ref="timeline" label="Current frame" minimum={this._animationControl.from} maximum={this._animationControl.to}
+                            <SliderLineComponent ref={this.timelineRef} label="Current frame" minimum={this._animationControl.from} maximum={this._animationControl.to}
                                 step={(this._animationControl.to - this._animationControl.from) / 1000.0} directValue={this.state.currentFrame}
                                 onInput={value => this.onCurrentFrameChange(value)}
                             />

+ 6 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -36,6 +36,8 @@ interface ITexturePropertyGridComponentProps {
 export class TexturePropertyGridComponent extends React.Component<ITexturePropertyGridComponentProps> {
 
     private _adtInstrumentation: Nullable<AdvancedDynamicTextureInstrumentation>;
+    private textureLineRef: React.RefObject<TextureLineComponent>;
+    
 
     constructor(props: ITexturePropertyGridComponentProps) {
         super(props);
@@ -51,6 +53,8 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         this._adtInstrumentation = new AdvancedDynamicTextureInstrumentation(adt);
         this._adtInstrumentation!.captureRenderTime = true;
         this._adtInstrumentation!.captureLayoutTime = true;
+
+        this.textureLineRef = React.createRef();
     }
 
     componentWillUnmount() {
@@ -84,7 +88,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
 
     foreceRefresh() {
         this.forceUpdate();
-        (this.refs["textureLine"] as TextureLineComponent).updatePreview();
+        (this.textureLineRef.current as TextureLineComponent).updatePreview();
     }
 
     render() {
@@ -128,7 +132,7 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
         return (
             <div className="pane">
                 <LineContainerComponent globalState={this.props.globalState} title="PREVIEW">
-                    <TextureLineComponent ref="textureLine" texture={texture} width={256} height={256} globalState={this.props.globalState} />
+                    <TextureLineComponent ref={this.textureLineRef} texture={texture} width={256} height={256} globalState={this.props.globalState} />
                     <FileButtonLineComponent label="Load texture from file" onClick={(file) => this.updateTexture(file)} accept=".jpg, .png, .tga, .dds, .env" />
                     <TextInputLineComponent label="URL" value={textureUrl} lockObject={this.props.lockObject} onChange={url => {
                         (texture as Texture).updateURL(url);

+ 40 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/particleSystems/particleSystemPropertyGridComponent.tsx

@@ -0,0 +1,40 @@
+import * as React from "react";
+
+import { Observable } from "babylonjs/Misc/observable";
+
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { TextLineComponent } from "../../../lines/textLineComponent";
+import { LockObject } from "../lockObject";
+import { GlobalState } from '../../../../globalState';
+import { CustomPropertyGridComponent } from '../customPropertyGridComponent';
+import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
+
+interface IParticleSystemPropertyGridComponentProps {
+    globalState: GlobalState;
+    system: IParticleSystem,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class ParticleSystemPropertyGridComponent extends React.Component<IParticleSystemPropertyGridComponentProps> {
+    constructor(props: IParticleSystemPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const system = this.props.system;
+
+        return (
+            <div className="pane">
+                <CustomPropertyGridComponent globalState={this.props.globalState} target={system}
+                    lockObject={this.props.lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
+                    <TextLineComponent label="ID" value={system.id} />
+                    <TextLineComponent label="Class" value={system.getClassName()} />                                      
+                </LineContainerComponent>                
+            </div>
+        );
+    }
+}

+ 14 - 7
inspector/src/components/embedHost/embedHostComponent.tsx

@@ -25,23 +25,30 @@ interface IEmbedHostComponentProps {
 
 export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps> {
     private _once = true;
+    private splitRef: React.RefObject<HTMLDivElement>;
+    private topPartRef: React.RefObject<HTMLDivElement>;
+    private bottomPartRef: React.RefObject<HTMLDivElement>;
 
     constructor(props: IEmbedHostComponentProps) {
         super(props);
     }
 
     componentDidMount() {
-        const container = this.refs.split;
+        const container = this.splitRef.current;
 
         if (!container) {
             return;
         }
 
-        Split([this.refs.topPart, this.refs.bottomPart], {
+        Split([this.topPartRef.current, this.bottomPartRef.current], {
             direction: "vertical",
             minSize: [200, 200],
             gutterSize: 4
-        })
+        });
+
+        this.splitRef = React.createRef();
+        this.topPartRef = React.createRef();
+        this.bottomPartRef = React.createRef();
     }
 
     renderContent() {
@@ -66,15 +73,15 @@ export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps
         }
 
         return (
-            <div ref="split" id="split" className="noPopup">
-                <div id="topPart" ref="topPart">
+            <div ref={this.splitRef} id="split" className="noPopup">
+                <div id="topPart" ref={this.topPartRef}>
                     <SceneExplorerComponent scene={this.props.scene}
                         extensibilityGroups={this.props.extensibilityGroups}
                         globalState={this.props.globalState}
                         popupMode={true}
                         noHeader={true} />
                 </div>
-                <div id="bottomPart" ref="bottomPart" style={{ marginTop: "4px", overflow: "hidden" }}>
+                <div id="bottomPart" ref={this.bottomPartRef} style={{ marginTop: "4px", overflow: "hidden" }}>
                     <ActionTabsComponent scene={this.props.scene}
                         globalState={this.props.globalState}
                         popupMode={true}
@@ -82,7 +89,7 @@ export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps
                         initialTab={this.props.initialTab} />
                 </div>
             </div>
-        )
+        );
     }
 
     render() {

+ 5 - 5
inspector/src/components/globalState.ts

@@ -9,7 +9,7 @@ import { Light } from "babylonjs/Lights/light";
 import { LightGizmo } from "babylonjs/Gizmos/lightGizmo";
 import { PropertyChangedEvent } from "./propertyChangedEvent";
 import { ReplayRecorder } from './replayRecorder';
-import { Tools } from '../tools';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 
 export class GlobalState {
     public onSelectionChangedObservable: Observable<any>;
@@ -36,7 +36,7 @@ export class GlobalState {
 
     public get onlyUseEulers(): boolean {
         if (this._onlyUseEulers === null) {
-            this._onlyUseEulers = Tools.ReadLocalBooleanSettings("settings_onlyUseEulers", true);
+            this._onlyUseEulers = DataStorage.ReadBoolean("settings_onlyUseEulers", true);
         }
 
         return this._onlyUseEulers!;
@@ -45,14 +45,14 @@ export class GlobalState {
     public set onlyUseEulers(value: boolean) {
         this._onlyUseEulers = value;
 
-        Tools.StoreLocalBooleanSettings("settings_onlyUseEulers", value);
+        DataStorage.WriteBoolean("settings_onlyUseEulers", value);
     }
 
     private _ignoreBackfacesForPicking: Nullable<boolean> = null;
 
     public get ignoreBackfacesForPicking(): boolean {
         if (this._ignoreBackfacesForPicking === null) {
-            this._ignoreBackfacesForPicking = Tools.ReadLocalBooleanSettings("settings_ignoreBackfacesForPicking", false);
+            this._ignoreBackfacesForPicking = DataStorage.ReadBoolean("settings_ignoreBackfacesForPicking", false);
         }
 
         return this._ignoreBackfacesForPicking!;
@@ -61,7 +61,7 @@ export class GlobalState {
     public set ignoreBackfacesForPicking(value: boolean) {
         this._ignoreBackfacesForPicking = value;
 
-        Tools.StoreLocalBooleanSettings("settings_ignoreBackfacesForPicking", value);
+        DataStorage.WriteBoolean("settings_ignoreBackfacesForPicking", value);
     }
 
     public init(propertyChangedObservable: Observable<PropertyChangedEvent>) {

+ 3 - 1
inspector/src/components/sceneExplorer/extensionsComponent.tsx

@@ -11,11 +11,13 @@ interface IExtensionsComponentProps {
 
 export class ExtensionsComponent extends React.Component<IExtensionsComponentProps, { popupVisible: boolean }> {
     private _popup: Nullable<HTMLDivElement>;
+    private extensionRef: React.RefObject<HTMLDivElement>;
 
     constructor(props: IExtensionsComponentProps) {
         super(props);
 
         this.state = { popupVisible: false };
+        this.extensionRef = React.createRef();
     }
 
     showPopup() {
@@ -54,7 +56,7 @@ export class ExtensionsComponent extends React.Component<IExtensionsComponentPro
         }
 
         return (
-            <div ref="extensions" className="extensions" onClick={() => this.showPopup()}>
+            <div ref={this.extensionRef} className="extensions" onClick={() => this.showPopup()}>
                 <div title="Additional options" className="icon">
                     <FontAwesomeIcon icon={faEllipsisH} />
                 </div>

+ 20 - 2
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -19,6 +19,7 @@ import { FreeCamera } from 'babylonjs/Cameras/freeCamera';
 import { DirectionalLight } from 'babylonjs/Lights/directionalLight';
 import { SSAORenderingPipeline } from 'babylonjs/PostProcesses/RenderPipeline/Pipelines/ssaoRenderingPipeline';
 import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
+import { ParticleHelper } from 'babylonjs/Particles/particleHelper';
 
 require("./sceneExplorer.scss");
 
@@ -57,6 +58,8 @@ interface ISceneExplorerComponentProps {
 export class SceneExplorerComponent extends React.Component<ISceneExplorerComponentProps, { filter: Nullable<string>, selectedEntity: any, scene: Scene }> {
     private _onSelectionChangeObserver: Nullable<Observer<any>>;
     private _onNewSceneAddedObserver: Nullable<Observer<Scene>>;
+    private sceneExplorerRef: React.RefObject<Resizable>;
+
 
     private _once = true;
     private _hooked = false;
@@ -69,6 +72,8 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
         this.state = { filter: null, selectedEntity: null, scene: this.props.scene };
 
         this.sceneMutationFunc = this.processMutation.bind(this);
+
+        this.sceneExplorerRef = React.createRef();
     }
 
     processMutation() {
@@ -293,7 +298,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             }
         });
 
-        
+        // Materials
         let materialsContextMenus: { label: string, action: () => void }[] = [];
         materialsContextMenus.push({
             label: "Add new node material",
@@ -313,6 +318,16 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             materials.push(...scene.multiMaterials);
         }
 
+        // Particle systems
+        let particleSystemsContextMenus: { label: string, action: () => void }[] = [];
+        particleSystemsContextMenus.push({
+            label: "Add new particle system",
+            action: () => {
+                let newSystem = ParticleHelper.CreateDefault(Vector3.Zero(), 1000, scene);
+                this.props.globalState.onSelectionChangedObservable.notifyObservers(newSystem);
+            }
+        });
+
         return (
             <div id="tree" onContextMenu={e => e.preventDefault()}>
                 <SceneExplorerFilterComponent onFilter={(filter) => this.filterContent(filter)} />
@@ -336,6 +351,9 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 <TreeItemComponent globalState={this.props.globalState} extensibilityGroups={this.props.extensibilityGroups}
                     contextMenuItems={pipelineContextMenus}
                     selectedEntity={this.state.selectedEntity} items={pipelines} label="Rendering pipelines" offset={1} filter={this.state.filter} />
+                <TreeItemComponent globalState={this.props.globalState} 
+                    contextMenuItems={particleSystemsContextMenus} 
+                    extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.state.selectedEntity} items={scene.particleSystems} label="Particle systems" offset={1} filter={this.state.filter} />
                 {
                     guiElements && guiElements.length > 0 &&
                     <TreeItemComponent globalState={this.props.globalState} extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.state.selectedEntity} items={guiElements} label="GUI" offset={1} filter={this.state.filter} />
@@ -390,7 +408,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
         }
 
         return (
-            <Resizable tabIndex={-1} id="sceneExplorer" ref="sceneExplorer" size={{ height: "100%" }} minWidth={300} maxWidth={600} minHeight="100%" enable={{ top: false, right: true, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }} onKeyDown={(keyEvent) => this.processKeys(keyEvent)}>
+            <Resizable tabIndex={-1} id="sceneExplorer" ref={this.sceneExplorerRef} size={{ height: "100%" }} minWidth={300} maxWidth={600} minHeight="100%" enable={{ top: false, right: true, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }} onKeyDown={(keyEvent) => this.processKeys(keyEvent)}>
                 {
                     !this.props.noHeader &&
                     <HeaderComponent title="SCENE EXPLORER" noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} />

+ 0 - 14
inspector/src/tools.ts

@@ -1,18 +1,4 @@
 export class Tools {
-    public static StoreLocalBooleanSettings(key: string, value: boolean) {
-        if (typeof (Storage) !== "undefined") {
-            localStorage.setItem(key, value ? "true" : "false");
-        }
-    }
-
-    public static ReadLocalBooleanSettings(key: string, defaultValue: boolean): boolean {
-        if (typeof (Storage) !== "undefined" && localStorage.getItem(key) !== null) {
-            return localStorage.getItem(key) === "true";
-        } else {
-            return defaultValue;
-        }
-    }
-
     public static LookForItem(item: any, selectedEntity: any): boolean {
         if (item === selectedEntity) {
             return true;

+ 6 - 6
nodeEditor/src/components/preview/previewAreaComponent.tsx

@@ -1,7 +1,7 @@
 
 import * as React from "react";
 import { GlobalState } from '../../globalState';
-import { DataStorage } from '../../dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 import { Observer } from 'babylonjs/Misc/observable';
 import { Nullable } from 'babylonjs/types';
 
@@ -32,14 +32,14 @@ export class PreviewAreaComponent extends React.Component<IPreviewAreaComponentP
 
     changeBackFaceCulling(value: boolean) {        
         this.props.globalState.backFaceCulling = value;
-        DataStorage.StoreBoolean("BackFaceCulling", value);
+        DataStorage.WriteBoolean("BackFaceCulling", value);
         this.props.globalState.onBackFaceCullingChanged.notifyObservers();
         this.forceUpdate();
     }
 
     changeDepthPrePass(value: boolean) {        
         this.props.globalState.depthPrePass = value;
-        DataStorage.StoreBoolean("DepthPrePass", value);
+        DataStorage.WriteBoolean("DepthPrePass", value);
         this.props.globalState.onDepthPrePassChanged.notifyObservers();
         this.forceUpdate();
     }    
@@ -70,7 +70,7 @@ export class PreviewAreaComponent extends React.Component<IPreviewAreaComponentP
                         title="Turn on/off hemispheric light"  
                         onClick={() => {
                             this.props.globalState.hemisphericLight = !this.props.globalState.hemisphericLight;                            
-                            DataStorage.StoreBoolean("HemisphericLight", this.props.globalState.hemisphericLight);
+                            DataStorage.WriteBoolean("HemisphericLight", this.props.globalState.hemisphericLight);
                             this.props.globalState.onLightUpdated.notifyObservers();
                             this.forceUpdate();
                         }} className={"button hemispheric-light" + (this.props.globalState.hemisphericLight ? " selected" : "")}>
@@ -80,7 +80,7 @@ export class PreviewAreaComponent extends React.Component<IPreviewAreaComponentP
                         title="Turn on/off direction light #1"  
                         onClick={() => {
                             this.props.globalState.directionalLight1 = !this.props.globalState.directionalLight1;                       
-                            DataStorage.StoreBoolean("DirectionalLight1", this.props.globalState.directionalLight1);
+                            DataStorage.WriteBoolean("DirectionalLight1", this.props.globalState.directionalLight1);
                             this.props.globalState.onLightUpdated.notifyObservers();
                             this.forceUpdate();
                         }} className={"button direction-light-1" + (this.props.globalState.directionalLight1 ? " selected" : "")}>
@@ -91,7 +91,7 @@ export class PreviewAreaComponent extends React.Component<IPreviewAreaComponentP
                         title="Turn on/off direction light #0"  
                         onClick={() => {
                             this.props.globalState.directionalLight0 = !this.props.globalState.directionalLight0;                       
-                            DataStorage.StoreBoolean("DirectionalLight0", this.props.globalState.directionalLight0);
+                            DataStorage.WriteBoolean("DirectionalLight0", this.props.globalState.directionalLight0);
                             this.props.globalState.onLightUpdated.notifyObservers();
                             this.forceUpdate();
                         }} className={"button direction-light-0" + (this.props.globalState.directionalLight0 ? " selected" : "")}>

+ 9 - 7
nodeEditor/src/components/preview/previewMeshControlComponent.tsx

@@ -3,7 +3,7 @@ import * as React from "react";
 import { GlobalState } from '../../globalState';
 import { Color3, Color4 } from 'babylonjs/Maths/math.color';
 import { PreviewMeshType } from './previewMeshType';
-import { DataStorage } from '../../dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 import { OptionsLineComponent } from '../../sharedComponents/optionsLineComponent';
 import * as ReactDOM from 'react-dom';
 
@@ -19,10 +19,12 @@ interface IPreviewMeshControlComponent {
 
 export class PreviewMeshControlComponent extends React.Component<IPreviewMeshControlComponent> {
     private colorInputRef: React.RefObject<HTMLInputElement>;
+    private filePickerRef: React.RefObject<HTMLInputElement>;
 
     constructor(props: IPreviewMeshControlComponent) {
         super(props);
         this.colorInputRef = React.createRef();
+        this.filePickerRef = React.createRef();
     }
 
     changeMeshType(newOne: PreviewMeshType) {
@@ -33,7 +35,7 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
         this.props.globalState.previewMeshType = newOne;
         this.props.globalState.onPreviewCommandActivated.notifyObservers();
 
-        DataStorage.StoreNumber("PreviewMeshType", newOne);
+        DataStorage.WriteNumber("PreviewMeshType", newOne);
 
         this.forceUpdate();
     }
@@ -64,9 +66,9 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
     changeBackground(value: string) {
         const newColor = Color3.FromHexString(value);
 
-        DataStorage.StoreNumber("BackgroundColorR", newColor.r);
-        DataStorage.StoreNumber("BackgroundColorG", newColor.g);
-        DataStorage.StoreNumber("BackgroundColorB", newColor.b);
+        DataStorage.WriteNumber("BackgroundColorR", newColor.r);
+        DataStorage.WriteNumber("BackgroundColorG", newColor.g);
+        DataStorage.WriteNumber("BackgroundColorB", newColor.b);
 
         const newBackgroundColor = Color4.FromColor3(newColor, 1.0);
         this.props.globalState.backgroundColor = newBackgroundColor;
@@ -103,13 +105,13 @@ export class PreviewMeshControlComponent extends React.Component<IPreviewMeshCon
                                 if (value !== PreviewMeshType.Custom + 1) {
                                     this.changeMeshType(value);
                                 } else {
-                                    (ReactDOM.findDOMNode(this.refs["file-picker"]) as HTMLElement).click();
+                                    this.filePickerRef.current?.click();
                                 }
                             }} />
                 <div style={{
                     display: "none"
                 }} title="Preview with a custom mesh" >
-                    <input ref="file-picker" id="file-picker" type="file" onChange={evt => this.useCustomMesh(evt)} accept=".gltf, .glb, .babylon, .obj"/>
+                    <input ref={this.filePickerRef} id="file-picker" type="file" onChange={evt => this.useCustomMesh(evt)} accept=".gltf, .glb, .babylon, .obj"/>
                 </div>
                 <div
                     title="Turn-table animation"

+ 4 - 4
nodeEditor/src/components/propertyTab/propertyTabComponent.tsx

@@ -9,7 +9,7 @@ import { FileButtonLineComponent } from '../../sharedComponents/fileButtonLineCo
 import { Tools } from 'babylonjs/Misc/tools';
 import { SerializationTools } from '../../serializationTools';
 import { CheckBoxLineComponent } from '../../sharedComponents/checkBoxLineComponent';
-import { DataStorage } from '../../dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 import { GraphNode } from '../../diagram/graphNode';
 import { SliderLineComponent } from '../../sharedComponents/sliderLineComponent';
 import { GraphFrame } from '../../diagram/graphFrame';
@@ -202,14 +202,14 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                         <CheckBoxLineComponent label="Embed textures when saving" 
                             isSelected={() => DataStorage.ReadBoolean("EmbedTextures", true)}
                             onSelect={(value: boolean) => {
-                                DataStorage.StoreBoolean("EmbedTextures", value);
+                                DataStorage.WriteBoolean("EmbedTextures", value);
                             }}
                         />
                         <SliderLineComponent label="Grid size" minimum={0} maximum={100} step={5} 
                             decimalCount={0} 
                             directValue={gridSize}
                             onChange={value => {
-                                DataStorage.StoreNumber("GridSize", value);                                
+                                DataStorage.WriteNumber("GridSize", value);                                
                                 this.props.globalState.onGridSizeChanged.notifyObservers();
                                 this.forceUpdate();
                             }}
@@ -217,7 +217,7 @@ export class PropertyTabComponent extends React.Component<IPropertyTabComponentP
                         <CheckBoxLineComponent label="Show grid" 
                             isSelected={() => DataStorage.ReadBoolean("ShowGrid", true)}
                             onSelect={(value: boolean) => {
-                                DataStorage.StoreBoolean("ShowGrid", value);                
+                                DataStorage.WriteBoolean("ShowGrid", value);                
                                 this.props.globalState.onGridSizeChanged.notifyObservers();
                             }}
                         />

+ 0 - 65
nodeEditor/src/dataStorage.ts

@@ -1,65 +0,0 @@
-export class DataStorage {
-    private static _InMemoryStorage: { [key: string]: boolean | number };
-
-    public static ReadBoolean(key: string, defaultValue: boolean): boolean {
-        try {
-            if (this._InMemoryStorage && this._InMemoryStorage[key] !== undefined) {
-                return this._InMemoryStorage[key] as boolean;
-            } else if (typeof (Storage) !== "undefined" && localStorage.getItem(key) !== null) {
-                return localStorage.getItem(key) === "true";
-            } else {
-                return defaultValue;
-            }
-        }
-        catch (e) {
-            this._InMemoryStorage = {};
-            this._InMemoryStorage[key] = defaultValue;
-            return defaultValue;
-        }
-    }
-
-    public static StoreBoolean(key: string, value: boolean) {
-        try {
-            if (this._InMemoryStorage) {
-                this._InMemoryStorage[key] = value;
-            } else if (typeof (Storage) !== "undefined") {
-                localStorage.setItem(key, value ? "true" : "false");
-            }
-        }
-        catch (e) {
-            this._InMemoryStorage = {};
-            this._InMemoryStorage[key] = value;
-        }
-    }
-
-    public static ReadNumber(key: string, defaultValue: number): number {
-        try {
-            if (this._InMemoryStorage && this._InMemoryStorage[key] !== undefined) {
-                return this._InMemoryStorage[key] as number;
-            } else if (typeof (Storage) !== "undefined" && localStorage.getItem(key) !== null) {
-                return parseFloat(localStorage.getItem(key)!);
-            } else {
-                return defaultValue;
-            }
-        }
-        catch (e) {
-            this._InMemoryStorage = {};
-            this._InMemoryStorage[key] = defaultValue;
-            return defaultValue;
-        }
-    }
-
-    public static StoreNumber(key: string, value: number) {
-        try {
-            if (this._InMemoryStorage) {
-                this._InMemoryStorage[key] = value;
-            } else if (typeof (Storage) !== "undefined") {
-                localStorage.setItem(key, value.toString());
-            }
-        }
-        catch (e) {
-            this._InMemoryStorage = {};
-            this._InMemoryStorage[key] = value;
-        }
-    }
-}

+ 74 - 0
nodeEditor/src/diagram/graphCanvas.scss

@@ -244,6 +244,44 @@
                 }
             }
 
+            .top-right-corner-handle{
+                background-color: transparent;
+                height: 4px;
+                z-index: 21;
+                cursor: ne-resize;
+                width: 4px;
+                margin-left: -6px;
+
+                &::after {
+                    background-color: transparent;
+                    cursor: ne-resize;
+                    margin-left: unset;
+                    top: -4px;
+                    height: 10px;
+                    width: 10px;
+                }
+            }
+
+
+            .bottom-right-corner-handle{
+                background-color: transparent;
+                height: 0px;
+                z-index: 21;
+                cursor: nw-resize;
+                grid-area: 4 / 2 / 4 / 2;;
+                margin-left: -2px;
+
+
+                &::after {
+                    background-color: transparent;
+                    height: 10px;
+                    cursor: nw-resize;
+                    top: unset;
+                    bottom: -4px;
+                    width: 10px;               
+                }
+            }
+
             .left-handle {
                 grid-area: 1 / 1 / 3 / 1;
                 width: 4px;
@@ -261,6 +299,42 @@
                 }
             }
 
+            .top-left-corner-handle{
+                background-color: transparent;
+                height: 4px;
+                z-index: 21;
+                cursor: nw-resize;
+                width: 4px;
+                margin-left: -4px;
+
+                &::before {
+                    background-color: transparent;
+                    cursor: nw-resize;
+                    margin-left: unset;
+                    top: -4px;
+                    height: 10px;
+                    width: 10px;
+                }
+            }
+
+            .bottom-left-corner-handle{
+                background-color: transparent;
+                height: 0px;
+                z-index: 21;
+                cursor: sw-resize;
+                grid-area: 4 / 1 / 4 / 1;
+
+
+                &::before {
+                    background-color: transparent;
+                    height: 10px;
+                    cursor: sw-resize;
+                    top: unset;
+                    bottom: -4px;
+                    width: 10px;               
+                }
+            }
+
             .top-handle {
                 grid-area: 1 / 1 / 1 / 1;
                 background-color: transparent;

+ 1 - 1
nodeEditor/src/diagram/graphCanvas.tsx

@@ -11,7 +11,7 @@ import { NodeMaterialConnectionPoint, NodeMaterialConnectionPointDirection, Node
 import { Vector2 } from 'babylonjs/Maths/math.vector';
 import { FragmentOutputBlock } from 'babylonjs/Materials/Node/Blocks/Fragment/fragmentOutputBlock';
 import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
-import { DataStorage } from '../dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 import { GraphFrame } from './graphFrame';
 import { IEditorData } from '../nodeLocationInfo';
 

+ 317 - 44
nodeEditor/src/diagram/graphFrame.ts

@@ -13,7 +13,11 @@ enum ResizingDirection {
         Right,
         Left,
         Top,
-        Bottom
+        Bottom,
+        TopRight,
+        TopLeft,
+        BottomRight,
+        BottomLeft
 }
 
 export class GraphFrame {
@@ -324,7 +328,29 @@ export class GraphFrame {
         this.element.appendChild(topHandle);
         topHandle.addEventListener("pointerdown", this._onTopHandlePointerDown);
 
-        this._headerTextElement = root.ownerDocument!.createElement("div"); 
+        const topRightCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
+        topRightCornerHandle.className = "handle right-handle top-right-corner-handle";
+        this.element.appendChild(topRightCornerHandle);
+        topRightCornerHandle.addEventListener("pointerdown", this._onTopRightHandlePointerDown);
+
+        const bottomRightCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
+        bottomRightCornerHandle.className = "handle right-handle bottom-right-corner-handle";
+        this.element.appendChild(bottomRightCornerHandle);
+        bottomRightCornerHandle.addEventListener("pointerdown", this._onBottomRightHandlePointerDown);
+
+        const topLeftCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
+        topLeftCornerHandle.className = "handle left-handle top-left-corner-handle";
+        this.element.appendChild(topLeftCornerHandle);
+        topLeftCornerHandle.addEventListener("pointerdown", this._onTopLeftHandlePointerDown);
+
+        const bottomLeftCornerHandle: HTMLDivElement = root.ownerDocument!.createElement("div");
+        bottomLeftCornerHandle.className = "handle left-handle bottom-left-corner-handle";
+        this.element.appendChild(bottomLeftCornerHandle);
+        bottomLeftCornerHandle.addEventListener("pointerdown", this._onBottomLeftHandlePointerDown);
+
+        // add header elements
+
+        this._headerTextElement = root.ownerDocument!.createElement("div");
         this._headerTextElement.classList.add("frame-box-header-title");
         this._headerElement.appendChild(this._headerTextElement);
 
@@ -542,10 +568,26 @@ export class GraphFrame {
         }
     }
 
+    private _isResizingTop(){
+        return this._resizingDirection === ResizingDirection.Top || this._resizingDirection === ResizingDirection.TopRight || this._resizingDirection === ResizingDirection.TopLeft;
+    }
+
+    private _isResizingRight(){
+        return this._resizingDirection === ResizingDirection.Right || this._resizingDirection === ResizingDirection.TopRight || this._resizingDirection === ResizingDirection.BottomRight;
+    }
+
+    private _isResizingBottom(){
+        return this._resizingDirection === ResizingDirection.Bottom || this._resizingDirection === ResizingDirection.BottomLeft || this._resizingDirection === ResizingDirection.BottomRight;
+    }
+
+    private _isResizingLeft() {
+        return this._resizingDirection === ResizingDirection.Left || this._resizingDirection === ResizingDirection.TopLeft || this._resizingDirection === ResizingDirection.BottomLeft;
+    }
+
     private _onRightHandlePointerDown = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
-        this.initResizing(evt)
+        this.initResizing(evt);
         _this._resizingDirection = ResizingDirection.Right;
         _this.mouseXLimit = evt.clientX - (_this.width - _this._minFrameWidth);
         _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onRightHandlePointerUp);
@@ -562,10 +604,10 @@ export class GraphFrame {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
         if (_this.mouseXLimit) {
-            if (_this._resizingDirection !== ResizingDirection.Right || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientX < xLimit) {
+            if (!_this._isResizingRight() || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientX < xLimit) {
                 return;
             }
-            if (_this._resizingDirection === ResizingDirection.Right ) {
+            if (_this._isResizingRight()) {
                 evt.stopPropagation();
                 const distanceMouseMoved = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
                 _this._expandRight(distanceMouseMoved, evt.clientX);
@@ -574,20 +616,10 @@ export class GraphFrame {
         }
     }
 
-    private _expandRight(widthModification: number, x: number) {
-        const frameElementWidth = parseFloat(this.element.style.width.replace("px", ""));
-        if ((frameElementWidth + widthModification) > 20) {
-            this._mouseStartPointX =  x;
-            this.element.style.width = `${frameElementWidth + widthModification}px`;
-        }
-        this.updateMinHeightWithComments();
-        this.height = this._borderElement.offsetHeight;
-    }
-
     private _onRightHandlePointerUp = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
-        if (_this._resizingDirection === ResizingDirection.Right) {
+        if (_this._isResizingRight()) {
             _this.width = parseFloat(_this.element.style.width.replace("px", ""));
             _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onRightHandlePointerUp);
             _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onRightHandlePointerMove);
@@ -598,7 +630,7 @@ export class GraphFrame {
     private _onBottomHandlePointerDown = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
-        this.initResizing(evt);
+        _this.initResizing(evt);
         _this._resizingDirection = ResizingDirection.Bottom;
         _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onBottomHandlePointerMove);
         _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onBottomHandlePointerUp);
@@ -624,11 +656,6 @@ export class GraphFrame {
         }
     }
 
-    private _expandBottom(heightModification: number) {
-        const frameElementHeight = parseFloat(this.element.style.height.replace("px", ""));
-        this.element.style.height = `${frameElementHeight + heightModification}px`;
-    }
-
     private _onBottomHandlePointerUp = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
@@ -643,7 +670,7 @@ export class GraphFrame {
     private _onLeftHandlePointerDown = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
-        this.initResizing(evt);
+        _this.initResizing(evt);
         _this._resizingDirection = ResizingDirection.Left;
         _this.mouseXLimit = evt.clientX + _this.width - _this._minFrameWidth;
         _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onLeftHandlePointerUp);
@@ -672,15 +699,6 @@ export class GraphFrame {
         }
     }
 
-    private _expandLeft(widthModification: number) {
-            const frameElementWidth = parseFloat(this.element.style.width.replace("px", ""));
-            const frameElementLeft = parseFloat(this.element.style.left.replace("px", ""));
-            this.element.style.width = `${frameElementWidth - widthModification}px`;
-            this.element.style.left = `${frameElementLeft + widthModification}px`;
-            this.updateMinHeightWithComments();
-            this.height = this._borderElement.offsetHeight;
-    }
-
     private _onLeftHandlePointerUp = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
@@ -696,7 +714,7 @@ export class GraphFrame {
     private _onTopHandlePointerDown = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
-        this.initResizing(evt)
+        _this.initResizing(evt);
         _this._resizingDirection = ResizingDirection.Top;
         _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onTopHandlePointerUp);
         _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onTopHandlePointerMove);
@@ -711,10 +729,10 @@ export class GraphFrame {
     private _moveTopHandle = (evt: PointerEvent, yLimit: number) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
-        if (_this._resizingDirection !== ResizingDirection.Top || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientY > yLimit) {
+        if (!_this._isResizingTop() || _this._mouseStartPointX === null || _this._mouseStartPointY === null || evt.clientY > yLimit) {
             return;
         }
-        if (_this._resizingDirection === ResizingDirection.Top) {
+        if (_this._isResizingTop()) {
             evt.stopPropagation();
             const distanceMouseMoved = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
             _this._expandTop(distanceMouseMoved);
@@ -722,18 +740,10 @@ export class GraphFrame {
         }
     }
 
-    private _expandTop(heightModification: number) {
-            const frameElementHeight = parseFloat(this.element.style.height.replace("px", ""));
-            const frameElementTop = parseFloat(this.element.style.top.replace("px", ""));
-            this.element.style.height = `${frameElementHeight - heightModification}px`;
-            this.element.style.top = `${frameElementTop + heightModification}px`;
-
-    }
-
     private _onTopHandlePointerUp = (evt: PointerEvent) => {
         // tslint:disable-next-line: no-this-assignment
         const _this = this;
-        if (_this._resizingDirection === ResizingDirection.Top) {
+        if (_this._isResizingTop()) {
             _this.y = parseFloat(_this.element.style.top!.replace("px", ""));
             _this.height = parseFloat(_this.element.style.height.replace("px", ""));
             _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onTopHandlePointerUp);
@@ -742,6 +752,269 @@ export class GraphFrame {
         }
     }
 
+    private _onTopRightHandlePointerDown = (evt: PointerEvent) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        _this.initResizing(evt);
+        _this._resizingDirection = ResizingDirection.TopRight;
+        _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onTopRightHandlePointerUp);
+        _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onTopRightHandlePointerMove);
+    }
+
+    private _onTopRightHandlePointerMove = (evt: PointerEvent) => {
+        const topSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
+        const yLimit = (this._mouseStartPointY as number) + topSlack;
+        const rightSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
+        const xLimit = (this._mouseStartPointX as number) - rightSlack;
+        this._moveTopRightHandle(evt, xLimit, yLimit);
+    }
+
+    private _moveTopRightHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (!(_this._isResizingTop() && _this._isResizingRight()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
+            return;
+        }
+        if (_this._isResizingRight() && _this._isResizingTop()) {
+            evt.stopPropagation();
+            if (evt.clientY < yLimit && evt.clientX > xLimit) { // able to move in X and Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandRight(distanceMouseMovedX, evt.clientX);
+                _this._mouseStartPointX =  evt.clientX;
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandTop(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            } else if (evt.clientY > yLimit && evt.clientX > xLimit) { // able to move in X but not Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandRight(distanceMouseMovedX, evt.clientX);
+                _this._mouseStartPointX =  evt.clientX;
+            } else if (evt.clientY < yLimit && evt.clientX < xLimit) { // able to move in Y but not X
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandTop(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            }
+        }
+    }
+
+    private _onTopRightHandlePointerUp = (evt: PointerEvent) => {
+        evt.stopPropagation();
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (_this._resizingDirection === ResizingDirection.TopRight) {
+            _this.y = parseFloat(_this.element.style.top!.replace("px", ""));
+            _this.height = parseFloat(_this.element.style.height.replace("px", ""));
+            _this.width = parseFloat(_this.element.style.width.replace("px", ""));
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onTopRightHandlePointerUp);
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onTopRightHandlePointerMove);
+            _this.cleanUpResizing(evt);
+        }
+    }
+
+    private _onBottomRightHandlePointerDown = (evt: PointerEvent) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        _this.initResizing(evt);
+        _this._resizingDirection = ResizingDirection.BottomRight;
+        _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onBottomRightHandlePointerUp);
+        _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onBottomRightHandlePointerMove);
+    }
+
+    private _onBottomRightHandlePointerMove = (evt: PointerEvent) => {
+        const bottomSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
+        const yLimit = (this._mouseStartPointY as number) - bottomSlack;
+        const rightSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
+        const xLimit = (this._mouseStartPointX as number) - rightSlack;
+        this._moveBottomRightHandle(evt, xLimit, yLimit);
+    }
+
+    private _moveBottomRightHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (!(_this._isResizingBottom() && _this._isResizingRight()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
+            return;
+        }
+        if (_this._isResizingRight() && _this._isResizingBottom()) {
+            evt.stopPropagation();
+            if (evt.clientY > yLimit && evt.clientX > xLimit) { // able to move in X and Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandRight(distanceMouseMovedX, evt.clientX);
+                _this._mouseStartPointX =  evt.clientX;
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandBottom(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            } else if (evt.clientY < yLimit && evt.clientX > xLimit) { // able to move in X but not Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandRight(distanceMouseMovedX, evt.clientX);
+                _this._mouseStartPointX =  evt.clientX;
+            } else if (evt.clientY > yLimit && evt.clientX < xLimit) { // able to move in Y but not X
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandBottom(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            }
+        }
+    }
+
+    private _onBottomRightHandlePointerUp = (evt: PointerEvent) => {
+        evt.stopPropagation();
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (_this._resizingDirection === ResizingDirection.BottomRight) {
+            _this.height = parseFloat(_this.element.style.height.replace("px", ""));
+            _this.width = parseFloat(_this.element.style.width.replace("px", ""));
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onBottomRightHandlePointerUp);
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onBottomRightHandlePointerMove);
+            _this.cleanUpResizing(evt);
+        }
+    }
+
+    private _onBottomLeftHandlePointerDown = (evt: PointerEvent) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        _this.initResizing(evt);
+        _this._resizingDirection = ResizingDirection.BottomLeft;
+        _this.mouseXLimit = evt.clientX + _this.width - _this._minFrameWidth;
+        _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onBottomLeftHandlePointerUp);
+        _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onBottomLeftHandlePointerMove);
+    }
+
+    private _onBottomLeftHandlePointerMove = (evt: PointerEvent) => {
+        const bottomSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
+        const yLimit = (this._mouseStartPointY as number) - bottomSlack;
+        const leftSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
+        const xLimit = (this._mouseStartPointX as number) + leftSlack;
+        this._moveBottomLeftHandle(evt, xLimit, yLimit);
+    }
+
+    private _moveBottomLeftHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (!(_this._isResizingBottom() && _this._isResizingLeft()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
+            return;
+        }
+        if (_this._isResizingLeft() && _this._isResizingBottom()) {
+            evt.stopPropagation();
+            if (evt.clientY > yLimit && evt.clientX < xLimit) { // able to move in X and Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandLeft(distanceMouseMovedX);
+                _this._mouseStartPointX =  evt.clientX;
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandBottom(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            } else if (evt.clientY < yLimit && evt.clientX < xLimit) { // able to move in X but not Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandLeft(distanceMouseMovedX);
+                _this._mouseStartPointX =  evt.clientX;
+            } else if (evt.clientY > yLimit && evt.clientX > xLimit) { // able to move in Y but not X
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandBottom(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            }
+        }
+    }
+
+    private _onBottomLeftHandlePointerUp = (evt: PointerEvent) => {
+        evt.stopPropagation();
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (_this._resizingDirection === ResizingDirection.BottomLeft) {
+            _this.height = parseFloat(_this.element.style.height.replace("px", ""));
+            _this.x = parseFloat(_this.element.style.left!.replace("px", ""));
+            _this.width = parseFloat(_this.element.style.width.replace("px", ""));
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onBottomLeftHandlePointerUp);
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onBottomLeftHandlePointerMove);
+            _this.cleanUpResizing(evt);
+        }
+    }
+
+    private _onTopLeftHandlePointerDown = (evt: PointerEvent) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        _this.initResizing(evt);
+        _this._resizingDirection = ResizingDirection.TopLeft;
+        _this.mouseXLimit = evt.clientX + _this.width - _this._minFrameWidth;
+        _this._ownerCanvas.hostCanvas.addEventListener("pointerup", _this._onTopLeftHandlePointerUp);
+        _this._ownerCanvas.hostCanvas.addEventListener("pointermove", _this._onTopLeftHandlePointerMove);
+    }
+
+    private _onTopLeftHandlePointerMove = (evt: PointerEvent) => {
+        const topSlack = (this.element.offsetHeight - this._minFrameHeight) * this._ownerCanvas.zoom;
+        const yLimit = (this._mouseStartPointY as number) + topSlack;
+        const leftSlack = (this.element.offsetWidth - this._minFrameWidth) * this._ownerCanvas.zoom;
+        const xLimit = (this._mouseStartPointX as number) + leftSlack;
+        this._moveTopLeftHandle(evt, xLimit, yLimit);
+    }
+
+    private _moveTopLeftHandle = (evt: PointerEvent, xLimit: number, yLimit: number) => {
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (!(_this._isResizingTop() && _this._isResizingLeft()) || _this._mouseStartPointX === null || _this._mouseStartPointY === null) {
+            return;
+        }
+        if (_this._isResizingLeft() && _this._isResizingTop()) {
+            evt.stopPropagation();
+            if (evt.clientY < yLimit  && evt.clientX < xLimit) { // able to move in X and Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandLeft(distanceMouseMovedX);
+                _this._mouseStartPointX =  evt.clientX;
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandTop(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            } else if (evt.clientY > yLimit  && evt.clientX < xLimit) { // able to move in X but not Y
+                const distanceMouseMovedX = (evt.clientX - _this._mouseStartPointX) / _this._ownerCanvas.zoom;
+                _this._expandLeft(distanceMouseMovedX);
+                _this._mouseStartPointX =  evt.clientX;
+            } else if (evt.clientY < yLimit  && evt.clientX > xLimit) { // able to move in Y but not X
+                const distanceMouseMovedY = (evt.clientY - _this._mouseStartPointY) / _this._ownerCanvas.zoom;
+                _this._expandTop(distanceMouseMovedY);
+                _this._mouseStartPointY =  evt.clientY;
+            }
+        }
+    }
+
+    private _onTopLeftHandlePointerUp = (evt: PointerEvent) => {
+        evt.stopPropagation();
+        // tslint:disable-next-line: no-this-assignment
+        const _this = this;
+        if (_this._resizingDirection === ResizingDirection.TopLeft) {
+            _this.y = parseFloat(_this.element.style.top!.replace("px", ""));
+            _this.height = parseFloat(_this.element.style.height.replace("px", ""));
+            _this.x = parseFloat(_this.element.style.left!.replace("px", ""));
+            _this.width = parseFloat(_this.element.style.width.replace("px", ""));
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointerup", _this._onTopLeftHandlePointerUp);
+            _this._ownerCanvas.hostCanvas.removeEventListener("pointermove", _this._onTopLeftHandlePointerMove);
+            _this.cleanUpResizing(evt);
+        }
+    }
+
+    private _expandLeft(widthModification: number) {
+        const frameElementWidth = parseFloat(this.element.style.width.replace("px", ""));
+        const frameElementLeft = parseFloat(this.element.style.left.replace("px", ""));
+        this.element.style.width = `${frameElementWidth - widthModification}px`;
+        this.element.style.left = `${frameElementLeft + widthModification}px`;
+        this.updateMinHeightWithComments();
+}
+
+    private _expandTop(heightModification: number) {
+        const frameElementHeight = parseFloat(this.element.style.height.replace("px", ""));
+        const frameElementTop = parseFloat(this.element.style.top.replace("px", ""));
+        this.element.style.height = `${frameElementHeight - heightModification}px`;
+        this.element.style.top = `${frameElementTop + heightModification}px`;
+    }
+
+    private _expandRight(widthModification: number, x: number) {
+        const frameElementWidth = parseFloat(this.element.style.width.replace("px", ""));
+        if ((frameElementWidth + widthModification) > 20) {
+            this._mouseStartPointX =  x;
+            this.element.style.width = `${frameElementWidth + widthModification}px`;
+        }
+        this.updateMinHeightWithComments();
+    }
+
+    private _expandBottom(heightModification: number) {
+        const frameElementHeight = parseFloat(this.element.style.height.replace("px", ""));
+        this.element.style.height = `${frameElementHeight + heightModification}px`;
+    }
+
     public dispose() {
         this.isCollapsed = false;
 

+ 3 - 3
nodeEditor/src/globalState.ts

@@ -4,7 +4,7 @@ import { Observable } from 'babylonjs/Misc/observable';
 import { LogEntry } from './components/log/logComponent';
 import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
 import { PreviewMeshType } from './components/preview/previewMeshType';
-import { DataStorage } from './dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 import { Color4 } from 'babylonjs/Maths/math.color';
 import { GraphNode } from './diagram/graphNode';
 import { Vector2 } from 'babylonjs/Maths/math.vector';
@@ -33,8 +33,8 @@ export class GlobalState {
     onBackFaceCullingChanged = new Observable<void>();
     onDepthPrePassChanged = new Observable<void>();
     onAnimationCommandActivated = new Observable<void>();
-    onCandidateLinkMoved = new Observable<Nullable<Vector2>>();   
-    onSelectionBoxMoved = new Observable<ClientRect | DOMRect>();       
+    onCandidateLinkMoved = new Observable<Nullable<Vector2>>();
+    onSelectionBoxMoved = new Observable<ClientRect | DOMRect>();
     onFrameCreated = new Observable<GraphFrame>();
     onCandidatePortSelected = new Observable<Nullable<NodePort>>();
     onGetNodeFromBlock: (block: NodeMaterialBlock) => GraphNode;

+ 3 - 3
nodeEditor/src/graphEditor.tsx

@@ -6,7 +6,7 @@ import { NodeListComponent } from './components/nodeList/nodeListComponent';
 import { PropertyTabComponent } from './components/propertyTab/propertyTabComponent';
 import { Portal } from './portal';
 import { LogComponent, LogEntry } from './components/log/logComponent';
-import { DataStorage } from './dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 import { NodeMaterialBlockConnectionPointTypes } from 'babylonjs/Materials/Node/Enums/nodeMaterialBlockConnectionPointTypes';
 import { InputBlock } from 'babylonjs/Materials/Node/Blocks/Input/inputBlock';
 import { Nullable } from 'babylonjs/types';
@@ -485,11 +485,11 @@ export class GraphEditor extends React.Component<IGraphEditorProps, IGraphEditor
         if (forLeft) {
             this._leftWidth += deltaX;
             this._leftWidth = Math.max(150, Math.min(400, this._leftWidth));
-            DataStorage.StoreNumber("LeftWidth", this._leftWidth);
+            DataStorage.WriteNumber("LeftWidth", this._leftWidth);
         } else {
             this._rightWidth -= deltaX;
             this._rightWidth = Math.max(250, Math.min(500, this._rightWidth));
-            DataStorage.StoreNumber("RightWidth", this._rightWidth);
+            DataStorage.WriteNumber("RightWidth", this._rightWidth);
             rootElement.ownerDocument!.getElementById("preview")!.style.height = this._rightWidth + "px";
         }
 

+ 1 - 1
nodeEditor/src/serializationTools.ts

@@ -1,7 +1,7 @@
 import { NodeMaterial } from 'babylonjs/Materials/Node/nodeMaterial';
 import { GlobalState } from './globalState';
 import { Texture } from 'babylonjs/Materials/Textures/texture';
-import { DataStorage } from './dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 import { NodeMaterialBlock } from 'babylonjs/Materials/Node/nodeMaterialBlock';
 
 export class SerializationTools {

+ 5 - 1
nodeEditor/src/sharedComponents/fileButtonLineComponent.tsx

@@ -7,8 +7,12 @@ interface IFileButtonLineComponentProps {
 }
 
 export class FileButtonLineComponent extends React.Component<IFileButtonLineComponentProps> {
+    private uploadRef: React.RefObject<HTMLInputElement>;
+
     constructor(props: IFileButtonLineComponentProps) {
         super(props);
+
+        this.uploadRef = React.createRef();
     }
 
     onChange(evt: any) {
@@ -26,7 +30,7 @@ export class FileButtonLineComponent extends React.Component<IFileButtonLineComp
                 <label htmlFor="file-upload" className="file-upload">
                     {this.props.label}
                 </label>
-                <input ref="upload" id="file-upload" type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} />
+                <input ref={this.uploadRef} id="file-upload" type="file" accept={this.props.accept} onChange={evt => this.onChange(evt)} />
             </div>
         );
     }

+ 2 - 2
nodeEditor/src/sharedComponents/lineContainerComponent.tsx

@@ -1,7 +1,7 @@
 import * as React from "react";
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faChevronDown } from '@fortawesome/free-solid-svg-icons';
-import { DataStorage } from '../dataStorage';
+import { DataStorage } from 'babylonjs/Misc/dataStorage';
 
 
 interface ILineContainerComponentProps {
@@ -22,7 +22,7 @@ export class LineContainerComponent extends React.Component<ILineContainerCompon
     switchExpandedState(): void {
         const newState = !this.state.isExpanded;
 
-        DataStorage.StoreBoolean(this.props.title, newState);
+        DataStorage.WriteBoolean(this.props.title, newState);
 
         this.setState({ isExpanded: newState });
     }

+ 11 - 7
nodeEditor/src/sharedComponents/textureLineComponent.tsx

@@ -16,14 +16,16 @@ interface ITextureLineComponentProps {
 }
 
 export interface ITextureLineComponentState {
-    displayRed: boolean, 
-    displayGreen: boolean, 
-    displayBlue: boolean, 
-    displayAlpha: boolean, 
-    face: number
+    displayRed: boolean;
+    displayGreen: boolean;
+    displayBlue: boolean;
+    displayAlpha: boolean;
+    face: number;
 }
 
 export class TextureLineComponent extends React.Component<ITextureLineComponentProps, ITextureLineComponentState> {
+    private canvasRef: React.RefObject<HTMLCanvasElement>;
+
     constructor(props: ITextureLineComponentProps) {
         super(props);
 
@@ -34,6 +36,8 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
             displayAlpha: true,
             face: 0
         };
+
+        this.canvasRef = React.createRef();
     }
 
     shouldComponentUpdate(nextProps: ITextureLineComponentProps, nextState: { displayRed: boolean, displayGreen: boolean, displayBlue: boolean, displayAlpha: boolean, face: number }): boolean {
@@ -49,7 +53,7 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
     }
 
     public updatePreview() {
-        TextureLineComponent.UpdatePreview(this.refs.canvas as HTMLCanvasElement, this.props.texture, this.props.width, this.state, undefined, this.props.globalState);
+        TextureLineComponent.UpdatePreview(this.canvasRef.current as HTMLCanvasElement, this.props.texture, this.props.width, this.state, undefined, this.props.globalState);
     }
 
     public static UpdatePreview(previewCanvas: HTMLCanvasElement, texture: BaseTexture, width: number, options: ITextureLineComponentState, onReady?: ()=> void, globalState?: any) {
@@ -206,7 +210,7 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
                         <button className={this.state.displayRed && this.state.displayGreen ? "all command selected" : "all command"} onClick={() => this.setState({ displayRed: true, displayGreen: true, displayBlue: true, displayAlpha: true })}>ALL</button>
                     </div>
                 }
-                <canvas ref="canvas" className="preview" />
+                <canvas ref={this.canvasRef} className="preview" />
             </div>
         );
     }

+ 21 - 1
src/Audio/audioEngine.ts

@@ -75,6 +75,26 @@ export interface IAudioEngine extends IDisposable {
      * This is helpful to resume play once browser policies have been satisfied.
      */
     unlock(): void;
+
+    /**
+     * Gets the global volume sets on the master gain.
+     * @returns the global volume if set or -1 otherwise
+     */
+    getGlobalVolume(): number;
+
+    /**
+     * Sets the global volume of your experience (sets on the master gain).
+     * @param newVolume Defines the new global volume of the application
+     */
+    setGlobalVolume(newVolume: number): void;
+
+    /**
+     * Connect the audio engine to an audio analyser allowing some amazing
+     * synchornization between the sounds/music and your visualization (VuMeter for instance).
+     * @see http://doc.babylonjs.com/how_to/playing_sounds_and_music#using-the-analyser
+     * @param analyser The analyser to connect to the engine
+     */
+    connectToAnalyser(analyser: Analyser): void;
 }
 
 // Sets the default audio engine to Babylon.js
@@ -376,4 +396,4 @@ export class AudioEngine implements IAudioEngine {
             this._connectedAnalyser.connectAudioNodes(this.masterGain, this._audioContext.destination);
         }
     }
-}
+}

+ 4 - 0
src/Audio/sound.ts

@@ -746,6 +746,10 @@ export class Sound {
                             length = length || this._length;
                             offset = offset || this._offset;
 
+                            if (this._soundSource) {
+                                this._soundSource.stop();
+                            }
+
                             this._soundSource = Engine.audioEngine.audioContext.createBufferSource();
                             this._soundSource.buffer = this._audioBuffer;
                             this._soundSource.connect(this._inputAudioNode);

+ 6 - 0
src/Engines/IPipelineContext.ts

@@ -13,5 +13,11 @@ export interface IPipelineContext {
     isReady: boolean;
 
     /** @hidden */
+    _getVertexShaderCode(): string | null;
+
+    /** @hidden */
+    _getFragmentShaderCode(): string | null;
+
+    /** @hidden */
     _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void;
 }

+ 8 - 0
src/Engines/WebGL/webGLPipelineContext.ts

@@ -38,4 +38,12 @@ export class WebGLPipelineContext implements IPipelineContext {
             onCompiled(this.program);
         }
     }
+
+    public _getVertexShaderCode(): string | null {
+        return this.vertexShader ? this.engine._getShaderSource(this.vertexShader) : null;
+    }
+
+    public _getFragmentShaderCode(): string | null {
+        return this.fragmentShader ? this.engine._getShaderSource(this.fragmentShader) : null;
+    }
 }

+ 8 - 0
src/Engines/nativeEngine.ts

@@ -107,6 +107,14 @@ class NativePipelineContext implements IPipelineContext {
     public isAsync = false;
     public isReady = false;
 
+    public _getVertexShaderCode(): string | null {
+        return null;
+    }
+
+    public _getFragmentShaderCode(): string | null {
+        return null;
+    }
+
     // TODO: what should this do?
     public _handlesSpectorRebuildCallback(onCompiled: (compiledObject: any) => void): void {
         throw new Error("Not implemented");

+ 6 - 2
src/Engines/thinEngine.ts

@@ -2081,6 +2081,11 @@ export class ThinEngine {
         return shader;
     }
 
+    /** @hidden */
+    public _getShaderSource(shader: WebGLShader): Nullable<string> {
+        return this._gl.getShaderSource(shader);
+    }
+
     /**
      * Directly creates a webGL program
      * @param pipelineContext  defines the pipeline context to attach to
@@ -2834,11 +2839,10 @@ export class ThinEngine {
 
             if (EngineStore.UseFallbackTexture) {
                 this.createTexture(EngineStore.FallbackTexture, noMipmap, texture.invertY, scene, samplingMode, null, onError, buffer, texture);
-                return;
             }
 
             if (onError) {
-                onError(message || "Unknown error", exception);
+                onError((message || "Unknown error") + (EngineStore.UseFallbackTexture ? " - Fallback texture was used" : ""), exception);
             }
         };
 

+ 22 - 18
src/Layers/effectLayer.ts

@@ -653,18 +653,21 @@ export abstract class EffectLayer {
         }
 
         var material = subMesh.getMaterial();
-        var mesh = subMesh.getRenderingMesh();
+        var ownerMesh = subMesh.getMesh();
+        var replacementMesh = ownerMesh._internalAbstractMeshDataInfo._actAsRegularMesh ? ownerMesh : null;
+        var renderingMesh = subMesh.getRenderingMesh();
+        var effectiveMesh = replacementMesh ? replacementMesh : renderingMesh;
         var scene = this._scene;
         var engine = scene.getEngine();
 
-        mesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
+        effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
 
         if (!material) {
             return;
         }
 
         // Do not block in blend mode.
-        if (!this._canRenderMesh(mesh, material)) {
+        if (!this._canRenderMesh(renderingMesh, material)) {
             return;
         }
 
@@ -672,28 +675,28 @@ export abstract class EffectLayer {
         engine.setState(material.backFaceCulling);
 
         // Managing instances
-        var batch = mesh._getInstancesRenderList(subMesh._id);
+        var batch = renderingMesh._getInstancesRenderList(subMesh._id, !!replacementMesh);
         if (batch.mustReturn) {
             return;
         }
 
         // Early Exit per mesh
-        if (!this._shouldRenderMesh(mesh)) {
+        if (!this._shouldRenderMesh(renderingMesh)) {
             return;
         }
 
         var hardwareInstancedRendering = batch.hardwareInstancedRendering[subMesh._id];
 
-        this._setEmissiveTextureAndColor(mesh, subMesh, material);
+        this._setEmissiveTextureAndColor(renderingMesh, subMesh, material);
 
-        this.onBeforeRenderMeshToEffect.notifyObservers(mesh);
+        this.onBeforeRenderMeshToEffect.notifyObservers(ownerMesh);
 
-        if (this._useMeshMaterial(mesh)) {
-            mesh.render(subMesh, hardwareInstancedRendering);
+        if (this._useMeshMaterial(renderingMesh)) {
+            renderingMesh.render(subMesh, hardwareInstancedRendering, replacementMesh || undefined);
         }
         else if (this._isReady(subMesh, hardwareInstancedRendering, this._emissiveTextureAndColor.texture)) {
             engine.enableEffect(this._effectLayerMapGenerationEffect);
-            mesh._bind(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode);
+            renderingMesh._bind(subMesh, this._effectLayerMapGenerationEffect, Material.TriangleFillMode);
 
             this._effectLayerMapGenerationEffect.setMatrix("viewProjection", scene.getTransformMatrix());
 
@@ -735,11 +738,11 @@ export abstract class EffectLayer {
             }
 
             // Bones
-            if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                const skeleton = mesh.skeleton;
+            if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
+                const skeleton = renderingMesh.skeleton;
 
                 if (skeleton.isUsingTextureForMatrices) {
-                    const boneTexture = skeleton.getTransformMatrixTexture(mesh);
+                    const boneTexture = skeleton.getTransformMatrixTexture(renderingMesh);
                     if (!boneTexture) {
                         return;
                     }
@@ -747,12 +750,12 @@ export abstract class EffectLayer {
                     this._effectLayerMapGenerationEffect.setTexture("boneSampler", boneTexture);
                     this._effectLayerMapGenerationEffect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
                 } else {
-                    this._effectLayerMapGenerationEffect.setMatrices("mBones", skeleton.getTransformMatrices((mesh)));
+                    this._effectLayerMapGenerationEffect.setMatrices("mBones", skeleton.getTransformMatrices((renderingMesh)));
                 }
             }
 
             // Morph targets
-            MaterialHelper.BindMorphTargetParameters(mesh, this._effectLayerMapGenerationEffect);
+            MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effectLayerMapGenerationEffect);
 
             // Alpha mode
             if (enableAlphaMode) {
@@ -760,14 +763,15 @@ export abstract class EffectLayer {
             }
 
             // Draw
-            mesh._processRendering(subMesh, this._effectLayerMapGenerationEffect, material.fillMode, batch, hardwareInstancedRendering,
-                (isInstance, world) => this._effectLayerMapGenerationEffect.setMatrix("world", world));
+            var world = effectiveMesh.getWorldMatrix();
+            renderingMesh._processRendering(subMesh, this._effectLayerMapGenerationEffect, material.fillMode, batch, hardwareInstancedRendering,
+                (isInstance, w) => this._effectLayerMapGenerationEffect.setMatrix("world", world));
         } else {
             // Need to reset refresh rate of the main map
             this._mainTexture.resetRefreshCounter();
         }
 
-        this.onAfterRenderMeshToEffect.notifyObservers(mesh);
+        this.onAfterRenderMeshToEffect.notifyObservers(ownerMesh);
     }
 
     /**

+ 2 - 2
src/Layers/glowLayer.ts

@@ -536,8 +536,8 @@ export class GlowLayer extends EffectLayer {
      */
     public unReferenceMeshFromUsingItsOwnMaterial(mesh: AbstractMesh): void {
         let index = this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId);
-        while (index > 0) {
-            this._meshesUsingTheirOwnMaterials.slice(index, index + 1);
+        while (index >= 0) {
+            this._meshesUsingTheirOwnMaterials.splice(index, 1);
             index = this._meshesUsingTheirOwnMaterials.indexOf(mesh.uniqueId);
         }
     }

+ 18 - 13
src/Lights/Shadows/shadowGenerator.ts

@@ -1001,12 +1001,15 @@ export class ShadowGenerator implements IShadowGenerator {
     }
 
     protected _renderSubMeshForShadowMap(subMesh: SubMesh): void {
-        var mesh = subMesh.getRenderingMesh();
+        var ownerMesh = subMesh.getMesh();
+        var replacementMesh = ownerMesh._internalAbstractMeshDataInfo._actAsRegularMesh ? ownerMesh : null;
+        var renderingMesh = subMesh.getRenderingMesh();
+        var effectiveMesh = replacementMesh ? replacementMesh : renderingMesh;
         var scene = this._scene;
         var engine = scene.getEngine();
         let material = subMesh.getMaterial();
 
-        mesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
+        effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
 
         if (!material || subMesh.verticesCount === 0) {
             return;
@@ -1016,7 +1019,7 @@ export class ShadowGenerator implements IShadowGenerator {
         engine.setState(material.backFaceCulling);
 
         // Managing instances
-        var batch = mesh._getInstancesRenderList(subMesh._id);
+        var batch = renderingMesh._getInstancesRenderList(subMesh._id, !!replacementMesh);
         if (batch.mustReturn) {
             return;
         }
@@ -1024,7 +1027,7 @@ export class ShadowGenerator implements IShadowGenerator {
         var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null) && (batch.visibleInstances[subMesh._id] !== undefined);
         if (this.isReady(subMesh, hardwareInstancedRendering)) {
             engine.enableEffect(this._effect);
-            mesh._bind(subMesh, this._effect, material.fillMode);
+            renderingMesh._bind(subMesh, this._effect, material.fillMode);
 
             this._effect.setFloat3("biasAndScale", this.bias, this.normalBias, this.depthScale);
 
@@ -1050,11 +1053,11 @@ export class ShadowGenerator implements IShadowGenerator {
             }
 
             // Bones
-            if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                const skeleton = mesh.skeleton;
+            if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
+                const skeleton = renderingMesh.skeleton;
 
                 if (skeleton.isUsingTextureForMatrices) {
-                    const boneTexture = skeleton.getTransformMatrixTexture(mesh);
+                    const boneTexture = skeleton.getTransformMatrixTexture(renderingMesh);
 
                     if (!boneTexture) {
                         return;
@@ -1063,12 +1066,12 @@ export class ShadowGenerator implements IShadowGenerator {
                     this._effect.setTexture("boneSampler", boneTexture);
                     this._effect.setFloat("boneTextureWidth", 4.0 * (skeleton.bones.length + 1));
                 } else {
-                    this._effect.setMatrices("mBones", skeleton.getTransformMatrices((mesh)));
+                    this._effect.setMatrices("mBones", skeleton.getTransformMatrices((renderingMesh)));
                 }
             }
 
             // Morph targets
-            MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
+            MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effect);
 
             // Clip planes
             MaterialHelper.BindClipPlane(this._effect, scene);
@@ -1080,12 +1083,14 @@ export class ShadowGenerator implements IShadowGenerator {
             }
 
             // Observables
-            this.onBeforeShadowMapRenderMeshObservable.notifyObservers(mesh);
+            this.onBeforeShadowMapRenderMeshObservable.notifyObservers(renderingMesh);
             this.onBeforeShadowMapRenderObservable.notifyObservers(this._effect);
 
             // Draw
-            mesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
-                (isInstance, world) => this._effect.setMatrix("world", world));
+
+            var world = effectiveMesh.getWorldMatrix();
+            renderingMesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
+                (isInstance, w) => this._effect.setMatrix("world", world));
 
             if (this.forceBackFacesOnly) {
                 engine.setState(true, 0, false, false);
@@ -1093,7 +1098,7 @@ export class ShadowGenerator implements IShadowGenerator {
 
             // Observables
             this.onAfterShadowMapRenderObservable.notifyObservers(this._effect);
-            this.onAfterShadowMapRenderMeshObservable.notifyObservers(mesh);
+            this.onAfterShadowMapRenderMeshObservable.notifyObservers(renderingMesh);
 
         } else {
             // Need to reset refresh rate of the shadowMap

+ 3 - 1
src/Materials/Textures/renderTargetTexture.ts

@@ -764,7 +764,9 @@ export class RenderTargetTexture extends Texture {
                         if (!mesh.isAnInstance) {
                             mesh._internalAbstractMeshDataInfo._onlyForInstancesIntermediate = false;
                         } else {
-                            mesh = (mesh as InstancedMesh).sourceMesh;
+                            if (!mesh._internalAbstractMeshDataInfo._actAsRegularMesh) {
+                                mesh = (mesh as InstancedMesh).sourceMesh;
+                            }
                         }
                         mesh._internalAbstractMeshDataInfo._isActiveIntermediate = true;
 

+ 46 - 0
src/Materials/effect.ts

@@ -76,6 +76,10 @@ export class Effect implements IDisposable {
      */
     public static ShadersRepository = "src/Shaders/";
     /**
+     * Enable logging of the shader code when a compilation error occurs
+     */
+    public static LogShaderCodeOnCompilationError = true;
+    /**
      * Name of the effect.
      */
     public name: any = null;
@@ -612,6 +616,25 @@ export class Effect implements IDisposable {
         }
     }
 
+    private _getShaderCodeAndErrorLine(code: Nullable<string>, error: Nullable<string>, isFragment: boolean): [Nullable<string>, Nullable<string>] {
+        const regexp = isFragment ? /FRAGMENT SHADER ERROR: 0:(\d+?):/ : /VERTEX SHADER ERROR: 0:(\d+?):/;
+
+        let errorLine = null;
+
+        if (error && code) {
+            const res = error.match(regexp);
+            if (res && res.length === 2) {
+                const lineNumber = parseInt(res[1]);
+                const lines = code.split("\n", -1);
+                if (lines.length >= lineNumber) {
+                    errorLine = `Offending line [${lineNumber}] in ${isFragment ? "fragment" : "vertex"} code: ${lines[lineNumber - 1]}`;
+                }
+            }
+        }
+
+        return [code, errorLine];
+    }
+
     private _processCompilationErrors(e: any, previousPipelineContext: Nullable<IPipelineContext> = null) {
         this._compilationError = e.message;
         let attributesNames = this._attributesNames;
@@ -626,6 +649,29 @@ export class Effect implements IDisposable {
             return " " + attribute;
         }));
         Logger.Error("Defines:\r\n" + this.defines);
+        if (Effect.LogShaderCodeOnCompilationError) {
+            let lineErrorVertex = null, lineErrorFragment = null, code = null;
+            if (this._pipelineContext?._getVertexShaderCode()) {
+                [code, lineErrorVertex] = this._getShaderCodeAndErrorLine(this._pipelineContext._getVertexShaderCode(), this._compilationError, false);
+                if (code) {
+                    Logger.Error("Vertex code:");
+                    Logger.Error(code);
+                }
+            }
+            if (this._pipelineContext?._getFragmentShaderCode()) {
+                [code, lineErrorFragment] = this._getShaderCodeAndErrorLine(this._pipelineContext?._getFragmentShaderCode(), this._compilationError, true);
+                if (code) {
+                    Logger.Error("Fragment code:");
+                    Logger.Error(code);
+                }
+            }
+            if (lineErrorVertex) {
+                Logger.Error(lineErrorVertex);
+            }
+            if (lineErrorFragment) {
+                Logger.Error(lineErrorFragment);
+            }
+        }
         Logger.Error("Error: " + this._compilationError);
         if (previousPipelineContext) {
             this._pipelineContext = previousPipelineContext;

+ 16 - 1
src/Meshes/meshSimplification.ts

@@ -297,7 +297,7 @@ class Reference {
  * @author RaananW
  * @see http://doc.babylonjs.com/how_to/in-browser_mesh_simplification
  */
-class QuadraticErrorSimplification implements ISimplifier {
+export class QuadraticErrorSimplification implements ISimplifier {
 
     private triangles: Array<DecimationTriangle>;
     private vertices: Array<DecimationVertex>;
@@ -305,19 +305,34 @@ class QuadraticErrorSimplification implements ISimplifier {
 
     private _reconstructedMesh: Mesh;
 
+    /** Gets or sets the number pf sync interations */
     public syncIterations = 5000;
 
+    /** Gets or sets the aggressiveness of the simplifier */
     public aggressiveness: number;
+
+    /** Gets or sets the number of allowed iterations for decimation */
     public decimationIterations: number;
 
+    /** Gets or sets the espilon to use for bounding box computation */
     public boundingBoxEpsilon: number;
 
+    /**
+     * Creates a new QuadraticErrorSimplification
+     * @param _mesh defines the target mesh
+     */
     constructor(private _mesh: Mesh) {
         this.aggressiveness = 7;
         this.decimationIterations = 100;
         this.boundingBoxEpsilon = Epsilon;
     }
 
+    /**
+     * Simplification of a given mesh according to the given settings.
+     * Since this requires computation, it is assumed that the function runs async.
+     * @param settings The settings of the simplification, including quality and distance
+     * @param successCallback A callback that will be called after the mesh was simplified.
+     */
     public simplify(settings: ISimplificationSettings, successCallback: (simplifiedMesh: Mesh) => void) {
         this.initDecimatedMesh();
         //iterating through the submeshes array, one after the other.

+ 91 - 0
src/Misc/dataStorage.ts

@@ -0,0 +1,91 @@
+interface IStorage {
+    getItem: (key: string) => string | null;
+    setItem: (key: string, value: string) => void;
+}
+
+/**
+ * Class for storing data to local storage if available or in-memory storage otherwise
+ */
+export class DataStorage {
+    private static _Storage: IStorage = DataStorage._GetStorage();
+
+    private static _GetStorage(): IStorage {
+        try {
+            localStorage.setItem("test", "");
+            localStorage.removeItem("test");
+            return localStorage;
+        }
+        catch {
+            const inMemoryStorage: { [key: string]: string } = {};
+            return {
+                getItem: (key) => {
+                    const value = inMemoryStorage[key];
+                    return value === undefined ? null : value;
+                },
+                setItem: (key, value) => {
+                    inMemoryStorage[key] = value;
+                }
+            };
+        }
+    }
+
+    /**
+     * Reads a string from the data storage
+     * @param key The key to read
+     * @param defaultValue The value if the key doesn't exist
+     * @returns The string value
+     */
+    public static ReadString(key: string, defaultValue: string): string {
+        const value = this._Storage.getItem(key);
+        return (value !== null ? value : defaultValue);
+    }
+
+    /**
+     * Writes a string to the data storage
+     * @param key The key to write
+     * @param value The value to write
+     */
+    public static WriteString(key: string, value: string): void {
+        this._Storage.setItem(key, value);
+    }
+
+    /**
+     * Reads a boolean from the data storage
+     * @param key The key to read
+     * @param defaultValue The value if the key doesn't exist
+     * @returns The boolean value
+     */
+    public static ReadBoolean(key: string, defaultValue: boolean): boolean {
+        const value = this._Storage.getItem(key);
+        return (value !== null ? (value === "true") : defaultValue);
+    }
+
+    /**
+     * Writes a boolean to the data storage
+     * @param key The key to write
+     * @param value The value to write
+     */
+    public static WriteBoolean(key: string, value: boolean) {
+        this._Storage.setItem(key, value ? "true" : "false");
+    }
+
+    /**
+     * Reads a number from the data storage
+     * @param key The key to read
+     * @param defaultValue The value if the key doesn't exist
+     * @returns The number value
+     */
+    public static ReadNumber(key: string, defaultValue: number): number {
+        const value = this._Storage.getItem(key);
+        return (value !== null ? parseFloat(value) : defaultValue);
+    }
+
+    /**
+     * Writes a number to the data storage
+     * @param key The key to write
+     * @param value The value to write
+     */
+    public static WriteNumber(key: string, value: number) {
+        this._Storage.setItem(key, value.toString());
+    }
+}

+ 1 - 0
src/Misc/index.ts

@@ -47,3 +47,4 @@ export * from "./stringTools";
 export * from "./dataReader";
 export * from "./minMaxReducer";
 export * from "./depthReducer";
+export * from "./dataStorage";

+ 6 - 0
src/Particles/IParticleSystem.ts

@@ -312,6 +312,12 @@ export interface IParticleSystem {
      */
     isReady(): boolean;
     /**
+     * Returns the string "ParticleSystem"
+     * @returns a string containing the class name
+     */
+    getClassName(): string;
+
+    /**
      * Adds a new color gradient
      * @param gradient defines the gradient to use (between 0 and 1)
      * @param color1 defines the color to affect to the specified gradient

+ 17 - 13
src/PostProcesses/volumetricLightScatteringPostProcess.ts

@@ -291,12 +291,15 @@ export class VolumetricLightScatteringPostProcess extends PostProcess {
 
         // Custom render function for submeshes
         var renderSubMesh = (subMesh: SubMesh): void => {
-            var mesh = subMesh.getRenderingMesh();
-            if (this._meshExcluded(mesh)) {
+            var ownerMesh = subMesh.getMesh();
+            var replacementMesh = ownerMesh._internalAbstractMeshDataInfo._actAsRegularMesh ? ownerMesh : null;
+            var renderingMesh = subMesh.getRenderingMesh();
+            var effectiveMesh = replacementMesh ? replacementMesh : renderingMesh;
+            if (this._meshExcluded(renderingMesh)) {
                 return;
             }
 
-            mesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
+            effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
 
             let material = subMesh.getMaterial();
 
@@ -304,14 +307,14 @@ export class VolumetricLightScatteringPostProcess extends PostProcess {
                 return;
             }
 
-            var scene = mesh.getScene();
+            var scene = renderingMesh.getScene();
             var engine = scene.getEngine();
 
             // Culling
             engine.setState(material.backFaceCulling);
 
             // Managing instances
-            var batch = mesh._getInstancesRenderList(subMesh._id);
+            var batch = renderingMesh._getInstancesRenderList(subMesh._id, !!replacementMesh);
 
             if (batch.mustReturn) {
                 return;
@@ -321,7 +324,7 @@ export class VolumetricLightScatteringPostProcess extends PostProcess {
 
             if (this._isReady(subMesh, hardwareInstancedRendering)) {
                 var effect: Effect = this._volumetricLightScatteringPass;
-                if (mesh === this.mesh) {
+                if (renderingMesh === this.mesh) {
                     if (subMesh.effect) {
                         effect = subMesh.effect;
                     } else {
@@ -330,10 +333,10 @@ export class VolumetricLightScatteringPostProcess extends PostProcess {
                 }
 
                 engine.enableEffect(effect);
-                mesh._bind(subMesh, effect, material.fillMode);
+                renderingMesh._bind(subMesh, effect, material.fillMode);
 
-                if (mesh === this.mesh) {
-                    material.bind(mesh.getWorldMatrix(), mesh);
+                if (renderingMesh === this.mesh) {
+                    material.bind(effectiveMesh.getWorldMatrix(), renderingMesh);
                 }
                 else {
                     this._volumetricLightScatteringPass.setMatrix("viewProjection", scene.getTransformMatrix());
@@ -350,14 +353,15 @@ export class VolumetricLightScatteringPostProcess extends PostProcess {
                     }
 
                     // Bones
-                    if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                        this._volumetricLightScatteringPass.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+                    if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
+                        this._volumetricLightScatteringPass.setMatrices("mBones", renderingMesh.skeleton.getTransformMatrices(renderingMesh));
                     }
                 }
 
                 // Draw
-                mesh._processRendering(subMesh, this._volumetricLightScatteringPass, Material.TriangleFillMode, batch, hardwareInstancedRendering,
-                    (isInstance, world) => effect.setMatrix("world", world));
+                var world = effectiveMesh.getWorldMatrix();
+                renderingMesh._processRendering(subMesh, this._volumetricLightScatteringPass, Material.TriangleFillMode, batch, hardwareInstancedRendering,
+                    (isInstance, w) => effect.setMatrix("world", world));
             }
         };
 

+ 13 - 9
src/Rendering/depthRenderer.ts

@@ -94,12 +94,15 @@ export class DepthRenderer {
 
         // Custom render function
         var renderSubMesh = (subMesh: SubMesh): void => {
-            var mesh = subMesh.getRenderingMesh();
+            var ownerMesh = subMesh.getMesh();
+            var replacementMesh = ownerMesh._internalAbstractMeshDataInfo._actAsRegularMesh ? ownerMesh : null;
+            var renderingMesh = subMesh.getRenderingMesh();
+            var effectiveMesh = replacementMesh ? replacementMesh : renderingMesh;
             var scene = this._scene;
             var engine = scene.getEngine();
             let material = subMesh.getMaterial();
 
-            mesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
+            effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
 
             if (!material) {
                 return;
@@ -109,7 +112,7 @@ export class DepthRenderer {
             engine.setState(material.backFaceCulling, 0, false, scene.useRightHandedSystem);
 
             // Managing instances
-            var batch = mesh._getInstancesRenderList(subMesh._id);
+            var batch = renderingMesh._getInstancesRenderList(subMesh._id, !!replacementMesh);
 
             if (batch.mustReturn) {
                 return;
@@ -120,7 +123,7 @@ export class DepthRenderer {
             var camera = this._camera || scene.activeCamera;
             if (this.isReady(subMesh, hardwareInstancedRendering) && camera) {
                 engine.enableEffect(this._effect);
-                mesh._bind(subMesh, this._effect, material.fillMode);
+                renderingMesh._bind(subMesh, this._effect, material.fillMode);
 
                 this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
 
@@ -137,16 +140,17 @@ export class DepthRenderer {
                 }
 
                 // Bones
-                if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                    this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+                if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
+                    this._effect.setMatrices("mBones", renderingMesh.skeleton.getTransformMatrices(renderingMesh));
                 }
 
                 // Morph targets
-                MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
+                MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effect);
 
                 // Draw
-                mesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
-                    (isInstance, world) => this._effect.setMatrix("world", world));
+                var world = effectiveMesh.getWorldMatrix();
+                renderingMesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
+                    (isInstance, w) => this._effect.setMatrix("world", world));
             }
         };
 

+ 25 - 21
src/Rendering/geometryBufferRenderer.ts

@@ -390,7 +390,10 @@ export class GeometryBufferRenderer {
 
         // Custom render function
         var renderSubMesh = (subMesh: SubMesh): void => {
-            var mesh = subMesh.getRenderingMesh();
+            var ownerMesh = subMesh.getMesh();
+            var replacementMesh = ownerMesh._internalAbstractMeshDataInfo._actAsRegularMesh ? ownerMesh : null;
+            var renderingMesh = subMesh.getRenderingMesh();
+            var effectiveMesh = replacementMesh ? replacementMesh : renderingMesh;
             var scene = this._scene;
             var engine = scene.getEngine();
             let material = <any> subMesh.getMaterial();
@@ -399,18 +402,18 @@ export class GeometryBufferRenderer {
                 return;
             }
 
-            mesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
+            effectiveMesh._internalAbstractMeshDataInfo._isActiveIntermediate = false;
 
             // Velocity
-            if (this._enableVelocity && !this._previousTransformationMatrices[mesh.uniqueId]) {
-                this._previousTransformationMatrices[mesh.uniqueId] = {
+            if (this._enableVelocity && !this._previousTransformationMatrices[effectiveMesh.uniqueId]) {
+                this._previousTransformationMatrices[effectiveMesh.uniqueId] = {
                     world: Matrix.Identity(),
                     viewProjection: scene.getTransformMatrix()
                 };
 
-                if (mesh.skeleton) {
-                    const bonesTransformations = mesh.skeleton.getTransformMatrices(mesh);
-                    this._previousBonesTransformationMatrices[mesh.uniqueId] = this._copyBonesTransformationMatrices(bonesTransformations, new Float32Array(bonesTransformations.length));
+                if (renderingMesh.skeleton) {
+                    const bonesTransformations = renderingMesh.skeleton.getTransformMatrices(renderingMesh);
+                    this._previousBonesTransformationMatrices[renderingMesh.uniqueId] = this._copyBonesTransformationMatrices(bonesTransformations, new Float32Array(bonesTransformations.length));
                 }
             }
 
@@ -418,17 +421,18 @@ export class GeometryBufferRenderer {
             engine.setState(material.backFaceCulling, 0, false, scene.useRightHandedSystem);
 
             // Managing instances
-            var batch = mesh._getInstancesRenderList(subMesh._id);
+            var batch = renderingMesh._getInstancesRenderList(subMesh._id, !!replacementMesh);
 
             if (batch.mustReturn) {
                 return;
             }
 
             var hardwareInstancedRendering = (engine.getCaps().instancedArrays) && (batch.visibleInstances[subMesh._id] !== null);
+            var world = effectiveMesh.getWorldMatrix();
 
             if (this.isReady(subMesh, hardwareInstancedRendering)) {
                 engine.enableEffect(this._effect);
-                mesh._bind(subMesh, this._effect, material.fillMode);
+                renderingMesh._bind(subMesh, this._effect, material.fillMode);
 
                 this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
                 this._effect.setMatrix("view", scene.getViewMatrix());
@@ -464,33 +468,33 @@ export class GeometryBufferRenderer {
                 }
 
                 // Bones
-                if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-                    this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+                if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
+                    this._effect.setMatrices("mBones", renderingMesh.skeleton.getTransformMatrices(renderingMesh));
                     if (this._enableVelocity) {
-                        this._effect.setMatrices("mPreviousBones", this._previousBonesTransformationMatrices[mesh.uniqueId]);
+                        this._effect.setMatrices("mPreviousBones", this._previousBonesTransformationMatrices[renderingMesh.uniqueId]);
                     }
                 }
 
                 // Morph targets
-                MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
+                MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effect);
 
                 // Velocity
                 if (this._enableVelocity) {
-                    this._effect.setMatrix("previousWorld", this._previousTransformationMatrices[mesh.uniqueId].world);
-                    this._effect.setMatrix("previousViewProjection", this._previousTransformationMatrices[mesh.uniqueId].viewProjection);
+                    this._effect.setMatrix("previousWorld", this._previousTransformationMatrices[effectiveMesh.uniqueId].world);
+                    this._effect.setMatrix("previousViewProjection", this._previousTransformationMatrices[effectiveMesh.uniqueId].viewProjection);
                 }
 
                 // Draw
-                mesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
-                    (isInstance, world) => this._effect.setMatrix("world", world));
+                renderingMesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
+                    (isInstance, w) => this._effect.setMatrix("world", world));
             }
 
             // Velocity
             if (this._enableVelocity) {
-                this._previousTransformationMatrices[mesh.uniqueId].world = mesh.getWorldMatrix().clone();
-                this._previousTransformationMatrices[mesh.uniqueId].viewProjection = this._scene.getTransformMatrix().clone();
-                if (mesh.skeleton) {
-                    this._copyBonesTransformationMatrices(mesh.skeleton.getTransformMatrices(mesh), this._previousBonesTransformationMatrices[mesh.uniqueId]);
+                this._previousTransformationMatrices[effectiveMesh.uniqueId].world = world.clone();
+                this._previousTransformationMatrices[effectiveMesh.uniqueId].viewProjection = this._scene.getTransformMatrix().clone();
+                if (renderingMesh.skeleton) {
+                    this._copyBonesTransformationMatrices(renderingMesh.skeleton.getTransformMatrices(renderingMesh), this._previousBonesTransformationMatrices[effectiveMesh.uniqueId]);
                 }
             }
         };

+ 13 - 10
src/Rendering/outlineRenderer.ts

@@ -163,7 +163,10 @@ export class OutlineRenderer implements ISceneComponent {
             return;
         }
 
-        var mesh = subMesh.getRenderingMesh();
+        var ownerMesh = subMesh.getMesh();
+        var replacementMesh = ownerMesh._internalAbstractMeshDataInfo._actAsRegularMesh ? ownerMesh : null;
+        var renderingMesh = subMesh.getRenderingMesh();
+        var effectiveMesh = replacementMesh ? replacementMesh : renderingMesh;
         var material = subMesh.getMaterial();
 
         if (!material || !scene.activeCamera) {
@@ -177,19 +180,19 @@ export class OutlineRenderer implements ISceneComponent {
             this._effect.setFloat("logarithmicDepthConstant", 2.0 / (Math.log(scene.activeCamera.maxZ + 1.0) / Math.LN2));
         }
 
-        this._effect.setFloat("offset", useOverlay ? 0 : mesh.outlineWidth);
-        this._effect.setColor4("color", useOverlay ? mesh.overlayColor : mesh.outlineColor, useOverlay ? mesh.overlayAlpha : material.alpha);
+        this._effect.setFloat("offset", useOverlay ? 0 : renderingMesh.outlineWidth);
+        this._effect.setColor4("color", useOverlay ? renderingMesh.overlayColor : renderingMesh.outlineColor, useOverlay ? renderingMesh.overlayAlpha : material.alpha);
         this._effect.setMatrix("viewProjection", scene.getTransformMatrix());
 
         // Bones
-        if (mesh.useBones && mesh.computeBonesUsingShaders && mesh.skeleton) {
-            this._effect.setMatrices("mBones", mesh.skeleton.getTransformMatrices(mesh));
+        if (renderingMesh.useBones && renderingMesh.computeBonesUsingShaders && renderingMesh.skeleton) {
+            this._effect.setMatrices("mBones", renderingMesh.skeleton.getTransformMatrices(renderingMesh));
         }
 
         // Morph targets
-        MaterialHelper.BindMorphTargetParameters(mesh, this._effect);
+        MaterialHelper.BindMorphTargetParameters(renderingMesh, this._effect);
 
-        mesh._bind(subMesh, this._effect, material.fillMode);
+        renderingMesh._bind(subMesh, this._effect, material.fillMode);
 
         // Alpha test
         if (material && material.needAlphaTesting()) {
@@ -201,9 +204,9 @@ export class OutlineRenderer implements ISceneComponent {
         }
 
         engine.setZOffset(-this.zOffset);
-
-        mesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
-            (isInstance, world) => { this._effect.setMatrix("world", world); });
+        var world = effectiveMesh.getWorldMatrix();
+        renderingMesh._processRendering(subMesh, this._effect, material.fillMode, batch, hardwareInstancedRendering,
+            (isInstance, w) => { this._effect.setMatrix("world", world); });
 
         engine.setZOffset(0);
     }