Przeglądaj źródła

Merge remote-tracking branch 'upstream/master' into xrRightHanded

Raanan Weber 5 lat temu
rodzic
commit
8f1b2080f1
94 zmienionych plików z 4351 dodań i 602 usunięć
  1. 42 0
      Playground/templates.json
  2. 10 1
      Tools/Gulp/helpers/gulp-processConstants.js
  3. 17 6
      Tools/Gulp/tasks/gulpTasks-localRun.js
  4. 78 4
      dist/preview release/babylon.d.ts
  5. 2 2
      dist/preview release/babylon.js
  6. 157 16
      dist/preview release/babylon.max.js
  7. 1 1
      dist/preview release/babylon.max.js.map
  8. 159 8
      dist/preview release/babylon.module.d.ts
  9. 78 4
      dist/preview release/documentation.d.ts
  10. 1 1
      dist/preview release/glTF2Interface/package.json
  11. 48 48
      dist/preview release/gui/babylon.gui.js
  12. 1 1
      dist/preview release/gui/babylon.gui.js.map
  13. 2 2
      dist/preview release/gui/package.json
  14. 8 8
      dist/preview release/inspector/babylon.inspector.bundle.js
  15. 850 118
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  16. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  17. 160 13
      dist/preview release/inspector/babylon.inspector.d.ts
  18. 347 26
      dist/preview release/inspector/babylon.inspector.module.d.ts
  19. 7 7
      dist/preview release/inspector/package.json
  20. 3 3
      dist/preview release/loaders/package.json
  21. 2 2
      dist/preview release/materialsLibrary/package.json
  22. 2 2
      dist/preview release/nodeEditor/package.json
  23. 1 1
      dist/preview release/package.json
  24. 1 1
      dist/preview release/packagesSizeBaseLine.json
  25. 2 2
      dist/preview release/postProcessesLibrary/package.json
  26. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  27. 3 3
      dist/preview release/serializers/package.json
  28. 159 8
      dist/preview release/viewer/babylon.module.d.ts
  29. 11 11
      dist/preview release/viewer/babylon.viewer.js
  30. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  31. 15 3
      dist/preview release/what's new.md
  32. 9 1
      inspector/src/components/actionTabs/lines/floatLineComponent.tsx
  33. 1 1
      inspector/src/components/actionTabs/lines/sliderLineComponent.tsx
  34. 22 0
      inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx
  35. 102 105
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx
  36. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx
  37. 187 6
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss
  38. 25 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/playhead.tsx
  39. 90 3
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx
  40. 116 0
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/timeline.tsx
  41. 4 4
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx
  42. 2 0
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx
  43. 55 0
      inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spriteManagerPropertyGridComponent.tsx
  44. 84 0
      inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spritePropertyGridComponent.tsx
  45. 30 0
      inspector/src/components/sceneExplorer/entities/spriteManagerTreeItemComponent.tsx
  46. 30 0
      inspector/src/components/sceneExplorer/entities/spriteTreeItemComponent.tsx
  47. 25 1
      inspector/src/components/sceneExplorer/sceneExplorer.scss
  48. 15 0
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  49. 13 8
      inspector/src/components/sceneExplorer/treeItemComponent.tsx
  50. 12 0
      inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx
  51. 79 0
      loaders/src/glTF/2.0/Extensions/KHR_materials_ior.ts
  52. 14 5
      loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts
  53. 1 0
      loaders/src/glTF/2.0/Extensions/index.ts
  54. 1 1
      package.json
  55. 1 1
      src/Bones/skeleton.ts
  56. 3 1
      src/Engines/Processors/shaderProcessor.ts
  57. 12 0
      src/Engines/constants.ts
  58. 2 2
      src/Engines/thinEngine.ts
  59. 2 11
      src/Materials/Node/Blocks/PBR/reflectivityBlock.ts
  60. 108 57
      src/Materials/PBR/pbrBaseMaterial.ts
  61. 33 13
      src/Materials/PBR/pbrMaterial.ts
  62. 7 3
      src/Materials/PBR/pbrSubSurfaceConfiguration.ts
  63. 209 0
      src/Materials/Textures/Filtering/hdrFiltering.ts
  64. 30 0
      src/Materials/Textures/baseTexture.ts
  65. 14 3
      src/Materials/Textures/hdrCubeTexture.ts
  66. 4 3
      src/Materials/Textures/index.ts
  67. 54 6
      src/Materials/effectRenderer.ts
  68. 7 0
      src/Materials/standardMaterial.ts
  69. 2 1
      src/Meshes/mesh.ts
  70. 4 3
      src/Particles/EmitterTypes/coneParticleEmitter.ts
  71. 13 1
      src/Probes/reflectionProbe.ts
  72. 20 0
      src/Rendering/boundingBoxRenderer.ts
  73. 1 1
      src/Rendering/geometryBufferRenderer.ts
  74. 224 0
      src/Shaders/ShadersInclude/hdrFilteringFunctions.fx
  75. 48 0
      src/Shaders/ShadersInclude/helperFunctions.fx
  76. 155 0
      src/Shaders/ShadersInclude/importanceSampling.fx
  77. 19 6
      src/Shaders/ShadersInclude/pbrBRDFFunctions.fx
  78. 1 1
      src/Shaders/ShadersInclude/pbrBlockReflectance.fx
  79. 12 2
      src/Shaders/ShadersInclude/pbrBlockReflectance0.fx
  80. 6 3
      src/Shaders/ShadersInclude/pbrBlockReflection.fx
  81. 4 8
      src/Shaders/ShadersInclude/pbrBlockReflectivity.fx
  82. 1 0
      src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx
  83. 16 1
      src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx
  84. 4 0
      src/Shaders/ShadersInclude/pbrUboDeclaration.fx
  85. 5 0
      src/Shaders/ShadersInclude/pbrVertexDeclaration.fx
  86. 31 21
      src/Shaders/default.fragment.fx
  87. 15 0
      src/Shaders/hdrFiltering.fragment.fx
  88. 16 0
      src/Shaders/hdrFiltering.vertex.fx
  89. 13 0
      src/Shaders/pbr.fragment.fx
  90. 15 0
      src/Shaders/pbr.vertex.fx
  91. 64 5
      src/Sprites/sprite.ts
  92. 83 3
      src/Sprites/spriteManager.ts
  93. 1 1
      src/node.ts
  94. 3 3
      tests/validation/config.json

+ 42 - 0
Playground/templates.json

@@ -24,6 +24,42 @@
     "language" : "javascript"
   },
   {
+    "label" : "Create a point light",
+    "documentation" : "https://doc.babylonjs.com/babylon101/lights#the-point-light",
+    "insertText" : "var pointLight = new BABYLON.PointLight(\"${1:pointLight}\", new BABYLON.Vector3(${2:0},${3:5},${4:0}), scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Create a directional light",
+    "documentation" : "https://doc.babylonjs.com/babylon101/lights#the-directional-light",
+    "insertText" : "var dirLight = new BABYLON.DirectionalLight(\"${1:dirLight}\", new BABYLON.Vector3(${2:0.25},${3:-1},${4:-0.25}), scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Create a spot light",
+    "documentation" : "https://doc.babylonjs.com/babylon101/lights#the-spot-light",
+    "insertText" : "var spotLight = new BABYLON.SpotLight(\"${1:spotLight}\", new BABYLON.Vector3(${2:0}, ${3:30}, ${4:-10}), new BABYLON.Vector3(${5:0}, ${6:-1}, ${7:0}), ${8:Math.PI / 3}, ${9:2}, scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Create a hemispheric light",
+    "documentation" : "https://doc.babylonjs.com/babylon101/lights#the-hemispheric-light",
+    "insertText" : "var hemiLight = new BABYLON.HemisphericLight(\"${1:hemiLight}\", new BABYLON.Vector3(${2:0}, ${3:1}, ${4:0}), scene);",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Load a Node Material from snippet w/callback",
+    "documentation" : "https://doc.babylonjs.com/how_to/node_material#loading-from-a-file-saved-from-the-node-material-editor",
+    "insertText" : "BABYLON.NodeMaterial.ParseFromSnippetAsync(\"${1:your_snippet_url_no_#}\", scene).then(nodeMaterial => {\n     ${2:mesh_to_apply_node_material_to}.material = nodeMaterial;\n});",
+    "language" : "javascript"
+  },
+  {
+    "label" : "Show the Inspector",
+    "documentation" : "https://doc.babylonjs.com/features/playground_debuglayer",
+    "insertText" : "scene.debugLayer.show({\n     embedMode:true\n});",
+    "language" : "javascript"
+  },
+  {
     "label" : "Create an Arc Rotate Camera w/Degrees",
     "documentation" : "https://doc.babylonjs.com/babylon101/cameras#arc-rotate-camera",
     "insertText" : "var camera = new BABYLON.ArcRotateCamera(\"${1:camera}\", BABYLON.Tools.ToRadians(${2:90}), BABYLON.Tools.ToRadians(${3:65}), ${4:10}, ${5:BABYLON.Vector3.Zero()}, scene);",
@@ -40,5 +76,11 @@
   "documentation" : "https://doc.babylonjs.com/resources/external_pg_assets",
   "insertText" : "BABYLON.SceneLoader.ImportMesh(\"${1:meshName}\", \"${2:url to the mesh parent directory}\", \"${3:Mesh filename.fileextension}\", scene, function(newMeshes){\n\n});",
   "language" : "javascript"
+  },
+  {
+    "label" : "Setup a shadow generator",
+    "documentation" : "https://doc.babylonjs.com/babylon101/shadows",
+    "insertText" : "var shadowGenerator = new BABYLON.ShadowGenerator(${1:size}, ${2:the_light_source});\nshadowGenerator.getShadowMap().renderList.push(${3:the_mesh_that_casts_a_shadow});\n${4:mesh_that_receives_the_shadow}.receiveShadows = true;",
+    "language" : "javascript"
   }
 ]

+ 10 - 1
Tools/Gulp/helpers/gulp-processConstants.js

@@ -2,13 +2,22 @@
 var through = require('through2');
 var PluginError = require('plugin-error');
 const fs = require('fs');
-const babylonConstants = require(__dirname + '/../../../dist/preview release/babylon.max').Constants;
+const constantModule = __dirname + '/../../../dist/preview release/babylon.max';
 
