Browse Source

Merge branch 'master' of https://github.com/BabylonJS/Babylon.js into fix-comment-ifdef-shader

Popov72 5 years ago
parent
commit
31c91a3e8b
54 changed files with 1433 additions and 241 deletions
  1. 42 0
      Playground/templates.json
  2. 8 0
      dist/preview release/babylon.d.ts
  3. 2 2
      dist/preview release/babylon.js
  4. 23 8
      dist/preview release/babylon.max.js
  5. 1 1
      dist/preview release/babylon.max.js.map
  6. 17 0
      dist/preview release/babylon.module.d.ts
  7. 8 0
      dist/preview release/documentation.d.ts
  8. 1 1
      dist/preview release/glTF2Interface/package.json
  9. 2 2
      dist/preview release/gui/package.json
  10. 8 8
      dist/preview release/inspector/babylon.inspector.bundle.js
  11. 119 58
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  12. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  13. 18 1
      dist/preview release/inspector/babylon.inspector.d.ts
  14. 36 2
      dist/preview release/inspector/babylon.inspector.module.d.ts
  15. 7 7
      dist/preview release/inspector/package.json
  16. 3 3
      dist/preview release/loaders/package.json
  17. 2 2
      dist/preview release/materialsLibrary/package.json
  18. 2 2
      dist/preview release/nodeEditor/package.json
  19. 1 1
      dist/preview release/package.json
  20. 1 1
      dist/preview release/packagesSizeBaseLine.json
  21. 2 2
      dist/preview release/postProcessesLibrary/package.json
  22. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  23. 3 3
      dist/preview release/serializers/package.json
  24. 17 0
      dist/preview release/viewer/babylon.module.d.ts
  25. 6 6
      dist/preview release/viewer/babylon.viewer.js
  26. 1 1
      dist/preview release/viewer/babylon.viewer.max.js
  27. 8 0
      dist/preview release/what's new.md
  28. 48 67
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx
  29. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationPropertyGridComponent.tsx
  30. 95 7
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss
  31. 3 2
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/playhead.tsx
  32. 90 3
      inspector/src/components/actionTabs/tabs/propertyGrids/animations/svgDraggableArea.tsx
  33. 1 1
      package.json
  34. 12 0
      src/Engines/constants.ts
  35. 2 2
      src/Engines/thinEngine.ts
  36. 24 2
      src/Materials/PBR/pbrBaseMaterial.ts
  37. 197 0
      src/Materials/Textures/Filtering/hdrFiltering.ts
  38. 30 0
      src/Materials/Textures/baseTexture.ts
  39. 12 3
      src/Materials/Textures/hdrCubeTexture.ts
  40. 4 3
      src/Materials/Textures/index.ts
  41. 54 6
      src/Materials/effectRenderer.ts
  42. 4 3
      src/Particles/EmitterTypes/coneParticleEmitter.ts
  43. 2 1
      src/Probes/reflectionProbe.ts
  44. 20 0
      src/Rendering/boundingBoxRenderer.ts
  45. 1 1
      src/Rendering/geometryBufferRenderer.ts
  46. 224 0
      src/Shaders/ShadersInclude/hdrFilteringFunctions.fx
  47. 48 0
      src/Shaders/ShadersInclude/helperFunctions.fx
  48. 155 0
      src/Shaders/ShadersInclude/importanceSampling.fx
  49. 6 3
      src/Shaders/ShadersInclude/pbrBlockReflection.fx
  50. 5 1
      src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx
  51. 21 21
      src/Shaders/default.fragment.fx
  52. 15 0
      src/Shaders/hdrFiltering.fragment.fx
  53. 16 0
      src/Shaders/hdrFiltering.vertex.fx
  54. 2 0
      src/Shaders/pbr.fragment.fx

+ 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"
   }
 ]

+ 8 - 0
dist/preview release/babylon.d.ts

@@ -68866,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>;

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


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


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


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

@@ -72584,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) */
@@ -72637,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>;
@@ -144914,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>;

+ 8 - 0
dist/preview release/documentation.d.ts

@@ -68866,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"

+ 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": "*"

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


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


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


+ 18 - 1
dist/preview release/inspector/babylon.inspector.d.ts

@@ -552,7 +552,10 @@ declare module INSPECTOR {
         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;
@@ -561,6 +564,11 @@ declare module INSPECTOR {
         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;
     }
 }
@@ -588,6 +596,7 @@ declare module INSPECTOR {
 declare module INSPECTOR {
     interface IPlayheadProps {
         frame: number;
+        offset: number;
     }
     export class Playhead extends React.Component<IPlayheadProps> {
         constructor(props: IPlayheadProps);
@@ -604,6 +613,9 @@ declare module INSPECTOR {
         scene: BABYLON.Scene;
         entity: BABYLON.IAnimatable;
     }
+    interface ICanvasAxis {
+        value: number;
+    }
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: BABYLON.Animation[];
         animationName: string;
@@ -613,16 +625,21 @@ declare module INSPECTOR {
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
+        frameAxisLength: ICanvasAxis[];
     }> {
         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(event: React.MouseEvent<HTMLDivElement>): void;
+        addAnimation(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {

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

@@ -628,7 +628,10 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         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;
@@ -637,6 +640,11 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         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;
     }
 }
@@ -667,6 +675,7 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
     import * as React from "react";
     interface IPlayheadProps {
         frame: number;
+        offset: number;
     }
     export class Playhead extends React.Component<IPlayheadProps> {
         constructor(props: IPlayheadProps);
@@ -691,6 +700,9 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         scene: Scene;
         entity: IAnimatable;
     }
+    interface ICanvasAxis {
+        value: number;
+    }
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: Animation[];
         animationName: string;
@@ -700,16 +712,21 @@ declare module "babylonjs-inspector/components/actionTabs/tabs/propertyGrids/ani
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
+        frameAxisLength: ICanvasAxis[];
     }> {
         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(event: React.MouseEvent<HTMLDivElement>): void;
+        addAnimation(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: Vector2, index: number): void;
         getAnimationProperties(animation: Animation): {
@@ -3578,7 +3595,10 @@ declare module INSPECTOR {
         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;
@@ -3587,6 +3607,11 @@ declare module INSPECTOR {
         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;
     }
 }
@@ -3614,6 +3639,7 @@ declare module INSPECTOR {
 declare module INSPECTOR {
     interface IPlayheadProps {
         frame: number;
+        offset: number;
     }
     export class Playhead extends React.Component<IPlayheadProps> {
         constructor(props: IPlayheadProps);
@@ -3630,6 +3656,9 @@ declare module INSPECTOR {
         scene: BABYLON.Scene;
         entity: BABYLON.IAnimatable;
     }
+    interface ICanvasAxis {
+        value: number;
+    }
     export class AnimationCurveEditorComponent extends React.Component<IAnimationCurveEditorComponentProps, {
         animations: BABYLON.Animation[];
         animationName: string;
@@ -3639,16 +3668,21 @@ declare module INSPECTOR {
         currentPathData: string | undefined;
         svgKeyframes: IKeyframeSvgPoint[] | undefined;
         currentFrame: number;
+        frameAxisLength: ICanvasAxis[];
     }> {
         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(event: React.MouseEvent<HTMLDivElement>): void;
+        addAnimation(): void;
         addKeyFrame(event: React.MouseEvent<SVGSVGElement>): void;
         updateKeyframe(keyframe: BABYLON.Vector2, index: number): void;
         getAnimationProperties(animation: BABYLON.Animation): {

+ 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":115808,"engineOnly":152211,"sceneOnly":511353,"minGridMaterial":644586,"minStandardMaterial":786437}
+{"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": "*"

+ 17 - 0
dist/preview release/viewer/babylon.module.d.ts

@@ -72584,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) */
@@ -72637,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>;
@@ -144914,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>;

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


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


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

@@ -3,6 +3,7 @@
 ## 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))
@@ -21,6 +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 `boundingBoxRenderer.onBeforeBoxRenderingObservable` and `boundingBoxRenderer.onAfterBoxRenderingObservable` ([Deltakosh](https://github.com/deltakosh))
 
 ### Engine
 
@@ -119,6 +121,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))
@@ -173,6 +180,7 @@
 - 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
 

+ 48 - 67
inspector/src/components/actionTabs/tabs/propertyGrids/animations/animationCurveEditorComponent.tsx

@@ -1,6 +1,6 @@
 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';
@@ -10,6 +10,7 @@ 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");
@@ -24,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, currentFrame: number }> {
+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: "", currentFrame: 0 }
+        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>) {
@@ -47,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);
 
@@ -149,7 +162,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
         anim.setKeys(keys);
 
         this.setState({ svgKeyframes: svgKeyframes })
-
     }
 
     getAnimationProperties(animation: Animation) {
@@ -192,8 +204,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
             data = this.curvePath(keyframes, data, middle, easingFunction as EasingFunction)
         }
 
-
-
         return data;
 
     }
@@ -211,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) {
@@ -281,8 +290,6 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
     }
 
-
-
     renderPoints(updatedSvgKeyFrame: IKeyframeSvgPoint, index: number) {
 
         let animation = this.state.selected as Animation;
@@ -406,7 +413,7 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
     }
 
-    changeCurrentFrame(frame: number){
+    changeCurrentFrame(frame: number) {
         this.setState({ currentFrame: frame });
     }
 
@@ -425,62 +432,41 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                         <div className="animation-list">
 
                             <div>
-                                <div>
+                                <div className="label-input">
                                     <label>Animation Name</label>
                                     <input type="text" value={this.state.animationName} onChange={(e) => this.handleNameChange(e)}></input>
                                 </div>
-                                <div>
+                                <div className="label-input">
                                     <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>
+                                <ButtonLineComponent label={"Add Animation"} onClick={() => this.addAnimation()} />
                             </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>
-
-                            <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>
-                                })}
+                            <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>
+                                    })}
 
-                            </ul>
+                                </ul>
+                            </div>
                         </div>
-                        <div className="graph-chart">
+                        <div ref={this._graphCanvas} className="graph-chart">
 
-                            <Playhead frame={this.state.currentFrame}/>
+                            <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  */}
-                                <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>
+                                {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>
@@ -494,15 +480,15 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
                                 <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>
+                                <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>
@@ -518,17 +504,12 @@ export class AnimationCurveEditorComponent extends React.Component<IAnimationCur
 
                             }
 