+let _babylonConstants = undefined;
+function getBabylonConstants() {
+    if (!_babylonConstants) {
+        _babylonConstants = require(constantModule).Constants;
+    }
+    return _babylonConstants;
+}
 
 /**
  * Replace all constants by their inlined values.
  */
 function processConstants(sourceCode) {
+    const babylonConstants = getBabylonConstants();
+
     var regexImport = /import { Constants } from .*;/g;
     sourceCode = sourceCode.replace(regexImport, "");
 

+ 17 - 6
Tools/Gulp/tasks/gulpTasks-localRun.js

@@ -28,13 +28,23 @@ gulp.task("webserver", function () {
         middleware: function (connect, opt) {
             return [function (req, res, next) {
                 const baseUrl =  (req.url.indexOf('dist') !== -1 || req.url.indexOf('Tools') !== -1  || req.url.indexOf('temp/') !== -1);
-                if (!baseUrl && req.headers['referer'] && req.headers['referer'].indexOf('/Playground/') !== -1 && req.url.indexOf('/Playground/') === -1) {
-                    req.url = "/Playground/" + req.url;
-                    res.writeHead(301, {
-                        'Location': req.url
-                    });
-                    return res.end();
+                let referer = req.headers['referer'];
+                if (!baseUrl && referer) {
+                    referer = referer.toLowerCase();
+                    if (referer.indexOf('/playground/') !== -1 && req.url.indexOf('/Playground/') === -1) {
+                        req.url = "/Playground/" + req.url;
+                        res.writeHead(301, {
+                            'Location': req.url
+                        });
+                        return res.end();
+                    }
+                    if (referer.indexOf('/localdev/') !== -1 && referer.indexOf(req.originalUrl) === -1) {
+                        if (!fs.existsSync(rootRelativePath + req.originalUrl)) {
+                            req.url = "/Playground/" + req.url.replace(/localDev/ig, "");
+                        }
+                    }
                 }
+
                 const pgMath = req.url.match(/\/Playground\/pg\/(.*)/);
                 if (pgMath) {
                     const isAFile = req.url.indexOf('.') !== -1;
@@ -61,6 +71,7 @@ gulp.task("webserver", function () {
                         req.url += ".js";
                     }
                 }
+
                 next();
             }]
         }

+ 78 - 4
dist/preview release/babylon.d.ts

@@ -10026,6 +10026,10 @@ declare module BABYLON {
      */
     export interface ISpriteManager extends IDisposable {
         /**
+         * Gets manager's name
+         */
+        name: string;
+        /**
          * Restricts the camera to viewing objects with the same layerMask.
          * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
          */
@@ -10035,6 +10039,10 @@ declare module BABYLON {
          */
         isPickable: boolean;
         /**
+         * Gets the hosting scene
+         */
+        scene: Scene;
+        /**
          * Specifies the rendering group id for this mesh (0 by default)
          * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
          */
@@ -10044,6 +10052,14 @@ declare module BABYLON {
          */
         sprites: Array<Sprite>;
         /**
+         * Gets or sets the spritesheet texture
+         */
+        texture: Texture;
+        /** Defines the default width of a cell in the spritesheet */
+        cellWidth: number;
+        /** Defines the default height of a cell in the spritesheet */
+        cellHeight: number;
+        /**
          * Tests the intersection of a sprite with a specific ray.
          * @param ray The ray we are sending to test the collision
          * @param camera The camera space we are sending rays in
@@ -10114,6 +10130,23 @@ declare module BABYLON {
         private _effectBase;
         private _effectFog;
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the array of sprites
+         */
+        get children(): Sprite[];
+        /**
+         * Gets the hosting scene
+         */
+        get scene(): Scene;
+        /**
+         * Gets or sets the capacity of the manager
+         */
+        get capacity(): number;
+        set capacity(value: number);
+        /**
          * Gets or sets the spritesheet texture
          */
         get texture(): Texture;
@@ -10146,6 +10179,11 @@ declare module BABYLON {
         constructor(
         /** defines the manager's name */
         name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon?: number, samplingMode?: number, fromPacked?: boolean, spriteJSON?: any | null);
+        /**
+         * Returns the string "SpriteManager"
+         * @returns "SpriteManager"
+         */
+        getClassName(): string;
         private _makePacked;
         private _appendSpriteVertex;
         private _checkTextureAlpha;
@@ -18137,7 +18175,7 @@ declare module BABYLON {
      * Class used to represent a sprite
      * @see http://doc.babylonjs.com/babylon101/sprites
      */
-    export class Sprite {
+    export class Sprite implements IAnimatable {
         /** defines the name */
         name: string;
         /** Gets or sets the current world position */
@@ -18155,13 +18193,13 @@ declare module BABYLON {
         /** Gets or sets the cell reference in the sprite sheet, uses sprite's filename when added to sprite sheet */
         cellRef: string;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in U axis */
-        invertU: number;
+        invertU: boolean;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in B axis */
-        invertV: number;
+        invertV: boolean;
         /** Gets or sets a boolean indicating that this sprite should be disposed after animation ends */
         disposeWhenFinishedAnimating: boolean;
         /** Gets the list of attached animations */
-        animations: Animation[];
+        animations: Nullable<Array<Animation>>;
         /** Gets or sets a boolean indicating if the sprite can be picked */
         isPickable: boolean;
         /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
@@ -18201,6 +18239,14 @@ declare module BABYLON {
         get size(): number;
         set size(value: number);
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the manager of this sprite
+         */
+        get manager(): ISpriteManager;
+        /**
          * Creates a new Sprite
          * @param name defines the name
          * @param manager defines the manager
@@ -18209,6 +18255,23 @@ declare module BABYLON {
         /** defines the name */
         name: string, manager: ISpriteManager);
         /**
+         * Returns the string "Sprite"
+         * @returns "Sprite"
+         */
+        getClassName(): string;
+        /** Gets or sets the initial key for the animation (setting it will restart the animation)  */
+        get fromIndex(): number;
+        set fromIndex(value: number);
+        /** Gets or sets the end key for the animation (setting it will restart the animation)  */
+        get toIndex(): number;
+        set toIndex(value: number);
+        /** Gets or sets a boolean indicating if the animation is looping (setting it will restart the animation)  */
+        get loopAnimation(): boolean;
+        set loopAnimation(value: boolean);
+        /** Gets or sets the delay between cell changes (setting it will restart the animation)  */
+        get delay(): number;
+        set delay(value: number);
+        /**
          * Starts an animation
          * @param from defines the initial key
          * @param to defines the end key
@@ -26892,6 +26955,9 @@ declare module BABYLON {
         PREMULTIPLYALPHA: boolean;
         ALPHATEST_AFTERALLALPHACOMPUTATIONS: boolean;
         ALPHABLEND: boolean;
+        RGBDLIGHTMAP: boolean;
+        RGBDREFLECTION: boolean;
+        RGBDREFRACTION: boolean;
         IMAGEPROCESSING: boolean;
         VIGNETTE: boolean;
         VIGNETTEBLENDMODEMULTIPLY: boolean;
@@ -68800,6 +68866,14 @@ declare module BABYLON {
          */
         showBackLines: boolean;
         /**
+         * Observable raised before rendering a bounding box
+         */
+        onBeforeBoxRenderingObservable: Observable<BoundingBox>;
+        /**
+         * Observable raised after rendering a bounding box
+         */
+        onAfterBoxRenderingObservable: Observable<BoundingBox>;
+        /**
          * @hidden
          */
         renderList: SmartArray<BoundingBox>;

Plik diff jest za duży
+ 2 - 2
dist/preview release/babylon.js


Plik diff jest za duży
+ 157 - 16
dist/preview release/babylon.max.js


Plik diff jest za duży
+ 1 - 1
dist/preview release/babylon.max.js.map


+ 159 - 8
dist/preview release/babylon.module.d.ts

@@ -10224,6 +10224,10 @@ declare module "babylonjs/Sprites/spriteManager" {
      */
     export interface ISpriteManager extends IDisposable {
         /**
+         * Gets manager's name
+         */
+        name: string;
+        /**
          * Restricts the camera to viewing objects with the same layerMask.
          * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
          */
@@ -10233,6 +10237,10 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         isPickable: boolean;
         /**
+         * Gets the hosting scene
+         */
+        scene: Scene;
+        /**
          * Specifies the rendering group id for this mesh (0 by default)
          * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
          */
@@ -10242,6 +10250,14 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         sprites: Array<Sprite>;
         /**
+         * Gets or sets the spritesheet texture
+         */
+        texture: Texture;
+        /** Defines the default width of a cell in the spritesheet */
+        cellWidth: number;
+        /** Defines the default height of a cell in the spritesheet */
+        cellHeight: number;
+        /**
          * Tests the intersection of a sprite with a specific ray.
          * @param ray The ray we are sending to test the collision
          * @param camera The camera space we are sending rays in
@@ -10312,6 +10328,23 @@ declare module "babylonjs/Sprites/spriteManager" {
         private _effectBase;
         private _effectFog;
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the array of sprites
+         */
+        get children(): Sprite[];
+        /**
+         * Gets the hosting scene
+         */
+        get scene(): Scene;
+        /**
+         * Gets or sets the capacity of the manager
+         */
+        get capacity(): number;
+        set capacity(value: number);
+        /**
          * Gets or sets the spritesheet texture
          */
         get texture(): Texture;
@@ -10344,6 +10377,11 @@ declare module "babylonjs/Sprites/spriteManager" {
         constructor(
         /** defines the manager's name */
         name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon?: number, samplingMode?: number, fromPacked?: boolean, spriteJSON?: any | null);
+        /**
+         * Returns the string "SpriteManager"
+         * @returns "SpriteManager"
+         */
+        getClassName(): string;
         private _makePacked;
         private _appendSpriteVertex;
         private _checkTextureAlpha;
@@ -18636,11 +18674,13 @@ declare module "babylonjs/Sprites/sprite" {
     import { ISpriteManager } from "babylonjs/Sprites/spriteManager";
     import { Color4 } from "babylonjs/Maths/math.color";
     import { Observable } from "babylonjs/Misc/observable";
+    import { IAnimatable } from "babylonjs/Animations/animatable.interface";
+    import { Animation } from "babylonjs/Animations/animation";
     /**
      * Class used to represent a sprite
      * @see http://doc.babylonjs.com/babylon101/sprites
      */
-    export class Sprite {
+    export class Sprite implements IAnimatable {
         /** defines the name */
         name: string;
         /** Gets or sets the current world position */
@@ -18658,13 +18698,13 @@ declare module "babylonjs/Sprites/sprite" {
         /** Gets or sets the cell reference in the sprite sheet, uses sprite's filename when added to sprite sheet */
         cellRef: string;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in U axis */
-        invertU: number;
+        invertU: boolean;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in B axis */
-        invertV: number;
+        invertV: boolean;
         /** Gets or sets a boolean indicating that this sprite should be disposed after animation ends */
         disposeWhenFinishedAnimating: boolean;
         /** Gets the list of attached animations */
-        animations: Animation[];
+        animations: Nullable<Array<Animation>>;
         /** Gets or sets a boolean indicating if the sprite can be picked */
         isPickable: boolean;
         /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
@@ -18704,6 +18744,14 @@ declare module "babylonjs/Sprites/sprite" {
         get size(): number;
         set size(value: number);
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the manager of this sprite
+         */
+        get manager(): ISpriteManager;
+        /**
          * Creates a new Sprite
          * @param name defines the name
          * @param manager defines the manager
@@ -18712,6 +18760,23 @@ declare module "babylonjs/Sprites/sprite" {
         /** defines the name */
         name: string, manager: ISpriteManager);
         /**
+         * Returns the string "Sprite"
+         * @returns "Sprite"
+         */
+        getClassName(): string;
+        /** Gets or sets the initial key for the animation (setting it will restart the animation)  */
+        get fromIndex(): number;
+        set fromIndex(value: number);
+        /** Gets or sets the end key for the animation (setting it will restart the animation)  */
+        get toIndex(): number;
+        set toIndex(value: number);
+        /** Gets or sets a boolean indicating if the animation is looping (setting it will restart the animation)  */
+        get loopAnimation(): boolean;
+        set loopAnimation(value: boolean);
+        /** Gets or sets the delay between cell changes (setting it will restart the animation)  */
+        get delay(): number;
+        set delay(value: number);
+        /**
          * Starts an animation
          * @param from defines the initial key
          * @param to defines the end key
@@ -27749,6 +27814,9 @@ declare module "babylonjs/Materials/standardMaterial" {
         PREMULTIPLYALPHA: boolean;
         ALPHATEST_AFTERALLALPHACOMPUTATIONS: boolean;
         ALPHABLEND: boolean;
+        RGBDLIGHTMAP: boolean;
+        RGBDREFLECTION: boolean;
+        RGBDREFRACTION: boolean;
         IMAGEPROCESSING: boolean;
         VIGNETTE: boolean;
         VIGNETTEBLENDMODEMULTIPLY: boolean;
@@ -72516,6 +72584,7 @@ declare module "babylonjs/Rendering/boundingBoxRenderer" {
     import "babylonjs/Shaders/color.fragment";
     import "babylonjs/Shaders/color.vertex";
     import { Color3 } from "babylonjs/Maths/math.color";
+    import { Observable } from "babylonjs/Misc/observable";
     module "babylonjs/scene" {
         interface Scene {
             /** @hidden (Backing field) */
@@ -72569,6 +72638,14 @@ declare module "babylonjs/Rendering/boundingBoxRenderer" {
          */
         showBackLines: boolean;
         /**
+         * Observable raised before rendering a bounding box
+         */
+        onBeforeBoxRenderingObservable: Observable<BoundingBox>;
+        /**
+         * Observable raised after rendering a bounding box
+         */
+        onAfterBoxRenderingObservable: Observable<BoundingBox>;
+        /**
          * @hidden
          */
         renderList: SmartArray<BoundingBox>;
@@ -86006,6 +86083,10 @@ declare module BABYLON {
      */
     export interface ISpriteManager extends IDisposable {
         /**
+         * Gets manager's name
+         */
+        name: string;
+        /**
          * Restricts the camera to viewing objects with the same layerMask.
          * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
          */
@@ -86015,6 +86096,10 @@ declare module BABYLON {
          */
         isPickable: boolean;
         /**
+         * Gets the hosting scene
+         */
+        scene: Scene;
+        /**
          * Specifies the rendering group id for this mesh (0 by default)
          * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
          */
@@ -86024,6 +86109,14 @@ declare module BABYLON {
          */
         sprites: Array<Sprite>;
         /**
+         * Gets or sets the spritesheet texture
+         */
+        texture: Texture;
+        /** Defines the default width of a cell in the spritesheet */
+        cellWidth: number;
+        /** Defines the default height of a cell in the spritesheet */
+        cellHeight: number;
+        /**
          * Tests the intersection of a sprite with a specific ray.
          * @param ray The ray we are sending to test the collision
          * @param camera The camera space we are sending rays in
@@ -86094,6 +86187,23 @@ declare module BABYLON {
         private _effectBase;
         private _effectFog;
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the array of sprites
+         */
+        get children(): Sprite[];
+        /**
+         * Gets the hosting scene
+         */
+        get scene(): Scene;
+        /**
+         * Gets or sets the capacity of the manager
+         */
+        get capacity(): number;
+        set capacity(value: number);
+        /**
          * Gets or sets the spritesheet texture
          */
         get texture(): Texture;
@@ -86126,6 +86236,11 @@ declare module BABYLON {
         constructor(
         /** defines the manager's name */
         name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon?: number, samplingMode?: number, fromPacked?: boolean, spriteJSON?: any | null);
+        /**
+         * Returns the string "SpriteManager"
+         * @returns "SpriteManager"
+         */
+        getClassName(): string;
         private _makePacked;
         private _appendSpriteVertex;
         private _checkTextureAlpha;
@@ -94117,7 +94232,7 @@ declare module BABYLON {
      * Class used to represent a sprite
      * @see http://doc.babylonjs.com/babylon101/sprites
      */
-    export class Sprite {
+    export class Sprite implements IAnimatable {
         /** defines the name */
         name: string;
         /** Gets or sets the current world position */
@@ -94135,13 +94250,13 @@ declare module BABYLON {
         /** Gets or sets the cell reference in the sprite sheet, uses sprite's filename when added to sprite sheet */
         cellRef: string;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in U axis */
-        invertU: number;
+        invertU: boolean;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in B axis */
-        invertV: number;
+        invertV: boolean;
         /** Gets or sets a boolean indicating that this sprite should be disposed after animation ends */
         disposeWhenFinishedAnimating: boolean;
         /** Gets the list of attached animations */
-        animations: Animation[];
+        animations: Nullable<Array<Animation>>;
         /** Gets or sets a boolean indicating if the sprite can be picked */
         isPickable: boolean;
         /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
@@ -94181,6 +94296,14 @@ declare module BABYLON {
         get size(): number;
         set size(value: number);
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the manager of this sprite
+         */
+        get manager(): ISpriteManager;
+        /**
          * Creates a new Sprite
          * @param name defines the name
          * @param manager defines the manager
@@ -94189,6 +94312,23 @@ declare module BABYLON {
         /** defines the name */
         name: string, manager: ISpriteManager);
         /**
+         * Returns the string "Sprite"
+         * @returns "Sprite"
+         */
+        getClassName(): string;
+        /** Gets or sets the initial key for the animation (setting it will restart the animation)  */
+        get fromIndex(): number;
+        set fromIndex(value: number);
+        /** Gets or sets the end key for the animation (setting it will restart the animation)  */
+        get toIndex(): number;
+        set toIndex(value: number);
+        /** Gets or sets a boolean indicating if the animation is looping (setting it will restart the animation)  */
+        get loopAnimation(): boolean;
+        set loopAnimation(value: boolean);
+        /** Gets or sets the delay between cell changes (setting it will restart the animation)  */
+        get delay(): number;
+        set delay(value: number);
+        /**
          * Starts an animation
          * @param from defines the initial key
          * @param to defines the end key
@@ -102872,6 +103012,9 @@ declare module BABYLON {
         PREMULTIPLYALPHA: boolean;
         ALPHATEST_AFTERALLALPHACOMPUTATIONS: boolean;
         ALPHABLEND: boolean;
+        RGBDLIGHTMAP: boolean;
+        RGBDREFLECTION: boolean;
+        RGBDREFRACTION: boolean;
         IMAGEPROCESSING: boolean;
         VIGNETTE: boolean;
         VIGNETTEBLENDMODEMULTIPLY: boolean;
@@ -144780,6 +144923,14 @@ declare module BABYLON {
          */
         showBackLines: boolean;
         /**
+         * Observable raised before rendering a bounding box
+         */
+        onBeforeBoxRenderingObservable: Observable<BoundingBox>;
+        /**
+         * Observable raised after rendering a bounding box
+         */
+        onAfterBoxRenderingObservable: Observable<BoundingBox>;
+        /**
          * @hidden
          */
         renderList: SmartArray<BoundingBox>;

+ 78 - 4
dist/preview release/documentation.d.ts

@@ -10026,6 +10026,10 @@ declare module BABYLON {
      */
     export interface ISpriteManager extends IDisposable {
         /**
+         * Gets manager's name
+         */
+        name: string;
+        /**
          * Restricts the camera to viewing objects with the same layerMask.
          * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
          */
@@ -10035,6 +10039,10 @@ declare module BABYLON {
          */
         isPickable: boolean;
         /**
+         * Gets the hosting scene
+         */
+        scene: Scene;
+        /**
          * Specifies the rendering group id for this mesh (0 by default)
          * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
          */
@@ -10044,6 +10052,14 @@ declare module BABYLON {
          */
         sprites: Array<Sprite>;
         /**
+         * Gets or sets the spritesheet texture
+         */
+        texture: Texture;
+        /** Defines the default width of a cell in the spritesheet */
+        cellWidth: number;
+        /** Defines the default height of a cell in the spritesheet */
+        cellHeight: number;
+        /**
          * Tests the intersection of a sprite with a specific ray.
          * @param ray The ray we are sending to test the collision
          * @param camera The camera space we are sending rays in
@@ -10114,6 +10130,23 @@ declare module BABYLON {
         private _effectBase;
         private _effectFog;
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the array of sprites
+         */
+        get children(): Sprite[];
+        /**
+         * Gets the hosting scene
+         */
+        get scene(): Scene;
+        /**
+         * Gets or sets the capacity of the manager
+         */
+        get capacity(): number;
+        set capacity(value: number);
+        /**
          * Gets or sets the spritesheet texture
          */
         get texture(): Texture;
@@ -10146,6 +10179,11 @@ declare module BABYLON {
         constructor(
         /** defines the manager's name */
         name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon?: number, samplingMode?: number, fromPacked?: boolean, spriteJSON?: any | null);
+        /**
+         * Returns the string "SpriteManager"
+         * @returns "SpriteManager"
+         */
+        getClassName(): string;
         private _makePacked;
         private _appendSpriteVertex;
         private _checkTextureAlpha;
@@ -18137,7 +18175,7 @@ declare module BABYLON {
      * Class used to represent a sprite
      * @see http://doc.babylonjs.com/babylon101/sprites
      */
-    export class Sprite {
+    export class Sprite implements IAnimatable {
         /** defines the name */
         name: string;
         /** Gets or sets the current world position */
@@ -18155,13 +18193,13 @@ declare module BABYLON {
         /** Gets or sets the cell reference in the sprite sheet, uses sprite's filename when added to sprite sheet */
         cellRef: string;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in U axis */
-        invertU: number;
+        invertU: boolean;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in B axis */
-        invertV: number;
+        invertV: boolean;
         /** Gets or sets a boolean indicating that this sprite should be disposed after animation ends */
         disposeWhenFinishedAnimating: boolean;
         /** Gets the list of attached animations */
-        animations: Animation[];
+        animations: Nullable<Array<Animation>>;
         /** Gets or sets a boolean indicating if the sprite can be picked */
         isPickable: boolean;
         /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
@@ -18201,6 +18239,14 @@ declare module BABYLON {
         get size(): number;
         set size(value: number);
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the manager of this sprite
+         */
+        get manager(): ISpriteManager;
+        /**
          * Creates a new Sprite
          * @param name defines the name
          * @param manager defines the manager
@@ -18209,6 +18255,23 @@ declare module BABYLON {
         /** defines the name */
         name: string, manager: ISpriteManager);
         /**
+         * Returns the string "Sprite"
+         * @returns "Sprite"
+         */
+        getClassName(): string;
+        /** Gets or sets the initial key for the animation (setting it will restart the animation)  */
+        get fromIndex(): number;
+        set fromIndex(value: number);
+        /** Gets or sets the end key for the animation (setting it will restart the animation)  */
+        get toIndex(): number;
+        set toIndex(value: number);
+        /** Gets or sets a boolean indicating if the animation is looping (setting it will restart the animation)  */
+        get loopAnimation(): boolean;
+        set loopAnimation(value: boolean);
+        /** Gets or sets the delay between cell changes (setting it will restart the animation)  */
+        get delay(): number;
+        set delay(value: number);
+        /**
          * Starts an animation
          * @param from defines the initial key
          * @param to defines the end key
@@ -26892,6 +26955,9 @@ declare module BABYLON {
         PREMULTIPLYALPHA: boolean;
         ALPHATEST_AFTERALLALPHACOMPUTATIONS: boolean;
         ALPHABLEND: boolean;
+        RGBDLIGHTMAP: boolean;
+        RGBDREFLECTION: boolean;
+        RGBDREFRACTION: boolean;
         IMAGEPROCESSING: boolean;
         VIGNETTE: boolean;
         VIGNETTEBLENDMODEMULTIPLY: boolean;
@@ -68800,6 +68866,14 @@ declare module BABYLON {
          */
         showBackLines: boolean;
         /**
+         * Observable raised before rendering a bounding box
+         */
+        onBeforeBoxRenderingObservable: Observable<BoundingBox>;
+        /**
+         * Observable raised after rendering a bounding box
+         */
+        onAfterBoxRenderingObservable: Observable<BoundingBox>;
+        /**
          * @hidden
          */
         renderList: SmartArray<BoundingBox>;

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

@@ -1,7 +1,7 @@
 {
     "name": "babylonjs-gltf2interface",
     "description": "A typescript declaration of babylon's gltf2 inteface.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 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_Misc_perfCounter__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__) {
 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/Misc/perfCounter");
+/* 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___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/Misc/perfCounter");
+/* 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___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");
@@ -1481,7 +1481,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_5__);
 
 
@@ -1713,7 +1713,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/Misc/perfCounter");
+/* 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___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");
@@ -1896,7 +1896,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/Misc/perfCounter");
+/* 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___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");
@@ -3285,7 +3285,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/Misc/perfCounter");
+/* 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___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");
@@ -3700,7 +3700,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/Misc/perfCounter");
+/* 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___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");
@@ -5626,7 +5626,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -5859,7 +5859,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -5956,7 +5956,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_4__);
 
 
@@ -6414,7 +6414,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/Misc/perfCounter");
+/* 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___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");
 
@@ -7341,7 +7341,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -7380,7 +7380,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/Misc/perfCounter");
+/* 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___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");
@@ -8393,7 +8393,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/Misc/perfCounter");
+/* 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___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");
@@ -8664,7 +8664,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/Misc/perfCounter");
+/* 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___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");
@@ -8934,7 +8934,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/Misc/perfCounter");
+/* 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___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");
@@ -9141,7 +9141,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -9291,7 +9291,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_7__);
 
 
@@ -10914,7 +10914,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/Misc/perfCounter");
+/* 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___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");
@@ -11244,7 +11244,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_3__);
 
 
@@ -11837,7 +11837,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_2__);
 
 
@@ -12092,7 +12092,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/Misc/perfCounter");
+/* 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___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");
@@ -12360,7 +12360,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/Misc/perfCounter");
+/* 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___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");
@@ -12823,7 +12823,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/Misc/perfCounter");
+/* 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___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");
@@ -13212,7 +13212,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -13437,7 +13437,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_0__);
 
 var tmpRect = [
@@ -13586,7 +13586,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/Misc/perfCounter");
+/* 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___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");
 
@@ -13729,7 +13729,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/Misc/perfCounter");
+/* 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___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");
 
@@ -14035,7 +14035,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_typeStore__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -14354,7 +14354,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/Misc/perfCounter");
+/* 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___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");
 
@@ -14397,7 +14397,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/Misc/perfCounter");
+/* 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___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");
@@ -14578,7 +14578,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/Misc/perfCounter");
+/* 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___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");
 
@@ -14735,7 +14735,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/Misc/perfCounter");
+/* 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___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");
 
@@ -15141,7 +15141,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/Misc/perfCounter");
+/* 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___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");
@@ -15227,7 +15227,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/Misc/perfCounter");
+/* 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___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");
@@ -15721,7 +15721,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/Misc/perfCounter");
+/* 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___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");
@@ -15776,7 +15776,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/Misc/perfCounter");
+/* 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___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");
@@ -15903,7 +15903,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/Misc/perfCounter");
+/* 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___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");
@@ -15989,7 +15989,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/Misc/perfCounter");
+/* 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___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");
 
@@ -16114,7 +16114,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/Misc/perfCounter");
+/* 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___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");
 
@@ -16305,7 +16305,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/Misc/perfCounter");
+/* 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___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");
 
@@ -16572,7 +16572,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/Misc/perfCounter");
+/* 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___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");
@@ -16895,7 +16895,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentPixelShader';
@@ -16917,7 +16917,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentVertexShader';
@@ -16940,7 +16940,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/Misc/perfCounter");
+/* 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___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -17242,14 +17242,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Misc/perfCounter":
+/***/ "babylonjs/Maths/math.vector":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_perfCounter__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Maths_math_vector__;
 
 /***/ })
 

Plik diff jest za duży
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-gui",
     "description": "The Babylon.js GUI library is an extension you can use to generate interactive user interface. It is build on top of the DynamicTexture.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.14"
+        "babylonjs": "4.2.0-alpha.15"
     },
     "engines": {
         "node": "*"

Plik diff jest za duży
+ 8 - 8
dist/preview release/inspector/babylon.inspector.bundle.js


Plik diff jest za duży
+ 850 - 118
dist/preview release/inspector/babylon.inspector.bundle.max.js


Plik diff jest za duży
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


+ 160 - 13
dist/preview release/inspector/babylon.inspector.d.ts

@@ -492,6 +492,7 @@ declare module INSPECTOR {
         step?: string;
         digits?: number;
         useEuler?: boolean;
+        min?: number;
     }
     export class FloatLineComponent extends React.Component<IFloatLineComponentProps, {
         value: string;
@@ -512,8 +513,11 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface IAnchorSvgPointProps {
-        point: BABYLON.Vector2;
+        control: BABYLON.Vector2;
         anchor: BABYLON.Vector2;
+        active: boolean;
+        type: string;
+        index: string;
     }
     export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps> {
         constructor(props: IAnchorSvgPointProps);
@@ -521,8 +525,17 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    export interface IKeyframeSvgPoint {
+        keyframePoint: BABYLON.Vector2;
+        rightControlPoint: BABYLON.Vector2 | null;
+        leftControlPoint: BABYLON.Vector2 | null;
+        id: string;
+    }
     interface IKeyframeSvgPointProps {
-        point: BABYLON.Vector2;
+        keyframePoint: BABYLON.Vector2;
+        leftControlPoint: BABYLON.Vector2 | null;
+        rightControlPoint: BABYLON.Vector2 | null;
+        id: string;
     }
     export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
         constructor(props: IKeyframeSvgPointProps);
@@ -530,36 +543,120 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ISvgDraggableAreaProps {
+        keyframeSvgPoints: IKeyframeSvgPoint[];
+        updatePosition: (updatedKeyframe: IKeyframeSvgPoint, index: number) => void;
+    }
+    export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
+        private _active;
+        private _isCurrentPointControl;
+        private _currentPointIndex;
+        private _draggableArea;
+        private _panStart;
+        private _panStop;
+        constructor(props: ISvgDraggableAreaProps);
+        componentDidMount(): void;
+        dragStart(e: React.TouchEvent<SVGSVGElement>): void;
+        dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        drag(e: React.TouchEvent<SVGSVGElement>): void;
+        drag(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        dragEnd(e: React.TouchEvent<SVGSVGElement>): void;
+        dragEnd(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        getMousePosition(e: React.TouchEvent<SVGSVGElement>): BABYLON.Vector2 | undefined;
+        getMousePosition(e: React.MouseEvent<SVGSVGElement, MouseEvent>): BABYLON.Vector2 | undefined;
+        panDirection(): void;
+        panTo(direction: string, value: number): void;
+        keyDown(e: KeyboardEvent): void;
+        keyUp(e: KeyboardEvent): void;
+        focus(e: React.MouseEvent<SVGSVGElement>): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface ITimelineProps {
+        keyframes: BABYLON.IAnimationKey[];
+        selected: BABYLON.IAnimationKey;
+        currentFrame: number;
+        onCurrentFrameChange: (frame: number) => void;
+    }
+    export class Timeline extends React.Component<ITimelineProps, {
+        selected: BABYLON.IAnimationKey;
+    }> {
+        readonly _frames: object[];
+        private _scrollable;
+        constructor(props: ITimelineProps);
+        handleInputChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        nextFrame(event: React.MouseEvent<HTMLDivElement>): void;
+        previousFrame(event: React.MouseEvent<HTMLDivElement>): void;
+        nextKeyframe(event: React.MouseEvent<HTMLDivElement>): void;
+        previousKeyframe(event: React.MouseEvent<HTMLDivElement>): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface IPlayheadProps {
+        frame: number;
+        offset: number;
+    }
+    export class Playhead extends React.Component<IPlayheadProps> {
+        constructor(props: IPlayheadProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface IAnimationCurveEditorComponentProps {
         close: (event: any) => void;
+        playOrPause: () => void;
         title: string;
         animations: BABYLON.Animation[];
         entityName: string;
+        scene: BABYLON.Scene;
+        entity: BABYLON.IAnimatable;
+    }
+    interface ICanvasAxis {
+        value: number;
     }
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
+        animations: BABYLON.Animation[];
+        animationName: string;
+        animationTargetProperty: string;
         isOpen: boolean;
         selected: BABYLON.Animation;
         currentPathData: string | undefined;
-        anchorPoints: {
-            point: BABYLON.Vector2;
-            anchor: BABYLON.Vector2;
-        }[] | null;
-        keyframes: BABYLON.Vector2[] | null;
+        svgKeyframes: IKeyframeSvgPoint[] | undefined;
+        currentFrame: number;
+        frameAxisLength: ICanvasAxis[];
     }> {
-        private _anchorPoints;
-        private _keyframes;
+        readonly _heightScale: number;
+        readonly _canvasLength: number;
+        private _playheadOffset;
+        private _newAnimations;
+        private _svgKeyframes;
+        private _frames;
+        private _isPlaying;
+        private _graphCanvas;
         constructor(props: IAnimationCurveEditorComponentProps);
+        componentDidMount(): void;
+        handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        addAnimation(): void;
+        addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
+        updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {
             easingType: string | undefined;
             easingMode: number | undefined;
         };
         getPathData(animation: BABYLON.Animation): string;
-        curvePath(keyframes: BABYLON.IAnimationKey[], data: string, heightScale: number, middle: number, easingFunction: BABYLON.EasingFunction): string;
-        linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, heightScale: number, middle: number): string;
-        setAnchorPoint(point: BABYLON.Vector2, anchor: BABYLON.Vector2): void;
-        setKeyframePoint(point: BABYLON.Vector2): void;
+        drawAllFrames(initialKey: BABYLON.IAnimationKey, endKey: BABYLON.IAnimationKey, easingFunction: BABYLON.EasingFunction): void;
+        curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
+        renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
+        linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
+        setKeyframePointLinear(point: BABYLON.Vector2, index: number): void;
+        setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
+        isAnimationPlaying(): void;
         selectAnimation(animation: BABYLON.Animation): void;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
+        changeCurrentFrame(frame: number): void;
         render(): JSX.Element;
     }
 }
@@ -1713,6 +1810,33 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ISpriteManagerPropertyGridComponentProps {
+        globalState: GlobalState;
+        spriteManager: BABYLON.SpriteManager;
+        lockObject: LockObject;
+        onSelectionChangedObservable?: BABYLON.Observable<any>;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        constructor(props: ISpriteManagerPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface ISpritePropertyGridComponentProps {
+        globalState: GlobalState;
+        sprite: BABYLON.Sprite;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+        onSelectionChangedObservable?: BABYLON.Observable<any>;
+    }
+    export class SpritePropertyGridComponent extends React.Component<ISpritePropertyGridComponentProps> {
+        constructor(props: ISpritePropertyGridComponentProps);
+        onManagerLink(): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     export class PropertyGridTabComponent extends PaneComponent {
         private _timerIntervalId;
         private _lockObject;
@@ -2066,6 +2190,28 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ISpriteManagerTreeItemComponentProps {
+        spriteManager: BABYLON.SpriteManager;
+        extensibilityGroups?: BABYLON.IExplorerExtensibilityGroup[];
+        onClick: () => void;
+    }
+    export class SpriteManagerTreeItemComponent extends React.Component<ISpriteManagerTreeItemComponentProps> {
+        constructor(props: ISpriteManagerTreeItemComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface ISpriteTreeItemComponentProps {
+        sprite: BABYLON.Sprite;
+        extensibilityGroups?: BABYLON.IExplorerExtensibilityGroup[];
+        onClick: () => void;
+    }
+    export class SpriteTreeItemComponent extends React.Component<ISpriteTreeItemComponentProps> {
+        constructor(props: ISpriteTreeItemComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface ITreeItemSpecializedComponentProps {
         label: string;
         entity?: any;
@@ -2121,6 +2267,7 @@ declare module INSPECTOR {
         label: string;
         offset: number;
         filter: BABYLON.Nullable<string>;
+        forceSubitems?: boolean;
         globalState: GlobalState;
         entity?: any;
         selectedEntity: any;

+ 347 - 26
dist/preview release/inspector/babylon.inspector.module.d.ts

@@ -561,6 +561,7 @@ declare module "babylonjs-inspector/components/actionTabs/lines/floatLineCompone
         step?: string;
         digits?: number;
         useEuler?: boolean;
+        min?: number;
     }
     export class FloatLineComponent extends React.Component<IFloatLineComponentProps, {
         value: string;
@@ -583,8 +584,11 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
     import * as React from "react";
     import { Vector2 } from 'babylonjs/Maths/math.vector';
     interface IAnchorSvgPointProps {
-        point: Vector2;
+        control: Vector2;
         anchor: Vector2;
+        active: boolean;
+        type: string;
+        index: string;
     }
     export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps> {
         constructor(props: IAnchorSvgPointProps);
@@ -594,50 +598,152 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint" {
     import * as React from "react";
     import { Vector2 } from 'babylonjs/Maths/math.vector';
+    export interface IKeyframeSvgPoint {
+        keyframePoint: Vector2;
+        rightControlPoint: Vector2 | null;
+        leftControlPoint: Vector2 | null;
+        id: string;
+    }
     interface IKeyframeSvgPointProps {
-        point: Vector2;
+        keyframePoint: Vector2;
+        leftControlPoint: Vector2 | null;
+        rightControlPoint: Vector2 | null;
+        id: string;
     }
     export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
         constructor(props: IKeyframeSvgPointProps);
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea" {
+    import * as React from "react";
+    import { Vector2 } from 'babylonjs/Maths/math.vector';
+    import { IKeyframeSvgPoint } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint";
+    interface ISvgDraggableAreaProps {
+        keyframeSvgPoints: IKeyframeSvgPoint[];
+        updatePosition: (updatedKeyframe: IKeyframeSvgPoint, index: number) => void;
+    }
+    export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
+        private _active;
+        private _isCurrentPointControl;
+        private _currentPointIndex;
+        private _draggableArea;
+        private _panStart;
+        private _panStop;
+        constructor(props: ISvgDraggableAreaProps);
+        componentDidMount(): void;
+        dragStart(e: React.TouchEvent<SVGSVGElement>): void;
+        dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        drag(e: React.TouchEvent<SVGSVGElement>): void;
+        drag(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        dragEnd(e: React.TouchEvent<SVGSVGElement>): void;
+        dragEnd(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        getMousePosition(e: React.TouchEvent<SVGSVGElement>): Vector2 | undefined;
+        getMousePosition(e: React.MouseEvent<SVGSVGElement, MouseEvent>): Vector2 | undefined;
+        panDirection(): void;
+        panTo(direction: string, value: number): void;
+        keyDown(e: KeyboardEvent): void;
+        keyUp(e: KeyboardEvent): void;
+        focus(e: React.MouseEvent<SVGSVGElement>): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/timeline" {
+    import * as React from "react";
+    import { IAnimationKey } from 'babylonjs/Animations/animationKey';
+    interface ITimelineProps {
+        keyframes: IAnimationKey[];
+        selected: IAnimationKey;
+        currentFrame: number;
+        onCurrentFrameChange: (frame: number) => void;
+    }
+    export class Timeline extends React.Component<ITimelineProps, {
+        selected: IAnimationKey;
+    }> {
+        readonly _frames: object[];
+        private _scrollable;
+        constructor(props: ITimelineProps);
+        handleInputChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        nextFrame(event: React.MouseEvent<HTMLDivElement>): void;
+        previousFrame(event: React.MouseEvent<HTMLDivElement>): void;
+        nextKeyframe(event: React.MouseEvent<HTMLDivElement>): void;
+        previousKeyframe(event: React.MouseEvent<HTMLDivElement>): void;
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/playhead" {
+    import * as React from "react";
+    interface IPlayheadProps {
+        frame: number;
+        offset: number;
+    }
+    export class Playhead extends React.Component<IPlayheadProps> {
+        constructor(props: IPlayheadProps);
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent" {
     import * as React from "react";
     import { Animation } from 'babylonjs/Animations/animation';
     import { Vector2 } from 'babylonjs/Maths/math.vector';
     import { EasingFunction } from 'babylonjs/Animations/easing';
     import { IAnimationKey } from 'babylonjs/Animations/animationKey';
+    import { IKeyframeSvgPoint } from "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/animations/keyframeSvgPoint";
+    import { Scene } from "babylonjs/scene";
+    import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
     interface IAnimationCurveEditorComponentProps {
         close: (event: any) => void;
+        playOrPause: () => void;
         title: string;
         animations: Animation[];
         entityName: string;
+        scene: Scene;
+        entity: IAnimatable;
+    }
+    interface ICanvasAxis {
+        value: number;
     }
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
+        animations: Animation[];
+        animationName: string;
+        animationTargetProperty: string;
         isOpen: boolean;
         selected: Animation;
         currentPathData: string | undefined;
-        anchorPoints: {
-            point: Vector2;
-            anchor: Vector2;
-        }[] | null;
-        keyframes: Vector2[] | null;
+        svgKeyframes: IKeyframeSvgPoint[] | undefined;
+        currentFrame: number;
+        frameAxisLength: ICanvasAxis[];
     }> {
-        private _anchorPoints;
-        private _keyframes;
+        readonly _heightScale: number;
+        readonly _canvasLength: number;
+        private _playheadOffset;
+        private _newAnimations;
+        private _svgKeyframes;
+        private _frames;
+        private _isPlaying;
+        private _graphCanvas;
         constructor(props: IAnimationCurveEditorComponentProps);
+        componentDidMount(): void;
+        handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        addAnimation(): void;
+        addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
+        updateKeyframe(keyframe: Vector2, index: number): void;
         getAnimationProperties(animation: Animation): {
             easingType: string | undefined;
             easingMode: number | undefined;
         };
         getPathData(animation: Animation): string;
-        curvePath(keyframes: IAnimationKey[], data: string, heightScale: number, middle: number, easingFunction: EasingFunction): string;
-        linearInterpolation(keyframes: IAnimationKey[], data: string, heightScale: number, middle: number): string;
-        setAnchorPoint(point: Vector2, anchor: Vector2): void;
-        setKeyframePoint(point: Vector2): void;
+        drawAllFrames(initialKey: IAnimationKey, endKey: IAnimationKey, easingFunction: EasingFunction): void;
+        curvePath(keyframes: IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction): string;
+        renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
+        linearInterpolation(keyframes: IAnimationKey[], data: string, middle: number): string;
+        setKeyframePointLinear(point: Vector2, index: number): void;
+        setKeyframePoint(controlPoints: Vector2[], index: number, keyframesCount: number): void;
+        isAnimationPlaying(): void;
         selectAnimation(animation: Animation): void;
         interpolateControlPoints(p0: Vector2, p1: Vector2, u: number, p2: Vector2, v: number, p3: Vector2): Vector2[] | undefined;
+        changeCurrentFrame(frame: number): void;
         render(): JSX.Element;
     }
 }
@@ -2195,6 +2301,45 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/par
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/sprites/spriteManagerPropertyGridComponent" {
+    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 { SpriteManager } from 'babylonjs/Sprites/spriteManager';
+    interface ISpriteManagerPropertyGridComponentProps {
+        globalState: GlobalState;
+        spriteManager: SpriteManager;
+        lockObject: LockObject;
+        onSelectionChangedObservable?: Observable<any>;
+        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+    }
+    export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        constructor(props: ISpriteManagerPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/sprites/spritePropertyGridComponent" {
+    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 { Sprite } from 'babylonjs/Sprites/sprite';
+    interface ISpritePropertyGridComponentProps {
+        globalState: GlobalState;
+        sprite: Sprite;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
+        onSelectionChangedObservable?: Observable<any>;
+    }
+    export class SpritePropertyGridComponent extends React.Component<ISpritePropertyGridComponentProps> {
+        constructor(props: ISpritePropertyGridComponentProps);
+        onManagerLink(): void;
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGridTabComponent" {
     import { PaneComponent, IPaneComponentProps } from "babylonjs-inspector/components/actionTabs/paneComponent";
     export class PropertyGridTabComponent extends PaneComponent {
@@ -2611,6 +2756,34 @@ declare module "babylonjs-inspector/components/sceneExplorer/entities/particleSy
         render(): JSX.Element;
     }
 }
+declare module "babylonjs-inspector/components/sceneExplorer/entities/spriteManagerTreeItemComponent" {
+    import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
+    import * as React from 'react';
+    import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
+    interface ISpriteManagerTreeItemComponentProps {
+        spriteManager: SpriteManager;
+        extensibilityGroups?: IExplorerExtensibilityGroup[];
+        onClick: () => void;
+    }
+    export class SpriteManagerTreeItemComponent extends React.Component<ISpriteManagerTreeItemComponentProps> {
+        constructor(props: ISpriteManagerTreeItemComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module "babylonjs-inspector/components/sceneExplorer/entities/spriteTreeItemComponent" {
+    import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
+    import * as React from 'react';
+    import { Sprite } from 'babylonjs/Sprites/sprite';
+    interface ISpriteTreeItemComponentProps {
+        sprite: Sprite;
+        extensibilityGroups?: IExplorerExtensibilityGroup[];
+        onClick: () => void;
+    }
+    export class SpriteTreeItemComponent extends React.Component<ISpriteTreeItemComponentProps> {
+        constructor(props: ISpriteTreeItemComponentProps);
+        render(): JSX.Element;
+    }
+}
 declare module "babylonjs-inspector/components/sceneExplorer/treeItemSpecializedComponent" {
     import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
     import * as React from "react";
@@ -2678,6 +2851,7 @@ declare module "babylonjs-inspector/components/sceneExplorer/treeItemComponent"
         label: string;
         offset: number;
         filter: Nullable<string>;
+        forceSubitems?: boolean;
         globalState: GlobalState;
         entity?: any;
         selectedEntity: any;
@@ -3361,6 +3535,7 @@ declare module INSPECTOR {
         step?: string;
         digits?: number;
         useEuler?: boolean;
+        min?: number;
     }
     export class FloatLineComponent extends React.Component<IFloatLineComponentProps, {
         value: string;
@@ -3381,8 +3556,11 @@ declare module INSPECTOR {
 }
 declare module INSPECTOR {
     interface IAnchorSvgPointProps {
-        point: BABYLON.Vector2;
+        control: BABYLON.Vector2;
         anchor: BABYLON.Vector2;
+        active: boolean;
+        type: string;
+        index: string;
     }
     export class AnchorSvgPoint extends React.Component<IAnchorSvgPointProps> {
         constructor(props: IAnchorSvgPointProps);
@@ -3390,8 +3568,17 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    export interface IKeyframeSvgPoint {
+        keyframePoint: BABYLON.Vector2;
+        rightControlPoint: BABYLON.Vector2 | null;
+        leftControlPoint: BABYLON.Vector2 | null;
+        id: string;
+    }
     interface IKeyframeSvgPointProps {
-        point: BABYLON.Vector2;
+        keyframePoint: BABYLON.Vector2;
+        leftControlPoint: BABYLON.Vector2 | null;
+        rightControlPoint: BABYLON.Vector2 | null;
+        id: string;
     }
     export class KeyframeSvgPoint extends React.Component<IKeyframeSvgPointProps> {
         constructor(props: IKeyframeSvgPointProps);
@@ -3399,36 +3586,120 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ISvgDraggableAreaProps {
+        keyframeSvgPoints: IKeyframeSvgPoint[];
+        updatePosition: (updatedKeyframe: IKeyframeSvgPoint, index: number) => void;
+    }
+    export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps> {
+        private _active;
+        private _isCurrentPointControl;
+        private _currentPointIndex;
+        private _draggableArea;
+        private _panStart;
+        private _panStop;
+        constructor(props: ISvgDraggableAreaProps);
+        componentDidMount(): void;
+        dragStart(e: React.TouchEvent<SVGSVGElement>): void;
+        dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        drag(e: React.TouchEvent<SVGSVGElement>): void;
+        drag(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        dragEnd(e: React.TouchEvent<SVGSVGElement>): void;
+        dragEnd(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
+        getMousePosition(e: React.TouchEvent<SVGSVGElement>): BABYLON.Vector2 | undefined;
+        getMousePosition(e: React.MouseEvent<SVGSVGElement, MouseEvent>): BABYLON.Vector2 | undefined;
+        panDirection(): void;
+        panTo(direction: string, value: number): void;
+        keyDown(e: KeyboardEvent): void;
+        keyUp(e: KeyboardEvent): void;
+        focus(e: React.MouseEvent<SVGSVGElement>): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface ITimelineProps {
+        keyframes: BABYLON.IAnimationKey[];
+        selected: BABYLON.IAnimationKey;
+        currentFrame: number;
+        onCurrentFrameChange: (frame: number) => void;
+    }
+    export class Timeline extends React.Component<ITimelineProps, {
+        selected: BABYLON.IAnimationKey;
+    }> {
+        readonly _frames: object[];
+        private _scrollable;
+        constructor(props: ITimelineProps);
+        handleInputChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        nextFrame(event: React.MouseEvent<HTMLDivElement>): void;
+        previousFrame(event: React.MouseEvent<HTMLDivElement>): void;
+        nextKeyframe(event: React.MouseEvent<HTMLDivElement>): void;
+        previousKeyframe(event: React.MouseEvent<HTMLDivElement>): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface IPlayheadProps {
+        frame: number;
+        offset: number;
+    }
+    export class Playhead extends React.Component<IPlayheadProps> {
+        constructor(props: IPlayheadProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface IAnimationCurveEditorComponentProps {
         close: (event: any) => void;
+        playOrPause: () => void;
         title: string;
         animations: BABYLON.Animation[];
         entityName: string;
+        scene: BABYLON.Scene;
+        entity: BABYLON.IAnimatable;
+    }
+    interface ICanvasAxis {
+        value: number;
     }
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
+        animations: BABYLON.Animation[];
+        animationName: string;
+        animationTargetProperty: string;
         isOpen: boolean;
         selected: BABYLON.Animation;
         currentPathData: string | undefined;
-        anchorPoints: {
-            point: BABYLON.Vector2;
-            anchor: BABYLON.Vector2;
-        }[] | null;
-        keyframes: BABYLON.Vector2[] | null;
+        svgKeyframes: IKeyframeSvgPoint[] | undefined;
+        currentFrame: number;
+        frameAxisLength: ICanvasAxis[];
     }> {
-        private _anchorPoints;
-        private _keyframes;
+        readonly _heightScale: number;
+        readonly _canvasLength: number;
+        private _playheadOffset;
+        private _newAnimations;
+        private _svgKeyframes;
+        private _frames;
+        private _isPlaying;
+        private _graphCanvas;
         constructor(props: IAnimationCurveEditorComponentProps);
+        componentDidMount(): void;
+        handleNameChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        handlePropertyChange(event: React.ChangeEvent<HTMLInputElement>): void;
+        addAnimation(): void;
+        addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
+        updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {
             easingType: string | undefined;
             easingMode: number | undefined;
         };
         getPathData(animation: BABYLON.Animation): string;
-        curvePath(keyframes: BABYLON.IAnimationKey[], data: string, heightScale: number, middle: number, easingFunction: BABYLON.EasingFunction): string;
-        linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, heightScale: number, middle: number): string;
-        setAnchorPoint(point: BABYLON.Vector2, anchor: BABYLON.Vector2): void;
-        setKeyframePoint(point: BABYLON.Vector2): void;
+        drawAllFrames(initialKey: BABYLON.IAnimationKey, endKey: BABYLON.IAnimationKey, easingFunction: BABYLON.EasingFunction): void;
+        curvePath(keyframes: BABYLON.IAnimationKey[], data: string, middle: number, easingFunction: BABYLON.EasingFunction): string;
+        renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number): void;
+        linearInterpolation(keyframes: BABYLON.IAnimationKey[], data: string, middle: number): string;
+        setKeyframePointLinear(point: BABYLON.Vector2, index: number): void;
+        setKeyframePoint(controlPoints: BABYLON.Vector2[], index: number, keyframesCount: number): void;
+        isAnimationPlaying(): void;
         selectAnimation(animation: BABYLON.Animation): void;
         interpolateControlPoints(p0: BABYLON.Vector2, p1: BABYLON.Vector2, u: number, p2: BABYLON.Vector2, v: number, p3: BABYLON.Vector2): BABYLON.Vector2[] | undefined;
+        changeCurrentFrame(frame: number): void;
         render(): JSX.Element;
     }
 }
@@ -4582,6 +4853,33 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ISpriteManagerPropertyGridComponentProps {
+        globalState: GlobalState;
+        spriteManager: BABYLON.SpriteManager;
+        lockObject: LockObject;
+        onSelectionChangedObservable?: BABYLON.Observable<any>;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+    }
+    export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+        constructor(props: ISpriteManagerPropertyGridComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface ISpritePropertyGridComponentProps {
+        globalState: GlobalState;
+        sprite: BABYLON.Sprite;
+        lockObject: LockObject;
+        onPropertyChangedObservable?: BABYLON.Observable<PropertyChangedEvent>;
+        onSelectionChangedObservable?: BABYLON.Observable<any>;
+    }
+    export class SpritePropertyGridComponent extends React.Component<ISpritePropertyGridComponentProps> {
+        constructor(props: ISpritePropertyGridComponentProps);
+        onManagerLink(): void;
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     export class PropertyGridTabComponent extends PaneComponent {
         private _timerIntervalId;
         private _lockObject;
@@ -4935,6 +5233,28 @@ declare module INSPECTOR {
     }
 }
 declare module INSPECTOR {
+    interface ISpriteManagerTreeItemComponentProps {
+        spriteManager: BABYLON.SpriteManager;
+        extensibilityGroups?: BABYLON.IExplorerExtensibilityGroup[];
+        onClick: () => void;
+    }
+    export class SpriteManagerTreeItemComponent extends React.Component<ISpriteManagerTreeItemComponentProps> {
+        constructor(props: ISpriteManagerTreeItemComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
+    interface ISpriteTreeItemComponentProps {
+        sprite: BABYLON.Sprite;
+        extensibilityGroups?: BABYLON.IExplorerExtensibilityGroup[];
+        onClick: () => void;
+    }
+    export class SpriteTreeItemComponent extends React.Component<ISpriteTreeItemComponentProps> {
+        constructor(props: ISpriteTreeItemComponentProps);
+        render(): JSX.Element;
+    }
+}
+declare module INSPECTOR {
     interface ITreeItemSpecializedComponentProps {
         label: string;
         entity?: any;
@@ -4990,6 +5310,7 @@ declare module INSPECTOR {
         label: string;
         offset: number;
         filter: BABYLON.Nullable<string>;
+        forceSubitems?: boolean;
         globalState: GlobalState;
         entity?: any;
         selectedEntity: any;

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -29,12 +29,12 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.14",
-        "babylonjs-gui": "4.2.0-alpha.14",
-        "babylonjs-loaders": "4.2.0-alpha.14",
-        "babylonjs-materials": "4.2.0-alpha.14",
-        "babylonjs-serializers": "4.2.0-alpha.14",
-        "babylonjs-gltf2interface": "4.2.0-alpha.14"
+        "babylonjs": "4.2.0-alpha.15",
+        "babylonjs-gui": "4.2.0-alpha.15",
+        "babylonjs-loaders": "4.2.0-alpha.15",
+        "babylonjs-materials": "4.2.0-alpha.15",
+        "babylonjs-serializers": "4.2.0-alpha.15",
+        "babylonjs-gltf2interface": "4.2.0-alpha.15"
     },
     "devDependencies": {
         "@types/react": "~16.7.3",

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-loaders",
     "description": "The Babylon.js file loaders library is an extension you can use to load different 3D file types into a Babylon scene.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.2.0-alpha.14",
-        "babylonjs": "4.2.0-alpha.14"
+        "babylonjs-gltf2interface": "4.2.0-alpha.15",
+        "babylonjs": "4.2.0-alpha.15"
     },
     "engines": {
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-materials",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.14"
+        "babylonjs": "4.2.0-alpha.15"
     },
     "engines": {
         "node": "*"

+ 2 - 2
dist/preview release/nodeEditor/package.json

@@ -4,14 +4,14 @@
     },
     "name": "babylonjs-node-editor",
     "description": "The Babylon.js node material editor.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
     },
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.14"
+        "babylonjs": "4.2.0-alpha.15"
     },
     "files": [
         "babylon.nodeEditor.max.js.map",

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

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

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

@@ -1 +1 @@
-{"thinEngineOnly":115811,"engineOnly":152214,"sceneOnly":511356,"minGridMaterial":644589,"minStandardMaterial":786440}
+{"thinEngineOnly":115808,"engineOnly":152211,"sceneOnly":511359,"minGridMaterial":644636,"minStandardMaterial":786986}

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-post-process",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.14"
+        "babylonjs": "4.2.0-alpha.15"
     },
     "engines": {
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-procedural-textures",
     "description": "The Babylon.js materials library is a collection of advanced materials to be used in a Babylon.js scene.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,7 +28,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.14"
+        "babylonjs": "4.2.0-alpha.15"
     },
     "engines": {
         "node": "*"

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-serializers",
     "description": "The Babylon.js serializers library is an extension you can use to serialize Babylon scenes.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,8 +28,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.2.0-alpha.14",
-        "babylonjs-gltf2interface": "4.2.0-alpha.14"
+        "babylonjs": "4.2.0-alpha.15",
+        "babylonjs-gltf2interface": "4.2.0-alpha.15"
     },
     "engines": {
         "node": "*"

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

@@ -10224,6 +10224,10 @@ declare module "babylonjs/Sprites/spriteManager" {
      */
     export interface ISpriteManager extends IDisposable {
         /**
+         * Gets manager's name
+         */
+        name: string;
+        /**
          * Restricts the camera to viewing objects with the same layerMask.
          * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
          */
@@ -10233,6 +10237,10 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         isPickable: boolean;
         /**
+         * Gets the hosting scene
+         */
+        scene: Scene;
+        /**
          * Specifies the rendering group id for this mesh (0 by default)
          * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
          */
@@ -10242,6 +10250,14 @@ declare module "babylonjs/Sprites/spriteManager" {
          */
         sprites: Array<Sprite>;
         /**
+         * Gets or sets the spritesheet texture
+         */
+        texture: Texture;
+        /** Defines the default width of a cell in the spritesheet */
+        cellWidth: number;
+        /** Defines the default height of a cell in the spritesheet */
+        cellHeight: number;
+        /**
          * Tests the intersection of a sprite with a specific ray.
          * @param ray The ray we are sending to test the collision
          * @param camera The camera space we are sending rays in
@@ -10312,6 +10328,23 @@ declare module "babylonjs/Sprites/spriteManager" {
         private _effectBase;
         private _effectFog;
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the array of sprites
+         */
+        get children(): Sprite[];
+        /**
+         * Gets the hosting scene
+         */
+        get scene(): Scene;
+        /**
+         * Gets or sets the capacity of the manager
+         */
+        get capacity(): number;
+        set capacity(value: number);
+        /**
          * Gets or sets the spritesheet texture
          */
         get texture(): Texture;
@@ -10344,6 +10377,11 @@ declare module "babylonjs/Sprites/spriteManager" {
         constructor(
         /** defines the manager's name */
         name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon?: number, samplingMode?: number, fromPacked?: boolean, spriteJSON?: any | null);
+        /**
+         * Returns the string "SpriteManager"
+         * @returns "SpriteManager"
+         */
+        getClassName(): string;
         private _makePacked;
         private _appendSpriteVertex;
         private _checkTextureAlpha;
@@ -18636,11 +18674,13 @@ declare module "babylonjs/Sprites/sprite" {
     import { ISpriteManager } from "babylonjs/Sprites/spriteManager";
     import { Color4 } from "babylonjs/Maths/math.color";
     import { Observable } from "babylonjs/Misc/observable";
+    import { IAnimatable } from "babylonjs/Animations/animatable.interface";
+    import { Animation } from "babylonjs/Animations/animation";
     /**
      * Class used to represent a sprite
      * @see http://doc.babylonjs.com/babylon101/sprites
      */
-    export class Sprite {
+    export class Sprite implements IAnimatable {
         /** defines the name */
         name: string;
         /** Gets or sets the current world position */
@@ -18658,13 +18698,13 @@ declare module "babylonjs/Sprites/sprite" {
         /** Gets or sets the cell reference in the sprite sheet, uses sprite's filename when added to sprite sheet */
         cellRef: string;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in U axis */
-        invertU: number;
+        invertU: boolean;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in B axis */
-        invertV: number;
+        invertV: boolean;
         /** Gets or sets a boolean indicating that this sprite should be disposed after animation ends */
         disposeWhenFinishedAnimating: boolean;
         /** Gets the list of attached animations */
-        animations: Animation[];
+        animations: Nullable<Array<Animation>>;
         /** Gets or sets a boolean indicating if the sprite can be picked */
         isPickable: boolean;
         /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
@@ -18704,6 +18744,14 @@ declare module "babylonjs/Sprites/sprite" {
         get size(): number;
         set size(value: number);
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the manager of this sprite
+         */
+        get manager(): ISpriteManager;
+        /**
          * Creates a new Sprite
          * @param name defines the name
          * @param manager defines the manager
@@ -18712,6 +18760,23 @@ declare module "babylonjs/Sprites/sprite" {
         /** defines the name */
         name: string, manager: ISpriteManager);
         /**
+         * Returns the string "Sprite"
+         * @returns "Sprite"
+         */
+        getClassName(): string;
+        /** Gets or sets the initial key for the animation (setting it will restart the animation)  */
+        get fromIndex(): number;
+        set fromIndex(value: number);
+        /** Gets or sets the end key for the animation (setting it will restart the animation)  */
+        get toIndex(): number;
+        set toIndex(value: number);
+        /** Gets or sets a boolean indicating if the animation is looping (setting it will restart the animation)  */
+        get loopAnimation(): boolean;
+        set loopAnimation(value: boolean);
+        /** Gets or sets the delay between cell changes (setting it will restart the animation)  */
+        get delay(): number;
+        set delay(value: number);
+        /**
          * Starts an animation
          * @param from defines the initial key
          * @param to defines the end key
@@ -27749,6 +27814,9 @@ declare module "babylonjs/Materials/standardMaterial" {
         PREMULTIPLYALPHA: boolean;
         ALPHATEST_AFTERALLALPHACOMPUTATIONS: boolean;
         ALPHABLEND: boolean;
+        RGBDLIGHTMAP: boolean;
+        RGBDREFLECTION: boolean;
+        RGBDREFRACTION: boolean;
         IMAGEPROCESSING: boolean;
         VIGNETTE: boolean;
         VIGNETTEBLENDMODEMULTIPLY: boolean;
@@ -72516,6 +72584,7 @@ declare module "babylonjs/Rendering/boundingBoxRenderer" {
     import "babylonjs/Shaders/color.fragment";
     import "babylonjs/Shaders/color.vertex";
     import { Color3 } from "babylonjs/Maths/math.color";
+    import { Observable } from "babylonjs/Misc/observable";
     module "babylonjs/scene" {
         interface Scene {
             /** @hidden (Backing field) */
@@ -72569,6 +72638,14 @@ declare module "babylonjs/Rendering/boundingBoxRenderer" {
          */
         showBackLines: boolean;
         /**
+         * Observable raised before rendering a bounding box
+         */
+        onBeforeBoxRenderingObservable: Observable<BoundingBox>;
+        /**
+         * Observable raised after rendering a bounding box
+         */
+        onAfterBoxRenderingObservable: Observable<BoundingBox>;
+        /**
          * @hidden
          */
         renderList: SmartArray<BoundingBox>;
@@ -86006,6 +86083,10 @@ declare module BABYLON {
      */
     export interface ISpriteManager extends IDisposable {
         /**
+         * Gets manager's name
+         */
+        name: string;
+        /**
          * Restricts the camera to viewing objects with the same layerMask.
          * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
          */
@@ -86015,6 +86096,10 @@ declare module BABYLON {
          */
         isPickable: boolean;
         /**
+         * Gets the hosting scene
+         */
+        scene: Scene;
+        /**
          * Specifies the rendering group id for this mesh (0 by default)
          * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
          */
@@ -86024,6 +86109,14 @@ declare module BABYLON {
          */
         sprites: Array<Sprite>;
         /**
+         * Gets or sets the spritesheet texture
+         */
+        texture: Texture;
+        /** Defines the default width of a cell in the spritesheet */
+        cellWidth: number;
+        /** Defines the default height of a cell in the spritesheet */
+        cellHeight: number;
+        /**
          * Tests the intersection of a sprite with a specific ray.
          * @param ray The ray we are sending to test the collision
          * @param camera The camera space we are sending rays in
@@ -86094,6 +86187,23 @@ declare module BABYLON {
         private _effectBase;
         private _effectFog;
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the array of sprites
+         */
+        get children(): Sprite[];
+        /**
+         * Gets the hosting scene
+         */
+        get scene(): Scene;
+        /**
+         * Gets or sets the capacity of the manager
+         */
+        get capacity(): number;
+        set capacity(value: number);
+        /**
          * Gets or sets the spritesheet texture
          */
         get texture(): Texture;
@@ -86126,6 +86236,11 @@ declare module BABYLON {
         constructor(
         /** defines the manager's name */
         name: string, imgUrl: string, capacity: number, cellSize: any, scene: Scene, epsilon?: number, samplingMode?: number, fromPacked?: boolean, spriteJSON?: any | null);
+        /**
+         * Returns the string "SpriteManager"
+         * @returns "SpriteManager"
+         */
+        getClassName(): string;
         private _makePacked;
         private _appendSpriteVertex;
         private _checkTextureAlpha;
@@ -94117,7 +94232,7 @@ declare module BABYLON {
      * Class used to represent a sprite
      * @see http://doc.babylonjs.com/babylon101/sprites
      */
-    export class Sprite {
+    export class Sprite implements IAnimatable {
         /** defines the name */
         name: string;
         /** Gets or sets the current world position */
@@ -94135,13 +94250,13 @@ declare module BABYLON {
         /** Gets or sets the cell reference in the sprite sheet, uses sprite's filename when added to sprite sheet */
         cellRef: string;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in U axis */
-        invertU: number;
+        invertU: boolean;
         /** Gets or sets a boolean indicating if UV coordinates should be inverted in B axis */
-        invertV: number;
+        invertV: boolean;
         /** Gets or sets a boolean indicating that this sprite should be disposed after animation ends */
         disposeWhenFinishedAnimating: boolean;
         /** Gets the list of attached animations */
-        animations: Animation[];
+        animations: Nullable<Array<Animation>>;
         /** Gets or sets a boolean indicating if the sprite can be picked */
         isPickable: boolean;
         /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
@@ -94181,6 +94296,14 @@ declare module BABYLON {
         get size(): number;
         set size(value: number);
         /**
+         * Gets or sets the unique id of the sprite
+         */
+        uniqueId: number;
+        /**
+         * Gets the manager of this sprite
+         */
+        get manager(): ISpriteManager;
+        /**
          * Creates a new Sprite
          * @param name defines the name
          * @param manager defines the manager
@@ -94189,6 +94312,23 @@ declare module BABYLON {
         /** defines the name */
         name: string, manager: ISpriteManager);
         /**
+         * Returns the string "Sprite"
+         * @returns "Sprite"
+         */
+        getClassName(): string;
+        /** Gets or sets the initial key for the animation (setting it will restart the animation)  */
+        get fromIndex(): number;
+        set fromIndex(value: number);
+        /** Gets or sets the end key for the animation (setting it will restart the animation)  */
+        get toIndex(): number;
+        set toIndex(value: number);
+        /** Gets or sets a boolean indicating if the animation is looping (setting it will restart the animation)  */
+        get loopAnimation(): boolean;
+        set loopAnimation(value: boolean);
+        /** Gets or sets the delay between cell changes (setting it will restart the animation)  */
+        get delay(): number;
+        set delay(value: number);
+        /**
          * Starts an animation
          * @param from defines the initial key
          * @param to defines the end key
@@ -102872,6 +103012,9 @@ declare module BABYLON {
         PREMULTIPLYALPHA: boolean;
         ALPHATEST_AFTERALLALPHACOMPUTATIONS: boolean;
         ALPHABLEND: boolean;
+        RGBDLIGHTMAP: boolean;
+        RGBDREFLECTION: boolean;
+        RGBDREFRACTION: boolean;
         IMAGEPROCESSING: boolean;
         VIGNETTE: boolean;
         VIGNETTEBLENDMODEMULTIPLY: boolean;
@@ -144780,6 +144923,14 @@ declare module BABYLON {
          */
         showBackLines: boolean;
         /**
+         * Observable raised before rendering a bounding box
+         */
+        onBeforeBoxRenderingObservable: Observable<BoundingBox>;
+        /**
+         * Observable raised after rendering a bounding box
+         */
+        onAfterBoxRenderingObservable: Observable<BoundingBox>;
+        /**
          * @hidden
          */
         renderList: SmartArray<BoundingBox>;

Plik diff jest za duży
+ 11 - 11
dist/preview release/viewer/babylon.viewer.js


Plik diff jest za duży
+ 1 - 1
dist/preview release/viewer/babylon.viewer.max.js


+ 15 - 3
dist/preview release/what's new.md

@@ -3,9 +3,12 @@
 ## Major updates
 
 - Added particle editor to the Inspector ([Deltakosh](https://github.com/deltakosh))
+- Added sprite editor to the Inspector ([Deltakosh](https://github.com/deltakosh))
 - Added the `ShadowDepthWrapper` class to support accurate shadow generation for custom as well as node material shaders. [Doc](https://doc.babylonjs.com/babylon101/shadows#custom-shadow-map-shaders) ([Popov72](https://github.com/Popov72))
 - Added Babylon.js Texture [tools](https://www.babylonjs.com/tools/ibl) to prefilter HDR files ([Sebavan](https://github.com/sebavan/))
 - Added editing of PBR materials and Post processes in the node material editor ([Popov72](https://github.com/Popov72))
+- Added Curve editor to view selected entity's animations in the Inspector ([pixelspace](https://github.com/devpixelspace))
+- Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
 
 ## Updates
 
@@ -19,7 +22,7 @@
 - Added support for `material.disableColorWrite` ([Deltakosh](https://github.com/deltakosh))
 - The Mesh Asset Task also accepts File as sceneInput ([RaananW](https://github.com/RaananW))
 - Added support preserving vert colors for CSG objects ([PirateJC](https://github.com/PirateJC))
-- Added support in `ShadowGenerator` for fast fake soft transparent shadows ([Popov72](https://github.com/Popov72))
+- Added `boundingBoxRenderer.onBeforeBoxRenderingObservable` and `boundingBoxRenderer.onAfterBoxRenderingObservable` ([Deltakosh](https://github.com/deltakosh))
 
 ### Engine
 
@@ -42,9 +45,7 @@
 - Added right click options to create PBR and Standard Materials ([Deltakosh](https://github.com/deltakosh))
 - Added support for recording GIF ([Deltakosh](https://github.com/deltakosh))
 - Popup Window available (To be used in Curve Editor) ([pixelspace](https://github.com/devpixelspace))
-- Curve Editor to view current entity's animations (Read only) ([pixelspace](https://github.com/devpixelspace))
 - Add support to update inspector when switching to new scene ([belfortk](https://github.com/belfortk))
-- Drag keyframes in Curve Editor ([pixelspace](https://github.com/devpixelspace))
 
 ### Cameras
 
@@ -70,6 +71,8 @@
 - Get the list of cameras retrieved from a gLTF file when loaded through the asset container ([Popov72](https://github.com/Popov72))
 - Fixed SceneLoader.ImportAnimations. Now targets nodes based on "targetProperty" ([#7931](https://github.com/BabylonJS/Babylon.js/issues/7931)) ([phenry20](https://github.com/phenry20))
 - Renamed KHR_mesh_instancing extension to EXT_mesh_gpu_instancing ([#7945](https://github.com/BabylonJS/Babylon.js/issues/7945)) ([drigax](https://github.com/Drigax))
+- Added support for KHR_materials_ior for glTF loader. ([Sebavan](https://github.com/sebavan/))
+- Added support for KHR_materials_specular for glTF loader. ([Sebavan](https://github.com/sebavan/))
 
 ### Navigation
 
@@ -84,6 +87,8 @@
 - Added `AddAttribute` to `CustomMaterial` and `PBRCustomMaterial` ([Popov72](https://github.com/Popov72))
 - `setTexture` and `setTextureArray` from `ShaderMaterial` take now a `BaseTexture` as input instead of a `Texture`, allowing to pass a `CubeTexture` ([Popov72](https://github.com/Popov72))
 - Allow parenthesis usage in `#if` expressions in shader code ([Popov72](https://github.com/Popov72))
+- Added to `StandardMaterial` RGBD ReflectionTexture, RefractionTexture and LightmapTexture support. ([MackeyK24](https://github.com/MackeyK24))
+- Allow using the single comment syntax `// comment` in a `#if` construct in shader code ([Popov72](https://github.com/Popov72))
 
 ### WebXR
 
@@ -118,6 +123,11 @@
 - Added local space support for GPU particles ([CraigFeldpsar](https://github.com/craigfeldspar))
 - Added ability to update also colors and uvs of solid particle vertices ([jerome](https://github.com/jbousquie))
 
+### Textures
+
+- .HDR environment files will now give accurate PBR reflections ([CraigFeldpsar](https://github.com/craigfeldspar))
+- Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar))
+
 ### Build
 
 - Fixed an issue with gulp webpack, webpack stream and the viewer ([RaananW](https://github.com/RaananW))
@@ -172,9 +182,11 @@
 - Playground didn't work if query params were added to the URL ([RaananW](https://github.com/RaananW))
 - Fixed Path3D `_distances` / length computation ([Poolminer](https://github.com/Poolminer))
 - Make sure bone matrices are up to date when calling `TransformNode.attachToBone` ([Popov72](https://github.com/Popov72))
+- Fix display problem with transparent objects and SSAO2 pipeline (bug in the `GeometryBufferRenderer`) ([Popov72](https://github.com/Popov72))
 
 ## Breaking changes
 
 - `EffectRenderer.render` now takes a `RenderTargetTexture` or an `InternalTexture` as the output texture and only a single `EffectWrapper` for its first argument ([Popov72](https://github.com/Popov72))
 - Sound's `updateOptions` takes `options.length` and `options.offset` as seconds and not milliseconds ([RaananW](https://github.com/RaananW))
 - HDRCubeTexture default rotation is now similar to the industry one. You might need to add a rotation on y of 90 degrees if you scene changes ([Sebavan](https://github.com/sebavan/))
+- PBRMaterial index of refraction is now defined as index of refraction and not the inverse of it ([Sebavan](https://github.com/sebavan/))

+ 9 - 1
inspector/src/components/actionTabs/lines/floatLineComponent.tsx

@@ -18,7 +18,8 @@ interface IFloatLineComponentProps {
     additionalClass?: string;
     step?: string,
     digits?: number;
-    useEuler?: boolean
+    useEuler?: boolean;
+    min?: number
 }
 
 export class FloatLineComponent extends React.Component<IFloatLineComponentProps, { value: string }> {
@@ -83,6 +84,13 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
             valueAsNumber = parseFloat(valueString);
         }
 
+        if (!isNaN(valueAsNumber) && this.props.min !== undefined) {
+            if (valueAsNumber < this.props.min) {
+                valueAsNumber = this.props.min;
+                valueString = valueAsNumber.toString();
+            }            
+        }
+
         this._localChange = true;
         this.setState({ value: valueString });
 

+ 1 - 1
inspector/src/components/actionTabs/lines/sliderLineComponent.tsx

@@ -49,7 +49,7 @@ export class SliderLineComponent extends React.Component<ISliderLineComponentPro
             currentState = nextProps.maximum;
         }
 
-        if (currentState !== nextState.value || this._localChange) {
+        if (currentState !== nextState.value || this._localChange || nextProps.maximum !== this.props.maximum || nextProps.minimum !== this.props.minimum) {
             nextState.value = currentState;
             this._localChange = false;
             return true;

+ 22 - 0
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -91,6 +91,10 @@ import { MultiMaterial } from 'babylonjs/Materials/multiMaterial';
 import { MultiMaterialPropertyGridComponent } from './propertyGrids/materials/multiMaterialPropertyGridComponent';
 import { ParticleSystemPropertyGridComponent } from './propertyGrids/particleSystems/particleSystemPropertyGridComponent';
 import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
+import { SpriteManagerPropertyGridComponent } from './propertyGrids/sprites/spriteManagerPropertyGridComponent';
+import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
+import { SpritePropertyGridComponent } from './propertyGrids/sprites/spritePropertyGridComponent';
+import { Sprite } from 'babylonjs/Sprites/sprite';
 
 export class PropertyGridTabComponent extends PaneComponent {
     private _timerIntervalId: number;
@@ -137,6 +141,24 @@ export class PropertyGridTabComponent extends PaneComponent {
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
+            if (className === "Sprite") {
+                const sprite = entity as Sprite;
+                return (<SpritePropertyGridComponent sprite={sprite}
+                    globalState={this.props.globalState}
+                    lockObject={this._lockObject}
+                    onSelectionChangedObservable={this.props.onSelectionChangedObservable}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "SpriteManager") {
+                const spriteManager = entity as SpriteManager;
+                return (<SpriteManagerPropertyGridComponent spriteManager={spriteManager}
+                    globalState={this.props.globalState}
+                    lockObject={this._lockObject}                    
+                    onSelectionChangedObservable={this.props.onSelectionChangedObservable}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
             if (className.indexOf("Mesh") !== -1) {
                 const mesh = entity as Mesh;
                 if (mesh.getTotalVertices() > 0) {

+ 102 - 105
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -1,13 +1,16 @@
 import * as React from "react";
 import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { faTimes, faPlusCircle } from "@fortawesome/free-solid-svg-icons";
+import { faTimes } from "@fortawesome/free-solid-svg-icons";
 import { Animation } from 'babylonjs/Animations/animation';
 import { Vector2 } from 'babylonjs/Maths/math.vector';
 import { EasingFunction, BezierCurveEase } from 'babylonjs/Animations/easing';
 import { IAnimationKey } from 'babylonjs/Animations/animationKey';
 import { IKeyframeSvgPoint } from './keyframeSvgPoint';
 import { SvgDraggableArea } from './svgDraggableArea';
+import { Timeline } from './timeline';
+import { Playhead } from './playhead';
 import { Scene } from "babylonjs/scene";
+import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
 import { IAnimatable } from 'babylonjs/Animations/animatable.interface';
 
 require("./curveEditor.scss");
@@ -22,17 +25,30 @@ interface IAnimationCurveEditorComponentProps {
     entity: IAnimatable;
 }
 
-export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, animationTargetProperty: string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, svgKeyframes: IKeyframeSvgPoint[] | undefined }> {
+interface ICanvasAxis {
+    value: number;
+}
+
+export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, { animations: Animation[], animationName: string, animationTargetProperty: string, isOpen: boolean, selected: Animation, currentPathData: string | undefined, svgKeyframes: IKeyframeSvgPoint[] | undefined, currentFrame: number, frameAxisLength: ICanvasAxis[] }> {
 
     readonly _heightScale: number = 100;
+    readonly _canvasLength: number = 20;
+    private _playheadOffset: number = 0;
     private _newAnimations: Animation[] = [];
     private _svgKeyframes: IKeyframeSvgPoint[] = [];
     private _frames: Vector2[] = [];
     private _isPlaying: boolean = false;
+    private _graphCanvas: React.RefObject<HTMLDivElement>;
     constructor(props: IAnimationCurveEditorComponentProps) {
         super(props);
-        this.state = { animations: this._newAnimations, selected: this.props.animations[0], isOpen: true, currentPathData: this.getPathData(this.props.animations[0]), svgKeyframes: this._svgKeyframes, animationTargetProperty: 'position.x', animationName: "" }
+        this._graphCanvas = React.createRef();
+        this.state = { animations: this._newAnimations, selected: this.props.animations[0], isOpen: true, currentPathData: this.getPathData(this.props.animations[0]), svgKeyframes: this._svgKeyframes, animationTargetProperty: 'position.x', animationName: "", currentFrame: 0, frameAxisLength: (new Array(this._canvasLength)).fill(0).map((s, i) => { return { value: i * 10 } }) }
+    }
 
+    componentDidMount() {
+        if (this._graphCanvas.current) {
+            this._playheadOffset = (this._graphCanvas.current.children[1].clientWidth) / (this._canvasLength * 10)
+        }
     }
 
     handleNameChange(event: React.ChangeEvent<HTMLInputElement>) {
@@ -45,8 +61,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         this.setState({ animationTargetProperty: event.target.value });
     }
 
-    addAnimation(event: React.MouseEvent<HTMLDivElement>) {
-        event.preventDefault();
+    addAnimation() {
         if (this.state.animationName != "" && this.state.animationTargetProperty != "") {
             let animation = new Animation(this.state.animationName, this.state.animationTargetProperty, 30, Animation.ANIMATIONTYPE_FLOAT, Animation.ANIMATIONLOOPMODE_CYCLE);
 
@@ -147,7 +162,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         anim.setKeys(keys);
 
         this.setState({ svgKeyframes: svgKeyframes })
-
     }
 
     getAnimationProperties(animation: Animation) {
@@ -190,8 +204,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
             data = this.curvePath(keyframes, data, middle, easingFunction as EasingFunction)
         }
 
-
-
         return data;
 
     }
@@ -209,7 +221,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
             this._frames.push(new Vector2(i, value));
 
         }
-
     }
 
     curvePath(keyframes: IAnimationKey[], data: string, middle: number, easingFunction: EasingFunction) {
@@ -279,8 +290,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
     }
 
-
-
     renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) {
 
         let animation = this.state.selected as Animation;
@@ -340,12 +349,12 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
     }
 
     isAnimationPlaying() {
-          this._isPlaying = this.props.scene.getAllAnimatablesByTarget(this.props.entity).length > 0;
-           if (this._isPlaying){
+        this._isPlaying = this.props.scene.getAllAnimatablesByTarget(this.props.entity).length > 0;
+        if (this._isPlaying) {
             this.props.playOrPause();
-           } else {
-               this._isPlaying = false;
-           }
+        } else {
+            this._isPlaying = false;
+        }
     }
 
     selectAnimation(animation: Animation) {
@@ -404,6 +413,10 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
     }
 
+    changeCurrentFrame(frame: number) {
+        this.setState({ currentFrame: frame });
+    }
+
     render() {
         return (
             <div id="animation-curve-editor">
@@ -415,102 +428,86 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                 </div>
                 <div className="content">
 
-                    <div className="animation-list">
+                    <div className="row">
+                        <div className="animation-list">
 
-                        <div>
                             <div>
-                                <label>Animation Name</label>
-                                <input type="text" value={this.state.animationName} onChange={(e) => this.handleNameChange(e)}></input>
-                            </div>
-                            <div>
-                                <label>Target Property</label>
-                                <input type="text" value={this.state.animationTargetProperty} onChange={(e) => this.handlePropertyChange(e)}></input>
-                            </div>
-                            <div className="add" onClick={(e) => this.addAnimation(e)}>
-                                <FontAwesomeIcon icon={faPlusCircle} />
+                                <div className="label-input">
+                                    <label>Animation Name</label>
+                                    <input type="text" value={this.state.animationName} onChange={(e) => this.handleNameChange(e)}></input>
+                                </div>
+                                <div className="label-input">
+                                    <label>Target Property</label>
+                                    <input type="text" value={this.state.animationTargetProperty} onChange={(e) => this.handlePropertyChange(e)}></input>
+                                </div>
+                                <ButtonLineComponent label={"Add Animation"} onClick={() => this.addAnimation()} />
                             </div>
-                        </div>
-
-                        <h2>{this.props.entityName}</h2>
-                        <ul>
-                            {this.props.animations && this.props.animations.map((animation, i) => {
-                                return <li className={this.state.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.selectAnimation(animation)}>{animation.name} <strong>{animation.targetProperty}</strong></li>
-                            })}
 
-                        </ul>
+                            <div className="object-tree">
+                                <h2>{this.props.entityName}</h2>
+                                <ul>
+                                    {this.props.animations && this.props.animations.map((animation, i) => {
+                                        return <li className={this.state.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.selectAnimation(animation)}>{animation.name} <strong>{animation.targetProperty}</strong></li>
+                                    })}
 
-                        <h2>New Animations</h2>
-                        <ul>
-                            {this.state.animations && this.state.animations.map((animation, i) => {
-                                return <li className={this.state.selected.name === animation.name ? 'active' : ''} key={i} onClick={() => this.selectAnimation(animation)}>{animation.name} <strong>{animation.targetProperty}</strong></li>
-                            })}
+                                </ul>
+                            </div>
+                        </div>
+                        <div ref={this._graphCanvas} className="graph-chart">
+
+                            <Playhead frame={this.state.currentFrame} offset={this._playheadOffset} />
+
+                            {this.state.svgKeyframes && <SvgDraggableArea keyframeSvgPoints={this.state.svgKeyframes} updatePosition={(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) => this.renderPoints(updatedSvgKeyFrame, index)}>
+
+                                {/* Frame Labels  */}
+                                { /* Vertical Grid  */}
+                                {this.state.frameAxisLength.map((f, i) =>
+                                    <svg key={i}>
+                                        <text x={f.value} y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>{f.value}</text>
+                                        <line x1={f.value} y1="0" x2={f.value} y2="100"></line>
+                                    </svg>
+                                )}
+
+                                { /* Value Labels  */}
+                                <text x="0" y="10" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.8</text>
+                                <text x="0" y="20" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.6</text>
+                                <text x="0" y="30" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.4</text>
+                                <text x="0" y="40" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.2</text>
+                                <text x="0" y="50" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1</text>
+                                <text x="0" y="60" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.8</text>
+                                <text x="0" y="70" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.6</text>
+                                <text x="0" y="80" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.4</text>
+                                <text x="0" y="90" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.2</text>
+
+                                { /* Horizontal Grid  */}
+                                <line x1="0" y1="10" x2="1000" y2="10"></line>
+                                <line x1="0" y1="20" x2="1000" y2="20"></line>
+                                <line x1="0" y1="30" x2="1000" y2="30"></line>
+                                <line x1="0" y1="40" x2="1000" y2="40"></line>
+                                <line x1="0" y1="50" x2="1000" y2="50"></line>
+                                <line x1="0" y1="60" x2="1000" y2="60"></line>
+                                <line x1="0" y1="70" x2="1000" y2="70"></line>
+                                <line x1="0" y1="80" x2="1000" y2="80"></line>
+                                <line x1="0" y1="90" x2="1000" y2="90"></line>
+
+                                { /* Single Curve -Modify this for multiple selection and view  */}
+                                <path id="curve" d={this.state.currentPathData} style={{ stroke: 'red', fill: 'none', strokeWidth: '0.5' }}></path>
+
+                                {this._frames && this._frames.map(frame =>
+                                    <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
+                                        <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
+                                    </svg>
+
+                                )}
+
+                            </SvgDraggableArea>
+
+                            }
 
-                        </ul>
+                        </div>
                     </div>
-                    <div className="graph-chart">
-
-                        {this.state.svgKeyframes && <SvgDraggableArea keyframeSvgPoints={this.state.svgKeyframes} updatePosition={(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) => this.renderPoints(updatedSvgKeyFrame, index)}>
-
-                            {/* Frame Labels  */}
-                            <text x="10" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>10</text>
-                            <text x="20" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>20</text>
-                            <text x="30" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>30</text>
-                            <text x="40" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>40</text>
-                            <text x="50" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>50</text>
-                            <text x="60" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>60</text>
-                            <text x="70" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>70</text>
-                            <text x="80" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>80</text>
-                            <text x="90" y="0" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>90</text>
-
-                            { /* Vertical Grid  */}
-                            <line x1="10" y1="0" x2="10" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="20" y1="0" x2="20" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="30" y1="0" x2="30" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="40" y1="0" x2="40" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="50" y1="0" x2="50" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="60" y1="0" x2="60" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="70" y1="0" x2="70" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="80" y1="0" x2="80" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="90" y1="0" x2="90" y2="100" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-
-                            { /* Value Labels  */}
-                            <text x="0" y="10" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.8</text>
-                            <text x="0" y="20" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.6</text>
-                            <text x="0" y="30" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.4</text>
-                            <text x="0" y="40" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1.2</text>
-                            <text x="0" y="50" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>1</text>
-                            <text x="0" y="60" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.8</text>
-                            <text x="0" y="70" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.6</text>
-                            <text x="0" y="80" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.4</text>
-                            <text x="0" y="90" dx="-1em" style={{ font: 'italic 0.2em sans-serif' }}>0.2</text>
-
-                            { /* Horizontal Grid  */}
-                            <line x1="0" y1="10" x2="100" y2="10" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="20" x2="100" y2="20" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="30" x2="100" y2="30" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="40" x2="100" y2="40" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="50" x2="100" y2="50" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="60" x2="100" y2="60" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="70" x2="100" y2="70" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="80" x2="100" y2="80" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-                            <line x1="0" y1="90" x2="100" y2="90" style={{ stroke: 'black', strokeWidth: '0.2px' }}></line>
-
-                            { /* Single Curve -Modify this for multiple selection and view  */}
-                            <path id="curve" d={this.state.currentPathData} style={{ stroke: 'red', fill: 'none', strokeWidth: '0.5' }}></path>
-
-                            {this._frames && this._frames.map(frame =>
-                                <svg x={frame.x} y={frame.y} style={{ overflow: 'visible' }}>
-                                    <circle cx="0" cy="0" r="2" stroke="black" strokeWidth="1" fill="white" />
-                                </svg>
-
-                            )}
-
-                        </SvgDraggableArea>
-
-                        }
-
-                        Animation name: {this.state.selected.name}
-
+                    <div className="row">
+                        <Timeline currentFrame={this.state.currentFrame} onCurrentFrameChange={(frame: number) => this.changeCurrentFrame(frame)} keyframes={this.state.selected.getKeys()} selected={this.state.selected.getKeys()[0]}></Timeline>
                     </div>
                 </div>
             </div>

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

@@ -199,7 +199,7 @@ export class AnimationGridComponent extends React.Component<IAnimationGridCompon
                                 this._isCurveEditorOpen && <PopupComponent
                                     id="curve-editor"
                                     title="Curve Animation Editor"
-                                    size={{ width: 950, height: 890 }}
+                                    size={{ width: 950, height: 512 }}
                                     onOpen={(window: Window) => { window.console.log("Window opened!!") }}
                                     onClose={(window: Window) => this.onCloseAnimationCurveEditor(window)}>
 

+ 187 - 6
inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss

@@ -26,11 +26,71 @@
         display: flex;
         align-items: flex-start;
         justify-content: flex-start;
+        flex-direction: column;
+
+        .row {
+            display: flex;
+            align-items: stretch;
+            justify-content: flex-start;
+            flex-direction: row;
+            width: 100vw;
+            height: 85vh;
+
+            .timeline{
+                width: 100vw;
+                background: gray;
+                display: flex;
+                align-items: center;
+                justify-content: stretch;
+                height: 2.5rem;
+
+                .display-line {
+                    width: 80vw;
+                    height: 2em;
+                    overflow: hidden;
+                    overflow-x: scroll;
+                    scrollbar-color: cornflowerblue slategrey;
+                    scrollbar-width: thin;
+                    margin-right: 1.3em;
+                    padding-left: 1em;
+                    padding-right: 1em;
+
+                    &::-webkit-scrollbar{
+                        height: 0.4em;
+                    }
+
+                    &::-webkit-scrollbar-track {
+                        box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
+                      }
+                       
+                    &::-webkit-scrollbar-thumb {
+                        background-color: cornflowerblue;
+                        outline: 1px solid slategrey;
+                      }
+                }
+
+                .controls {
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    width: 8em;
+
+                    .input-frame input {
+                        width: 3em;
+                    }
+
+                    .button {
+                        margin-left: 0.5em;
+                        margin-right: 0.5em;
+                    }
+                }
+            }
+        }
 
         .animation-list{
             padding: 1.5rem;
-            background: lightgrey;
-            height: 100vh;
+            background: rgb(87, 86, 86);
+            color: white;
             ul {
                 list-style:none;
                 padding-left: 0px;
@@ -57,6 +117,20 @@
                 
                 
             }
+
+            .object-tree{
+                background-color: rgba(0, 0, 0, 0.3);
+                padding: 10px;
+                margin-top: 19px;
+                height: 15em;
+            }
+
+            .label-input{
+                display: grid;
+                height: 54px;
+                place-items: center stretch;
+                color:white;
+            }
         }
 
         .sample-chart{
@@ -69,15 +143,122 @@
 
         .graph-chart{
             flex: 1 1 0%;
-            margin: 25% auto;
+            overflow-x: scroll;
             padding-left: 32px;
+            overflow-y: scroll;
+            scroll-behavior: smooth;
+            background-color: #444444;
+
             .linear{
-                width: 300px;
-                height: 300px;
+                height: 360px;
                 overflow: visible;
-                background-color: 'aliceblue';
+                border: 0px solid lightgrey;
+
+                svg {
+                    overflow: visible;
+                }
+
+                &:focus {
+                    outline-color: transparent;
+                }
+
+                line {
+                    stroke: #cecece;
+                    stroke-width: 0.2;
+                }
+                text {
+                    fill: #cecece;
+                }
+            }
+
+            .playhead-wrapper {
+                position: relative;
+                left: -13px;
+            }
+
+            .playhead {
+                width: fit-content;
+                background-color: #ffc60e;
+                color: black;
+                text-align: center;
+                min-width: 2em;
+                justify-content: center;
+                display: flex;
+                padding: 0.1em;
+                font-size: 0.75em;
+            }
+
+            .playhead-triangle {
+                background-color: transparent;
+                width: 0px;
+                height: 0px;
+                border-left: 13.5px solid transparent;
+                border-right: 13.5px solid transparent;
+                border-top: 12px solid #ffc60e;
             }
+
+            .playhead-line {
+                width: 2px;
+                height: calc(90vh - 100px);
+                background-color: rgb(255, 198, 14);
+                position: absolute;
+                margin-left: 12.5px;
+            }
+        }
+    }
+
+    .buttonLine {
+        height: 30px;
+        display: grid;
+        align-items: center;
+        justify-items: stretch;
+
+        input[type="file"] {
+            display: none;
+        }
+
+        .file-upload {
+            background: #222222;
+            border: 1px solid rgb(51, 122, 183);
+            margin: 5px 10px 5px 10px;
+            color:white;
+            padding: 4px 5px;
+            opacity: 0.9;
+            cursor: pointer;
+            text-align: center;
+        }
+
+        .file-upload:hover {
+            opacity: 1.0;
+        }
+
+        .file-upload:active {
+            transform: scale(0.98);
+            transform-origin: 0.5 0.5;
         }
+
+        button {
+            background: #222222;
+            border: 1px solid rgb(51, 122, 183);
+            margin: 5px 10px 5px 10px;
+            color:white;
+            padding: 4px 5px;
+            opacity: 0.9;
+            cursor: pointer;
+        }
+
+        button:hover {
+            opacity: 1.0;
+        }
+
+        button:active {
+            background: #282828;
+        }   
+        
+        button:focus {
+            border: 1px solid rgb(51, 122, 183);
+            outline: 0px;
+        }  
     }
 
 }

+ 25 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/playhead.tsx

@@ -0,0 +1,25 @@
+
+import * as React from "react";
+
+interface IPlayheadProps {
+   frame: number;
+   offset: number;
+}
+
+export class Playhead extends React.Component<IPlayheadProps>{ 
+    constructor(props: IPlayheadProps) {
+        super(props);
+    }
+     
+    render() { 
+       return (
+           <div className="playhead-wrapper" id="playhead" style={{left: `calc(${this.props.frame * (this.props.offset) }px - 13px)`}}>
+            <div className="playhead">{this.props.frame}</div>
+            <div className="playhead-triangle"></div>
+            <div className="playhead-line"></div>
+           </div>
+        )
+    }
+} 
+
+

+ 90 - 3
inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx

@@ -4,7 +4,7 @@ import { KeyframeSvgPoint, IKeyframeSvgPoint } from './keyframeSvgPoint';
 
 interface ISvgDraggableAreaProps {
     keyframeSvgPoints: IKeyframeSvgPoint[];
-    updatePosition: (updatedKeyframe: IKeyframeSvgPoint, index: number) => void
+    updatePosition: (updatedKeyframe: IKeyframeSvgPoint, index: number) => void;
 }
 
 export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{
@@ -13,14 +13,23 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{
     private _isCurrentPointControl: string;
     private _currentPointIndex: number;
     private _draggableArea: React.RefObject<SVGSVGElement>;
+    private _panStart: Vector2;
+    private _panStop: Vector2;
 
     constructor(props: ISvgDraggableAreaProps) {
         super(props);
         this._currentPointIndex = -1;
         this._isCurrentPointControl = "";
         this._draggableArea = React.createRef();
+        this._panStart = new Vector2(0, 0);
+        this._panStop = new Vector2(0, 0);
     }
 
+    componentDidMount() {
+        this._draggableArea.current?.addEventListener("keydown", this.keyDown.bind(this));
+        this._draggableArea.current?.addEventListener("keyup", this.keyUp.bind(this));
+    }  
+
     dragStart(e: React.TouchEvent<SVGSVGElement>): void;
     dragStart(e: React.MouseEvent<SVGSVGElement, MouseEvent>): void;
     dragStart(e: any): void {
@@ -33,6 +42,12 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{
                 this._isCurrentPointControl = e.target.getAttribute("type");
             }
         }
+
+        if (e.target.classList.contains("pannable")) {
+            if (e.buttons === 1 && e.ctrlKey) {
+                this._panStart.set(e.clientX, e.clientY);
+            }
+        }
     }
 
     drag(e: React.TouchEvent<SVGSVGElement>): void;
@@ -69,6 +84,13 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{
         this._active = false;
         this._currentPointIndex = -1;
         this._isCurrentPointControl = "";
+
+        if (e.target.classList.contains("pannable")) {
+            if (this._panStart.x !== 0 && this._panStart.y !== 0) {
+                this._panStop.set(e.clientX, e.clientY);
+                this.panDirection();
+            }
+        }
     }
 
     getMousePosition(e: React.TouchEvent<SVGSVGElement>): Vector2 | undefined;
@@ -90,10 +112,74 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{
         }
     }
 
+    panDirection() {
+
+        // Movement Right to Left
+        if (this._panStart.x > this._panStop.x) {
+            console.log("right to left");
+            this.panTo("right", Math.abs(this._panStart.x - this._panStop.x));
+        }
+
+        // Movement Right to Left
+        if (this._panStart.x < this._panStop.x) {
+            this.panTo("left", Math.abs(this._panStart.x - this._panStop.x));
+            console.log("left to right");
+        }
+
+        // Movement Bottom to Up
+        if (this._panStart.y > this._panStop.y) {
+            console.log("down up");
+        }
+
+        // Movement Up to Bottom
+        if (this._panStart.y < this._panStop.y) {
+            console.log("up down");
+        }
+
+        this._panStart.set(0, 0);
+        this._panStop.set(0, 0);
+
+    }
+
+    panTo(direction: string, value: number) {
+
+        switch (direction) {
+            case "left":
+                (this._draggableArea.current?.parentElement as HTMLDivElement).scrollLeft -= (value * 1);
+                break;
+            case "right":
+                (this._draggableArea.current?.parentElement as HTMLDivElement).scrollLeft += (value * 1);
+                break;
+            case "top":
+                break;
+            case "down":
+                break;
+        }
+    }
+
+    keyDown(e: KeyboardEvent) {
+        e.preventDefault();
+        if (e.keyCode === 17) {
+            this._draggableArea.current?.style.setProperty("cursor", "grab");
+        }
+    }
+
+    keyUp(e: KeyboardEvent) {
+        e.preventDefault();
+        if (e.keyCode === 17) {
+            this._draggableArea.current?.style.setProperty("cursor", "initial");
+        }
+    }
+
+    focus(e: React.MouseEvent<SVGSVGElement>) {
+        e.preventDefault();
+        this._draggableArea.current?.focus();
+    }
+
     render() {
         return (
             <>
-                <svg className="linear" style={{ border: '1px solid black' }} ref={this._draggableArea}
+                <svg className="linear pannable" ref={this._draggableArea}  tabIndex={0}
 
                     onMouseMove={(e) => this.drag(e)}
                     onTouchMove={(e) => this.drag(e)}
@@ -104,8 +190,9 @@ export class SvgDraggableArea extends React.Component<ISvgDraggableAreaProps>{
                     onMouseUp={(e) => this.dragEnd(e)}
                     onMouseLeave={(e) => this.dragEnd(e)}
                     // Add way to add new keyframe
+                    onClick={(e) => this.focus(e)}
 
-                    viewBox="0 0 100 100" preserveAspectRatio="none">
+                    viewBox="0 0 200 100">
 
                     {this.props.children}
                     {this.props.keyframeSvgPoints.map((keyframe, i) =>

+ 116 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/animations/timeline.tsx

@@ -0,0 +1,116 @@
+
+import * as React from "react";
+import { IAnimationKey } from 'babylonjs/Animations/animationKey';
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+import { faCaretRight, faCaretLeft, faStepBackward, faStepForward } from "@fortawesome/free-solid-svg-icons";
+
+interface ITimelineProps {
+    keyframes: IAnimationKey[];
+    selected: IAnimationKey;
+    currentFrame: number;
+    onCurrentFrameChange: (frame: number) => void;
+}
+
+
+export class Timeline extends React.Component<ITimelineProps, { selected: IAnimationKey }>{
+    readonly _frames: object[] = Array(300).fill({});
+    private _scrollable: React.RefObject<HTMLDivElement>;
+    constructor(props: ITimelineProps) {
+        super(props);
+        this.state = { selected: this.props.selected };
+        this._scrollable = React.createRef();
+    }
+
+    handleInputChange(event: React.ChangeEvent<HTMLInputElement>) {
+        this.props.onCurrentFrameChange(parseInt(event.target.value));
+        event.preventDefault();
+    }
+
+    nextFrame(event: React.MouseEvent<HTMLDivElement>) {
+        event.preventDefault();
+        this.props.onCurrentFrameChange(this.props.currentFrame + 1);
+        (this._scrollable.current as HTMLDivElement).scrollLeft = this.props.currentFrame * 5;
+    }
+
+    previousFrame(event: React.MouseEvent<HTMLDivElement>) {
+        event.preventDefault();
+        if (this.props.currentFrame !== 0) {
+            this.props.onCurrentFrameChange(this.props.currentFrame - 1);
+            (this._scrollable.current as HTMLDivElement).scrollLeft = -(this.props.currentFrame * 5);
+        }
+    }
+
+    nextKeyframe(event: React.MouseEvent<HTMLDivElement>) {
+        event.preventDefault();
+        let first = this.props.keyframes.find(kf => kf.frame > this.props.currentFrame);
+        if (first) {
+            this.props.onCurrentFrameChange(first.frame);
+            this.setState({ selected: first });
+            (this._scrollable.current as HTMLDivElement).scrollLeft = first.frame * 5;
+        }
+    }
+
+    previousKeyframe(event: React.MouseEvent<HTMLDivElement>) {
+        event.preventDefault();
+        let first = this.props.keyframes.find(kf => kf.frame < this.props.currentFrame);
+        if (first) {
+            this.props.onCurrentFrameChange(first.frame);
+            this.setState({ selected: first });
+            (this._scrollable.current as HTMLDivElement).scrollLeft = -(first.frame * 5);
+        }
+    }
+
+    render() {
+        return (
+            <>
+                <div className="timeline">
+                    <div ref={this._scrollable} className="display-line">
+                        <svg viewBox="0 0 2010 100" style={{ width: 2000 }}>
+
+                            <line x1={this.props.currentFrame * 10} y1="10" x2={this.props.currentFrame * 10} y2="20" style={{ stroke: '#12506b', strokeWidth: 6 }} />
+
+                            {
+                                this.props.keyframes.map((kf, i) => {
+
+                                    return <svg key={`kf_${i}`}>
+                                        <line x1={kf.frame * 10} y1="10" x2={kf.frame * 10} y2="20" style={{ stroke: 'red', strokeWidth: 6 }} />
+                                    </svg>
+
+                                })
+                            }
+
+                            {
+                                this._frames.map((frame, i) => {
+
+                                    return <svg key={`tl_${i}`}>
+                                        {i % 10 === 0 ? <text x={(i * 10) - 3} y="8" style={{ fontSize: 10 }}>{i}</text> : null}
+                                        <line x1={i * 10} y1="10" x2={i * 10} y2="20" style={{ stroke: 'black', strokeWidth: 0.5 }} />
+                                    </svg>
+
+                                })
+                            }
+
+                        </svg>
+                    </div>
+                    <div className="controls">
+                        <div className="input-frame">
+                            <input type="number" value={this.props.currentFrame} onChange={(e) => this.handleInputChange(e)}></input>
+                        </div>
+                        <div className="previous-frame button" onClick={(e) => this.previousFrame(e)}>
+                            <FontAwesomeIcon icon={faCaretLeft} />
+                        </div>
+                        <div className="previous-key-frame button" onClick={(e) => this.previousKeyframe(e)}>
+                            <FontAwesomeIcon icon={faStepBackward} />
+                        </div>
+                        <div className="next-key-frame button" onClick={(e) => this.nextKeyframe(e)}>
+                            <FontAwesomeIcon icon={faStepForward} />
+                        </div>
+                        <div className="next-frame button" onClick={(e) => this.nextFrame(e)}>
+                            <FontAwesomeIcon icon={faCaretRight} />
+                        </div>
+                    </div>
+                </div>
+            </>
+        )
+    }
+} 

+ 4 - 4
inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx

@@ -151,11 +151,11 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="METALLIC WORKFLOW">
                     <SliderLineComponent label="Metallic" target={material} propertyName="metallic" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <SliderLineComponent label="Metallic F0" target={material} propertyName="metallicF0Factor" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <CheckBoxLineComponent label="Metallic F0 From Map" target={material} propertyName="useMetallicF0FactorFromMetallicTexture"
-                        onValueChanged={() => this.forceUpdate()}
-                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Roughness" target={material} propertyName="roughness" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Index of Refraction" target={material} propertyName="indexOfRefraction" minimum={1} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="F0 Factor" target={material} propertyName="metallicF0Factor" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Color3LineComponent label="Reflectance Color" target={material} propertyName="metallicReflectanceColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} isLinear={true} />
+                    <TextureLinkLineComponent label="Reflectance Texture" texture={material.metallicReflectanceTexture} onTextureCreated={(texture) => material.metallicReflectanceTexture = texture} onTextureRemoved={() => material.metallicReflectanceTexture = null} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="CLEAR COAT">
                     <CheckBoxLineComponent label="Enabled" target={material.clearCoat} propertyName="isEnabled"

+ 2 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx

@@ -29,6 +29,7 @@ import { AbstractMesh } from 'babylonjs/Meshes/abstractMesh';
 import { ButtonLineComponent } from '../../../lines/buttonLineComponent';
 import { TextInputLineComponent } from '../../../lines/textInputLineComponent';
 import { AnimationGridComponent } from '../animations/animationPropertyGridComponent';
+import { RenderingManager } from 'babylonjs/Rendering/renderingManager';
 
 interface IMeshPropertyGridComponentProps {
     globalState: GlobalState;
@@ -389,6 +390,7 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
                         !mesh.parent &&
                         <CheckBoxLineComponent label="Infinite distance" target={mesh} propertyName="infiniteDistance" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     }
+                    <SliderLineComponent label="Rendering group ID" decimalCount={0} target={mesh} propertyName="renderingGroupId" minimum={RenderingManager.MIN_RENDERINGGROUPS} maximum={RenderingManager.MAX_RENDERINGGROUPS - 1} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />                    
                 </LineContainerComponent>
                 {
                     mesh.morphTargetManager != null &&

+ 55 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spriteManagerPropertyGridComponent.tsx

@@ -0,0 +1,55 @@
+import * as React from "react";
+
+import { Observable } from "babylonjs/Misc/observable";
+
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { LockObject } from "../lockObject";
+import { LineContainerComponent } from '../../../lineContainerComponent';
+import { GlobalState } from '../../../../globalState';
+import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
+import { TextInputLineComponent } from '../../../lines/textInputLineComponent';
+import { TextLineComponent } from '../../../lines/textLineComponent';
+import { CheckBoxLineComponent } from '../../../lines/checkBoxLineComponent';
+import { FloatLineComponent } from '../../../lines/floatLineComponent';
+import { SliderLineComponent } from '../../../lines/sliderLineComponent';
+import { RenderingManager } from 'babylonjs/Rendering/renderingManager';
+import { TextureLinkLineComponent } from '../../../lines/textureLinkLineComponent';
+
+interface ISpriteManagerPropertyGridComponentProps {
+    globalState: GlobalState;
+    spriteManager: SpriteManager;
+    lockObject: LockObject;
+    onSelectionChangedObservable?: Observable<any>;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class SpriteManagerPropertyGridComponent extends React.Component<ISpriteManagerPropertyGridComponentProps> {
+    constructor(props: ISpriteManagerPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const spriteManager = this.props.spriteManager;
+
+        return (
+            <div className="pane">
+                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={spriteManager} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                    <TextLineComponent label="Unique ID" value={spriteManager.uniqueId.toString()} />
+                    <FloatLineComponent label="Capacity" isInteger={true} target={spriteManager} propertyName="capacity" />
+                    <TextureLinkLineComponent label="Texture" texture={spriteManager.texture} onSelectionChangedObservable={this.props.onSelectionChangedObservable}/>
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="PROPERTIES">
+                    <CheckBoxLineComponent label="Pickable" target={spriteManager} propertyName="isPickable" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Fog enabled" target={spriteManager} propertyName="fogEnabled" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="No depth write" target={spriteManager} propertyName="disableDepthWrite" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Rendering group ID" decimalCount={0} target={spriteManager} propertyName="renderingGroupId" minimum={RenderingManager.MIN_RENDERINGGROUPS} maximum={RenderingManager.MAX_RENDERINGGROUPS - 1} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="DEFAULT SIZE">
+                    <FloatLineComponent label="Cell width" isInteger={true} target={spriteManager} propertyName="cellWidth" min={0} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                    <FloatLineComponent label="Cell height" isInteger={true} target={spriteManager} propertyName="cellHeight" min={0} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 84 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/sprites/spritePropertyGridComponent.tsx

@@ -0,0 +1,84 @@
+import * as React from "react";
+
+import { Observable } from "babylonjs/Misc/observable";
+
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { LockObject } from "../lockObject";
+import { LineContainerComponent } from '../../../lineContainerComponent';
+import { GlobalState } from '../../../../globalState';
+import { TextInputLineComponent } from '../../../lines/textInputLineComponent';
+import { TextLineComponent } from '../../../lines/textLineComponent';
+import { Sprite } from 'babylonjs/Sprites/sprite';
+import { CheckBoxLineComponent } from '../../../lines/checkBoxLineComponent';
+import { Vector3LineComponent } from '../../../lines/vector3LineComponent';
+import { Color4LineComponent } from '../../../lines/color4LineComponent';
+import { FloatLineComponent } from '../../../lines/floatLineComponent';
+import { SliderLineComponent } from '../../../lines/sliderLineComponent';
+
+interface ISpritePropertyGridComponentProps {
+    globalState: GlobalState;
+    sprite: Sprite;
+    lockObject: LockObject;
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>;    
+    onSelectionChangedObservable?: Observable<any>;
+}
+
+export class SpritePropertyGridComponent extends React.Component<ISpritePropertyGridComponentProps> {
+    constructor(props: ISpritePropertyGridComponentProps) {
+        super(props);
+    }
+
+    onManagerLink() {
+        if (!this.props.onSelectionChangedObservable) {
+            return;
+        }
+
+        
+        const sprite = this.props.sprite;
+        this.props.onSelectionChangedObservable.notifyObservers(sprite.manager);
+    }
+
+    render() {
+        const sprite = this.props.sprite;
+        const manager = sprite.manager;
+        const textureSize = manager.texture.getSize();
+        let maxCellCount = 0;
+        
+        if (!textureSize.width || !textureSize.height) {
+            maxCellCount = Math.max(sprite.fromIndex, sprite.toIndex);
+        } else {
+            maxCellCount = (textureSize.width / manager.cellWidth) * (textureSize.height / manager.cellHeight);
+        }
+
+        return (
+            <div className="pane">
+                <LineContainerComponent globalState={this.props.globalState} title="GENERAL">
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Name" target={sprite} propertyName="name" onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                    <TextLineComponent label="Unique ID" value={sprite.uniqueId.toString()} />
+                    <TextLineComponent label="Link to manager" value={manager.name} onLink={() => this.onManagerLink()} />
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="PROPERTIES">
+                    <Vector3LineComponent label="Position" target={sprite} propertyName="position" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Pickable" target={sprite} propertyName="isPickable" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Use alpha for picking" target={sprite} propertyName="useAlphaForPicking" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Color4LineComponent label="Color" target={sprite} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent useEuler={this.props.globalState.onlyUseEulers} label="Angle" target={sprite} propertyName="angle" minimum={0} maximum={2 * Math.PI} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="CELL">
+                    <CheckBoxLineComponent label="Invert U axis" target={sprite} propertyName="invertU" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Invert V axis" target={sprite} propertyName="invertV" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="SCALE">
+                    <FloatLineComponent label="Width" target={sprite} propertyName="width" min={0} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                    <FloatLineComponent label="Height" target={sprite} propertyName="height" min={0} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="ANIMATION">
+                    <SliderLineComponent label="Start cell" decimalCount={0} target={sprite} propertyName="fromIndex" minimum={0} maximum={maxCellCount} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="End cell" decimalCount={0} target={sprite} propertyName="toIndex" minimum={0} maximum={maxCellCount} step={1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Loop" target={sprite} propertyName="loopAnimation" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent label="Delay" target={sprite} propertyName="delay" digits={0} min={0} isInteger={true} onPropertyChangedObservable={this.props.onPropertyChangedObservable}/>
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

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

@@ -0,0 +1,30 @@
+import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
+
+import { faAddressBook } from '@fortawesome/free-solid-svg-icons';
+import { TreeItemLabelComponent } from "../treeItemLabelComponent";
+import { ExtensionsComponent } from "../extensionsComponent";
+import * as React from 'react';
+import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
+
+interface ISpriteManagerTreeItemComponentProps {
+    spriteManager: SpriteManager,
+    extensibilityGroups?: IExplorerExtensibilityGroup[],
+    onClick: () => void
+}
+
+export class SpriteManagerTreeItemComponent extends React.Component<ISpriteManagerTreeItemComponentProps> {
+    constructor(props: ISpriteManagerTreeItemComponentProps) {
+        super(props);
+    }
+
+    render() {
+        return (
+            <div className="spriteManagerTools">
+                <TreeItemLabelComponent label={this.props.spriteManager.name || "No name"} onClick={() => this.props.onClick()} icon={faAddressBook} color="blanchedalmond" />
+                {
+                    <ExtensionsComponent target={this.props.spriteManager} extensibilityGroups={this.props.extensibilityGroups} />
+                }
+            </div>
+        )
+    }
+}

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

@@ -0,0 +1,30 @@
+import { IExplorerExtensibilityGroup } from "babylonjs/Debug/debugLayer";
+
+import { faGhost } from '@fortawesome/free-solid-svg-icons';
+import { TreeItemLabelComponent } from "../treeItemLabelComponent";
+import { ExtensionsComponent } from "../extensionsComponent";
+import * as React from 'react';
+import { Sprite } from 'babylonjs/Sprites/sprite';
+
+interface ISpriteTreeItemComponentProps {
+    sprite: Sprite,
+    extensibilityGroups?: IExplorerExtensibilityGroup[],
+    onClick: () => void
+}
+
+export class SpriteTreeItemComponent extends React.Component<ISpriteTreeItemComponentProps> {
+    constructor(props: ISpriteTreeItemComponentProps) {
+        super(props);
+    }
+
+    render() {
+        return (
+            <div className="spriteTools">
+                <TreeItemLabelComponent label={this.props.sprite.name || "No name"} onClick={() => this.props.onClick()} icon={faGhost} color="blanchedalmond" />
+                {
+                    <ExtensionsComponent target={this.props.sprite} extensibilityGroups={this.props.extensibilityGroups} />
+                }
+            </div>
+        )
+    }
+}

+ 25 - 1
inspector/src/components/sceneExplorer/sceneExplorer.scss

@@ -415,7 +415,31 @@
                 width: 20px;
                 grid-column: 3;
             }
-        }
+        }        
+        
+        .spriteTools {
+            grid-column: 2;
+            display: grid;
+            grid-template-columns: 1fr auto 5px;
+            align-items: center;
+
+            .extensions {
+                width: 20px;
+                grid-column: 2;
+            }
+        }   
+
+        .spriteManagerTools {
+            grid-column: 2;
+            display: grid;
+            grid-template-columns: 1fr auto 5px;
+            align-items: center;
+
+            .extensions {
+                width: 20px;
+                grid-column: 2;
+            }
+        }        
 
         .materialTools {
             grid-column: 2;

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

@@ -24,6 +24,7 @@ import { GPUParticleSystem } from 'babylonjs/Particles/gpuParticleSystem';
 import { SSAO2RenderingPipeline } from 'babylonjs/PostProcesses/RenderPipeline/Pipelines/ssao2RenderingPipeline';
 import { StandardMaterial } from 'babylonjs/Materials/standardMaterial';
 import { PBRMaterial } from 'babylonjs/Materials/PBR/pbrMaterial';
+import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
 
 require("./sceneExplorer.scss");
 
@@ -355,6 +356,16 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             materials.push(...scene.multiMaterials);
         }
 
+        // Sprite Managers
+        let spriteManagersContextMenus: { label: string, action: () => void }[] = [];
+        spriteManagersContextMenus.push({
+            label: "Add new sprite manager",
+            action: () => {
+                let newSpriteManager = new SpriteManager("Default sprite manager", "//playground.babylonjs.com/textures/player.png", 2, 64, scene);
+                this.props.globalState.onSelectionChangedObservable.notifyObservers(newSpriteManager);
+            }
+        });            
+
         // Particle systems
         let particleSystemsContextMenus: { label: string, action: () => void }[] = [];
         particleSystemsContextMenus.push({
@@ -405,6 +416,10 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 <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} />
+                <TreeItemComponent globalState={this.props.globalState} 
+                    contextMenuItems={spriteManagersContextMenus} 
+                    forceSubitems={true}
+                    extensibilityGroups={this.props.extensibilityGroups} selectedEntity={this.state.selectedEntity} items={scene.spriteManagers} label="Sprite managers" 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} />

+ 13 - 8
inspector/src/components/sceneExplorer/treeItemComponent.tsx

@@ -77,7 +77,8 @@ export interface ITreeItemComponentProps {
     items?: Nullable<any[]>,
     label: string,
     offset: number,
-    filter: Nullable<string>,
+    filter: Nullable<string>,    
+    forceSubitems?: boolean,
     globalState: GlobalState,
     entity?: any,
     selectedEntity: any,
@@ -145,20 +146,24 @@ export class TreeItemComponent extends React.Component<ITreeItemComponentProps,
     }
 
     render() {
-        const items = this.props.items;
+        let items = this.props.items;
 
         const marginStyle = {
             paddingLeft: (10 * (this.props.offset + 0.5)) + "px"
         }
 
         if (!items) {
-            return (
-                <div className="groupContainer" style={marginStyle}>
-                    <div>
-                        {this.props.label}
+            if (this.props.forceSubitems) {
+                items = [];
+            } else {
+                return (
+                    <div className="groupContainer" style={marginStyle}>
+                        <div>
+                            {this.props.label}
+                        </div>
                     </div>
-                </div>
-            )
+                )
+            }
         }
 
         if (!items.length) {

+ 12 - 0
inspector/src/components/sceneExplorer/treeItemSpecializedComponent.tsx

@@ -32,6 +32,10 @@ import { BoneTreeItemComponent } from './entities/boneTreeItemComponent';
 import { Bone } from 'babylonjs/Bones/bone';
 import { ParticleSystemTreeItemComponent } from './entities/particleSystemTreeItemComponent';
 import { IParticleSystem } from 'babylonjs/Particles/IParticleSystem';
+import { SpriteManagerTreeItemComponent } from './entities/spriteManagerTreeItemComponent';
+import { SpriteManager } from 'babylonjs/Sprites/spriteManager';
+import { SpriteTreeItemComponent } from './entities/spriteTreeItemComponent';
+import { Sprite } from 'babylonjs/Sprites/sprite';
 
 
 interface ITreeItemSpecializedComponentProps {
@@ -70,6 +74,14 @@ export class TreeItemSpecializedComponent extends React.Component<ITreeItemSpeci
                 }
             }
 
+            if (className.indexOf("SpriteManager") !== -1) {
+                return (<SpriteManagerTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} spriteManager={entity as SpriteManager} onClick={() => this.onClick()} />);
+            }
+
+            if (className.indexOf("Sprite") !== -1) {
+                return (<SpriteTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} sprite={entity as Sprite} onClick={() => this.onClick()} />);
+            }
+
             if (className.indexOf("Skeleton") !== -1) {
                 return (<SkeletonTreeItemComponent extensibilityGroups={this.props.extensibilityGroups} skeleton={entity as Skeleton} onClick={() => this.onClick()} />);
             }

+ 79 - 0
loaders/src/glTF/2.0/Extensions/KHR_materials_ior.ts

@@ -0,0 +1,79 @@
+import { Nullable } from "babylonjs/types";
+import { PBRMaterial } from "babylonjs/Materials/PBR/pbrMaterial";
+import { Material } from "babylonjs/Materials/material";
+
+import { IMaterial } from "../glTFLoaderInterfaces";
+import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
+import { GLTFLoader } from "../glTFLoader";
+
+const NAME = "KHR_materials_ior";
+
+interface IKHR_materials_ior {
+    ior: number;
+}
+
+/**
+ * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1718)
+ * !!! Experimental Extension Subject to Changes !!!
+ */
+export class KHR_materials_ior implements IGLTFLoaderExtension {
+    /**
+     * Default ior Value from the spec.
+     */
+    private static readonly _DEFAULT_IOR = 1.5;
+
+    /**
+     * The name of this extension.
+     */
+    public readonly name = NAME;
+
+    /**
+     * Defines whether this extension is enabled.
+     */
+    public enabled: boolean;
+
+    /**
+     * Defines a number that determines the order the extensions are applied.
+     */
+    public order = 180;
+
+    private _loader: GLTFLoader;
+
+    /** @hidden */
+    constructor(loader: GLTFLoader) {
+        this._loader = loader;
+        this.enabled = this._loader.isExtensionUsed(NAME);
+    }
+
+    /** @hidden */
+    public dispose() {
+        delete this._loader;
+    }
+
+    /** @hidden */
+    public loadMaterialPropertiesAsync(context: string, material: IMaterial, babylonMaterial: Material): Nullable<Promise<void>> {
+        return GLTFLoader.LoadExtensionAsync<IKHR_materials_ior>(context, material, this.name, (extensionContext, extension) => {
+            const promises = new Array<Promise<any>>();
+            promises.push(this._loader.loadMaterialPropertiesAsync(context, material, babylonMaterial));
+            promises.push(this._loadIorPropertiesAsync(extensionContext, extension, babylonMaterial));
+            return Promise.all(promises).then(() => { });
+        });
+    }
+
+    private _loadIorPropertiesAsync(context: string, properties: IKHR_materials_ior, babylonMaterial: Material): Promise<void> {
+        if (!(babylonMaterial instanceof PBRMaterial)) {
+            throw new Error(`${context}: Material type not supported`);
+        }
+
+        if (properties.ior !== undefined) {
+            babylonMaterial.indexOfRefraction = properties.ior;
+        }
+        else {
+            babylonMaterial.indexOfRefraction = KHR_materials_ior._DEFAULT_IOR;
+        }
+
+        return Promise.resolve();
+    }
+}
+
+GLTFLoader.RegisterExtension(NAME, (loader) => new KHR_materials_ior(loader));

+ 14 - 5
loaders/src/glTF/2.0/Extensions/KHR_materials_specular.ts

@@ -5,17 +5,18 @@ import { Material } from "babylonjs/Materials/material";
 import { IMaterial, ITextureInfo } from "../glTFLoaderInterfaces";
 import { IGLTFLoaderExtension } from "../glTFLoaderExtension";
 import { GLTFLoader } from "../glTFLoader";
+import { Color3 } from 'babylonjs/Maths/math.color';
 
 const NAME = "KHR_materials_specular";
 
 interface IKHR_materials_specular {
     specularFactor: number;
+    specularColorFactor: number[];
     specularTexture: ITextureInfo;
 }
 
 /**
- * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1677)
- * [Playground Sample](https://www.babylonjs-playground.com/frame.html#BNIZX6#4)
+ * [Proposed Specification](https://github.com/KhronosGroup/glTF/pull/1719)
  * !!! Experimental Extension Subject to Changes !!!
  */
 export class KHR_materials_specular implements IGLTFLoaderExtension {
@@ -62,16 +63,24 @@ export class KHR_materials_specular implements IGLTFLoaderExtension {
             throw new Error(`${context}: Material type not supported`);
         }
 
+        const promises = new Array<Promise<any>>();
+
         if (properties.specularFactor !== undefined) {
             babylonMaterial.metallicF0Factor = properties.specularFactor;
         }
 
+        if (properties.specularColorFactor !== undefined) {
+            babylonMaterial.metallicReflectanceColor = Color3.FromArray(properties.specularColorFactor);
+        }
+
         if (properties.specularTexture) {
-            // This does not allow a separate sampler for it at the moment but is currently under discussion.
-            babylonMaterial.useMetallicF0FactorFromMetallicTexture = true;
+            promises.push(this._loader.loadTextureInfoAsync(`${context}/specularTexture`, properties.specularTexture, (texture) => {
+                texture.name = `${babylonMaterial.name} (Specular F0 Color)`;
+                babylonMaterial.metallicReflectanceTexture = texture;
+            }));
         }
 
-        return Promise.resolve();
+        return Promise.all(promises).then(() => { });
     }
 }
 

+ 1 - 0
loaders/src/glTF/2.0/Extensions/index.ts

@@ -7,6 +7,7 @@ export * from "./KHR_materials_unlit";
 export * from "./KHR_materials_clearcoat";
 export * from "./KHR_materials_sheen";
 export * from "./KHR_materials_specular";
+export * from "./KHR_materials_ior";
 export * from "./KHR_mesh_quantization";
 export * from "./KHR_texture_basisu";
 export * from "./KHR_texture_transform";

+ 1 - 1
package.json

@@ -7,7 +7,7 @@
     ],
     "name": "babylonjs",
     "description": "Babylon.js is a JavaScript 3D engine based on webgl.",
-    "version": "4.2.0-alpha.14",
+    "version": "4.2.0-alpha.15",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 1 - 1
src/Bones/skeleton.ts

@@ -285,7 +285,7 @@ export class Skeleton implements IAnimatable {
      * @returns the requested animation range or null if not found
      */
     public getAnimationRange(name: string): Nullable<AnimationRange> {
-        return this._ranges[name];
+        return this._ranges[name] || null;
     }
 
     /**

+ 3 - 1
src/Engines/Processors/shaderProcessor.ts

@@ -123,7 +123,9 @@ export class ShaderProcessor {
     private static _BuildExpression(line: string, start: number): ShaderCodeTestNode {
         let node = new ShaderCodeTestNode();
         let command = line.substring(0, start);
-        let expression = line.substring(start).trim();
+        let expression = line.substring(start);
+
+        expression = expression.substring(0, ((expression.indexOf("//") + 1) || (expression.length + 1)) - 1).trim();
 
         if (command === "#ifdef") {
             node.testExpression = new ShaderDefineIsDefinedOperator(expression);

+ 12 - 0
src/Engines/constants.ts

@@ -241,6 +241,18 @@ export class Constants {
     /** Equirectangular Fixed Mirrored coordinates mode */
     public static readonly TEXTURE_FIXED_EQUIRECTANGULAR_MIRRORED_MODE = 9;
 
+    /** Offline (baking) quality for texture filtering */
+    public static readonly TEXTURE_FILTERING_QUALITY_OFFLINE = 4096;
+
+    /** High quality for texture filtering */
+    public static readonly TEXTURE_FILTERING_QUALITY_HIGH = 64;
+
+    /** Medium quality for texture filtering */
+    public static readonly TEXTURE_FILTERING_QUALITY_MEDIUM = 16;
+
+    /** Low quality for texture filtering */
+    public static readonly TEXTURE_FILTERING_QUALITY_LOW = 8;
+
     // Texture rescaling mode
     /** Defines that texture rescaling will use a floor to find the closer power of 2 size */
     public static readonly SCALEMODE_FLOOR = 1;

+ 2 - 2
src/Engines/thinEngine.ts

@@ -132,14 +132,14 @@ export class ThinEngine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.2.0-alpha.14";
+        return "babylonjs@4.2.0-alpha.15";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.2.0-alpha.14";
+        return "4.2.0-alpha.15";
     }
 
     /**

+ 2 - 11
src/Materials/Node/Blocks/PBR/reflectivityBlock.ts

@@ -40,12 +40,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
     public useRoughnessFromMetallicTextureGreen: boolean = true;
 
     /**
-     * Specifies whether the F0 factor can be fetched from the mettalic texture.
-     */
-    @editableInPropertyPage("Metallic F0 from alpha channel", PropertyTypeForEdition.Boolean, "METALLIC WORKFLOW", { "notifiers": { "update": true }})
-    public useMetallicF0FactorFromMetallicTexture: boolean = false;
-
-    /**
      * Create a new ReflectivityBlock
      * @param name defines the block name
      */
@@ -122,9 +116,10 @@ export class ReflectivityBlock extends NodeMaterialBlock {
             reflectivityOutParams reflectivityOut;
 
             reflectivityBlock(
-                vec4(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, 0., 0.04),
+                vec4(${this.metallic.associatedVariableName}, ${this.roughness.associatedVariableName}, 0., 0.),
             #ifdef METALLICWORKFLOW
                 surfaceAlbedo,
+                vec4(1.),
             #endif
             #ifdef REFLECTIVITY
                 vec3(0., 0., ${aoIntensityVarName}),
@@ -160,7 +155,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         defines.setValue("METALLNESSSTOREINMETALMAPBLUE", this.useMetallnessFromMetallicTextureBlue, true);
         defines.setValue("ROUGHNESSSTOREINMETALMAPALPHA", this.useRoughnessFromMetallicTextureAlpha, true);
         defines.setValue("ROUGHNESSSTOREINMETALMAPGREEN",  !this.useRoughnessFromMetallicTextureAlpha && this.useRoughnessFromMetallicTextureGreen, true);
-        defines.setValue("METALLICF0FACTORFROMMETALLICMAP", this.useMetallicF0FactorFromMetallicTexture, true);
     }
 
     protected _buildBlock(state: NodeMaterialBuildState) {
@@ -178,7 +172,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         codeString += `${this._codeVariableName}.useMetallnessFromMetallicTextureBlue = ${this.useMetallnessFromMetallicTextureBlue};\r\n`;
         codeString += `${this._codeVariableName}.useRoughnessFromMetallicTextureAlpha = ${this.useRoughnessFromMetallicTextureAlpha};\r\n`;
         codeString += `${this._codeVariableName}.useRoughnessFromMetallicTextureGreen = ${this.useRoughnessFromMetallicTextureGreen};\r\n`;
-        codeString += `${this._codeVariableName}.useMetallicF0FactorFromMetallicTexture = ${this.useMetallicF0FactorFromMetallicTexture};\r\n`;
 
         return codeString;
     }
@@ -190,7 +183,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         serializationObject.useMetallnessFromMetallicTextureBlue = this.useMetallnessFromMetallicTextureBlue;
         serializationObject.useRoughnessFromMetallicTextureAlpha = this.useRoughnessFromMetallicTextureAlpha;
         serializationObject.useRoughnessFromMetallicTextureGreen = this.useRoughnessFromMetallicTextureGreen;
-        serializationObject.useMetallicF0FactorFromMetallicTexture = this.useMetallicF0FactorFromMetallicTexture;
 
         return serializationObject;
     }
@@ -202,7 +194,6 @@ export class ReflectivityBlock extends NodeMaterialBlock {
         this.useMetallnessFromMetallicTextureBlue = serializationObject.useMetallnessFromMetallicTextureBlue;
         this.useRoughnessFromMetallicTextureAlpha = serializationObject.useRoughnessFromMetallicTextureAlpha;
         this.useRoughnessFromMetallicTextureGreen = serializationObject.useRoughnessFromMetallicTextureGreen;
-        this.useMetallicF0FactorFromMetallicTexture = serializationObject.useMetallicF0FactorFromMetallicTexture;
     }
 }
 

+ 108 - 57
src/Materials/PBR/pbrBaseMaterial.ts

@@ -17,6 +17,7 @@ import { IMaterialBRDFDefines, PBRBRDFConfiguration } from "./pbrBRDFConfigurati
 import { IMaterialSheenDefines, PBRSheenConfiguration } from "./pbrSheenConfiguration";
 import { IMaterialSubSurfaceDefines, PBRSubSurfaceConfiguration } from "./pbrSubSurfaceConfiguration";
 import { Color3, TmpColors } from '../../Maths/math.color';
+import { Scalar } from "../../Maths/math.scalar";
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { Effect, IEffectCreationOptions } from "../../Materials/effect";
@@ -37,6 +38,7 @@ import { IAnimatable } from '../../Animations/animatable.interface';
 import "../../Materials/Textures/baseTexture.polynomial";
 import "../../Shaders/pbr.fragment";
 import "../../Shaders/pbr.vertex";
+
 import { EffectFallbacks } from '../effectFallbacks';
 
 const onCreatedEffectParameters = { effect: null as unknown as Effect, subMesh: null as unknown as Nullable<SubMesh> };
@@ -54,6 +56,9 @@ export class PBRMaterialDefines extends MaterialDefines
     IMaterialSubSurfaceDefines {
     public PBR = true;
 
+    public NUM_SAMPLES = "0u";
+    public REALTIME_FILTERING = false;
+
     public MAINUV1 = false;
     public MAINUV2 = false;
     public UV1 = false;
@@ -101,7 +106,8 @@ export class PBRMaterialDefines extends MaterialDefines
     public ROUGHNESSSTOREINMETALMAPGREEN = false;
     public METALLNESSSTOREINMETALMAPBLUE = false;
     public AOSTOREINMETALMAPRED = false;
-    public METALLICF0FACTORFROMMETALLICMAP = false;
+    public METALLIC_REFLECTANCE = false;
+    public METALLIC_REFLECTANCEDIRECTUV = 0;
 
     public ENVIRONMENTBRDF = false;
     public ENVIRONMENTBRDF_RGBD = false;
@@ -406,19 +412,32 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _roughness: Nullable<number> = null;
 
     /**
-     * Specifies the an F0 factor to help configuring the material F0.
-     * Instead of the default 4%, 8% * factor will be used. As the factor is defaulting
-     * to 0.5 the previously hard coded value stays the same.
-     * Can also be used to scale the F0 values of the metallic texture.
+     * In metallic workflow, specifies an F0 factor to help configuring the material F0.
+     * By default the indexOfrefraction is used to compute F0;
+     *
+     * This is used as a factor against the default reflectance at normal incidence to tweak it.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor;
+     * F90 = metallicReflectanceColor;
      */
-    protected _metallicF0Factor = 0.5;
+    protected _metallicF0Factor = 1;
 
     /**
-     * Specifies whether the F0 factor can be fetched from the mettalic texture.
-     * If set to true, please adapt the metallicF0Factor to ensure it fits with
-     * your expectation as it multiplies with the texture data.
+     * In metallic workflow, specifies an F90 color to help configuring the material F90.
+     * By default the F90 is always 1;
+     *
+     * Please note that this factor is also used as a factor against the default reflectance at normal incidence.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor
+     * F90 = metallicReflectanceColor;
      */
-    protected _useMetallicF0FactorFromMetallicTexture = false;
+    protected _metallicReflectanceColor = Color3.White();
+
+    /**
+     * Defines to store metallicReflectanceColor in RGB and metallicF0Factor in A
+     * This is multiply against the scalar values defined in the material.
+     */
+    protected _metallicReflectanceTexture: Nullable<BaseTexture> = null;
 
     /**
      * Used to enable roughness/glossiness fetch from a separate channel depending on the current mode.
@@ -938,6 +957,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         }
                     }
 
+                    if (this._metallicReflectanceTexture) {
+                        if (!this._metallicReflectanceTexture.isReadyOrNotBlocking()) {
+                            return false;
+                        }
+                    }
+
                     if (this._microSurfaceTexture) {
                         if (!this._microSurfaceTexture.isReadyOrNotBlocking()) {
                             return false;
@@ -1033,6 +1058,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
     private _prepareEffect(mesh: AbstractMesh, defines: PBRMaterialDefines, onCompiled: Nullable<(effect: Effect) => void> = null, onError: Nullable<(effect: Effect, errors: string) => void> = null, useInstances: Nullable<boolean> = null, useClipPlane: Nullable<boolean> = null): Nullable<Effect> {
         this._prepareDefines(mesh, defines, useInstances, useClipPlane);
+
         if (!defines.isDirty) {
             return null;
         }
@@ -1156,12 +1182,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
         var shaderName = "pbr";
 
-        var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vEmissiveColor", "visibility", "vReflectionColor",
+        var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vMetallicReflectanceFactors", "vEmissiveColor", "visibility", "vReflectionColor",
             "vFogInfos", "vFogColor", "pointSize",
-            "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos",
+            "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos", "vMetallicReflectanceInfos",
             "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos",
             "mBones",
-            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix",
+            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "vClipPlane5", "vClipPlane6", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "metallicReflectanceMatrix",
             "vLightingIntensity",
             "logarithmicDepthConstant",
             "vSphericalX", "vSphericalY", "vSphericalZ",
@@ -1172,13 +1198,14 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             "vSphericalL2_2", "vSphericalL2_1", "vSphericalL20", "vSphericalL21", "vSphericalL22",
             "vReflectionMicrosurfaceInfos",
             "vTangentSpaceParams", "boneTextureWidth",
-            "vDebugMode"
+            "vDebugMode",
+            "vFilteringInfo", "linearRoughness"
         ];
 
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
             "bumpSampler", "lightmapSampler", "opacitySampler",
             "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh", "irradianceSampler",
-            "microSurfaceSampler", "environmentBrdfSampler", "boneSampler"];
+            "microSurfaceSampler", "environmentBrdfSampler", "boneSampler", "metallicReflectanceSampler"];
 
         var uniformBuffers = ["Material", "Scene"];
 
@@ -1275,6 +1302,13 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     defines.LODINREFLECTIONALPHA = reflectionTexture.lodLevelInAlpha;
                     defines.LINEARSPECULARREFLECTION = reflectionTexture.linearSpecularLOD;
 
+                    if (reflectionTexture.realTimeFiltering && reflectionTexture.realTimeFilteringQuality > 0) {
+                        defines.NUM_SAMPLES = reflectionTexture.realTimeFilteringQuality + "u";
+                        defines.REALTIME_FILTERING = true;
+                    } else {
+                        defines.REALTIME_FILTERING = false;
+                    }
+
                     if (reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE) {
                         defines.INVERTCUBICMAP = true;
                     }
@@ -1333,7 +1367,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         else if (reflectionTexture.isCube) {
                             defines.USESPHERICALFROMREFLECTIONMAP = true;
                             defines.USEIRRADIANCEMAP = false;
-                            if (this._forceIrradianceInFragment || scene.getEngine().getCaps().maxVaryingVectors <= 8) {
+                            if (this._forceIrradianceInFragment || reflectionTexture.realTimeFiltering || scene.getEngine().getCaps().maxVaryingVectors <= 8) {
                                 defines.USESPHERICALINVERTEX = false;
                             }
                             else {
@@ -1387,7 +1421,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         defines.ROUGHNESSSTOREINMETALMAPGREEN = !this._useRoughnessFromMetallicTextureAlpha && this._useRoughnessFromMetallicTextureGreen;
                         defines.METALLNESSSTOREINMETALMAPBLUE = this._useMetallnessFromMetallicTextureBlue;
                         defines.AOSTOREINMETALMAPRED = this._useAmbientOcclusionFromMetallicTextureRed;
-                        defines.METALLICF0FACTORFROMMETALLICMAP = this._useMetallicF0FactorFromMetallicTexture;
                     }
                     else if (this._reflectivityTexture) {
                         MaterialHelper.PrepareDefinesForMergedUV(this._reflectivityTexture, defines, "REFLECTIVITY");
@@ -1397,6 +1430,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         defines.REFLECTIVITY = false;
                     }
 
+                    if (this._metallicReflectanceTexture) {
+                        MaterialHelper.PrepareDefinesForMergedUV(this._metallicReflectanceTexture, defines, "METALLIC_REFLECTANCE");
+                    } else {
+                        defines.METALLIC_REFLECTANCE = false;
+                    }
+
                     if (this._microSurfaceTexture) {
                         MaterialHelper.PrepareDefinesForMergedUV(this._microSurfaceTexture, defines, "MICROSURFACEMAP");
                     } else {
@@ -1572,6 +1611,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         ubo.addUniform("vReflectivityColor", 4);
         ubo.addUniform("vEmissiveColor", 3);
         ubo.addUniform("visibility", 1);
+        ubo.addUniform("vMetallicReflectanceFactors", 4);
+        ubo.addUniform("vMetallicReflectanceInfos", 2);
+        ubo.addUniform("metallicReflectanceMatrix", 16);
 
         PBRClearCoatConfiguration.PrepareUniformBuffer(ubo);
         PBRAnisotropicConfiguration.PrepareUniformBuffer(ubo);
@@ -1681,6 +1723,14 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             ubo.updateVector3("vReflectionSize", cubeTexture.boundingBoxSize);
                         }
 
+                        if (reflectionTexture.realTimeFiltering) {
+                            const width = reflectionTexture.getSize().width;
+                            const alpha = this._roughness! * this._roughness!;
+
+                            this._activeEffect.setFloat2("vFilteringInfo", width, Scalar.Log2(width));
+                            this._activeEffect.setFloat("linearRoughness", alpha);
+                        }
+
                         if (!defines.USEIRRADIANCEMAP) {
                             var polynomials = reflectionTexture.sphericalPolynomial;
                             if (defines.USESPHERICALFROMREFLECTIONMAP && polynomials) {
@@ -1740,6 +1790,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             MaterialHelper.BindTextureMatrix(this._reflectivityTexture, ubo, "reflectivity");
                         }
 
+                        if (this._metallicReflectanceTexture) {
+                            ubo.updateFloat2("vMetallicReflectanceInfos", this._metallicReflectanceTexture.coordinatesIndex, this._metallicReflectanceTexture.level);
+                            MaterialHelper.BindTextureMatrix(this._metallicReflectanceTexture, ubo, "metallicReflectance");
+                        }
+
                         if (this._microSurfaceTexture) {
                             ubo.updateFloat2("vMicroSurfaceSamplerInfos", this._microSurfaceTexture.coordinatesIndex, this._microSurfaceTexture.level);
                             MaterialHelper.BindTextureMatrix(this._microSurfaceTexture, ubo, "microSurfaceSampler");
@@ -1767,13 +1822,21 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 if (defines.METALLICWORKFLOW) {
                     TmpColors.Color3[0].r = (this._metallic === undefined || this._metallic === null) ? 1 : this._metallic;
                     TmpColors.Color3[0].g = (this._roughness === undefined || this._roughness === null) ? 1 : this._roughness;
+                    ubo.updateColor4("vReflectivityColor", TmpColors.Color3[0], 1);
+
+                    const ior = this.subSurface.indexOfRefraction;
+                    const outside_ior = 1; // consider air as clear coat and other layaers would remap in the shader.
 
                     // We are here deriving our default reflectance from a common value for none metallic surface.
-                    // Default specular reflectance at normal incidence.
-                    // 4% corresponds to index of refraction (IOR) of 1.50, approximately equal to glass.
-                    // We then use 8% combined with a factor of 0.5 to allow some variations around the 0.04 default value.
-                    const metallicF0 = 0.08 * this._metallicF0Factor;
-                    ubo.updateColor4("vReflectivityColor", TmpColors.Color3[0], metallicF0);
+                    // Based of the schlick fresnel approximation model
+                    // for dielectrics.
+                    const f0 = Math.pow((ior - outside_ior) / (ior + outside_ior), 2);
+
+                    // Tweak the default F0 and F90 based on our given setup
+                    this._metallicReflectanceColor.scaleToRef(f0 * this._metallicF0Factor, TmpColors.Color3[0]);
+                    const metallicF90 = this._metallicF0Factor;
+
+                    ubo.updateColor4("vMetallicReflectanceFactors", TmpColors.Color3[0], metallicF90);
                 }
                 else {
                     ubo.updateColor4("vReflectivityColor", this._reflectivityColor, this._microSurface);
@@ -1849,6 +1912,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                         ubo.setTexture("reflectivitySampler", this._reflectivityTexture);
                     }
 
+                    if (this._metallicReflectanceTexture) {
+                        ubo.setTexture("metallicReflectanceSampler", this._metallicReflectanceTexture);
+                    }
+
                     if (this._microSurfaceTexture) {
                         ubo.setTexture("microSurfaceSampler", this._microSurfaceTexture);
                     }
@@ -2010,6 +2077,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             activeTextures.push(this._metallicTexture);
         }
 
+        if (this._metallicReflectanceTexture) {
+            activeTextures.push(this._metallicReflectanceTexture);
+        }
+
         if (this._microSurfaceTexture) {
             activeTextures.push(this._microSurfaceTexture);
         }
@@ -2064,6 +2135,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
+        if (this._metallicReflectanceTexture === texture) {
+            return true;
+        }
+
         if (this._microSurfaceTexture === texture) {
             return true;
         }
@@ -2089,45 +2164,21 @@ export abstract class PBRBaseMaterial extends PushMaterial {
      */
     public dispose(forceDisposeEffect?: boolean, forceDisposeTextures?: boolean): void {
         if (forceDisposeTextures) {
-            if (this._albedoTexture) {
-                this._albedoTexture.dispose();
-            }
-
-            if (this._ambientTexture) {
-                this._ambientTexture.dispose();
-            }
-
-            if (this._opacityTexture) {
-                this._opacityTexture.dispose();
-            }
-
-            if (this._reflectionTexture) {
-                this._reflectionTexture.dispose();
-            }
-
             if (this._environmentBRDFTexture && this.getScene().environmentBRDFTexture !== this._environmentBRDFTexture) {
                 this._environmentBRDFTexture.dispose();
             }
 
-            if (this._emissiveTexture) {
-                this._emissiveTexture.dispose();
-            }
-
-            if (this._metallicTexture) {
-                this._metallicTexture.dispose();
-            }
-
-            if (this._reflectivityTexture) {
-                this._reflectivityTexture.dispose();
-            }
-
-            if (this._bumpTexture) {
-                this._bumpTexture.dispose();
-            }
-
-            if (this._lightmapTexture) {
-                this._lightmapTexture.dispose();
-            }
+            this._albedoTexture?.dispose();
+            this._ambientTexture?.dispose();
+            this._opacityTexture?.dispose();
+            this._reflectionTexture?.dispose();
+            this._emissiveTexture?.dispose();
+            this._metallicTexture?.dispose();
+            this._reflectivityTexture?.dispose();
+            this._bumpTexture?.dispose();
+            this._lightmapTexture?.dispose();
+            this._metallicReflectanceTexture?.dispose();
+            this._microSurfaceTexture?.dispose();
         }
 
         this.subSurface.dispose(forceDisposeTextures);

+ 33 - 13
src/Materials/PBR/pbrMaterial.ts

@@ -165,23 +165,38 @@ export class PBRMaterial extends PBRBaseMaterial {
     public roughness: Nullable<number>;
 
     /**
-     * Specifies the an F0 factor to help configuring the material F0.
-     * Instead of the default 4%, 8% * factor will be used. As the factor is defaulting
-     * to 0.5 the previously hard coded value stays the same.
-     * Can also be used to scale the F0 values of the metallic texture.
+     * In metallic workflow, specifies an F0 factor to help configuring the material F0.
+     * By default the indexOfrefraction is used to compute F0;
+     *
+     * This is used as a factor against the default reflectance at normal incidence to tweak it.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor;
+     * F90 = metallicReflectanceColor;
      */
     @serialize()
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public metallicF0Factor = 0.5;
+    public metallicF0Factor = 1;
 
     /**
-     * Specifies whether the F0 factor can be fetched from the mettalic texture.
-     * If set to true, please adapt the metallicF0Factor to ensure it fits with
-     * your expectation as it multiplies with the texture data.
+     * In metallic workflow, specifies an F90 color to help configuring the material F90.
+     * By default the F90 is always 1;
+     *
+     * Please note that this factor is also used as a factor against the default reflectance at normal incidence.
+     *
+     * F0 = defaultF0 * metallicF0Factor * metallicReflectanceColor
+     * F90 = metallicReflectanceColor;
      */
-    @serialize()
+    @serializeAsColor3()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public metallicReflectanceColor = Color3.White();
+
+    /**
+     * Defines to store metallicReflectanceColor in RGB and metallicF0Factor in A
+     * This is multiply against the scalar values defined in the material.
+     */
+    @serializeAsTexture()
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public useMetallicF0FactorFromMetallicTexture = false;
+    public metallicReflectanceTexture: Nullable<BaseTexture>;
 
     /**
      * Used to enable roughness/glossiness fetch from a separate channel depending on the current mode.
@@ -264,13 +279,18 @@ export class PBRMaterial extends PBRBaseMaterial {
     public microSurface = 1.0;
 
     /**
-     * source material index of refraction (IOR)' / 'destination material IOR.
+     * Index of refraction of the material base layer.
+     * https://en.wikipedia.org/wiki/List_of_refractive_indices
+     *
+     * This does not only impact refraction but also the Base F0 of Dielectric Materials.
+     *
+     * From dielectric fresnel rules: F0 = square((iorT - iorI) / (iorT + iorI))
      */
     public get indexOfRefraction(): number {
-        return 1 / this.subSurface.indexOfRefraction;
+        return this.subSurface.indexOfRefraction;
     }
     public set indexOfRefraction(value: number) {
-        this.subSurface.indexOfRefraction = 1 / value;
+        this.subSurface.indexOfRefraction = value;
     }
 
     /**

+ 7 - 3
src/Materials/PBR/pbrSubSurfaceConfiguration.ts

@@ -113,14 +113,18 @@ export class PBRSubSurfaceConfiguration {
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
     public refractionTexture: Nullable<BaseTexture> = null;
 
-    private _indexOfRefraction = 1;
+    private _indexOfRefraction = 1.5;
     /**
-     * Defines the index of refraction used in the material.
+     * Index of refraction of the material base layer.
      * https://en.wikipedia.org/wiki/List_of_refractive_indices
+     *
+     * This does not only impact refraction but also the Base F0 of Dielectric Materials.
+     *
+     * From dielectric fresnel rules: F0 = square((iorT - iorI) / (iorT + iorI))
      */
     @serialize()
     @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public indexOfRefraction = 1;
+    public indexOfRefraction = 1.5;
 
     private _invertRefractionY = false;
     /**

+ 209 - 0
src/Materials/Textures/Filtering/hdrFiltering.ts

@@ -0,0 +1,209 @@
+import { Vector3 } from "../../../Maths/math";
+import { Scalar } from "../../../Maths/math.scalar";
+import { InternalTexture } from "../internalTexture";
+import { BaseTexture } from "../baseTexture";
+import { ThinEngine } from "../../../Engines/thinEngine";
+import { Effect } from "../../../Materials/effect";
+import { Constants } from "../../../Engines/constants";
+import { EffectWrapper, EffectRenderer } from "../../../Materials/effectRenderer";
+import { Nullable } from '../../../types';
+
+import "../../../Shaders/hdrFiltering.vertex";
+import "../../../Shaders/hdrFiltering.fragment";
+
+/**
+ * Options for texture filtering
+ */
+interface IHDRFilteringOptions {
+    /**
+     * Scales pixel intensity for the input HDR map.
+     */
+    hdrScale?: number;
+
+    /**
+     * Quality of the filter. Should be `Constants.TEXTURE_FILTERING_QUALITY_OFFLINE` for prefiltering
+     */
+    quality?: number;
+}
+
+/**
+ * Filters HDR maps to get correct renderings of PBR reflections
+ */
+export class HDRFiltering {
+
+    private _engine: ThinEngine;
+    private _effectRenderer: EffectRenderer;
+    private _effectWrapper: EffectWrapper;
+
+    private _lodGenerationOffset: number = 0;
+    private _lodGenerationScale: number = 0.8;
+
+    /**
+     * Quality switch for prefiltering. Should be set to `Constants.TEXTURE_FILTERING_QUALITY_OFFLINE` unless
+     * you care about baking speed.
+     */
+    public quality: number = Constants.TEXTURE_FILTERING_QUALITY_OFFLINE;
+
+    /**
+     * Scales pixel intensity for the input HDR map.
+     */
+    public hdrScale: number = 1;
+
+    /**
+     * Instantiates HDR filter for reflection maps
+     *
+     * @param engine Thin engine
+     * @param options Options
+     */
+    constructor(engine: ThinEngine, options: IHDRFilteringOptions = {}) {
+        // pass
+        this._engine = engine;
+        this.hdrScale = options.hdrScale || this.hdrScale;
+        this.quality = options.hdrScale || this.quality;
+    }
+
+    private _createRenderTarget(size: number): InternalTexture {
+        let textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE;
+        if (this._engine.getCaps().textureHalfFloatRender) {
+            textureType = Constants.TEXTURETYPE_HALF_FLOAT;
+        }
+        else if (this._engine.getCaps().textureFloatRender) {
+            textureType = Constants.TEXTURETYPE_FLOAT;
+        }
+
+        const texture = this._engine.createRenderTargetCubeTexture(size, {
+            format: Constants.TEXTUREFORMAT_RGBA,
+            type: textureType,
+            generateMipMaps: false,
+            generateDepthBuffer: false,
+            generateStencilBuffer: false,
+            samplingMode: Constants.TEXTURE_NEAREST_SAMPLINGMODE
+        });
+        this._engine.updateTextureWrappingMode(texture,
+            Constants.TEXTURE_CLAMP_ADDRESSMODE,
+            Constants.TEXTURE_CLAMP_ADDRESSMODE,
+            Constants.TEXTURE_CLAMP_ADDRESSMODE);
+
+        this._engine.updateTextureSamplingMode(Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, texture, true);
+
+        return texture;
+    }
+
+    private _prefilterInternal(texture: BaseTexture): BaseTexture {
+        const width = texture.getSize().width;
+        const mipmapsCount = Math.round(Scalar.Log2(width)) + 1;
+
+        const effect = this._effectWrapper.effect;
+        const outputTexture = this._createRenderTarget(width);
+        this._effectRenderer.setViewport();
+
+        const intTexture = texture.getInternalTexture();
+        if (intTexture) {
+            // Just in case generate fresh clean mips.
+            this._engine.updateTextureSamplingMode(Constants.TEXTURE_TRILINEAR_SAMPLINGMODE, intTexture, true);
+        }
+
+        this._effectRenderer.applyEffectWrapper(this._effectWrapper);
+
+        const directions = [
+            [new Vector3(0, 0, -1), new Vector3(0, -1, 0), new Vector3(1, 0, 0)], // PositiveX
+            [new Vector3(0, 0, 1), new Vector3(0, -1, 0), new Vector3(-1, 0, 0)], // NegativeX
+            [new Vector3(1, 0, 0), new Vector3(0, 0, 1), new Vector3(0, 1, 0)], // PositiveY
+            [new Vector3(1, 0, 0), new Vector3(0, 0, -1), new Vector3(0, -1, 0)], // NegativeY
+            [new Vector3(1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 1)], // PositiveZ
+            [new Vector3(-1, 0, 0), new Vector3(0, -1, 0), new Vector3(0, 0, -1)], // NegativeZ
+        ];
+
+        effect.setFloat("hdrScale", this.hdrScale);
+        effect.setFloat2("vFilteringInfo", texture.getSize().width, mipmapsCount);
+        effect.setTexture("inputTexture", texture);
+
+        for (let face = 0; face < 6; face++) {
+            effect.setVector3("up", directions[face][0]);
+            effect.setVector3("right", directions[face][1]);
+            effect.setVector3("front", directions[face][2]);
+
+            for (let lod = 0; lod < mipmapsCount; lod++) {
+
+                this._engine.bindFramebuffer(outputTexture, face, undefined, undefined, true, lod);
+                this._effectRenderer.applyEffectWrapper(this._effectWrapper);
+
+                let alpha = Math.pow(2, (lod - this._lodGenerationOffset) / this._lodGenerationScale) / width;
+                if (lod === 0) {
+                    alpha = 0;
+                }
+
+                effect.setFloat("linearRoughness", alpha);
+
+                this._effectRenderer.draw();
+            }
+        }
+
+        // Cleanup
+        this._effectRenderer.restoreStates();
+        this._engine.restoreDefaultFramebuffer();
+        this._engine._releaseFramebufferObjects(outputTexture);
+        this._engine._releaseTexture(texture._texture!);
+
+        // Internal Swap
+        outputTexture._swapAndDie(texture._texture!);
+        return texture;
+    }
+
+    private _createEffect(texture: BaseTexture, onCompiled?: Nullable<(effect: Effect) => void>): EffectWrapper {
+        const defines = [];
+        if (texture.gammaSpace) {
+            defines.push("#define GAMMA_INPUT");
+        }
+
+        defines.push("#define NUM_SAMPLES " + this.quality + "u"); // unsigned int
+
+        const effectWrapper = new EffectWrapper({
+            engine: this._engine,
+            name: "hdrFiltering",
+            vertexShader: "hdrFiltering",
+            fragmentShader: "hdrFiltering",
+            samplerNames: ["inputTexture"],
+            uniformNames: ["vSampleDirections", "vWeights", "up", "right", "front", "vFilteringInfo", "hdrScale", "linearRoughness"],
+            useShaderStore: true,
+            defines,
+            onCompiled: onCompiled
+        });
+
+        return effectWrapper;
+    }
+
+    /**
+     * Get a value indicating if the filter is ready to be used
+     * @param texture Texture to filter
+     * @returns true if the filter is ready
+     */
+    public isReady(texture: BaseTexture) {
+        return (texture.isReady() && this._effectWrapper.effect.isReady());
+    }
+
+    /**
+      * Prefilters a cube texture to have mipmap levels representing roughness values.
+      * Prefiltering will be invoked at the end of next rendering pass.
+      * This has to be done once the map is loaded, and has not been prefiltered by a third party software.
+      * See http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf for more information
+      * @param texture Texture to filter
+      * @param onFinished Callback when filtering is done
+      * @return Promise called when prefiltering is done
+      */
+    public prefilter(texture: BaseTexture, onFinished: Nullable<() => void> = null) {
+        return new Promise((resolve) => {
+            this._effectRenderer = new EffectRenderer(this._engine);
+            this._effectWrapper = this._createEffect(texture);
+            this._effectWrapper.effect.executeWhenCompiled(() => {
+                this._prefilterInternal(texture);
+                this._effectRenderer.dispose();
+                this._effectWrapper.dispose();
+                resolve();
+                if (onFinished) {
+                    onFinished();
+                }
+            });
+        });
+    }
+}

+ 30 - 0
src/Materials/Textures/baseTexture.ts

@@ -345,6 +345,36 @@ export class BaseTexture implements IAnimatable {
         return "BaseTexture";
     }
 
+    private _realTimeFiltering: boolean = false;
+    /**
+     * Enables realtime filtering on the texture.
+     */
+    public get realTimeFiltering() {
+        return this._realTimeFiltering;
+    }
+    public set realTimeFiltering(b: boolean) {
+        this._realTimeFiltering = b;
+        let scene = this.getScene();
+        if (scene) {
+            scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+        }
+    }
+
+    private _realTimeFilteringQuality: number = Constants.TEXTURE_FILTERING_QUALITY_LOW;
+    /**
+     * Quality switch for realtime filtering
+     */
+    public get realTimeFilteringQuality() : number {
+        return this._realTimeFilteringQuality;
+    }
+    public set realTimeFilteringQuality(n: number) {
+        this._realTimeFilteringQuality = n;
+        let scene = this.getScene();
+        if (scene) {
+            scene.markAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+        }
+    }
+
     /**
      * Define the list of animation attached to the texture.
      */

+ 14 - 3
src/Materials/Textures/hdrCubeTexture.ts

@@ -10,7 +10,7 @@ import { _TypeStore } from '../../Misc/typeStore';
 import { Tools } from '../../Misc/tools';
 import { ToGammaSpace } from '../../Maths/math.constants';
 import { ThinEngine } from '../../Engines/thinEngine';
-
+import { HDRFiltering } from "../../Materials/Textures/Filtering/hdrFiltering";
 import "../../Engines/Extensions/engine.rawTexture";
 import "../../Materials/Textures/baseTexture.polynomial";
 
@@ -33,6 +33,7 @@ export class HDRCubeTexture extends BaseTexture {
 
     private _generateHarmonics = true;
     private _noMipmap: boolean;
+    private _prefilterOnLoad: boolean;
     private _textureMatrix: Matrix;
     private _size: number;
     private _onLoad: Nullable<() => void> = null;
@@ -114,9 +115,9 @@ export class HDRCubeTexture extends BaseTexture {
      * @param noMipmap Forces to not generate the mipmap if true
      * @param generateHarmonics Specifies whether you want to extract the polynomial harmonics during the generation process
      * @param gammaSpace Specifies if the texture will be use in gamma or linear space (the PBR material requires those texture in linear space, but the standard material would require them in Gamma space)
-     * @param reserved Reserved flag for internal use.
+     * @param prefilterOnLoad Prefilters HDR texture to allow use of this texture as a PBR reflection texture.
      */
-    constructor(url: string, sceneOrEngine: Scene | ThinEngine, size: number, noMipmap = false, generateHarmonics = true, gammaSpace = false, reserved = false, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null) {
+    constructor(url: string, sceneOrEngine: Scene | ThinEngine, size: number, noMipmap = false, generateHarmonics = true, gammaSpace = false, prefilterOnLoad = false, onLoad: Nullable<() => void> = null, onError: Nullable<(message?: string, exception?: any) => void> = null) {
         super(sceneOrEngine);
 
         if (!url) {
@@ -128,6 +129,7 @@ export class HDRCubeTexture extends BaseTexture {
         this.hasAlpha = false;
         this.isCube = true;
         this._textureMatrix = Matrix.Identity();
+        this._prefilterOnLoad = prefilterOnLoad;
         this._onLoad = onLoad;
         this._onError = onError;
         this.gammaSpace = gammaSpace;
@@ -235,9 +237,18 @@ export class HDRCubeTexture extends BaseTexture {
                     results.push(dataFace);
                 }
             }
+
             return results;
         };
 
+        if (this._getEngine()!.webGLVersion >= 2 && this._prefilterOnLoad) {
+            const previousOnLoad = this._onLoad;
+            const hdrFiltering = new HDRFiltering(engine);
+            this._onLoad = () => {
+                hdrFiltering.prefilter(this, previousOnLoad);
+            };
+        }
+
         this._texture = engine.createRawCubeTextureFromUrl(this.url, this.getScene(), this._size,
             Constants.TEXTUREFORMAT_RGB,
             engine.getCaps().textureFloat ? Constants.TEXTURETYPE_FLOAT : Constants.TEXTURETYPE_UNSIGNED_INT,

+ 4 - 3
src/Materials/Textures/index.ts

@@ -4,20 +4,21 @@ export * from "./colorGradingTexture";
 export * from "./cubeTexture";
 export * from "./dynamicTexture";
 export * from "./equiRectangularCubeTexture";
+export * from "./Filtering/hdrFiltering";
 export * from "./hdrCubeTexture";
+export * from "./htmlElementTexture";
 export * from "./internalTexture";
 export * from "./internalTextureLoader";
 export * from "./Loaders/index";
 export * from "./mirrorTexture";
 export * from "./multiRenderTarget";
+export * from "./Packer/index";
 export * from "./Procedurals/index";
 export * from "./rawCubeTexture";
 export * from "./rawTexture";
-export * from "./rawTexture3D";
 export * from "./rawTexture2DArray";
+export * from "./rawTexture3D";
 export * from "./refractionTexture";
 export * from "./renderTargetTexture";
 export * from "./texture";
 export * from "./videoTexture";
-export * from "./htmlElementTexture";
-export * from "./Packer/index";

+ 54 - 6
src/Materials/effectRenderer.ts

@@ -90,6 +90,14 @@ export class EffectRenderer {
     }
 
     /**
+     * Restores engine states
+     */
+    public restoreStates(): void {
+        this.engine.depthCullingState.depthTest = true;
+        this.engine.stencilState.stencilTest = true;
+    }
+
+    /**
      * Draws a full screen quad.
      */
     public draw(): void {
@@ -108,7 +116,7 @@ export class EffectRenderer {
     public render(effectWrapper: EffectWrapper, outputTexture: Nullable<InternalTexture | RenderTargetTexture> = null) {
         // Ensure effect is ready
         if (!effectWrapper.effect.isReady()) {
-            return ;
+            return;
         }
 
         // Reset state
@@ -127,6 +135,8 @@ export class EffectRenderer {
         if (out) {
             this.engine.unBindFramebuffer(out);
         }
+
+        this.restoreStates();
     }
 
     /**
@@ -158,6 +168,10 @@ interface EffectWrapperCreationOptions {
      */
     fragmentShader: string;
     /**
+     * Use the shader store instead of direct source code
+     */
+    useShaderStore?: boolean;
+    /**
      * Vertex shader for the effect
      */
     vertexShader?: string;
@@ -174,6 +188,14 @@ interface EffectWrapperCreationOptions {
      */
     samplerNames?: Array<string>;
     /**
+      * Defines to use in the shader
+      */
+    defines?: Array<string>;
+    /**
+      * Callback when effect is compiled
+      */
+    onCompiled?: Nullable<(effect: Effect) => void>;
+    /**
      * The friendly name of the effect displayed in Spector.
      */
     name?: string;
@@ -199,6 +221,7 @@ export class EffectWrapper {
     constructor(creationOptions: EffectWrapperCreationOptions) {
         let effectCreationOptions: any;
         const uniformNames = creationOptions.uniformNames || [];
+
         if (creationOptions.vertexShader) {
             effectCreationOptions = {
                 fragmentSource: creationOptions.fragmentShader,
@@ -222,11 +245,36 @@ export class EffectWrapper {
             });
         }
 
-        this.effect = new Effect(effectCreationOptions,
-            creationOptions.attributeNames || ["position"],
-            uniformNames,
-            creationOptions.samplerNames,
-            creationOptions.engine);
+        const defines = creationOptions.defines ? creationOptions.defines.join("\n") : "";
+
+        if (creationOptions.useShaderStore) {
+            effectCreationOptions.fragment = effectCreationOptions.fragmentSource;
+            if (!effectCreationOptions.vertex) {
+                effectCreationOptions.vertex = effectCreationOptions.vertexSource;
+            }
+
+            delete effectCreationOptions.fragmentSource;
+            delete effectCreationOptions.vertexSource;
+
+            this.effect = creationOptions.engine.createEffect(effectCreationOptions.spectorName,
+                creationOptions.attributeNames || ["position"],
+                uniformNames,
+                creationOptions.samplerNames,
+                defines,
+                undefined,
+                creationOptions.onCompiled
+            );
+        } else {
+            this.effect = new Effect(effectCreationOptions,
+                creationOptions.attributeNames || ["position"],
+                uniformNames,
+                creationOptions.samplerNames,
+                creationOptions.engine,
+                defines,
+                undefined,
+                creationOptions.onCompiled,
+            );
+        }
     }
 
     /**

+ 7 - 0
src/Materials/standardMaterial.ts

@@ -120,6 +120,10 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr
     public ALPHATEST_AFTERALLALPHACOMPUTATIONS = false;
     public ALPHABLEND = true;
 
+    public RGBDLIGHTMAP = false;
+    public RGBDREFLECTION = false;
+    public RGBDREFRACTION = false;
+
     public IMAGEPROCESSING = false;
     public VIGNETTE = false;
     public VIGNETTEBLENDMODEMULTIPLY = false;
@@ -856,6 +860,7 @@ export class StandardMaterial extends PushMaterial {
                         defines.REFLECTIONOVERALPHA = this._useReflectionOverAlpha;
                         defines.INVERTCUBICMAP = (this._reflectionTexture.coordinatesMode === Texture.INVCUBIC_MODE);
                         defines.REFLECTIONMAP_3D = this._reflectionTexture.isCube;
+                        defines.RGBDREFLECTION = this._reflectionTexture.isRGBD;
 
                         switch (this._reflectionTexture.coordinatesMode) {
                             case Texture.EXPLICIT_MODE:
@@ -911,6 +916,7 @@ export class StandardMaterial extends PushMaterial {
                     } else {
                         MaterialHelper.PrepareDefinesForMergedUV(this._lightmapTexture, defines, "LIGHTMAP");
                         defines.USELIGHTMAPASSHADOWMAP = this._useLightmapAsShadowmap;
+                        defines.RGBDLIGHTMAP = this._lightmapTexture.isRGBD;
                     }
                 } else {
                     defines.LIGHTMAP = false;
@@ -951,6 +957,7 @@ export class StandardMaterial extends PushMaterial {
                         defines.REFRACTION = true;
 
                         defines.REFRACTIONMAP_3D = this._refractionTexture.isCube;
+                        defines.RGBDREFRACTION = this._refractionTexture.isRGBD;
                     }
                 } else {
                     defines.REFRACTION = false;

+ 2 - 1
src/Meshes/mesh.ts

@@ -1774,7 +1774,8 @@ export class Mesh extends AbstractMesh implements IGetSetVerticesData {
 
         var sideOrientation: Nullable<number>;
 
-        if (!instanceDataStorage.isFrozen && this._effectiveMaterial.backFaceCulling) {
+        if (!instanceDataStorage.isFrozen &&
+            (this._effectiveMaterial.backFaceCulling || this.overrideMaterialSideOrientation !== null)) {
             let mainDeterminant = effectiveMesh._getWorldMatrixDeterminant();
             sideOrientation = this.overrideMaterialSideOrientation;
             if (sideOrientation == null) {

+ 4 - 3
src/Particles/EmitterTypes/coneParticleEmitter.ts

@@ -218,8 +218,9 @@ export class ConeParticleEmitter implements IParticleEmitterType {
         this.radius = serializationObject.radius;
         this.angle = serializationObject.angle;
         this.directionRandomizer = serializationObject.directionRandomizer;
-        this.radiusRange = serializationObject.radiusRange;
-        this.heightRange = serializationObject.heightRange;
-        this.emitFromSpawnPointOnly = serializationObject.emitFromSpawnPointOnly;
+
+        this.radiusRange = serializationObject.radiusRange !== undefined ? serializationObject.radiusRange : 1;
+        this.heightRange = serializationObject.radiusRange !== undefined ? serializationObject.heightRange : 1;
+        this.emitFromSpawnPointOnly = serializationObject.emitFromSpawnPointOnly !== undefined ? serializationObject.emitFromSpawnPointOnly : false;
     }
 }

+ 13 - 1
src/Probes/reflectionProbe.ts

@@ -91,7 +91,19 @@ export class ReflectionProbe {
         }
         this._scene.reflectionProbes.push(this);
 
-        this._renderTargetTexture = new RenderTargetTexture(name, size, scene, generateMipMaps, true, useFloat ? Constants.TEXTURETYPE_FLOAT : Constants.TEXTURETYPE_UNSIGNED_INT, true);
+        let textureType = Constants.TEXTURETYPE_UNSIGNED_BYTE;
+        if (useFloat) {
+            const caps = this._scene.getEngine().getCaps();
+            if (caps.textureHalfFloatRender) {
+                textureType = Constants.TEXTURETYPE_HALF_FLOAT;
+            }
+            else if (caps.textureFloatRender) {
+                textureType = Constants.TEXTURETYPE_FLOAT;
+            }
+        }
+        this._renderTargetTexture = new RenderTargetTexture(name, size, scene, generateMipMaps, true, textureType, true);
+
+        this._renderTargetTexture.realTimeFiltering = true;
 
         this._renderTargetTexture.onBeforeRenderObservable.add((faceIndex: number) => {
             switch (faceIndex) {

+ 20 - 0
src/Rendering/boundingBoxRenderer.ts

@@ -18,6 +18,7 @@ import "../Shaders/color.fragment";
 import "../Shaders/color.vertex";
 import { DataBuffer } from '../Meshes/dataBuffer';
 import { Color3 } from '../Maths/math.color';
+import { Observable } from '../Misc/observable';
 
 declare module "../scene" {
     export interface Scene {
@@ -118,6 +119,17 @@ export class BoundingBoxRenderer implements ISceneComponent {
      * Defines if the renderer should show the back lines or not
      */
     public showBackLines = true;
+
+    /**
+     * Observable raised before rendering a bounding box
+     */
+    public onBeforeBoxRenderingObservable = new Observable<BoundingBox>();
+
+    /**
+     * Observable raised after rendering a bounding box
+     */
+    public onAfterBoxRenderingObservable = new Observable<BoundingBox>();
+
     /**
      * @hidden
      */
@@ -237,6 +249,9 @@ export class BoundingBoxRenderer implements ISceneComponent {
             if (boundingBox._tag !== renderingGroupId) {
                 continue;
             }
+
+            this.onBeforeBoxRenderingObservable.notifyObservers(boundingBox);
+
             var min = boundingBox.minimum;
             var max = boundingBox.maximum;
             var diff = max.subtract(min);
@@ -268,6 +283,8 @@ export class BoundingBoxRenderer implements ISceneComponent {
 
             // Draw order
             engine.drawElementsType(Material.LineListDrawMode, 0, 24);
+
+            this.onAfterBoxRenderingObservable.notifyObservers(boundingBox);
         }
         this._colorShader.unbind();
         engine.setDepthFunctionToLessOrEqual();
@@ -327,6 +344,9 @@ export class BoundingBoxRenderer implements ISceneComponent {
             return;
         }
 
+        this.onBeforeBoxRenderingObservable.clear();
+        this.onAfterBoxRenderingObservable.clear();
+
         this.renderList.dispose();
 
         this._colorShader.dispose();

+ 1 - 1
src/Rendering/geometryBufferRenderer.ts

@@ -216,7 +216,7 @@ export class GeometryBufferRenderer {
         // Alpha test
         if (material) {
             let needUv = false;
-            if (material.needAlphaBlending()) {
+            if (material.needAlphaTesting()) {
                 defines.push("#define ALPHATEST");
                 needUv = true;
             }

+ 224 - 0
src/Shaders/ShadersInclude/hdrFilteringFunctions.fx

@@ -0,0 +1,224 @@
+#if defined(WEBGL2) && defined(NUM_SAMPLES)
+	#if NUM_SAMPLES > 0u
+		uniform vec2 vFilteringInfo;
+		uniform float linearRoughness;
+
+		const float NUM_SAMPLES_FLOAT = float(NUM_SAMPLES);
+		const float NUM_SAMPLES_FLOAT_INVERSED = 1. / NUM_SAMPLES_FLOAT;
+
+		const float K = 4.;
+
+		//
+		//
+		// Importance sampling GGX - Trowbridge-Reitz
+		// ------------------------------------------
+		//
+		// Important samples are chosen to integrate Dggx() * cos(theta) over the hemisphere.
+		//
+		// All calculations are made in tangent space, with n = [0 0 1]
+		//
+		//             l        h (important sample)
+		//             .\      /.
+		//             . \    / .
+		//             .  \  /  .
+		//             .   \/   .
+		//         ----+---o----+-------> n [0 0 1]
+		//     cos(2*theta)     cos(theta)
+		//        = n•l            = n•h
+		//
+		//  v = n
+		//  f0 = f90 = 1
+		//  V = 1
+		//
+		//  h is micro facet's normal
+		//
+		//  l is the reflection of v (i.e.: n) around h  ==>  n•h = l•h = v•h
+		//
+		//  h = important_sample_ggx()
+		//
+		//  n•h = [0 0 1]•h = h.z
+		//
+		//  l = reflect(-n, h)
+		//    = 2 * (n•h) * h - n;
+		//
+		//  n•l = cos(2 * theta)
+		//      = cos(theta)^2 - sin(theta)^2
+		//      = (n•h)^2 - (1 - (n•h)^2)
+		//      = 2(n•h)^2 - 1
+		//
+		//
+		//  pdf() = D(h) <n•h> |J(h)|
+		//
+		//               1
+		//  |J(h)| = ----------
+		//            4 <v•h>
+		//
+		//    v = n -> <v•h>/<n•h> = 1
+		//
+		//  pdf() = D(h) / 4
+		//
+		//
+		// Pre-filtered importance sampling
+		// --------------------------------
+		//
+		//  see: "Real-time Shading with Filtered Importance Sampling", Jaroslav Krivanek
+		//  see: "GPU-Based Importance Sampling, GPU Gems 3", Mark Colbert
+		//
+		//
+		//                   Ωs
+		//     lod = log4(K ----)
+		//                   Ωp
+		//
+		//     log4(K) = 1, works well for box filters
+		//     K = 4
+		//
+		//             1
+		//     Ωs = ---------, solid-angle of an important sample
+		//           N * pdf
+		//
+		//              4 PI
+		//     Ωp ~ --------------, solid-angle of a sample in the base cubemap
+		//           texel_count
+		//
+		//
+		// Evaluating the integral
+		// -----------------------
+		//
+		//                    K     fr(h)
+		//            Er() = --- ∑ ------- L(h) <n•l>
+		//                    N  h   pdf
+		//
+		// with:
+		//
+		//            fr() = D(h)
+		//
+		//                       N
+		//            K = -----------------
+		//                    fr(h)
+		//                 ∑ ------- <n•l>
+		//                 h   pdf
+		//
+		//
+		//  It results that:
+		//
+		//            K           4 <v•h>
+		//    Er() = --- ∑ D(h) ------------ L(h) <n•l>
+		//            N  h        D(h) <n•h>
+		//
+		//    v = n -> <v•h>/<n•h> = 1
+		//
+		//              K
+		//    Er() = 4 --- ∑ L(h) <n•l>
+		//              N  h
+		//
+		//                  N       4
+		//    Er() = ------------- --- ∑ V(v) <n•l>
+		//             4 ∑ <n•l>    N
+		//
+		//
+		//  +------------------------------+
+		//  |          ∑ <n•l> L(h)        |
+		//  |  Er() = --------------       |
+		//  |            ∑ <n•l>           |
+		//  +------------------------------+
+		//
+		//
+
+		vec3 irradiance(samplerCube inputTexture, vec3 n) {
+		    vec3 result = vec3(0.0);
+			vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
+			tangent = normalize(cross(tangent, n));
+			vec3 bitangent = cross(n, tangent);
+			mat3 tbn = mat3(tangent, bitangent, n);
+
+		    float maxLevel = vFilteringInfo.y;
+		    float dim0 = vFilteringInfo.x;
+		    float omegaP = (4. * PI) / (6. * dim0 * dim0);
+
+		    for(uint i = 0u; i < NUM_SAMPLES; ++i)
+		    {
+		        vec2 Xi = hammersley(i, NUM_SAMPLES);
+		        vec3 Ls = hemisphereCosSample(Xi);
+
+		        Ls = normalize(Ls);
+
+		        vec3 Ns = vec3(0., 0., 1.);
+
+		        float NoL = dot(Ns, Ls);
+
+		        if (NoL > 0.) {
+		            float pdf_inversed = PI / NoL;
+
+		            float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
+		            float l = log4(omegaS) - log4(omegaP) + log4(K);
+		            float mipLevel = clamp(l, 0.0, maxLevel);
+
+		            vec3 c = textureCubeLodEXT(inputTexture, tbn * Ls, mipLevel).rgb;
+		            #ifdef GAMMA_INPUT
+		                c = toLinearSpace(c);
+		            #endif
+		            result += c;
+		        }
+		    }
+
+		    result = result * NUM_SAMPLES_FLOAT_INVERSED;
+
+		    return result;
+		}
+
+		vec3 radiance(samplerCube inputTexture, vec3 n) {
+			if (linearRoughness == 0.) {
+				vec3 c = textureCube(inputTexture, n).rgb;
+				#ifdef GAMMA_INPUT
+				    c = toLinearSpace(c);
+				#endif
+				return c;
+			}
+
+			vec3 result = vec3(0.);
+			vec3 tangent = abs(n.z) < 0.999 ? vec3(0., 0., 1.) : vec3(1., 0., 0.);
+			tangent = normalize(cross(tangent, n));
+			vec3 bitangent = cross(n, tangent);
+			mat3 tbn = mat3(tangent, bitangent, n);
+
+			float maxLevel = vFilteringInfo.y;
+			float dim0 = vFilteringInfo.x;
+			float omegaP = (4. * PI) / (6. * dim0 * dim0);
+
+			float weight = 0.;
+			for(uint i = 0u; i < NUM_SAMPLES; ++i)
+			{
+			    vec2 Xi = hammersley(i, NUM_SAMPLES);
+			    vec3 H = hemisphereImportanceSampleDggx(Xi, linearRoughness);
+
+			    float NoV = 1.;
+			    float NoH = H.z;
+			    float NoH2 = H.z * H.z;
+			    float NoL = 2. * NoH2 - 1.;
+			    vec3 L = vec3(2. * NoH * H.x, 2. * NoH * H.y, NoL);
+			    L = normalize(L);
+
+			    if (NoL > 0.) {
+			        float pdf_inversed = 4. / normalDistributionFunction_TrowbridgeReitzGGX(NoH, linearRoughness);
+
+			        float omegaS = NUM_SAMPLES_FLOAT_INVERSED * pdf_inversed;
+			        float l = log4(omegaS) - log4(omegaP) + log4(K);
+			        float mipLevel = clamp(float(l), 0.0, maxLevel);
+
+			        weight += NoL;
+
+			        vec3 c = textureCubeLodEXT(inputTexture, tbn * L, mipLevel).rgb;
+			        #ifdef GAMMA_INPUT
+			            c = toLinearSpace(c);
+			        #endif
+			        result += c * NoL;
+			    }
+			}
+
+			result = result / weight;
+
+			return result;
+		}
+
+	#endif
+#endif

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

@@ -73,6 +73,54 @@ float square(float value)
     return value * value;
 }
 
+#ifdef WEBGL2
+    // https://learnopengl.com/PBR/IBL/Specular-IBL
+    // Hammersley
+    float radicalInverse_VdC(uint bits) 
+    {
+        bits = (bits << 16u) | (bits >> 16u);
+        bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
+        bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
+        bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
+        bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
+        return float(bits) * 2.3283064365386963e-10; // / 0x100000000
+    }
+
+    vec2 hammersley(uint i, uint N)
+    {
+        return vec2(float(i)/float(N), radicalInverse_VdC(i));
+    }
+#else
+    // float vanDerCorpus(uint n, uint base)
+    // {
+    //     float invBase = 1.0 / float(base);
+    //     float denom   = 1.0;
+    //     float result  = 0.0;
+
+    //     for(uint i = 0u; i < 32u; ++i)
+    //     {
+    //         if(n > 0u)
+    //         {
+    //             denom   = mod(float(n), 2.0);
+    //             result += denom * invBase;
+    //             invBase = invBase / 2.0;
+    //             n       = uint(float(n) / 2.0);
+    //         }
+    //     }
+
+    //     return result;
+    // }
+
+    // vec2 hammersley(uint i, uint N)
+    // {
+    //     return vec2(float(i)/float(N), vanDerCorpus(i, 2u));
+    // }
+#endif
+
+float log4(float x) {
+    return log2(x) / 2.;
+}
+
 float pow5(float value) {
     float sq = value * value;
     return sq * sq * value;

+ 155 - 0
src/Shaders/ShadersInclude/importanceSampling.fx

@@ -0,0 +1,155 @@
+// https://www.tobias-franke.eu/log/2014/03/30/notes_on_importance_sampling.html
+//
+// Importance sampling
+// -------------------
+//
+// Important samples are chosen to integrate cos(theta) over the hemisphere.
+//
+// All calculations are made in tangent space, with n = [0 0 1]
+//
+//                      l (important sample)
+//                     /.
+//                    / .
+//                   /  .
+//                  /   .
+//         --------o----+-------> n (direction)
+//                   cos(theta)
+//                    = n•l
+//
+//
+//  'direction' is given as an input parameter, and serves as tge z direction of the tangent space.
+//
+//  l = important_sample_cos()
+//
+//  n•l = [0 0 1] • l = l.z
+//
+//           n•l
+//  pdf() = -----
+//           PI
+//
+vec3 hemisphereCosSample(vec2 u) {
+    // pdf = cosTheta / M_PI;
+    float phi = 2. * PI * u.x;
+
+    float cosTheta2 = 1. - u.y;
+    float cosTheta = sqrt(cosTheta2);
+    float sinTheta = sqrt(1. - cosTheta2);
+
+    return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+}
+
+// https://www.tobias-franke.eu/log/2014/03/30/notes_on_importance_sampling.html
+//
+//
+// Importance sampling GGX - Trowbridge-Reitz
+// ------------------------------------------
+//
+// Important samples are chosen to integrate Dggx() * cos(theta) over the hemisphere.
+//
+// All calculations are made in tangent space, with n = [0 0 1]
+//
+//                      h (important sample)
+//                     /.
+//                    / .
+//                   /  .
+//                  /   .
+//         --------o----+-------> n
+//                   cos(theta)
+//                    = n•h
+//
+//  h is micro facet's normal
+//  l is the reflection of v around h, l = reflect(-v, h)  ==>  v•h = l•h
+//
+//  n•v is given as an input parameter at runtime
+//
+//  Since n = [0 0 1], we also have v.z = n•v
+//
+//  Since we need to compute v•h, we chose v as below. This choice only affects the
+//  computation of v•h (and therefore the fresnel term too), but doesn't affect
+//  n•l, which only relies on l.z (which itself only relies on v.z, i.e.: n•v)
+//
+//      | sqrt(1 - (n•v)^2)     (sin)
+//  v = | 0
+//      | n•v                   (cos)
+//
+//
+//  h = important_sample_ggx()
+//
+vec3 hemisphereImportanceSampleDggx(vec2 u, float a) {
+    // pdf = D(a) * cosTheta
+    float phi = 2. * PI * u.x;
+
+    // NOTE: (aa-1) == (a-1)(a+1) produces better fp accuracy
+    float cosTheta2 = (1. - u.y) / (1. + (a + 1.) * ((a - 1.) * u.y));
+    float cosTheta = sqrt(cosTheta2);
+    float sinTheta = sqrt(1. - cosTheta2);
+
+    return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+}
+
+//
+//
+// Importance sampling Charlie
+// ---------------------------
+//
+// In order to pick the most significative samples and increase the convergence rate, we chose to
+// rely on Charlie's distribution function for the pdf as we do in hemisphereImportanceSampleDggx.
+//
+// To determine the direction we then need to resolve the cdf associated to the chosen pdf for random inputs.
+//
+// Knowing pdf() = DCharlie(h) <n•h>
+//
+// We need to find the cdf:
+//
+// / 2pi     / pi/2
+// |         |  (2 + (1 / a)) * sin(theta) ^ (1 / a) * cos(theta) * sin(theta)
+// / phi=0   / theta=0
+//
+// We sample theta and phi independently.
+//
+// 1. as in all the other isotropic cases phi = 2 * pi * epsilon
+//    (https://www.tobias-franke.eu/log/2014/03/30/notes_on_importance_sampling.html)
+//
+// 2. we need to solve the integral on theta:
+//
+//             / sTheta
+// P(sTheta) = |  (2 + (1 / a)) * sin(theta) ^ (1 / a + 1) * cos(theta) * dtheta
+//             / theta=0
+//
+// By subsitution of u = sin(theta) and du = cos(theta) * dtheta
+//
+// /
+// |  (2 + (1 / a)) * u ^ (1 / a + 1) * du
+// /
+//
+// = (2 + (1 / a)) * u ^ (1 / a + 2) / (1 / a + 2)
+//
+// = u ^ (1 / a + 2)
+//
+// = sin(theta) ^ (1 / a + 2)
+//
+//             +-                          -+ sTheta
+// P(sTheta) = |  sin(theta) ^ (1 / a + 2)  |
+//             +-                          -+ 0
+//
+// P(sTheta) = sin(sTheta) ^ (1 / a + 2)
+//
+// We now need to resolve the cdf for an epsilon value:
+//
+// epsilon = sin(theta) ^ (a / ( 2 * a + 1))
+//
+//  +--------------------------------------------+
+//  |                                            |
+//  |  sin(theta) = epsilon ^ (a / ( 2 * a + 1)) |
+//  |                                            |
+//  +--------------------------------------------+
+//
+vec3 hemisphereImportanceSampleDCharlie(vec2 u, float a) { 
+    // pdf = DistributionCharlie() * cosTheta
+    float phi = 2. * PI * u.x;
+
+    float sinTheta = pow(u.y, a / (2. * a + 1.));
+    float cosTheta = sqrt(1. - sinTheta * sinTheta);
+
+    return vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
+}

+ 19 - 6
src/Shaders/ShadersInclude/pbrBRDFFunctions.fx

@@ -29,6 +29,16 @@
         return brdfLookup.rgb;
     }
 
+    vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 specularEnvironmentR90, const vec3 environmentBrdf) {
+        #ifdef BRDF_V_HEIGHT_CORRELATED
+            vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.x + specularEnvironmentR0 * environmentBrdf.y;
+            // Simplification if F90 = 1 vec3 reflectance = (specularEnvironmentR90 - specularEnvironmentR0) * environmentBrdf.xxx + specularEnvironmentR0 * environmentBrdf.yyy;
+        #else
+            vec3 reflectance = specularEnvironmentR0 * environmentBrdf.x + specularEnvironmentR90 * environmentBrdf.y;
+        #endif
+        return reflectance;
+    }
+
     vec3 getReflectanceFromBRDFLookup(const vec3 specularEnvironmentR0, const vec3 environmentBrdf) {
         #ifdef BRDF_V_HEIGHT_CORRELATED
             vec3 reflectance = mix(environmentBrdf.xxx, environmentBrdf.yyy, specularEnvironmentR0);
@@ -76,18 +86,21 @@ float getBRDFLookupCharlieSheen(float NdotV, float perceptualRoughness)
 //                              Schlick/Fresnel
 // ______________________________________________________________________
 
+// iorI incident iorT transmitted
+
 // Schlick's approximation for R0 (Fresnel Reflectance Values)
 // Keep for references
-// vec3 getR0fromAirToSurfaceIOR(vec3 ior1) {
-//     return getR0fromIOR(ior1, vec3(1.0));
-// }
 
-// vec3 getR0fromIOR(vec3 ior1, vec3 ior2) {
-//     vec3 t = (ior1 - ior2) / (ior1 + ior2);
+// vec3 getR0fromIORs(vec3 iorT, vec3 iorI) { 
+//     vec3 t = (iorT - iorI) / (iorT + iorI);
 //     return t * t;
 // }
 
-// vec3 getIORfromAirToSurfaceR0(vec3 f0) {
+// vec3 getR0fromAirToSurfaceIORT(vec3 iorT) {
+//     return getR0fromIOR(iorT, vec3(1.0));
+// }
+
+// vec3 getIORTfromAirToSurfaceR0(vec3 f0) {
 //     vec3 s = sqrt(f0);
 //     return (1.0 + s) / (1.0 - s);
 // }

+ 1 - 1
src/Shaders/ShadersInclude/pbrBlockReflectance.fx

@@ -1,5 +1,5 @@
 #if defined(ENVIRONMENTBRDF) && !defined(REFLECTIONMAP_SKYBOX)
-    vec3 specularEnvironmentReflectance = getReflectanceFromBRDFLookup(clearcoatOut.specularEnvironmentR0, environmentBrdf);
+    vec3 specularEnvironmentReflectance = getReflectanceFromBRDFLookup(clearcoatOut.specularEnvironmentR0, specularEnvironmentR90, environmentBrdf);
 
     #ifdef RADIANCEOCCLUSION
         specularEnvironmentReflectance *= seo;

+ 12 - 2
src/Shaders/ShadersInclude/pbrBlockReflectance0.fx

@@ -1,4 +1,14 @@
 float reflectance = max(max(reflectivityOut.surfaceReflectivityColor.r, reflectivityOut.surfaceReflectivityColor.g), reflectivityOut.surfaceReflectivityColor.b);
-float reflectance90 = fresnelGrazingReflectance(reflectance);
 vec3 specularEnvironmentR0 = reflectivityOut.surfaceReflectivityColor.rgb;
-vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0) * reflectance90;
+
+#ifdef METALLICWORKFLOW
+    vec3 specularEnvironmentR90 = vec3(metallicReflectanceFactors.a);
+#else 
+    vec3 specularEnvironmentR90 = vec3(1.0, 1.0, 1.0);
+#endif
+
+// Back Compat
+#ifdef ALPHAFRESNEL
+    float reflectance90 = fresnelGrazingReflectance(reflectance);
+    specularEnvironmentR90 = specularEnvironmentR90 * reflectance90;
+#endif

+ 6 - 3
src/Shaders/ShadersInclude/pbrBlockReflection.fx

@@ -254,8 +254,12 @@
                     irradianceVector.z *= -1.0;
                 #endif
 
-                environmentIrradiance = computeEnvironmentIrradiance(irradianceVector);
-
+                #if defined(WEBGL2) && defined(REALTIME_FILTERING)
+                    environmentIrradiance = irradiance(reflectionSampler, irradianceVector);
+                #else
+                    environmentIrradiance = computeEnvironmentIrradiance(irradianceVector);
+                #endif
+                
                 #ifdef SS_TRANSLUCENCY
                     outParams.irradianceVector = irradianceVector;
                 #endif
@@ -273,7 +277,6 @@
         #endif
 
         environmentIrradiance *= vReflectionColor.rgb;
-
         outParams.environmentRadiance = environmentRadiance;
         outParams.environmentIrradiance = environmentIrradiance;
         outParams.reflectionCoords = reflectionCoords;

+ 4 - 8
src/Shaders/ShadersInclude/pbrBlockReflectivity.fx

@@ -21,6 +21,7 @@ void reflectivityBlock(
     const in vec4 vReflectivityColor,
 #ifdef METALLICWORKFLOW
     const in vec3 surfaceAlbedo,
+    const in vec4 metallicReflectanceFactors,
 #endif
 #ifdef REFLECTIVITY
     const in vec3 vReflectivityInfos,
@@ -82,7 +83,7 @@ void reflectivityBlock(
         // Diffuse is used as the base of the reflectivity.
         vec3 baseColor = surfaceAlbedo;
 
-        #ifdef REFLECTANCE
+        #ifdef FROSTBITE_REFLECTANCE
             // *** NOT USED ANYMORE ***
             // Following Frostbite Remapping,
             // https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf page 115
@@ -95,19 +96,14 @@ void reflectivityBlock(
             // Compute the converted reflectivity.
             surfaceReflectivityColor = mix(0.16 * reflectance * reflectance, baseColor, metallicRoughness.r);
         #else
-            vec3 metallicF0 = vec3(vReflectivityColor.a, vReflectivityColor.a, vReflectivityColor.a);
-            #ifdef METALLICF0FACTORFROMMETALLICMAP
-                #ifdef REFLECTIVITY
-                    metallicF0 *= surfaceMetallicOrReflectivityColorMap.a;
-                #endif
-            #endif
+            vec3 metallicF0 = metallicReflectanceFactors.rgb;
 
             #if DEBUGMODE > 0
                 outParams.metallicF0 = metallicF0;
             #endif
 
             // Compute the converted diffuse.
-            outParams.surfaceAlbedo = mix(baseColor.rgb * (1.0 - metallicF0.r), vec3(0., 0., 0.), metallicRoughness.r);
+            outParams.surfaceAlbedo = mix(baseColor.rgb * (1.0 - metallicF0), vec3(0., 0., 0.), metallicRoughness.r);
 
             // Compute the converted reflectivity.
             surfaceReflectivityColor = mix(metallicF0, baseColor, metallicRoughness.r);

+ 1 - 0
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -5,6 +5,7 @@ uniform vec4 vAlbedoColor;
 uniform vec4 vLightingIntensity;
 
 uniform vec4 vReflectivityColor;
+uniform vec4 vMetallicReflectanceFactors;
 uniform vec3 vEmissiveColor;
 
 uniform float visibility;

+ 16 - 1
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -75,6 +75,17 @@
     uniform sampler2D microSurfaceSampler;
 #endif
 
+#ifdef METALLIC_REFLECTANCE
+    #if METALLIC_REFLECTANCEDIRECTUV == 1
+        #define vMetallicReflectanceUV vMainUV1
+    #elif METALLIC_REFLECTANCEDIRECTUV == 2
+        #define vMetallicReflectanceUV vMainUV2
+    #else
+        varying vec2 vMetallicReflectanceUV;
+    #endif
+    uniform sampler2D metallicReflectanceSampler;
+#endif
+
 #ifdef CLEARCOAT
     #ifdef CLEARCOAT_TEXTURE
         #if CLEARCOAT_TEXTUREDIRECTUV == 1
@@ -144,7 +155,11 @@
         uniform samplerCube reflectionSampler;
         
         #ifdef LODBASEDMICROSFURACE
-            #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
+            #if defined(WEBGL2) && defined(REALTIME_FILTERING)
+                #define sampleReflectionLod(s, c, l) vec4(radiance(s, c), 1.0)
+            #else
+                #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
+            #endif
         #else
             uniform samplerCube reflectionSamplerLow;
             uniform samplerCube reflectionSamplerHigh;

+ 4 - 0
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -33,6 +33,10 @@ uniform Material
 
     uniform float visibility;
 
+    uniform vec4 vMetallicReflectanceFactors;
+    uniform vec2 vMetallicReflectanceInfos;
+    uniform mat4 metallicReflectanceMatrix;
+
     uniform vec2 vClearCoatParams;
     uniform vec4 vClearCoatRefractionParams;
     uniform vec2 vClearCoatInfos;

+ 5 - 0
src/Shaders/ShadersInclude/pbrVertexDeclaration.fx

@@ -31,6 +31,11 @@ uniform vec3 vReflectivityInfos;
 uniform mat4 reflectivityMatrix;
 #endif
 
+#ifdef METALLIC_REFLECTANCE
+uniform vec2 vMetallicReflectanceInfos;
+uniform mat4 metallicReflectanceMatrix;
+#endif
+
 #ifdef MICROSURFACEMAP
 uniform vec2 vMicroSurfaceSamplerInfos;
 uniform mat4 microSurfaceSamplerMatrix;

+ 31 - 21
src/Shaders/default.fragment.fx

@@ -254,13 +254,17 @@ void main(void) {
 	float shadow = 1.;
 
 #ifdef LIGHTMAP
-	vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb * vLightmapInfos.y;
+	vec4 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset);
+    #ifdef RGBDLIGHTMAP
+        lightmapColor.rgb = fromRGBD(lightmapColor);
+    #endif
+	lightmapColor.rgb *= vLightmapInfos.y
 #endif
 
 #include<lightFragment>[0..maxSimultaneousLights]
 
 	// Refraction
-	vec3 refractionColor = vec3(0., 0., 0.);
+	vec4 refractionColor = vec4(0., 0., 0., 1.);
 
 #ifdef REFRACTION
 	vec3 refractionVector = normalize(refract(-viewDirectionW, normalW, vRefractionInfos.y));
@@ -268,7 +272,7 @@ void main(void) {
 		refractionVector.y = refractionVector.y * vRefractionInfos.w;
 
 		if (dot(refractionVector, viewDirectionW) < 1.0) {
-			refractionColor = textureCube(refractionCubeSampler, refractionVector).rgb;
+			refractionColor = textureCube(refractionCubeSampler, refractionVector);
 		}
 	#else
 		vec3 vRefractionUVW = vec3(refractionMatrix * (view * vec4(vPositionW + refractionVector * vRefractionInfos.z, 1.0)));
@@ -277,16 +281,19 @@ void main(void) {
 
 		refractionCoords.y = 1.0 - refractionCoords.y;
 		
-		refractionColor = texture2D(refraction2DSampler, refractionCoords).rgb;
+		refractionColor = texture2D(refraction2DSampler, refractionCoords);
 	#endif
+    #ifdef RGBDREFRACTION
+        refractionColor.rgb = fromRGBD(refractionColor);
+    #endif
 	#ifdef IS_REFRACTION_LINEAR
-		refractionColor = toGammaSpace(refractionColor);
+		refractionColor.rgb = toGammaSpace(refractionColor.rgb);
 	#endif
-	refractionColor *= vRefractionInfos.x;
+	refractionColor.rgb *= vRefractionInfos.x;
 #endif
 
 // Reflection
-vec3 reflectionColor = vec3(0., 0., 0.);
+vec4 reflectionColor = vec4(0., 0., 0., 1.);
 
 #ifdef REFLECTION
 	vec3 vReflectionUVW = computeReflectionCoords(vec4(vPositionW, 1.0), normalW);
@@ -303,9 +310,9 @@ vec3 reflectionColor = vec3(0., 0., 0.);
 				#endif
 			#endif
 
-			reflectionColor = textureCube(reflectionCubeSampler, vReflectionUVW, bias).rgb;
+			reflectionColor = textureCube(reflectionCubeSampler, vReflectionUVW, bias);
 		#else
-			reflectionColor = textureCube(reflectionCubeSampler, vReflectionUVW).rgb;
+			reflectionColor = textureCube(reflectionCubeSampler, vReflectionUVW);
 		#endif
 	#else
 		vec2 coords = vReflectionUVW.xy;
@@ -315,23 +322,26 @@ vec3 reflectionColor = vec3(0., 0., 0.);
 		#endif
 
 		coords.y = 1.0 - coords.y;
-		reflectionColor = texture2D(reflection2DSampler, coords).rgb;
+		reflectionColor = texture2D(reflection2DSampler, coords);
 	#endif
+    #ifdef RGBDREFLECTION
+        reflectionColor.rgb = fromRGBD(reflectionColor);
+    #endif
 	#ifdef IS_REFLECTION_LINEAR
-		reflectionColor = toGammaSpace(reflectionColor);
+		reflectionColor.rgb = toGammaSpace(reflectionColor.rgb);
 	#endif
-	reflectionColor *= vReflectionInfos.x;
+	reflectionColor.rgb *= vReflectionInfos.x;
 	#ifdef REFLECTIONFRESNEL
 		float reflectionFresnelTerm = computeFresnelTerm(viewDirectionW, normalW, reflectionRightColor.a, reflectionLeftColor.a);
 
 		#ifdef REFLECTIONFRESNELFROMSPECULAR
 			#ifdef SPECULARTERM
-				reflectionColor *= specularColor.rgb * (1.0 - reflectionFresnelTerm) + reflectionFresnelTerm * reflectionRightColor.rgb;
+				reflectionColor.rgb *= specularColor.rgb * (1.0 - reflectionFresnelTerm) + reflectionFresnelTerm * reflectionRightColor.rgb;
 			#else
-				reflectionColor *= reflectionLeftColor.rgb * (1.0 - reflectionFresnelTerm) + reflectionFresnelTerm * reflectionRightColor.rgb;
+				reflectionColor.rgb *= reflectionLeftColor.rgb * (1.0 - reflectionFresnelTerm) + reflectionFresnelTerm * reflectionRightColor.rgb;
 			#endif
 		#else
-			reflectionColor *= reflectionLeftColor.rgb * (1.0 - reflectionFresnelTerm) + reflectionFresnelTerm * reflectionRightColor.rgb;
+			reflectionColor.rgb *= reflectionLeftColor.rgb * (1.0 - reflectionFresnelTerm) + reflectionFresnelTerm * reflectionRightColor.rgb;
 		#endif
 	#endif
 #endif
@@ -339,7 +349,7 @@ vec3 reflectionColor = vec3(0., 0., 0.);
 #ifdef REFRACTIONFRESNEL
 	float refractionFresnelTerm = computeFresnelTerm(viewDirectionW, normalW, refractionRightColor.a, refractionLeftColor.a);
 
-	refractionColor *= refractionLeftColor.rgb * (1.0 - refractionFresnelTerm) + refractionFresnelTerm * refractionRightColor.rgb;
+	refractionColor.rgb *= refractionLeftColor.rgb * (1.0 - refractionFresnelTerm) + refractionFresnelTerm * refractionRightColor.rgb;
 #endif
 
 #ifdef OPACITY
@@ -415,23 +425,23 @@ vec3 reflectionColor = vec3(0., 0., 0.);
 #endif
 
 #ifdef REFLECTIONOVERALPHA
-	alpha = clamp(alpha + dot(reflectionColor, vec3(0.3, 0.59, 0.11)), 0., 1.);
+	alpha = clamp(alpha + dot(reflectionColor.rgb, vec3(0.3, 0.59, 0.11)), 0., 1.);
 #endif
 
 	// Composition
 #ifdef EMISSIVEASILLUMINATION
-	vec4 color = vec4(clamp(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor + emissiveColor + refractionColor, 0.0, 1.0), alpha);
+	vec4 color = vec4(clamp(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor.rgb + emissiveColor + refractionColor.rgb, 0.0, 1.0), alpha);
 #else
-	vec4 color = vec4(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor + refractionColor, alpha);
+	vec4 color = vec4(finalDiffuse * baseAmbientColor + finalSpecular + reflectionColor.rgb + refractionColor.rgb, alpha);
 #endif
 
 //Old lightmap calculation method
 #ifdef LIGHTMAP
     #ifndef LIGHTMAPEXCLUDED
         #ifdef USELIGHTMAPASSHADOWMAP
-            color.rgb *= lightmapColor;
+            color.rgb *= lightmapColor.rgb;
         #else
-            color.rgb += lightmapColor;
+            color.rgb += lightmapColor.rgb;
         #endif
     #endif
 #endif

+ 15 - 0
src/Shaders/hdrFiltering.fragment.fx

@@ -0,0 +1,15 @@
+#include<helperFunctions>
+#include<importanceSampling>
+#include<pbrBRDFFunctions>
+#include<hdrFilteringFunctions>
+
+uniform samplerCube inputTexture;
+uniform float hdrScale;
+
+varying vec3 direction;
+
+void main() {
+    vec3 color = radiance(inputTexture, direction);
+
+    gl_FragColor = vec4(color * hdrScale, 1.0);
+}

+ 16 - 0
src/Shaders/hdrFiltering.vertex.fx

@@ -0,0 +1,16 @@
+// Attributes
+attribute vec2 position;
+
+// Output
+varying vec3 direction;
+
+// Uniforms
+uniform vec3 up;
+uniform vec3 right;
+uniform vec3 front;
+
+void main(void) {	
+	mat3 view = mat3(up, right, front);
+	direction = view * vec3(position, 1.0);
+	gl_Position = vec4(position, 0.0, 1.0);
+}

+ 13 - 0
src/Shaders/pbr.fragment.fx

@@ -31,6 +31,7 @@ precision highp float;
 
 // Helper Functions
 #include<helperFunctions>
+#include<importanceSampling>
 #include<pbrHelperFunctions>
 #include<imageProcessingFunctions>
 #include<shadowsFragmentFunctions>
@@ -38,6 +39,7 @@ precision highp float;
 #include<pbrDirectLightingSetupFunctions>
 #include<pbrDirectLightingFalloffFunctions>
 #include<pbrBRDFFunctions>
+#include<hdrFilteringFunctions>
 #include<pbrDirectLightingFunctions>
 #include<pbrIBLFunctions>
 #include<bumpFragmentMainFunctions>
@@ -142,10 +144,21 @@ void main(void) {
     vec4 microSurfaceTexel = texture2D(microSurfaceSampler, vMicroSurfaceSamplerUV + uvOffset) * vMicroSurfaceSamplerInfos.y;
 #endif
 
+#ifdef METALLICWORKFLOW
+    vec4 metallicReflectanceFactors = vMetallicReflectanceFactors;
+    #ifdef METALLIC_REFLECTANCE
+        vec4 metallicReflectanceFactorsMap = texture2D(metallicReflectanceSampler, vMetallicReflectanceUV + uvOffset);
+        metallicReflectanceFactorsMap = toLinearSpace(metallicReflectanceFactorsMap);
+
+        metallicReflectanceFactors *= metallicReflectanceFactorsMap;
+    #endif
+#endif
+
     reflectivityBlock(
         vReflectivityColor,
     #ifdef METALLICWORKFLOW
         surfaceAlbedo,
+        metallicReflectanceFactors,
     #endif
     #ifdef REFLECTIVITY
         vReflectivityInfos,

+ 15 - 0
src/Shaders/pbr.vertex.fx

@@ -62,6 +62,10 @@ varying vec2 vReflectivityUV;
 varying vec2 vMicroSurfaceSamplerUV;
 #endif
 
+#if defined(METALLIC_REFLECTANCE) && METALLIC_REFLECTANCEDIRECTUV == 0
+varying vec2 vMetallicReflectanceUV;
+#endif
+
 #if defined(BUMP) && BUMPDIRECTUV == 0
 varying vec2 vBumpUV;
 #endif
@@ -297,6 +301,17 @@ void main(void) {
     }
 #endif
 
+#if defined(METALLIC_REFLECTANCE) && METALLIC_REFLECTANCEDIRECTUV == 0 
+    if (vMetallicReflectanceInfos.x == 0.)
+    {
+        vMetallicReflectanceUV = vec2(metallicReflectanceMatrix * vec4(uvUpdated, 1.0, 0.0));
+    }
+    else
+    {
+        vMetallicReflectanceUV = vec2(metallicReflectanceMatrix * vec4(uv2, 1.0, 0.0));
+    }
+#endif
+
 #if defined(BUMP) && BUMPDIRECTUV == 0 
     if (vBumpInfos.x == 0.)
     {

+ 64 - 5
src/Sprites/sprite.ts

@@ -4,12 +4,14 @@ import { ActionManager } from "../Actions/actionManager";
 import { ISpriteManager } from "./spriteManager";
 import { Color4 } from '../Maths/math.color';
 import { Observable } from '../Misc/observable';
+import { IAnimatable } from '../Animations/animatable.interface';
+declare type Animation = import("../Animations/animation").Animation;
 
 /**
  * Class used to represent a sprite
  * @see http://doc.babylonjs.com/babylon101/sprites
  */
-export class Sprite {
+export class Sprite implements IAnimatable {
     /** Gets or sets the current world position */
     public position: Vector3;
     /** Gets or sets the main color */
@@ -25,13 +27,13 @@ export class Sprite {
     /** Gets or sets the cell reference in the sprite sheet, uses sprite's filename when added to sprite sheet */
     public cellRef: string;
     /** Gets or sets a boolean indicating if UV coordinates should be inverted in U axis */
-    public invertU = 0;
+    public invertU = false;
     /** Gets or sets a boolean indicating if UV coordinates should be inverted in B axis */
-    public invertV = 0;
+    public invertV = false;
     /** Gets or sets a boolean indicating that this sprite should be disposed after animation ends */
     public disposeWhenFinishedAnimating: boolean;
     /** Gets the list of attached animations */
-    public animations = new Array<Animation>();
+    public animations: Nullable<Array<Animation>> = new Array<Animation>();
     /** Gets or sets a boolean indicating if the sprite can be picked */
     public isPickable = false;
     /** Gets or sets a boolean indicating that sprite texture alpha will be used for precise picking (false by default) */
@@ -83,6 +85,18 @@ export class Sprite {
     }
 
     /**
+     * Gets or sets the unique id of the sprite
+     */
+    public uniqueId: number;
+
+    /**
+     * Gets the manager of this sprite
+     */
+    public get manager() {
+        return this._manager;
+    }
+
+    /**
      * Creates a new Sprite
      * @param name defines the name
      * @param manager defines the manager
@@ -94,11 +108,56 @@ export class Sprite {
         this._manager = manager;
 
         this._manager.sprites.push(this);
+        this.uniqueId = this._manager.scene.getUniqueId();
 
         this.position = Vector3.Zero();
     }
 
     /**
+     * Returns the string "Sprite"
+     * @returns "Sprite"
+     */
+    public getClassName(): string {
+        return "Sprite";
+    }
+
+    /** Gets or sets the initial key for the animation (setting it will restart the animation)  */
+    public get fromIndex() {
+        return this._fromIndex;
+    }
+
+    public set fromIndex(value: number) {
+        this.playAnimation(value, this._toIndex, this._loopAnimation, this._delay, this._onAnimationEnd);
+    }
+
+    /** Gets or sets the end key for the animation (setting it will restart the animation)  */
+    public get toIndex() {
+        return this._toIndex;
+    }
+
+    public set toIndex(value: number) {
+        this.playAnimation(this._fromIndex, value, this._loopAnimation, this._delay, this._onAnimationEnd);
+    }
+
+    /** Gets or sets a boolean indicating if the animation is looping (setting it will restart the animation)  */
+    public get loopAnimation() {
+        return this._loopAnimation;
+    }
+
+    public set loopAnimation(value: boolean) {
+        this.playAnimation(this._fromIndex, this._toIndex, value, this._delay, this._onAnimationEnd);
+    }
+
+    /** Gets or sets the delay between cell changes (setting it will restart the animation)  */
+    public get delay() {
+        return Math.max(this._delay, 1);
+    }
+
+    public set delay(value: number) {
+        this.playAnimation(this._fromIndex, this._toIndex, this._loopAnimation, value, this._onAnimationEnd);
+    }
+
+    /**
      * Starts an animation
      * @param from defines the initial key
      * @param to defines the end key
@@ -110,7 +169,7 @@ export class Sprite {
         this._fromIndex = from;
         this._toIndex = to;
         this._loopAnimation = loop;
-        this._delay = delay;
+        this._delay = delay || 1;
         this._animationStarted = true;
 
         if (from < to) {

+ 83 - 3
src/Sprites/spriteManager.ts

@@ -18,12 +18,19 @@ import { Logger } from "../Misc/logger";
 import "../Shaders/sprites.fragment";
 import "../Shaders/sprites.vertex";
 import { DataBuffer } from '../Meshes/dataBuffer';
+import { Engine } from '../Engines/engine';
 declare type Ray = import("../Culling/ray").Ray;
 
 /**
  * Defines the minimum interface to fullfil in order to be a sprite manager.
  */
 export interface ISpriteManager extends IDisposable {
+
+    /**
+     * Gets manager's name
+     */
+    name: string;
+
     /**
      * Restricts the camera to viewing objects with the same layerMask.
      * A camera with a layerMask of 1 will render spriteManager.layerMask & camera.layerMask!== 0
@@ -36,6 +43,11 @@ export interface ISpriteManager extends IDisposable {
     isPickable: boolean;
 
     /**
+     * Gets the hosting scene
+     */
+    scene: Scene;
+
+    /**
      * Specifies the rendering group id for this mesh (0 by default)
      * @see http://doc.babylonjs.com/resources/transparency_and_how_meshes_are_rendered#rendering-groups
      */
@@ -47,6 +59,16 @@ export interface ISpriteManager extends IDisposable {
     sprites: Array<Sprite>;
 
     /**
+     * Gets or sets the spritesheet texture
+     */
+    texture: Texture;
+
+    /** Defines the default width of a cell in the spritesheet */
+    cellWidth: number;
+    /** Defines the default height of a cell in the spritesheet */
+    cellHeight: number;
+
+    /**
      * Tests the intersection of a sprite with a specific ray.
      * @param ray The ray we are sending to test the collision
      * @param camera The camera space we are sending rays in
@@ -132,6 +154,36 @@ export class SpriteManager implements ISpriteManager {
     private _effectFog: Effect;
 
     /**
+     * Gets or sets the unique id of the sprite
+     */
+    public uniqueId: number;
+
+    /**
+     * Gets the array of sprites
+     */
+    public get children() {
+        return this.sprites;
+    }
+
+    /**
+     * Gets the hosting scene
+     */
+    public get scene() {
+        return this._scene;
+    }
+
+    /**
+     * Gets or sets the capacity of the manager
+     */
+    public get capacity() {
+        return this._capacity;
+    }
+
+    public set capacity(value: number) {
+        this._capacity = value;
+    }
+
+    /**
      * Gets or sets the spritesheet texture
      */
     public get texture(): Texture {
@@ -196,8 +248,9 @@ export class SpriteManager implements ISpriteManager {
         }
 
         this._epsilon = epsilon;
-        this._scene = scene;
+        this._scene = scene || Engine.LastCreatedScene;
         this._scene.spriteManagers.push(this);
+        this.uniqueId = this.scene.getUniqueId();
 
         var indices = [];
         var index = 0;
@@ -246,6 +299,14 @@ export class SpriteManager implements ISpriteManager {
         }
     }
 
+    /**
+     * Returns the string "SpriteManager"
+     * @returns "SpriteManager"
+     */
+    public getClassName(): string {
+        return "SpriteManager";
+    }
+
     private _makePacked(imgUrl: string, spriteJSON: any) {
         if (spriteJSON !== null) {
             try {
@@ -343,8 +404,15 @@ export class SpriteManager implements ISpriteManager {
         this._vertexData[arrayOffset + 5] = sprite.height;
         this._vertexData[arrayOffset + 6] = offsetX;
         this._vertexData[arrayOffset + 7] = offsetY;
-        // Inverts
-        this._vertexData[arrayOffset + 8] = sprite.invertU ? 1 : 0;
+
+        // Inverts according to Right Handed
+        if (this._scene.useRightHandedSystem) {
+            this._vertexData[arrayOffset + 8] = sprite.invertU ? 0 : 1;
+        }
+        else {
+            this._vertexData[arrayOffset + 8] = sprite.invertU ? 1 : 0;
+        }
+
         this._vertexData[arrayOffset + 9] = sprite.invertV ? 1 : 0;
         // CellIfo
         if (this._packedAndReady) {
@@ -633,6 +701,13 @@ export class SpriteManager implements ISpriteManager {
         // VBOs
         engine.bindBuffers(this._vertexBuffers, this._indexBuffer, effect);
 
+        // Handle Right Handed
+        const culling = engine.depthCullingState.cull || true;
+        const zOffset = engine.depthCullingState.zOffset;
+        if (this._scene.useRightHandedSystem) {
+            engine.setState(culling, zOffset, false, false);
+        }
+
         // Draw order
         engine.setDepthFunctionToLessOrEqual();
         if (!this.disableDepthWrite) {
@@ -646,6 +721,11 @@ export class SpriteManager implements ISpriteManager {
         engine.setAlphaMode(this._blendMode);
         engine.drawElementsType(Material.TriangleFillMode, 0, (offset / 4) * 6);
         engine.setAlphaMode(Constants.ALPHA_DISABLE);
+
+        // Restore Right Handed
+        if (this._scene.useRightHandedSystem) {
+            engine.setState(culling, zOffset, false, true);
+        }
     }
 
     /**

+ 1 - 1
src/node.ts

@@ -679,7 +679,7 @@ export class Node implements IBehaviorAware<Node> {
      * @returns null if not found else the requested animation range
      */
     public getAnimationRange(name: string): Nullable<AnimationRange> {
-        return this._ranges[name];
+        return this._ranges[name] || null;
     }
 
     /**

+ 3 - 3
tests/validation/config.json

@@ -144,7 +144,7 @@
         },
         {
             "title": "Simulate pointer",
-            "playgroundId": "#8MGKWK#196",
+            "playgroundId": "#8MGKWK#269",
             "referenceImage": "simulatePointer.png"
         },
         {
@@ -677,12 +677,12 @@
         },
         {
             "title": "PBR",
-            "playgroundId": "#LCA0Q4#0",
+            "playgroundId": "#LCA0Q4#27",
             "referenceImage": "pbr.png"
         },
         {
             "title": "PBR refraction",
-            "playgroundId": "#LCA0Q4#25",
+            "playgroundId": "#LCA0Q4#26",
             "referenceImage": "pbr_refraction.png"
         },
         {