-                        Animation name: {this.state.selected.name}
-
                         </div>
-                       
                     </div>
                     <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>
+                        <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)}>
 

+ 95 - 7
inspector/src/components/actionTabs/tabs/propertyGrids/animations/curveEditor.scss

@@ -34,6 +34,7 @@
             justify-content: flex-start;
             flex-direction: row;
             width: 100vw;
+            height: 85vh;
 
             .timeline{
                 width: 100vw;
@@ -88,8 +89,8 @@
 
         .animation-list{
             padding: 1.5rem;
-            background: lightgrey;
-            // height: 100vh;
+            background: rgb(87, 86, 86);
+            color: white;
             ul {
                 list-style:none;
                 padding-left: 0px;
@@ -116,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{
@@ -128,13 +143,32 @@
 
         .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 {
@@ -165,7 +199,7 @@
 
             .playhead-line {
                 width: 2px;
-                height: calc(45vh - 100px);
+                height: calc(90vh - 100px);
                 background-color: rgb(255, 198, 14);
                 position: absolute;
                 margin-left: 12.5px;
@@ -173,4 +207,58 @@
         }
     }
 
+    .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;
+        }  
+    }
+
 }

+ 3 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/animations/playhead.tsx

@@ -3,16 +3,17 @@ 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 * 3.02}px - 13px)`}}>
+           <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>

+ 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) =>

+ 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"

+ 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";
     }
 
     /**

+ 24 - 2
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;
@@ -1033,6 +1038,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;
         }
@@ -1172,7 +1178,8 @@ 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",
@@ -1275,6 +1282,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 +1347,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 {
@@ -1681,6 +1695,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) {

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

@@ -0,0 +1,197 @@
+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 {
+        const texture = this._engine.createRenderTargetCubeTexture(size, {
+            format: Constants.TEXTUREFORMAT_RGBA,
+            type: Constants.TEXTURETYPE_FLOAT,
+            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 nbRoughnessStops = 2;
+        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();
+                this._effectRenderer.restoreStates();
+            }
+        }
+
+        texture._texture!._webGLTexture = outputTexture._webGLTexture;
+        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?: () => void) {
+        return new Promise((resolve) => {
+            const callback = () => {
+                this._prefilterInternal(texture);
+                this._effectRenderer.dispose();
+                this._effectWrapper.dispose();
+                resolve();
+                if (onFinished) {
+                    onFinished();
+                }
+            };
+
+            this._effectRenderer = new EffectRenderer(this._engine);
+            this._effectWrapper = this._createEffect(texture, callback);
+        });
+    }
+}

+ 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.
      */

+ 12 - 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 = true, 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,16 @@ export class HDRCubeTexture extends BaseTexture {
                     results.push(dataFace);
                 }
             }
+
             return results;
         };
 
+        if (this._prefilterOnLoad) {
+            const previousOnLoad = this._onLoad;
+            const hdrFiltering = new HDRFiltering(engine);
+            this._onLoad = () => hdrFiltering.prefilter(this, previousOnLoad || undefined);
+        }
+
         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,
+            );
+        }
     }
 
     /**

+ 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;
     }
 }

+ 2 - 1
src/Probes/reflectionProbe.ts

@@ -92,7 +92,8 @@ 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);
-
+        this._renderTargetTexture.realTimeFiltering = true;
+        
         this._renderTargetTexture.onBeforeRenderObservable.add((faceIndex: number) => {
             switch (faceIndex) {
                 case 0:

+ 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 @@
+#ifdef 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);
+}

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

@@ -254,8 +254,12 @@
                     irradianceVector.z *= -1.0;
                 #endif
 
-                environmentIrradiance = computeEnvironmentIrradiance(irradianceVector);
-
+                #ifdef 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;

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

@@ -144,7 +144,11 @@
         uniform samplerCube reflectionSampler;
         
         #ifdef LODBASEDMICROSFURACE
-            #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
+            #ifdef 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;

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

@@ -254,7 +254,7 @@ void main(void) {
 	float shadow = 1.;
 
 #ifdef LIGHTMAP
-	vec3 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset).rgb;
+	vec4 lightmapColor = texture2D(lightmapSampler, vLightmapUV + uvOffset);
     #ifdef RGBDLIGHTMAP
         lightmapColor.rgb = fromRGBD(lightmapColor);
     #endif
@@ -264,7 +264,7 @@ void main(void) {
 #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));
@@ -272,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)));
@@ -281,19 +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);
@@ -310,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;
@@ -322,26 +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
@@ -349,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
@@ -425,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);
+}

+ 2 - 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>