Browse Source

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

sebavan 4 years ago
parent
commit
d854af3e46
88 changed files with 7741 additions and 5542 deletions
  1. 0 2
      .github/ISSUE_TEMPLATE.md
  2. 0 2
      .github/ISSUE_TEMPLATE/bug_report.md
  3. 1240 1061
      dist/preview release/babylon.d.ts
  4. 2 2
      dist/preview release/babylon.js
  5. 1068 642
      dist/preview release/babylon.max.js
  6. 1 1
      dist/preview release/babylon.max.js.map
  7. 1497 1124
      dist/preview release/babylon.module.d.ts
  8. 1242 1061
      dist/preview release/documentation.d.ts
  9. 2 0
      dist/preview release/gui/babylon.gui.d.ts
  10. 8 0
      dist/preview release/gui/babylon.gui.js
  11. 1 1
      dist/preview release/gui/babylon.gui.js.map
  12. 1 1
      dist/preview release/gui/babylon.gui.min.js
  13. 4 0
      dist/preview release/gui/babylon.gui.module.d.ts
  14. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js
  15. 2 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  16. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  17. 1 0
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  18. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  19. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  20. 1 0
      dist/preview release/loaders/babylon.glTFFileLoader.js
  21. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  22. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  23. 1 0
      dist/preview release/loaders/babylonjs.loaders.js
  24. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  25. 1 1
      dist/preview release/loaders/babylonjs.loaders.min.js
  26. 2 2
      dist/preview release/nodeEditor/babylon.nodeEditor.d.ts
  27. 5 5
      dist/preview release/nodeEditor/babylon.nodeEditor.js
  28. 74 46
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js
  29. 1 1
      dist/preview release/nodeEditor/babylon.nodeEditor.max.js.map
  30. 4 4
      dist/preview release/nodeEditor/babylon.nodeEditor.module.d.ts
  31. 1 1
      dist/preview release/packagesSizeBaseLine.json
  32. 16 1
      dist/preview release/serializers/babylon.glTF2Serializer.js
  33. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.js.map
  34. 1 1
      dist/preview release/serializers/babylon.glTF2Serializer.min.js
  35. 16 1
      dist/preview release/serializers/babylonjs.serializers.js
  36. 1 1
      dist/preview release/serializers/babylonjs.serializers.js.map
  37. 1 1
      dist/preview release/serializers/babylonjs.serializers.min.js
  38. 1497 1124
      dist/preview release/viewer/babylon.module.d.ts
  39. 116 96
      dist/preview release/viewer/babylon.viewer.js
  40. 2 2
      dist/preview release/viewer/babylon.viewer.max.js
  41. 4 2
      dist/preview release/what's new.md
  42. 1 1
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx
  43. 69 40
      nodeEditor/src/diagram/graphFrame.ts
  44. 0 1
      nodeEditor/src/diagram/nodePort.ts
  45. 16 2
      serializers/src/glTF/2.0/glTFExporter.ts
  46. 10 12
      src/Cameras/Inputs/flyCameraMouseInput.ts
  47. 10 6
      src/Cameras/Inputs/freeCameraMouseInput.ts
  48. 12 0
      src/Cameras/camera.ts
  49. 5 1
      src/Engines/constants.ts
  50. 160 119
      src/Engines/nativeEngine.ts
  51. 41 5
      src/Engines/thinEngine.ts
  52. 2 2
      src/Materials/Node/nodeMaterialBlockConnectionPoint.ts
  53. 22 2
      src/Materials/PBR/pbrBaseMaterial.ts
  54. 2 2
      src/Materials/PBR/pbrClearCoatConfiguration.ts
  55. 1 0
      src/Materials/PBR/pbrSubSurfaceConfiguration.ts
  56. 5 3
      src/Materials/Textures/Procedurals/proceduralTexture.ts
  57. 17 0
      src/Materials/material.ts
  58. 11 0
      src/Materials/materialDefines.ts
  59. 71 23
      src/Materials/materialHelper.ts
  60. 70 0
      src/Materials/prePassConfiguration.ts
  61. 20 0
      src/Materials/standardMaterial.ts
  62. 4 21
      src/PostProcesses/RenderPipeline/Pipelines/ssao2RenderingPipeline.ts
  63. 48 12
      src/PostProcesses/motionBlurPostProcess.ts
  64. 23 0
      src/PostProcesses/postProcess.ts
  65. 47 17
      src/PostProcesses/screenSpaceReflectionPostProcess.ts
  66. 26 0
      src/Rendering/motionBlurConfiguration.ts
  67. 1 1
      src/Rendering/prePassEffectConfiguration.ts
  68. 36 35
      src/Rendering/prePassRenderer.ts
  69. 28 0
      src/Rendering/screenSpaceReflectionsConfiguration.ts
  70. 0 15
      src/Rendering/ssao2Configuration.ts
  71. 4 1
      src/Shaders/ShadersInclude/bonesDeclaration.fx
  72. 1 1
      src/Shaders/ShadersInclude/pbrBlockClearcoat.fx
  73. 3 1
      src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx
  74. 1 5
      src/Shaders/ShadersInclude/pbrFragmentExtraDeclaration.fx
  75. 9 0
      src/Shaders/ShadersInclude/prePassDeclaration.fx
  76. 37 0
      src/Shaders/ShadersInclude/prePassVertex.fx
  77. 11 0
      src/Shaders/ShadersInclude/prePassVertexDeclaration.fx
  78. 6 0
      src/Shaders/ShadersInclude/subSurfaceScatteringFunctions.fx
  79. 21 4
      src/Shaders/default.fragment.fx
  80. 10 7
      src/Shaders/default.vertex.fx
  81. 23 0
      src/Shaders/pbr.fragment.fx
  82. 10 7
      src/Shaders/pbr.vertex.fx
  83. 4 0
      src/Shaders/screenSpaceReflection.fragment.fx
  84. 1 1
      src/XR/webXRFeaturesManager.ts
  85. BIN
      tests/validation/ReferenceImages/geometrybufferrenderer.png
  86. BIN
      tests/validation/ReferenceImages/motionBlur.png
  87. BIN
      tests/validation/ReferenceImages/ssr.png
  88. 21 0
      tests/validation/config.json

+ 0 - 2
.github/ISSUE_TEMPLATE.md

@@ -2,8 +2,6 @@
 
 We have a really active forum to help answering questions (https://forum.babylonjs.com/)
 
-*If you are convinced that you found a bug, please use the following template for your issue:*
-
 # Bugs
 
 - Bug repro on [playground](https://playground.babylonjs.com):

+ 0 - 2
.github/ISSUE_TEMPLATE/bug_report.md

@@ -11,8 +11,6 @@ assignees: ''
 
 We have a really active forum to help answering questions (https://forum.babylonjs.com/)
 
-*If you are convinced that you found a bug, please use the following template for your issue:*
-
 **Repro**
 - Bug repro on [playground](https://playground.babylonjs.com):
 - Expected result:

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


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


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


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


File diff suppressed because it is too large
+ 1497 - 1124
dist/preview release/babylon.module.d.ts


File diff suppressed because it is too large
+ 1242 - 1061
dist/preview release/documentation.d.ts


+ 2 - 0
dist/preview release/gui/babylon.gui.d.ts

@@ -2868,6 +2868,8 @@ declare module BABYLON.GUI {
         /** an array of SelectionGroups */
         groups?: SelectorGroup[]);
         protected _getTypeName(): string;
+        /** Gets the (stack) panel of the SelectionPanel  */
+        get panel(): StackPanel;
         /** Gets or sets the headerColor */
         get headerColor(): string;
         set headerColor(color: string);

+ 8 - 0
dist/preview release/gui/babylon.gui.js

@@ -10699,6 +10699,14 @@ var SelectionPanel = /** @class */ (function (_super) {
     SelectionPanel.prototype._getTypeName = function () {
         return "SelectionPanel";
     };
+    Object.defineProperty(SelectionPanel.prototype, "panel", {
+        /** Gets the (stack) panel of the SelectionPanel  */
+        get: function () {
+            return this._panel;
+        },
+        enumerable: false,
+        configurable: true
+    });
     Object.defineProperty(SelectionPanel.prototype, "headerColor", {
         /** Gets or sets the headerColor */
         get: function () {

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


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


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

@@ -2979,6 +2979,8 @@ declare module "babylonjs-gui/2D/controls/selector" {
         /** an array of SelectionGroups */
         groups?: SelectorGroup[]);
         protected _getTypeName(): string;
+        /** Gets the (stack) panel of the SelectionPanel  */
+        get panel(): StackPanel;
         /** Gets or sets the headerColor */
         get headerColor(): string;
         set headerColor(color: string);
@@ -7292,6 +7294,8 @@ declare module BABYLON.GUI {
         /** an array of SelectionGroups */
         groups?: SelectorGroup[]);
         protected _getTypeName(): string;
+        /** Gets the (stack) panel of the SelectionPanel  */
+        get panel(): StackPanel;
         /** Gets or sets the headerColor */
         get headerColor(): string;
         set headerColor(color: string);

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


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


+ 1 - 0
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -973,6 +973,7 @@ var KHR_materials_clearcoat = /** @class */ (function () {
         }
         var promises = new Array();
         babylonMaterial.clearCoat.isEnabled = true;
+        babylonMaterial.clearCoat.remapF0OnInterfaceChange = false;
         if (properties.clearcoatFactor != undefined) {
             babylonMaterial.clearCoat.intensity = properties.clearcoatFactor;
         }

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


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


+ 1 - 0
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -3324,6 +3324,7 @@ var KHR_materials_clearcoat = /** @class */ (function () {
         }
         var promises = new Array();
         babylonMaterial.clearCoat.isEnabled = true;
+        babylonMaterial.clearCoat.remapF0OnInterfaceChange = false;
         if (properties.clearcoatFactor != undefined) {
             babylonMaterial.clearCoat.intensity = properties.clearcoatFactor;
         }

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


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


+ 1 - 0
dist/preview release/loaders/babylonjs.loaders.js

@@ -4704,6 +4704,7 @@ var KHR_materials_clearcoat = /** @class */ (function () {
         }
         var promises = new Array();
         babylonMaterial.clearCoat.isEnabled = true;
+        babylonMaterial.clearCoat.remapF0OnInterfaceChange = false;
         if (properties.clearcoatFactor != undefined) {
             babylonMaterial.clearCoat.intensity = properties.clearcoatFactor;
         }

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


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


+ 2 - 2
dist/preview release/nodeEditor/babylon.nodeEditor.d.ts

@@ -180,7 +180,8 @@ declare module NODEEDITOR {
         private _createInputPort;
         private _markFramePortPositions;
         private _createFramePorts;
-        private removePortFromExposed;
+        private removePortFromExposedWithNode;
+        private removePortFromExposedWithLink;
         private createInputPorts;
         private createOutputPorts;
         private _redrawFramePorts;
@@ -279,7 +280,6 @@ declare module NODEEDITOR {
         protected _onCandidateLinkMovedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<BABYLON.Vector2>>>;
         protected _onSelectionChangedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
         protected _exposedOnFrame: boolean;
-        isExposed: boolean;
         delegatedPort: BABYLON.Nullable<FrameNodePort>;
         get element(): HTMLDivElement;
         get portName(): string;

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


+ 74 - 46
dist/preview release/nodeEditor/babylon.nodeEditor.max.js

@@ -67715,9 +67715,10 @@ var GraphFrame = /** @class */ (function () {
         this._frameInPorts.push(localPort);
         port.delegatedPort = localPort;
         this._controlledPorts.push(port);
-        if (!port.isExposed) {
-            port.isExposed = true;
+        port.exposedPortPosition = this._exposedInPorts.findIndex(function (nodePort) { return nodePort === port; });
+        if (port.exposedPortPosition < 0) {
             this._exposedInPorts.push(port);
+            port.exposedPortPosition = this._exposedInPorts.length - 1;
         }
     };
     // Mark ports with FramePortPosition for re-arrangement support
@@ -67766,79 +67767,107 @@ var GraphFrame = /** @class */ (function () {
             var node = _a[_i];
             node.isVisible = false;
         }
-        for (var _b = 0, _c = this._exposedOutPorts; _b < _c.length; _b++) { // Output
-            var port = _c[_b];
+        for (var i = 0; i < this._exposedOutPorts.length;) { // Output
+            var port = this._exposedOutPorts[i];
             if (port.node === null || port.node.enclosingFrameId != this.id) {
-                var index = this._exposedOutPorts.findIndex(function (nodePort) { return nodePort === port; });
-                this._exposedOutPorts.splice(index, 1);
-                port.isExposed = false;
+                if (this.removePortFromExposedWithNode(port, this._exposedOutPorts))
+                    continue;
             }
             else {
-                this.createOutputPorts(port, port.node);
+                if (!this.createOutputPorts(port, port.node) && this.removePortFromExposedWithNode(port, this._exposedOutPorts)) {
+                    continue;
+                }
             }
+            ++i;
         }
-        for (var _d = 0, _e = this._exposedInPorts; _d < _e.length; _d++) { // Input
-            var port = _e[_d];
+        for (var i = 0; i < this._exposedInPorts.length;) { // Input
+            var port = this._exposedInPorts[i];
             if (port.node === null || port.node.enclosingFrameId != this.id) {
-                var index = this._exposedInPorts.findIndex(function (nodePort) { return nodePort === port; });
-                this._exposedInPorts.splice(index, 1);
-                port.isExposed = false;
+                if (this.removePortFromExposedWithNode(port, this._exposedInPorts)) {
+                    continue;
+                }
             }
             else {
-                this.createInputPorts(port, port.node);
+                if (!this.createInputPorts(port, port.node) && this.removePortFromExposedWithNode(port, this._exposedInPorts)) {
+                    continue;
+                }
             }
+            ++i;
         }
-        for (var _f = 0, _g = this._nodes; _f < _g.length; _f++) {
-            var node = _g[_f];
-            for (var _h = 0, _j = node.outputPorts; _h < _j.length; _h++) { // Output
-                var port = _j[_h];
-                if (!port.isExposed) {
-                    if (this.createOutputPorts(port, node)) {
-                        this._exposedOutPorts.push(port);
-                        port.isExposed = true;
+        for (var _b = 0, _c = this._nodes; _b < _c.length; _b++) {
+            var node = _c[_b];
+            var _loop_1 = function (port_1) {
+                port_1.exposedPortPosition = this_1._exposedOutPorts.findIndex(function (nodePort) { return nodePort === port_1; });
+                if (port_1.exposedPortPosition < 0) {
+                    if (this_1.createOutputPorts(port_1, node)) {
+                        port_1.node.enclosingFrameId = this_1.id;
+                        this_1._exposedOutPorts.push(port_1);
+                        port_1.exposedPortPosition = this_1._exposedOutPorts.length - 1;
                     }
                 }
+            };
+            var this_1 = this;
+            for (var _d = 0, _e = node.outputPorts; _d < _e.length; _d++) {
+                var port_1 = _e[_d];
+                _loop_1(port_1);
             }
-            for (var _k = 0, _l = node.inputPorts; _k < _l.length; _k++) { // Input
-                var port = _l[_k];
-                if (!port.isExposed) {
+            for (var _f = 0, _g = node.inputPorts; _f < _g.length; _f++) { // Input
+                var port = _g[_f];
+                port.exposedPortPosition = this._exposedInPorts.findIndex(function (nodePort) { return nodePort === port; });
+                if (port.exposedPortPosition < 0) {
                     this.createInputPorts(port, node);
                 }
             }
         }
     };
-    GraphFrame.prototype.removePortFromExposed = function (nodeLink, exposedPorts) {
+    GraphFrame.prototype.removePortFromExposedWithNode = function (port, exposedPorts) {
+        var index = exposedPorts.findIndex(function (nodePort) { return nodePort === port; });
+        if (index >= 0) {
+            exposedPorts.splice(index, 1);
+            port.exposedPortPosition = -1;
+            return true;
+        }
+        return false;
+    };
+    GraphFrame.prototype.removePortFromExposedWithLink = function (nodeLink, exposedPorts) {
         var aPort = exposedPorts.findIndex(function (nodePort) { return nodePort === nodeLink.portA; });
         var bPort = exposedPorts.findIndex(function (nodePort) { return nodePort === nodeLink.portB; });
         if (aPort >= 0) {
             exposedPorts.splice(aPort, 1);
-            nodeLink.portA.isExposed = false;
+            nodeLink.portA.exposedPortPosition = -1;
         }
         else if (bPort >= 0) {
             exposedPorts.splice(bPort, 1);
-            if (nodeLink.portB)
-                nodeLink.portB.isExposed = false;
+            if (nodeLink.portB) {
+                nodeLink.portB.exposedPortPosition = -1;
+            }
         }
     };
     GraphFrame.prototype.createInputPorts = function (port, node) {
         var _this_1 = this;
         if (port.connectionPoint.isConnected) {
+            var portAdded = false;
             for (var _i = 0, _a = node.links; _i < _a.length; _i++) {
                 var link = _a[_i];
                 if (link.portB === port && this.nodes.indexOf(link.nodeA) === -1) {
                     this._createInputPort(port, node);
                     link.isVisible = true;
+                    portAdded = true;
                     var onLinkDisposedObserver = link.onDisposedObservable.add(function (nodeLink) {
-                        _this_1.removePortFromExposed(nodeLink, _this_1._exposedInPorts);
+                        _this_1.removePortFromExposedWithLink(nodeLink, _this_1._exposedInPorts);
                         _this_1._redrawFramePorts();
                     });
                     this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver);
                 }
             }
+            if (portAdded)
+                return true;
         }
         else if (port.exposedOnFrame) {
             this._createInputPort(port, node);
+            return true;
         }
+        return false;
     };
     GraphFrame.prototype.createOutputPorts = function (port, node) {
         var _this_1 = this;
@@ -67846,7 +67875,7 @@ var GraphFrame = /** @class */ (function () {
             var portAdded = false;
             for (var _i = 0, _a = node.links; _i < _a.length; _i++) {
                 var link = _a[_i];
-                if (link.portA === port && this.nodes.indexOf(link.nodeB) === -1 || (link.portA === port && port.exposedOnFrame)) {
+                if (link.portA === port && this.nodes.indexOf(link.nodeB) === -1) {
                     var localPort = void 0;
                     if (!portAdded) {
                         portAdded = true;
@@ -67854,7 +67883,7 @@ var GraphFrame = /** @class */ (function () {
                         this._frameOutPorts.push(localPort);
                         link.isVisible = true;
                         var onLinkDisposedObserver = link.onDisposedObservable.add(function (nodeLink) {
-                            _this_1.removePortFromExposed(nodeLink, _this_1._exposedOutPorts);
+                            _this_1.removePortFromExposedWithLink(nodeLink, _this_1._exposedOutPorts);
                             _this_1._redrawFramePorts();
                         });
                         this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver);
@@ -67868,9 +67897,17 @@ var GraphFrame = /** @class */ (function () {
                     }
                     port.delegatedPort = localPort;
                     this._controlledPorts.push(port);
-                    return true;
+                }
+                else if (port.exposedPortPosition >= 0 && !portAdded) {
+                    var localPort = _frameNodePort__WEBPACK_IMPORTED_MODULE_3__["FrameNodePort"].CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
+                    this._frameOutPorts.push(localPort);
+                    port.delegatedPort = localPort;
+                    this._controlledPorts.push(port);
+                    portAdded = true;
                 }
             }
+            if (portAdded)
+                return true;
         }
         else if (port.exposedOnFrame) {
             var localPort = _frameNodePort__WEBPACK_IMPORTED_MODULE_3__["FrameNodePort"].CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
@@ -68331,7 +68368,7 @@ var GraphFrame = /** @class */ (function () {
         newFrame.color = babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["Color3"].FromArray(serializationData.color);
         newFrame.comments = serializationData.comments;
         if (serializationData.blocks && map) {
-            var _loop_1 = function () {
+            var _loop_2 = function () {
                 var actualId = map[blockId];
                 var node_1 = canvas.nodes.filter(function (n) { return n.block.uniqueId === actualId; });
                 if (node_1.length) {
@@ -68341,7 +68378,7 @@ var GraphFrame = /** @class */ (function () {
             };
             for (var _i = 0, _a = serializationData.blocks; _i < _a.length; _i++) {
                 var blockId = _a[_i];
-                _loop_1();
+                _loop_2();
             }
         }
         else {
@@ -68352,25 +68389,17 @@ var GraphFrame = /** @class */ (function () {
             for (var _d = 0, _e = node.outputPorts; _d < _e.length; _d++) { // Output
                 var port = _e[_d];
                 if (port.exposedOnFrame) {
-                    port.isExposed = true;
-                    if (port.exposedPortPosition) {
+                    if (port.exposedPortPosition !== -1) {
                         newFrame._exposedOutPorts[port.exposedPortPosition] = port;
                     }
-                    else {
-                        newFrame._exposedOutPorts.push(port);
-                    }
                 }
             }
             for (var _f = 0, _g = node.inputPorts; _f < _g.length; _f++) { // Inports
                 var port = _g[_f];
                 if (port.exposedOnFrame) {
-                    port.isExposed = true;
-                    if (port.exposedPortPosition) {
+                    if (port.exposedPortPosition !== -1) {
                         newFrame._exposedInPorts[port.exposedPortPosition] = port;
                     }
-                    else {
-                        newFrame._exposedInPorts.push(port);
-                    }
                 }
             }
         }
@@ -69033,7 +69062,6 @@ var NodePort = /** @class */ (function () {
         var _this = this;
         this.connectionPoint = connectionPoint;
         this.node = node;
-        this.isExposed = false;
         this.delegatedPort = null;
         this._element = portContainer.ownerDocument.createElement("div");
         this._element.classList.add("port");

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


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

@@ -281,7 +281,8 @@ declare module "babylonjs-node-editor/diagram/graphFrame" {
         private _createInputPort;
         private _markFramePortPositions;
         private _createFramePorts;
-        private removePortFromExposed;
+        private removePortFromExposedWithNode;
+        private removePortFromExposedWithLink;
         private createInputPorts;
         private createOutputPorts;
         private _redrawFramePorts;
@@ -391,7 +392,6 @@ declare module "babylonjs-node-editor/diagram/nodePort" {
         protected _onCandidateLinkMovedObserver: Nullable<Observer<Nullable<Vector2>>>;
         protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
         protected _exposedOnFrame: boolean;
-        isExposed: boolean;
         delegatedPort: Nullable<FrameNodePort>;
         get element(): HTMLDivElement;
         get portName(): string;
@@ -2096,7 +2096,8 @@ declare module NODEEDITOR {
         private _createInputPort;
         private _markFramePortPositions;
         private _createFramePorts;
-        private removePortFromExposed;
+        private removePortFromExposedWithNode;
+        private removePortFromExposedWithLink;
         private createInputPorts;
         private createOutputPorts;
         private _redrawFramePorts;
@@ -2195,7 +2196,6 @@ declare module NODEEDITOR {
         protected _onCandidateLinkMovedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<BABYLON.Vector2>>>;
         protected _onSelectionChangedObserver: BABYLON.Nullable<BABYLON.Observer<BABYLON.Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
         protected _exposedOnFrame: boolean;
-        isExposed: boolean;
         delegatedPort: BABYLON.Nullable<FrameNodePort>;
         get element(): HTMLDivElement;
         get portName(): string;

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

@@ -1 +1 @@
-{"thinEngineOnly":117927,"engineOnly":154367,"sceneOnly":519894,"minGridMaterial":659986,"minStandardMaterial":810521}
+{"thinEngineOnly":118576,"engineOnly":155016,"sceneOnly":521091,"minGridMaterial":661799,"minStandardMaterial":816151}

+ 16 - 1
dist/preview release/serializers/babylon.glTF2Serializer.js

@@ -1891,6 +1891,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 /**
  * Converts Babylon Scene into glTF 2.0.
  * @hidden
@@ -2426,9 +2427,23 @@ var _Exporter = /** @class */ (function () {
                 break;
             }
             case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind: {
+                var meshMaterial = babylonTransformNode.material;
+                var convertToLinear = meshMaterial ? (meshMaterial instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["StandardMaterial"]) : true;
+                var vertexData = stride === 3 ? new babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color3"]() : new babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color4"]();
                 for (var k = 0, length_5 = meshAttributeArray.length / stride; k < length_5; ++k) {
                     index = k * stride;
-                    var vertexData = stride === 3 ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector3"].FromArray(meshAttributeArray, index) : babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
+                    if (stride === 3) {
+                        babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color3"].FromArrayToRef(meshAttributeArray, index, vertexData);
+                        if (convertToLinear) {
+                            vertexData.toLinearSpaceToRef(vertexData);
+                        }
+                    }
+                    else {
+                        babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color4"].FromArrayToRef(meshAttributeArray, index, vertexData);
+                        if (convertToLinear) {
+                            vertexData.toLinearSpaceToRef(vertexData);
+                        }
+                    }
                     vertexAttributes.push(vertexData.asArray());
                 }
                 break;

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


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


+ 16 - 1
dist/preview release/serializers/babylonjs.serializers.js

@@ -2076,6 +2076,7 @@ __webpack_require__.r(__webpack_exports__);
 
 
 
+
 /**
  * Converts Babylon Scene into glTF 2.0.
  * @hidden
@@ -2611,9 +2612,23 @@ var _Exporter = /** @class */ (function () {
                 break;
             }
             case babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["VertexBuffer"].ColorKind: {
+                var meshMaterial = babylonTransformNode.material;
+                var convertToLinear = meshMaterial ? (meshMaterial instanceof babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["StandardMaterial"]) : true;
+                var vertexData = stride === 3 ? new babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color3"]() : new babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color4"]();
                 for (var k = 0, length_5 = meshAttributeArray.length / stride; k < length_5; ++k) {
                     index = k * stride;
-                    var vertexData = stride === 3 ? babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector3"].FromArray(meshAttributeArray, index) : babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Vector4"].FromArray(meshAttributeArray, index);
+                    if (stride === 3) {
+                        babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color3"].FromArrayToRef(meshAttributeArray, index, vertexData);
+                        if (convertToLinear) {
+                            vertexData.toLinearSpaceToRef(vertexData);
+                        }
+                    }
+                    else {
+                        babylonjs_Maths_math_vector__WEBPACK_IMPORTED_MODULE_1__["Color4"].FromArrayToRef(meshAttributeArray, index, vertexData);
+                        if (convertToLinear) {
+                            vertexData.toLinearSpaceToRef(vertexData);
+                        }
+                    }
                     vertexAttributes.push(vertexData.asArray());
                 }
                 break;

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


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


File diff suppressed because it is too large
+ 1497 - 1124
dist/preview release/viewer/babylon.module.d.ts


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


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


+ 4 - 2
dist/preview release/what's new.md

@@ -5,7 +5,7 @@
 - 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 HDR texture filtering tools to the sandbox ([Sebavan](https://github.com/sebavan/))
+- Added HDR texture filtering tools to the sandbox. [Doc](https://medium.com/@babylonjs/real-time-pbr-filtering-is-coming-to-babylon-cb0e81159d79) ([Sebavan](https://github.com/sebavan/))
 - Reflection probes can now be used to give accurate shading with PBR ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added SubSurfaceScattering on PBR materials ([CraigFeldpsar](https://github.com/craigfeldspar) and ([Sebavan](https://github.com/sebavan/)))
 - Added edition of PBR materials, Post processes and Particle fragment shaders in the node material editor ([Popov72](https://github.com/Popov72))
@@ -243,7 +243,7 @@
 
 ### Post Processes
 
-- SSAO 2 is now using the brand new `PrePassRenderer` to avoid rendering the scene twice ([CraigFeldpsar](https://github.com/craigfeldspar)
+- SSAO 2, motion blur and screen space reflections are now using the brand new `PrePassRenderer` to avoid rendering the scene twice ([CraigFeldpsar](https://github.com/craigfeldspar)
 - Added Screen Space Curvature post process: [Doc](https://doc.babylonjs.com/how_to/how_to_use_postprocesses#screen-space-curvature) ([Popov72](https://github.com/Popov72) and [Sebavan](https://github.com/sebavan/))
 
 ## Bugs
@@ -322,6 +322,8 @@
 - Fix gltf2 Morph Target export code style, add additional test case for non-animation group created morph targets ([drigax](https://github.com/drigax))
 - Fix "Uncaught ReferenceError: name is not defined" ([outermeasure](https://github.com/outermeasure))
 - Fix wrong winding when applying a transform matrix on VertexData ([Popov72](https://github.com/Popov72))
+- Fix exporting vertex color of mesh with `StandardMaterial` when exporting to glTF ([Drigax](https://github.com/drigax))
+- Changed use of mousemove to pointermove in freeCameraMouseInput and flyCameraMouseInput to fix issue with Firefox ([PolygonalSun](https://github.com/PolygonalSun))
 
 ## Breaking changes
 - `FollowCamera.target` was renamed to `FollowCamera.meshTarget` to not be in conflict with `TargetCamera.target` ([Deltakosh](https://github.com/deltakosh))

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

@@ -375,7 +375,7 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
             };});
 
         materialOptions.splice(0, 0, {
-            label: "None",
+            label: "None (Default Fallback)",
             value: -1
         });
 

+ 69 - 40
nodeEditor/src/diagram/graphFrame.ts

@@ -90,9 +90,10 @@ export class GraphFrame {
         port.delegatedPort = localPort;
         this._controlledPorts.push(port);
 
-        if(!port.isExposed) {
-            port.isExposed = true;
+        port.exposedPortPosition = this._exposedInPorts.findIndex(nodePort => nodePort === port);
+        if(port.exposedPortPosition < 0) {
             this._exposedInPorts.push(port);
+            port.exposedPortPosition = this._exposedInPorts.length -1;
         }
     }
 
@@ -139,82 +140,110 @@ export class GraphFrame {
             node.isVisible = false;
         }
 
-        for (var port of this._exposedOutPorts) { // Output
+        for (var i = 0; i < this._exposedOutPorts.length; ) { // Output
+            var port = this._exposedOutPorts[i];
             if(port.node === null || port.node.enclosingFrameId != this.id) {
-                let index = this._exposedOutPorts.findIndex(nodePort => nodePort === port);
-                this._exposedOutPorts.splice(index,1);
-                port.isExposed = false;
+                if(this.removePortFromExposedWithNode(port, this._exposedOutPorts))
+                    continue;
             }
             else {
-                this.createOutputPorts(port, port.node);
+                if(!this.createOutputPorts(port, port.node) && this.removePortFromExposedWithNode(port, this._exposedOutPorts)) {
+                    continue;
+                }
             }
+            ++i;
         }
 
-        for (var port of this._exposedInPorts) { // Input
+        for (var i = 0; i < this._exposedInPorts.length; ) { // Input
+            var port = this._exposedInPorts[i];
             if(port.node === null || port.node.enclosingFrameId != this.id) {
-                let index = this._exposedInPorts.findIndex(nodePort => nodePort === port);
-                this._exposedInPorts.splice(index,1);
-                port.isExposed = false;
+                if(this.removePortFromExposedWithNode(port, this._exposedInPorts)) {
+                    continue;
+                }
             }
             else {
-                this.createInputPorts(port, port.node);
+                if(!this.createInputPorts(port, port.node) && this.removePortFromExposedWithNode(port, this._exposedInPorts)) {
+                    continue;
+                }
             }
+            ++i;
         }
 
         for (var node of this._nodes) {
-            for (var port of node.outputPorts) { // Output
-                if(!port.isExposed) {
+            for (let port of node.outputPorts) { // Output
+                port.exposedPortPosition = this._exposedOutPorts.findIndex(nodePort => nodePort === port);
+                if(port.exposedPortPosition < 0) {
                     if(this.createOutputPorts(port,node)) {
+                        port.node.enclosingFrameId = this.id;
                         this._exposedOutPorts.push(port);
-                        port.isExposed = true;
+                        port.exposedPortPosition = this._exposedOutPorts.length - 1;
                     }
                 }
             }
             for (var port of node.inputPorts) { // Input
-                if(!port.isExposed){
+                port.exposedPortPosition = this._exposedInPorts.findIndex(nodePort => nodePort === port);
+                if(port.exposedPortPosition < 0){
                     this.createInputPorts(port, node);
                 }
             }
         }
     }
 
-    private removePortFromExposed(nodeLink: NodeLink, exposedPorts: NodePort[])
+    private removePortFromExposedWithNode(port: NodePort,exposedPorts: NodePort[])
+    {
+        let index = exposedPorts.findIndex(nodePort => nodePort === port);
+        if(index >= 0) {
+            exposedPorts.splice(index,1)
+            port.exposedPortPosition = -1;
+            return true;
+        }
+        return false;
+    }
+
+    private removePortFromExposedWithLink(nodeLink: NodeLink, exposedPorts: NodePort[])
     {
         let aPort = exposedPorts.findIndex(nodePort => nodePort === nodeLink.portA);
         let bPort = exposedPorts.findIndex(nodePort => nodePort === nodeLink.portB);
         if(aPort >= 0) {
             exposedPorts.splice(aPort,1);
-            nodeLink.portA.isExposed = false;
+            nodeLink.portA.exposedPortPosition = -1;
         } else if(bPort >= 0) {
             exposedPorts.splice(bPort,1);
-            if(nodeLink.portB) nodeLink.portB.isExposed = false;
+            if(nodeLink.portB){ 
+                nodeLink.portB.exposedPortPosition = -1
+            }
         }
     }
     
     private createInputPorts(port: NodePort, node: GraphNode){
         if (port.connectionPoint.isConnected) {
+            let portAdded = false;
             for (var link of node.links) {
                 if (link.portB === port && this.nodes.indexOf(link.nodeA) === -1) {
                     this._createInputPort(port, node);
                     link.isVisible = true;
+                    portAdded = true;
                     const onLinkDisposedObserver = link.onDisposedObservable.add((nodeLink: NodeLink) => {
-                        this.removePortFromExposed(nodeLink, this._exposedInPorts);
+                        this.removePortFromExposedWithLink(nodeLink, this._exposedInPorts);
                         this._redrawFramePorts();
                     });
                     this._onNodeLinkDisposedObservers.push(onLinkDisposedObserver);
                 }
             }
-        } else if(port.exposedOnFrame) {
+            if(portAdded) return true;
+        } 
+        else if(port.exposedOnFrame) {
             this._createInputPort(port, node);
+            return true;
         }
+        return false;
     }
 
     private createOutputPorts(port: NodePort, node: GraphNode){
         if (port.connectionPoint.hasEndpoints) {
             let portAdded = false;
-
             for (var link of node.links) {
-                if (link.portA === port && this.nodes.indexOf(link.nodeB!) === -1 || (link.portA === port && port.exposedOnFrame)) {
+                if (link.portA === port && this.nodes.indexOf(link.nodeB!) === -1) {
                     let localPort: FrameNodePort;
 
                     if (!portAdded) {
@@ -225,7 +254,7 @@ export class GraphFrame {
                         link.isVisible = true;
 
                         const onLinkDisposedObserver = link.onDisposedObservable.add((nodeLink: NodeLink) => {
-                            this.removePortFromExposed(nodeLink, this._exposedOutPorts);
+                            this.removePortFromExposedWithLink(nodeLink, this._exposedOutPorts);
                             this._redrawFramePorts();
                         });
 
@@ -237,20 +266,29 @@ export class GraphFrame {
                     } else {
                         localPort = this.ports.filter(p => p.connectionPoint === port.connectionPoint)[0];
                     }
-
                     port.delegatedPort = localPort;
                     this._controlledPorts.push(port);
-                    return true;
+                }
+                else if(port.exposedPortPosition >= 0 && !portAdded)
+                {
+                    let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
+                    this._frameOutPorts.push(localPort);
+                    port.delegatedPort = localPort;
+                    this._controlledPorts.push(port);
+                    portAdded = true;
                 }
             }
-        } else if(port.exposedOnFrame) {
+            if(portAdded)  return true;
+        } 
+        else if(port.exposedOnFrame) {
             let localPort = FrameNodePort.CreateFrameNodePortElement(port.connectionPoint, node, this._outputPortContainer, null, this._ownerCanvas.globalState, false, GraphFrame._FramePortCounter++, this.id);
             this._frameOutPorts.push(localPort);
             port.delegatedPort = localPort;
             this._controlledPorts.push(port);
             return true;
         }
-        return false
+
+        return false;
     }
 
     private _redrawFramePorts() {
@@ -1427,28 +1465,19 @@ export class GraphFrame {
             newFrame.refresh();
         }
         
-        
         for (var node of newFrame.nodes) {
             for (var port of node.outputPorts) { // Output
                 if(port.exposedOnFrame) {
-                    port.isExposed = true;
-                    if(port.exposedPortPosition) { 
+                    if(port.exposedPortPosition !== -1) {
                         newFrame._exposedOutPorts[port.exposedPortPosition] = port;
                     }
-                    else {
-                        newFrame._exposedOutPorts.push(port);
-                    }
                 }
             }
 
             for (var port of node.inputPorts) { // Inports
                 if(port.exposedOnFrame) {
-                    port.isExposed = true;
-                    if(port.exposedPortPosition) { 
-                        newFrame._exposedInPorts[port.exposedPortPosition] = port;
-                    }
-                    else {
-                        newFrame._exposedInPorts.push(port);
+                    if(port.exposedPortPosition !== -1) {
+                         newFrame._exposedInPorts[port.exposedPortPosition] = port;
                     }
                 }
             }

+ 0 - 1
nodeEditor/src/diagram/nodePort.ts

@@ -20,7 +20,6 @@ export class NodePort {
     protected _onCandidateLinkMovedObserver: Nullable<Observer<Nullable<Vector2>>>;
     protected _onSelectionChangedObserver: Nullable<Observer<Nullable<GraphFrame | GraphNode | NodeLink | NodePort | FramePortData>>>;
     protected _exposedOnFrame: boolean;
-    public isExposed = false;
     public delegatedPort: Nullable<FrameNodePort> = null;
 
     public get element(): HTMLDivElement {

+ 16 - 2
serializers/src/glTF/2.0/glTFExporter.ts

@@ -2,7 +2,7 @@ import { AccessorType, IBufferView, IAccessor, INode, IScene, IMesh, IMaterial,
 
 import { FloatArray, Nullable, IndicesArray } from "babylonjs/types";
 import { Vector2, Vector3, Vector4, Quaternion, Matrix } from "babylonjs/Maths/math.vector";
-import { Color3 } from "babylonjs/Maths/math.color";
+import { Color3, Color4 } from "babylonjs/Maths/math.color";
 import { Tools } from "babylonjs/Misc/tools";
 import { VertexBuffer } from "babylonjs/Meshes/buffer";
 import { Node } from "babylonjs/node";
@@ -18,6 +18,7 @@ import { BaseTexture } from "babylonjs/Materials/Textures/baseTexture";
 import { Texture } from "babylonjs/Materials/Textures/texture";
 import { Material } from "babylonjs/Materials/material";
 import { MultiMaterial } from "babylonjs/Materials/multiMaterial";
+import { StandardMaterial } from 'babylonjs/Materials/standardMaterial';
 import { Engine } from "babylonjs/Engines/engine";
 import { Scene } from "babylonjs/scene";
 
@@ -728,9 +729,22 @@ export class _Exporter {
                 break;
             }
             case VertexBuffer.ColorKind: {
+                const meshMaterial = (babylonTransformNode as Mesh).material;
+                const convertToLinear = meshMaterial ? (meshMaterial instanceof StandardMaterial) : true;
+                const vertexData : Color3 | Color4 = stride === 3 ? new Color3() : new Color4();
                 for (let k = 0, length = meshAttributeArray.length / stride; k < length; ++k) {
                     index = k * stride;
-                    const vertexData = stride === 3 ? Vector3.FromArray(meshAttributeArray, index) : Vector4.FromArray(meshAttributeArray, index);
+                    if (stride === 3) {
+                        Color3.FromArrayToRef(meshAttributeArray, index, (vertexData as Color3));
+                        if (convertToLinear) {
+                            (vertexData as Color3).toLinearSpaceToRef((vertexData as Color3));
+                        }
+                    } else {
+                        Color4.FromArrayToRef(meshAttributeArray, index, (vertexData as Color4));
+                        if (convertToLinear) {
+                            (vertexData as Color4).toLinearSpaceToRef((vertexData as Color4));
+                        }
+                    }
                     vertexAttributes.push(vertexData.asArray());
                 }
                 break;

+ 10 - 12
src/Cameras/Inputs/flyCameraMouseInput.ts

@@ -59,7 +59,6 @@ export class FlyCameraMouseInput implements ICameraInput<FlyCamera> {
     @serialize()
     public angularSensibility = 1000.0;
 
-    private _mousemoveCallback: (e: MouseEvent) => void;
     private _observer: Nullable<Observer<PointerInfo>>;
     private _rollObserver: Nullable<Observer<Scene>>;
     private previousPosition: Nullable<{ x: number, y: number }> = null;
@@ -98,12 +97,6 @@ export class FlyCameraMouseInput implements ICameraInput<FlyCamera> {
                 }
             }
         );
-
-        // Helper function to keep 'this'.
-        this._mousemoveCallback = (e: any) => {
-            this._onMouseMove(e);
-        };
-        element.addEventListener("mousemove", this._mousemoveCallback, false);
     }
 
     /**
@@ -116,10 +109,6 @@ export class FlyCameraMouseInput implements ICameraInput<FlyCamera> {
 
             this.camera.getScene().onBeforeRenderObservable.remove(this._rollObserver);
 
-            if (this._mousemoveCallback) {
-                element.removeEventListener("mousemove", this._mousemoveCallback);
-            }
-
             this._observer = null;
             this._rollObserver = null;
             this.previousPosition = null;
@@ -184,6 +173,11 @@ export class FlyCameraMouseInput implements ICameraInput<FlyCamera> {
                 e.preventDefault();
                 this.element.focus();
             }
+
+            // This is required to move while pointer button is down
+            if (engine.isPointerLock) {
+                this._onMouseMove(p.event);
+            }
         } else
             // Mouse up.
             if (p.type === PointerEventTypes.POINTERUP && srcElement) {
@@ -202,7 +196,11 @@ export class FlyCameraMouseInput implements ICameraInput<FlyCamera> {
             } else
                 // Mouse move.
                 if (p.type === PointerEventTypes.POINTERMOVE) {
-                    if (!this.previousPosition || engine.isPointerLock) {
+                    if (!this.previousPosition) {
+                        if (engine.isPointerLock) {
+                            this._onMouseMove(p.event);
+                        }
+
                         return;
                     }
 

+ 10 - 6
src/Cameras/Inputs/freeCameraMouseInput.ts

@@ -94,6 +94,11 @@ export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
                         evt.preventDefault();
                         element.focus();
                     }
+
+                    // This is required to move while pointer button is down
+                    if (engine.isPointerLock && this._onMouseMove) {
+                        this._onMouseMove(p.event);
+                    }
                 }
                 else if (p.type === PointerEventTypes.POINTERUP && srcElement) {
                     try {
@@ -109,7 +114,11 @@ export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
                 }
 
                 else if (p.type === PointerEventTypes.POINTERMOVE) {
-                    if (!this.previousPosition || engine.isPointerLock) {
+                    if (!this.previousPosition) {
+                        if (engine.isPointerLock && this._onMouseMove) {
+                            this._onMouseMove(p.event);
+                        }
+
                         return;
                     }
 
@@ -161,7 +170,6 @@ export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
         };
 
         this._observer = this.camera.getScene().onPointerObservable.add(this._pointerInput, PointerEventTypes.POINTERDOWN | PointerEventTypes.POINTERUP | PointerEventTypes.POINTERMOVE);
-        element.addEventListener("mousemove", this._onMouseMove, false);
 
         element.addEventListener("contextmenu",
             <EventListener>this.onContextMenu.bind(this), false);
@@ -183,10 +191,6 @@ export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
         if (this._observer && element) {
             this.camera.getScene().onPointerObservable.remove(this._observer);
 
-            if (this._onMouseMove) {
-                element.removeEventListener("mousemove", this._onMouseMove);
-            }
-
             if (this.onContextMenu) {
                 element.removeEventListener("contextmenu", <EventListener>this.onContextMenu);
             }

+ 12 - 0
src/Cameras/camera.ts

@@ -625,6 +625,12 @@ export class Camera extends Node {
             this._postProcesses.splice(insertAt, 0, postProcess);
         }
         this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
+
+        // Update prePass
+        if (this._scene.prePassRenderer) {
+            this._scene.prePassRenderer.markAsDirty();
+        }
+
         return this._postProcesses.indexOf(postProcess);
     }
 
@@ -638,6 +644,12 @@ export class Camera extends Node {
         if (idx !== -1) {
             this._postProcesses[idx] = null;
         }
+
+        // Update prePass
+        if (this._scene.prePassRenderer) {
+            this._scene.prePassRenderer.markAsDirty();
+        }
+
         this._cascadePostProcessesToRigCams(); // also ensures framebuffer invalidated
     }
 

+ 5 - 1
src/Engines/constants.ts

@@ -282,9 +282,13 @@ export class Constants {
      */
     public static readonly MATERIAL_MiscDirtyFlag = 16;
     /**
+     * The dirty prepass flag value
+     */
+    public static readonly MATERIAL_PrePassDirtyFlag = 32;
+    /**
      * The all dirty flag value
      */
-    public static readonly MATERIAL_AllDirtyFlag = 31;
+    public static readonly MATERIAL_AllDirtyFlag = 63;
 
     /**
      * Returns the triangle fill mode

+ 160 - 119
src/Engines/nativeEngine.ts

@@ -26,6 +26,57 @@ import { ShaderCodeInliner } from "./Processors/shaderCodeInliner";
 import { WebGL2ShaderProcessor } from '../Engines/WebGL/webGL2ShaderProcessors';
 
 interface INativeEngine {
+
+    readonly SAMPLER_NEAREST: number;
+    readonly SAMPLER_BILINEAR: number;
+    readonly SAMPLER_TRILINEAR: number;
+    readonly SAMPLER_ANISOTROPIC: number;
+    readonly SAMPLER_POINT_COMPARE: number;
+    readonly SAMPLER_TRILINEAR_COMPARE: number;
+    readonly SAMPLER_MINBILINEAR_MAGPOINT: number;
+    readonly SAMPLER_MINPOINT_MAGPOINT_MIPLINEAR: number;
+    readonly SAMPLER_MINPOINT_MAGLINEAR_MIPPOINT: number;
+    readonly SAMPLER_MINPOINT_MAGLINEAR_MIPLINEAR: number;
+    readonly SAMPLER_MINLINEAR_MAGPOINT_MIPPOINT: number;
+
+    readonly DEPTH_TEST_LESS: number;
+    readonly DEPTH_TEST_LEQUAL: number;
+    readonly DEPTH_TEST_EQUAL: number;
+    readonly DEPTH_TEST_GEQUAL: number;
+    readonly DEPTH_TEST_GREATER: number;
+    readonly DEPTH_TEST_NOTEQUAL: number;
+    readonly DEPTH_TEST_NEVER: number;
+    readonly DEPTH_TEST_ALWAYS: number;
+
+    readonly CLEAR_FLAG_COLOR: number;
+    readonly CLEAR_FLAG_DEPTH: number;
+    readonly CLEAR_FLAG_STENCIL: number;
+
+    readonly ADDRESS_MODE_WRAP: number;
+    readonly ADDRESS_MODE_MIRROR: number;
+    readonly ADDRESS_MODE_CLAMP: number;
+    readonly ADDRESS_MODE_BORDER: number;
+    readonly ADDRESS_MODE_MIRROR_ONCE: number;
+
+    readonly TEXTURE_FORMAT_RGBA8: number;
+    readonly TEXTURE_FORMAT_RGBA32F: number;
+
+    readonly ATTRIB_TYPE_UINT8: number;
+    readonly ATTRIB_TYPE_INT16: number;
+    readonly ATTRIB_TYPE_FLOAT: number;
+
+    readonly ALPHA_DISABLE: number;
+    readonly ALPHA_ADD: number;
+    readonly ALPHA_COMBINE: number;
+    readonly ALPHA_SUBTRACT: number;
+    readonly ALPHA_MULTIPLY: number;
+    readonly ALPHA_MAXIMIZED: number;
+    readonly ALPHA_ONEONE: number;
+    readonly ALPHA_PREMULTIPLIED: number;
+    readonly ALPHA_PREMULTIPLIED_PORTERDUFF: number;
+    readonly ALPHA_INTERPOLATE: number;
+    readonly ALPHA_SCREENMODE: number;
+
     dispose(): void;
 
     requestAnimationFrame(callback: () => void): void;
@@ -143,61 +194,6 @@ class NativeDataBuffer extends DataBuffer {
     public nativeVertexBuffer?: any;
 }
 
-// TODO: change this to match bgfx.
-// Must match Filter enum in SpectreEngine.h.
-class NativeFilter {
-    public static readonly POINT = 0;
-    public static readonly MINPOINT_MAGPOINT_MIPPOINT = NativeFilter.POINT;
-    public static readonly BILINEAR = 1;
-    public static readonly MINLINEAR_MAGLINEAR_MIPPOINT = NativeFilter.BILINEAR;
-    public static readonly TRILINEAR = 2;
-    public static readonly MINLINEAR_MAGLINEAR_MIPLINEAR = NativeFilter.TRILINEAR;
-    public static readonly ANISOTROPIC = 3;
-    public static readonly POINT_COMPARE = 4;
-    public static readonly TRILINEAR_COMPARE = 5;
-    public static readonly MINBILINEAR_MAGPOINT = 6;
-    public static readonly MINLINEAR_MAGPOINT_MIPLINEAR = NativeFilter.MINBILINEAR_MAGPOINT;
-    public static readonly MINPOINT_MAGPOINT_MIPLINEAR = 7;
-    public static readonly MINPOINT_MAGLINEAR_MIPPOINT = 8;
-    public static readonly MINPOINT_MAGLINEAR_MIPLINEAR = 9;
-    public static readonly MINLINEAR_MAGPOINT_MIPPOINT = 10;
-}
-
-// depth test values
-// Values match bgfx defines
-class DepthTest {
-    public static readonly DEPTH_TEST_LESS = 16;
-    public static readonly DEPTH_TEST_LEQUAL = 32;
-    public static readonly DEPTH_TEST_EQUAL = 48;
-    public static readonly DEPTH_TEST_GEQUAL = 64;
-    public static readonly DEPTH_TEST_GREATER = 80;
-    public static readonly DEPTH_TEST_NOTEQUAL = 96;
-    public static readonly DEPTH_TEST_NEVER = 112;
-    public static readonly DEPTH_TEST_ALWAYS = 128;
-}
-
-// these flags match bgfx.
-class NativeClearFlags
-{
-    public static readonly CLEAR_COLOR = 1;
-    public static readonly CLEAR_DEPTH = 2;
-    public static readonly CLEAR_STENCIL = 4;
-}
-// TODO: change this to match bgfx.
-// Must match AddressMode enum in SpectreEngine.h.
-class NativeAddressMode {
-    public static readonly WRAP = 0;
-    public static readonly MIRROR = 1;
-    public static readonly CLAMP = 2;
-    public static readonly BORDER = 3;
-    public static readonly MIRROR_ONCE = 4;
-}
-
-class NativeTextureFormat {
-    public static readonly RGBA8 = 0;
-    public static readonly RGBA32F = 1;
-}
-
 /** @hidden */
 class NativeTexture extends InternalTexture {
     public getInternalTexture(): InternalTexture {
@@ -218,7 +214,7 @@ export class NativeEngine extends Engine {
     /** Defines the invalid handle returned by bgfx when resource creation goes wrong */
     private readonly INVALID_HANDLE = 65535;
     private _boundBuffersVertexArray: any = null;
-    private _currentDepthTest: number = DepthTest.DEPTH_TEST_LEQUAL;
+    private _currentDepthTest: number = this._native.DEPTH_TEST_LEQUAL;
 
     public getHardwareScalingLevel(): number {
         return 1.0;
@@ -358,15 +354,15 @@ export class NativeEngine extends Engine {
         var mode = 0;
         if (backBuffer && color) {
             this._native.clearColor(color.r, color.g, color.b, color.a !== undefined ? color.a : 1.0);
-            mode |= NativeClearFlags.CLEAR_COLOR;
+            mode |= this._native.CLEAR_FLAG_COLOR;
         }
         if (depth) {
             this._native.clearDepth(1.0);
-            mode |= NativeClearFlags.CLEAR_DEPTH;
+            mode |= this._native.CLEAR_FLAG_DEPTH;
         }
         if (stencil) {
             this._native.clearStencil(0);
-            mode |= NativeClearFlags.CLEAR_STENCIL;
+            mode |= this._native.CLEAR_FLAG_STENCIL;
         }
         this._native.clear(mode);
     }
@@ -418,7 +414,7 @@ export class NativeEngine extends Engine {
                             vertexBuffer.byteOffset,
                             vertexBuffer.byteStride,
                             vertexBuffer.getSize(),
-                            vertexBuffer.type,
+                            this._getNativeAttribType(vertexBuffer.type),
                             vertexBuffer.normalized);
                     }
                 }
@@ -643,7 +639,7 @@ export class NativeEngine extends Engine {
      * @param enable defines the state to set
      */
     public setDepthBuffer(enable: boolean): void {
-        this._native.setDepthTest(enable ? this._currentDepthTest : DepthTest.DEPTH_TEST_ALWAYS);
+        this._native.setDepthTest(enable ? this._currentDepthTest : this._native.DEPTH_TEST_ALWAYS);
     }
 
     /**
@@ -655,22 +651,22 @@ export class NativeEngine extends Engine {
     }
 
     public setDepthFunctionToGreater(): void {
-        this._currentDepthTest = DepthTest.DEPTH_TEST_GREATER;
+        this._currentDepthTest = this._native.DEPTH_TEST_GREATER;
         this._native.setDepthTest(this._currentDepthTest);
     }
 
     public setDepthFunctionToGreaterOrEqual(): void {
-        this._currentDepthTest = DepthTest.DEPTH_TEST_GEQUAL;
+        this._currentDepthTest = this._native.DEPTH_TEST_GEQUAL;
         this._native.setDepthTest(this._currentDepthTest);
     }
 
     public setDepthFunctionToLess(): void {
-        this._currentDepthTest = DepthTest.DEPTH_TEST_LESS;
+        this._currentDepthTest = this._native.DEPTH_TEST_LESS;
         this._native.setDepthTest(this._currentDepthTest);
     }
 
     public setDepthFunctionToLessOrEqual(): void {
-        this._currentDepthTest = DepthTest.DEPTH_TEST_LEQUAL;
+        this._currentDepthTest = this._native.DEPTH_TEST_LEQUAL;
         this._native.setDepthTest(this._currentDepthTest);
     }
 
@@ -721,6 +717,8 @@ export class NativeEngine extends Engine {
             return;
         }
 
+        mode = this._getNativeAlphaMode(mode);
+
         this._native.setBlendMode(mode);
 
         if (!noDepthWriteChange) {
@@ -1080,7 +1078,7 @@ export class NativeEngine extends Engine {
                     texture.height = texture.baseHeight;
                     texture.isReady = true;
 
-                    var filter = this._getSamplingFilter(samplingMode);
+                    var filter = this._getNativeSamplingMode(samplingMode);
                     this._native.setTextureSampling(webGLTexture, filter);
 
                     if (scene) {
@@ -1244,54 +1242,6 @@ export class NativeEngine extends Engine {
         return texture;
     }
 
-    // Returns a NativeFilter.XXXX value.
-    private _getSamplingFilter(samplingMode: number): number {
-        switch (samplingMode) {
-            case Constants.TEXTURE_BILINEAR_SAMPLINGMODE:
-                return NativeFilter.MINLINEAR_MAGLINEAR_MIPPOINT;
-            case Constants.TEXTURE_TRILINEAR_SAMPLINGMODE:
-                return NativeFilter.MINLINEAR_MAGLINEAR_MIPLINEAR;
-            case Constants.TEXTURE_NEAREST_SAMPLINGMODE:
-                return NativeFilter.MINPOINT_MAGPOINT_MIPLINEAR;
-            case Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
-                return NativeFilter.MINPOINT_MAGPOINT_MIPPOINT;
-            case Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
-                return NativeFilter.MINLINEAR_MAGPOINT_MIPPOINT;
-            case Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
-                return NativeFilter.MINLINEAR_MAGPOINT_MIPLINEAR;
-            case Constants.TEXTURE_NEAREST_LINEAR:
-                return NativeFilter.MINLINEAR_MAGPOINT_MIPLINEAR;
-            case Constants.TEXTURE_NEAREST_NEAREST:
-                return NativeFilter.MINPOINT_MAGPOINT_MIPPOINT;
-            case Constants.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
-                return NativeFilter.MINPOINT_MAGLINEAR_MIPPOINT;
-            case Constants.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
-                return NativeFilter.MINPOINT_MAGLINEAR_MIPLINEAR;
-            case Constants.TEXTURE_LINEAR_LINEAR:
-                return NativeFilter.MINLINEAR_MAGLINEAR_MIPLINEAR;
-            case Constants.TEXTURE_LINEAR_NEAREST:
-                return NativeFilter.MINPOINT_MAGLINEAR_MIPLINEAR;
-            case Constants.TEXTURE_NEAREST_NEAREST_MIPLINEAR:
-                return NativeFilter.MINPOINT_MAGPOINT_MIPLINEAR;
-            case Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST:
-                return NativeFilter.MINLINEAR_MAGLINEAR_MIPLINEAR;
-            default:
-                throw new Error("Unexpected sampling mode: " + samplingMode + ".");
-        }
-    }
-
-    private static _GetNativeTextureFormat(format: number, type: number): number {
-        if (format == Constants.TEXTUREFORMAT_RGBA && type == Constants.TEXTURETYPE_UNSIGNED_INT) {
-            return NativeTextureFormat.RGBA8;
-        }
-        else if (format == Constants.TEXTUREFORMAT_RGBA && type == Constants.TEXTURETYPE_FLOAT) {
-            return NativeTextureFormat.RGBA32F;
-        }
-        else {
-            throw new Error("Unexpected texture format or type: format " + format + ", type " + type + ".");
-        }
-    }
-
     public createRenderTargetTexture(size: number | { width: number, height: number }, options: boolean | RenderTargetCreationOptions): NativeTexture {
         let fullOptions = new RenderTargetCreationOptions();
 
@@ -1333,7 +1283,7 @@ export class NativeEngine extends Engine {
             texture._webGLTexture!,
             width,
             height,
-            NativeEngine._GetNativeTextureFormat(fullOptions.format, fullOptions.type),
+            this._getNativeTextureFormat(fullOptions.format, fullOptions.type),
             fullOptions.samplingMode!,
             fullOptions.generateStencilBuffer ? true : false,
             fullOptions.generateDepthBuffer,
@@ -1360,7 +1310,7 @@ export class NativeEngine extends Engine {
 
     public updateTextureSamplingMode(samplingMode: number, texture: InternalTexture): void {
         if (texture._webGLTexture) {
-            var filter = this._getSamplingFilter(samplingMode);
+            var filter = this._getNativeSamplingMode(samplingMode);
             this._native.setTextureSampling(texture._webGLTexture, filter);
         }
         texture.samplingMode = samplingMode;
@@ -1501,11 +1451,11 @@ export class NativeEngine extends Engine {
     private _getAddressMode(wrapMode: number): number {
         switch (wrapMode) {
             case Constants.TEXTURE_WRAP_ADDRESSMODE:
-                return NativeAddressMode.WRAP;
+                return this._native.ADDRESS_MODE_WRAP;
             case Constants.TEXTURE_CLAMP_ADDRESSMODE:
-                return NativeAddressMode.CLAMP;
+                return this._native.ADDRESS_MODE_CLAMP;
             case Constants.TEXTURE_MIRROR_ADDRESSMODE:
-                return NativeAddressMode.MIRROR;
+                return this._native.ADDRESS_MODE_MIRROR;
             default:
                 throw new Error("Unexpected wrap mode: " + wrapMode + ".");
         }
@@ -1551,4 +1501,95 @@ export class NativeEngine extends Engine {
     public _uploadImageToTexture(texture: InternalTexture, image: HTMLImageElement, faceIndex: number = 0, lod: number = 0) {
         throw new Error("_uploadArrayBufferViewToTexture not implemented.");
     }
+
+    // JavaScript-to-Native conversion helper functions.
+
+    private _getNativeSamplingMode(samplingMode: number): number {
+        switch (samplingMode) {
+            case Constants.TEXTURE_BILINEAR_SAMPLINGMODE:
+                return this._native.SAMPLER_BILINEAR;
+            case Constants.TEXTURE_TRILINEAR_SAMPLINGMODE:
+                return this._native.SAMPLER_TRILINEAR;
+            case Constants.TEXTURE_NEAREST_SAMPLINGMODE:
+                return this._native.SAMPLER_MINPOINT_MAGPOINT_MIPLINEAR;
+            case Constants.TEXTURE_NEAREST_NEAREST_MIPNEAREST:
+                return this._native.SAMPLER_NEAREST;
+            case Constants.TEXTURE_NEAREST_LINEAR_MIPNEAREST:
+                return this._native.SAMPLER_MINLINEAR_MAGPOINT_MIPPOINT;
+            case Constants.TEXTURE_NEAREST_LINEAR_MIPLINEAR:
+                return this._native.SAMPLER_MINBILINEAR_MAGPOINT;
+            case Constants.TEXTURE_NEAREST_LINEAR:
+                return this._native.SAMPLER_MINBILINEAR_MAGPOINT;
+            case Constants.TEXTURE_NEAREST_NEAREST:
+                return this._native.SAMPLER_NEAREST;
+            case Constants.TEXTURE_LINEAR_NEAREST_MIPNEAREST:
+                return this._native.SAMPLER_MINPOINT_MAGLINEAR_MIPPOINT;
+            case Constants.TEXTURE_LINEAR_NEAREST_MIPLINEAR:
+                return this._native.SAMPLER_MINPOINT_MAGLINEAR_MIPLINEAR;
+            case Constants.TEXTURE_LINEAR_LINEAR:
+                return this._native.SAMPLER_TRILINEAR;
+            case Constants.TEXTURE_LINEAR_NEAREST:
+                return this._native.SAMPLER_MINPOINT_MAGLINEAR_MIPLINEAR;
+            case Constants.TEXTURE_NEAREST_NEAREST_MIPLINEAR:
+                return this._native.SAMPLER_MINPOINT_MAGPOINT_MIPLINEAR;
+            case Constants.TEXTURE_LINEAR_LINEAR_MIPNEAREST:
+                return this._native.SAMPLER_TRILINEAR;
+            default:
+                throw new Error(`Unsupported sampling mode: ${samplingMode}.`);
+        }
+    }
+
+    private _getNativeTextureFormat(format: number, type: number): number {
+        if (format == Constants.TEXTUREFORMAT_RGBA && type == Constants.TEXTURETYPE_UNSIGNED_INT) {
+            return this._native.TEXTURE_FORMAT_RGBA8;
+        }
+        else if (format == Constants.TEXTUREFORMAT_RGBA && type == Constants.TEXTURETYPE_FLOAT) {
+            return this._native.TEXTURE_FORMAT_RGBA32F;
+        }
+        else {
+            throw new Error(`Unsupported texture format or type: format ${format}, type ${type}.`);
+        }
+    }
+
+    private _getNativeAlphaMode(mode: number): number {
+        switch (mode) {
+            case Constants.ALPHA_DISABLE:
+                return this._native.ALPHA_DISABLE;
+            case Constants.ALPHA_ADD:
+                return this._native.ALPHA_ADD;
+            case Constants.ALPHA_COMBINE:
+                return this._native.ALPHA_COMBINE;
+            case Constants.ALPHA_SUBTRACT:
+                return this._native.ALPHA_SUBTRACT;
+            case Constants.ALPHA_MULTIPLY:
+                return this._native.ALPHA_MULTIPLY;
+            case Constants.ALPHA_MAXIMIZED:
+                return this._native.ALPHA_MAXIMIZED;
+            case Constants.ALPHA_ONEONE:
+                return this._native.ALPHA_ONEONE;
+            case Constants.ALPHA_PREMULTIPLIED:
+                return this._native.ALPHA_PREMULTIPLIED;
+            case Constants.ALPHA_PREMULTIPLIED_PORTERDUFF:
+                return this._native.ALPHA_PREMULTIPLIED_PORTERDUFF;
+            case Constants.ALPHA_INTERPOLATE:
+                return this._native.ALPHA_INTERPOLATE;
+            case Constants.ALPHA_SCREENMODE:
+                return this._native.ALPHA_SCREENMODE;
+            default:
+                throw new Error(`Unsupported alpha mode: ${mode}.`);
+        }
+    }
+
+    private _getNativeAttribType(type: number): number {
+        switch (type) {
+            case VertexBuffer.UNSIGNED_BYTE:
+                return this._native.ATTRIB_TYPE_UINT8;
+            case VertexBuffer.SHORT:
+                return this._native.ATTRIB_TYPE_INT16;
+            case VertexBuffer.FLOAT:
+                return this._native.ATTRIB_TYPE_FLOAT;
+            default:
+                throw new Error(`Unsupported attribute type: ${type}.`);
+        }
+    }
 }

+ 41 - 5
src/Engines/thinEngine.ts

@@ -126,6 +126,11 @@ export interface EngineOptions extends WebGLContextAttributes {
      * Make the matrix computations to be performed in 64 bits instead of 32 bits. False by default
      */
     useHighPrecisionMatrix?: boolean;
+
+    /**
+     * Will prevent the system from falling back to software implementation if a hardware device cannot be created
+     */
+    failIfMajorPerformanceCaveat?: boolean;
 }
 
 /**
@@ -4307,25 +4312,56 @@ export class ThinEngine {
 
     // Statics
 
-    private static _isSupported: Nullable<boolean> = null;
+    private static _IsSupported: Nullable<boolean> = null;
+    private static _HasMajorPerformanceCaveat : Nullable<boolean> = null;
+
+    /**
+     * Gets a boolean indicating if the engine can be instanciated (ie. if a webGL context can be found)
+     */
+    public static get IsSupported(): boolean {
+        return this.isSupported(); // Backward compat
+    }
+
     /**
      * Gets a boolean indicating if the engine can be instanciated (ie. if a webGL context can be found)
      * @returns true if the engine can be created
      * @ignorenaming
      */
     public static isSupported(): boolean {
-        if (this._isSupported === null) {
+        if (this._HasMajorPerformanceCaveat !== null) {
+            return !this._HasMajorPerformanceCaveat; // We know it is performant so WebGL is supported
+        }
+
+        if (this._IsSupported === null) {
             try {
                 var tempcanvas = CanvasGenerator.CreateCanvas(1, 1);
                 var gl = tempcanvas.getContext("webgl") || (tempcanvas as any).getContext("experimental-webgl");
 
-                this._isSupported = gl != null && !!window.WebGLRenderingContext;
+                this._IsSupported = gl != null && !!window.WebGLRenderingContext;
+            } catch (e) {
+                this._IsSupported = false;
+            }
+        }
+
+        return this._IsSupported;
+    }
+
+    /**
+     * Gets a boolean indicating if the engine can be instanciated on a performant device (ie. if a webGL context can be found and it does not use a slow implementation)
+     */
+    public static get HasMajorPerformanceCaveat(): boolean {
+        if (this._HasMajorPerformanceCaveat === null) {
+            try {
+                var tempcanvas = CanvasGenerator.CreateCanvas(1, 1);
+                var gl = tempcanvas.getContext("webgl", { failIfMajorPerformanceCaveat: true }) || (tempcanvas as any).getContext("experimental-webgl", { failIfMajorPerformanceCaveat: true });
+
+                this._HasMajorPerformanceCaveat = !gl;
             } catch (e) {
-                this._isSupported = false;
+                this._HasMajorPerformanceCaveat = false;
             }
         }
 
-        return this._isSupported;
+        return this._HasMajorPerformanceCaveat;
     }
 
     /**

+ 2 - 2
src/Materials/Node/nodeMaterialBlockConnectionPoint.ts

@@ -470,8 +470,8 @@ export class NodeMaterialConnectionPoint {
             serializationObject.exposedPortPosition = this.exposedPortPosition;
         }
 
-        if (this.isExposedOnFrame) {
-            serializationObject.isExposedOnFrame = this.isExposedOnFrame;
+        if (this.isExposedOnFrame || this.exposedPortPosition >= 0) {
+            serializationObject.isExposedOnFrame = true;
             serializationObject.exposedPortPosition = this.exposedPortPosition;
         }
 

+ 22 - 2
src/Materials/PBR/pbrBaseMaterial.ts

@@ -16,6 +16,7 @@ import { IMaterialAnisotropicDefines, PBRAnisotropicConfiguration } from "./pbrA
 import { IMaterialBRDFDefines, PBRBRDFConfiguration } from "./pbrBRDFConfiguration";
 import { IMaterialSheenDefines, PBRSheenConfiguration } from "./pbrSheenConfiguration";
 import { IMaterialSubSurfaceDefines, PBRSubSurfaceConfiguration } from "./pbrSubSurfaceConfiguration";
+import { PrePassConfiguration } from "../prePassConfiguration";
 import { Color3, TmpColors } from '../../Maths/math.color';
 import { Scalar } from "../../Maths/math.scalar";
 
@@ -170,11 +171,18 @@ export class PBRMaterialDefines extends MaterialDefines
     public PREPASS_ALBEDO_INDEX = -1;
     public PREPASS_DEPTHNORMAL = false;
     public PREPASS_DEPTHNORMAL_INDEX = -1;
+    public PREPASS_POSITION = false;
+    public PREPASS_POSITION_INDEX = -1;
+    public PREPASS_VELOCITY = false;
+    public PREPASS_VELOCITY_INDEX = -1;
+    public PREPASS_REFLECTIVITY = false;
+    public PREPASS_REFLECTIVITY_INDEX = -1;
     public SCENE_MRT_COUNT = 0;
 
     public NUM_BONE_INFLUENCERS = 0;
     public BonesPerMesh = 0;
     public BONETEXTURE = false;
+    public BONES_VELOCITY_ENABLED = false;
 
     public NONUNIFORMSCALING = false;
 
@@ -224,7 +232,7 @@ export class PBRMaterialDefines extends MaterialDefines
     public CLEARCOAT_TEXTUREDIRECTUV = 0;
     public CLEARCOAT_BUMP = false;
     public CLEARCOAT_BUMPDIRECTUV = 0;
-    public CLEARCOAT_REMAPP_F0 = true;
+    public CLEARCOAT_REMAP_F0 = true;
     public CLEARCOAT_TINT = false;
     public CLEARCOAT_TINT_TEXTURE = false;
     public CLEARCOAT_TINT_TEXTUREDIRECTUV = 0;
@@ -818,6 +826,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     public readonly subSurface: PBRSubSurfaceConfiguration;
 
     /**
+     * Defines additionnal PrePass parameters for the material.
+     */
+    public readonly prePassConfiguration: PrePassConfiguration;
+
+    /**
      * Defines the detail map parameters for the material.
      */
     public readonly detailMap = new DetailMapConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
@@ -850,6 +863,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
         this._environmentBRDFTexture = BRDFTextureTools.GetEnvironmentBRDFTexture(scene);
         this.subSurface = new PBRSubSurfaceConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this), this._markScenePrePassDirty.bind(this), scene);
+        this.prePassConfiguration = new PrePassConfiguration();
     }
 
     /**
@@ -1282,6 +1296,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         PBRSheenConfiguration.AddUniforms(uniforms);
         PBRSheenConfiguration.AddSamplers(samplers);
 
+        PrePassConfiguration.AddUniforms(uniforms);
+        PrePassConfiguration.AddSamplers(uniforms);
+
         if (ImageProcessingConfiguration) {
             ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
             ImageProcessingConfiguration.PrepareSamplers(samplers, defines);
@@ -1748,6 +1765,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             this.bindOnlyWorldMatrix(world);
         }
 
+        // PrePass
+        this.prePassConfiguration.bindForSubMesh(this._activeEffect, scene, mesh, world, this.isFrozen);
+
         // Normal Matrix
         if (defines.OBJECTSPACE_NORMALMAP) {
             world.toNormalMatrix(this._normalMatrix);
@@ -1757,7 +1777,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         let mustRebind = this._mustRebind(scene, effect, mesh.visibility);
 
         // Bones
-        MaterialHelper.BindBonesParameters(mesh, this._activeEffect);
+        MaterialHelper.BindBonesParameters(mesh, this._activeEffect, this.prePassConfiguration);
 
         let reflectionTexture: Nullable<BaseTexture> = null;
         let ubo = this._uniformBuffer;

+ 2 - 2
src/Materials/PBR/pbrClearCoatConfiguration.ts

@@ -21,7 +21,7 @@ export interface IMaterialClearCoatDefines {
     CLEARCOAT_TEXTUREDIRECTUV: number;
     CLEARCOAT_BUMP: boolean;
     CLEARCOAT_BUMPDIRECTUV: number;
-    CLEARCOAT_REMAPP_F0: boolean;
+    CLEARCOAT_REMAP_F0: boolean;
 
     CLEARCOAT_TINT: boolean;
     CLEARCOAT_TINT_TEXTURE: boolean;
@@ -195,7 +195,7 @@ export class PBRClearCoatConfiguration {
     public prepareDefines(defines: IMaterialClearCoatDefines, scene: Scene): void {
         if (this._isEnabled) {
             defines.CLEARCOAT = true;
-            defines.CLEARCOAT_REMAPP_F0 = this._remapF0OnInterfaceChange;
+            defines.CLEARCOAT_REMAP_F0 = this._remapF0OnInterfaceChange;
 
             if (defines._areTexturesDirty) {
                 if (scene.texturesEnabled) {

+ 1 - 0
src/Materials/PBR/pbrSubSurfaceConfiguration.ts

@@ -253,6 +253,7 @@ export class PBRSubSurfaceConfiguration {
     }
     /** @hidden */
     public _markScenePrePassDirty(): void {
+        this._internalMarkAllSubMeshesAsTexturesDirty();
         this._internalMarkScenePrePassDirty();
     }
 

+ 5 - 3
src/Materials/Textures/Procedurals/proceduralTexture.ts

@@ -22,6 +22,7 @@ import { _TypeStore } from '../../../Misc/typeStore';
 import { NodeMaterial } from '../../Node/nodeMaterial';
 import { RenderTargetTextureSize } from '../../../Engines/Extensions/engine.renderTarget';
 import { EngineStore } from '../../../Engines/engineStore';
+import { Constants } from '../../../Engines/constants';
 
 /**
  * Procedural texturing is a way to programmatically create a texture. There are 2 types of procedural textures: code-only, and code that references some classic 2D images, sometimes calmpler' images.
@@ -114,8 +115,9 @@ export class ProceduralTexture extends Texture {
      * @param fallbackTexture Define a fallback texture in case there were issues to create the custom texture
      * @param generateMipMaps Define if the texture should creates mip maps or not
      * @param isCube Define if the texture is a cube texture or not (this will render each faces of the cube)
+     * @param textureType The FBO internal texture type
      */
-    constructor(name: string, size: RenderTargetTextureSize, fragment: any, scene: Nullable<Scene>, fallbackTexture: Nullable<Texture> = null, generateMipMaps = true, isCube = false) {
+    constructor(name: string, size: RenderTargetTextureSize, fragment: any, scene: Nullable<Scene>, fallbackTexture: Nullable<Texture> = null, generateMipMaps = true, isCube = false, textureType = Constants.TEXTURETYPE_UNSIGNED_INT) {
         super(null, scene, !generateMipMaps);
 
         scene = this.getScene() || EngineStore.LastCreatedScene!;
@@ -138,11 +140,11 @@ export class ProceduralTexture extends Texture {
         this._fallbackTexture = fallbackTexture;
 
         if (isCube) {
-            this._texture = this._fullEngine.createRenderTargetCubeTexture(size as number, { generateMipMaps: generateMipMaps, generateDepthBuffer: false, generateStencilBuffer: false });
+            this._texture = this._fullEngine.createRenderTargetCubeTexture(size as number, { generateMipMaps: generateMipMaps, generateDepthBuffer: false, generateStencilBuffer: false, type: textureType });
             this.setFloat("face", 0);
         }
         else {
-            this._texture = this._fullEngine.createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, generateDepthBuffer: false, generateStencilBuffer: false });
+            this._texture = this._fullEngine.createRenderTargetTexture(size, { generateMipMaps: generateMipMaps, generateDepthBuffer: false, generateStencilBuffer: false, type: textureType });
         }
 
         // VBO

+ 17 - 0
src/Materials/material.ts

@@ -130,6 +130,11 @@ export class Material implements IAnimatable {
     public static readonly MiscDirtyFlag = Constants.MATERIAL_MiscDirtyFlag;
 
     /**
+     * The dirty prepass flag value
+     */
+    public static readonly PrePassDirtyFlag = Constants.MATERIAL_PrePassDirtyFlag;
+
+    /**
      * The all dirty flag value
      */
     public static readonly AllDirtyFlag = Constants.MATERIAL_AllDirtyFlag;
@@ -1156,6 +1161,7 @@ export class Material implements IAnimatable {
     private static readonly _TextureDirtyCallBack = (defines: MaterialDefines) => defines.markAsTexturesDirty();
     private static readonly _FresnelDirtyCallBack = (defines: MaterialDefines) => defines.markAsFresnelDirty();
     private static readonly _MiscDirtyCallBack = (defines: MaterialDefines) => defines.markAsMiscDirty();
+    private static readonly _PrePassDirtyCallBack = (defines: MaterialDefines) => defines.markAsPrePassDirty();
     private static readonly _LightsDirtyCallBack = (defines: MaterialDefines) => defines.markAsLightDirty();
     private static readonly _AttributeDirtyCallBack = (defines: MaterialDefines) => defines.markAsAttributesDirty();
 
@@ -1207,6 +1213,10 @@ export class Material implements IAnimatable {
             Material._DirtyCallbackArray.push(Material._MiscDirtyCallBack);
         }
 
+        if (flag & Material.PrePassDirtyFlag) {
+            Material._DirtyCallbackArray.push(Material._PrePassDirtyCallBack);
+        }
+
         if (Material._DirtyCallbackArray.length) {
             this._markAllSubMeshesAsDirty(Material._RunDirtyCallBacks);
         }
@@ -1313,6 +1323,13 @@ export class Material implements IAnimatable {
     }
 
     /**
+     * Indicates that prepass needs to be re-calculated for all submeshes
+     */
+    protected _markAllSubMeshesAsPrePassDirty() {
+        this._markAllSubMeshesAsDirty(Material._MiscDirtyCallBack);
+    }
+
+    /**
      * Indicates that textures and misc need to be re-calculated for all submeshes
      */
     protected _markAllSubMeshesAsTexturesAndMiscDirty() {

+ 11 - 0
src/Materials/materialDefines.ts

@@ -21,6 +21,8 @@ export class MaterialDefines {
     /** @hidden */
     public _areMiscDirty = true;
     /** @hidden */
+    public _arePrePassDirty = true;
+    /** @hidden */
     public _areImageProcessingDirty = true;
 
     /** @hidden */
@@ -53,6 +55,7 @@ export class MaterialDefines {
         this._areLightsDirty = false;
         this._areLightsDisposed = false;
         this._areMiscDirty = false;
+        this._arePrePassDirty = false;
         this._areImageProcessingDirty = false;
     }
 
@@ -127,6 +130,14 @@ export class MaterialDefines {
     }
 
     /**
+     * Marks the prepass state as changed
+     */
+    public markAsPrePassDirty() {
+        this._arePrePassDirty = true;
+        this._isDirty = true;
+    }
+
+    /**
      * Rebuilds the material defines
      */
     public rebuild() {

+ 71 - 23
src/Materials/materialHelper.ts

@@ -9,6 +9,7 @@ import { Mesh } from "../Meshes/mesh";
 import { VertexBuffer } from "../Meshes/buffer";
 import { Light } from "../Lights/light";
 import { Constants } from "../Engines/constants";
+import { PrePassConfiguration } from "../Materials/prePassConfiguration";
 
 import { UniformBuffer } from "./uniformBuffer";
 import { Effect, IEffectCreationOptions } from "./effect";
@@ -203,6 +204,12 @@ export class MaterialHelper {
             } else {
                 defines["BonesPerMesh"] = (mesh.skeleton.bones.length + 1);
                 defines["BONETEXTURE"] = materialSupportsBoneTexture ? false : undefined;
+
+                const prePassRenderer = mesh.getScene().prePassRenderer;
+                if (prePassRenderer && prePassRenderer.enabled) {
+                    const nonExcluded = prePassRenderer.excludedSkinnedMesh.indexOf(mesh) === -1;
+                    defines["BONES_VELOCITY_ENABLED"] = nonExcluded;
+                }
             }
         } else {
             defines["NUM_BONE_INFLUENCERS"] = 0;
@@ -303,37 +310,63 @@ export class MaterialHelper {
      * @param canRenderToMRT Indicates if this material renders to several textures in the prepass
      */
     public static PrepareDefinesForPrePass(scene: Scene, defines: any, canRenderToMRT: boolean) {
-        var previousPrePass = defines.PREPASS;
+        const previousPrePass = defines.PREPASS;
+
+        if (!defines._arePrePassDirty) {
+            return;
+        }
+
+        const texturesList = [
+        {
+            type: Constants.PREPASS_POSITION_TEXTURE_TYPE,
+            define: "PREPASS_POSITION",
+            index: "PREPASS_POSITION_INDEX",
+        },
+        {
+            type: Constants.PREPASS_VELOCITY_TEXTURE_TYPE,
+            define: "PREPASS_VELOCITY",
+            index: "PREPASS_VELOCITY_INDEX",
+        },
+        {
+            type: Constants.PREPASS_REFLECTIVITY_TEXTURE_TYPE,
+            define: "PREPASS_REFLECTIVITY",
+            index: "PREPASS_REFLECTIVITY_INDEX",
+        },
+        {
+            type: Constants.PREPASS_IRRADIANCE_TEXTURE_TYPE,
+            define: "PREPASS_IRRADIANCE",
+            index: "PREPASS_IRRADIANCE_INDEX",
+        },
+        {
+            type: Constants.PREPASS_ALBEDO_TEXTURE_TYPE,
+            define: "PREPASS_ALBEDO",
+            index: "PREPASS_ALBEDO_INDEX",
+        },
+        {
+            type: Constants.PREPASS_DEPTHNORMAL_TEXTURE_TYPE,
+            define: "PREPASS_DEPTHNORMAL",
+            index: "PREPASS_DEPTHNORMAL_INDEX",
+        }];
 
         if (scene.prePassRenderer && scene.prePassRenderer.enabled && canRenderToMRT) {
             defines.PREPASS = true;
             defines.SCENE_MRT_COUNT = scene.prePassRenderer.mrtCount;
 
-            const irradianceIndex = scene.prePassRenderer.getIndex(Constants.PREPASS_IRRADIANCE_TEXTURE_TYPE);
-            if (irradianceIndex !== -1) {
-                defines.PREPASS_IRRADIANCE = true;
-                defines.PREPASS_IRRADIANCE_INDEX = irradianceIndex;
-            } else {
-                defines.PREPASS_IRRADIANCE = false;
-            }
-
-            const albedoIndex = scene.prePassRenderer.getIndex(Constants.PREPASS_ALBEDO_TEXTURE_TYPE);
-            if (albedoIndex !== -1) {
-                defines.PREPASS_ALBEDO = true;
-                defines.PREPASS_ALBEDO_INDEX = albedoIndex;
-            } else {
-                defines.PREPASS_ALBEDO = false;
+            for (let i = 0; i < texturesList.length; i++) {
+                const index = scene.prePassRenderer.getIndex(texturesList[i].type);
+                if (index !== -1) {
+                    defines[texturesList[i].define] = true;
+                    defines[texturesList[i].index] = index;
+                } else {
+                    defines[texturesList[i].define] = false;
+                }
             }
 
-            const depthNormalIndex = scene.prePassRenderer.getIndex(Constants.PREPASS_DEPTHNORMAL_TEXTURE_TYPE);
-            if (depthNormalIndex !== -1) {
-                defines.PREPASS_DEPTHNORMAL = true;
-                defines.PREPASS_DEPTHNORMAL_INDEX = depthNormalIndex;
-            } else {
-                defines.PREPASS_DEPTHNORMAL = false;
-            }
         } else {
             defines.PREPASS = false;
+            for (let i = 0; i < texturesList.length; i++) {
+                defines[texturesList[i].define] = false;
+            }
         }
 
         if (defines.PREPASS != previousPrePass) {
@@ -802,8 +835,9 @@ export class MaterialHelper {
      * Binds the bones information from the mesh to the effect.
      * @param mesh The mesh we are binding the information to render
      * @param effect The effect we are binding the data to
+     * @param prePassConfiguration Configuration for the prepass, in case prepass is activated
      */
-    public static BindBonesParameters(mesh?: AbstractMesh, effect?: Effect): void {
+    public static BindBonesParameters(mesh?: AbstractMesh, effect?: Effect, prePassConfiguration?: PrePassConfiguration): void {
         if (!effect || !mesh) {
             return;
         }
@@ -823,11 +857,25 @@ export class MaterialHelper {
 
                 if (matrices) {
                     effect.setMatrices("mBones", matrices);
+                    if (prePassConfiguration && mesh.getScene().prePassRenderer && mesh.getScene().prePassRenderer!.getIndex(Constants.PREPASS_VELOCITY_TEXTURE_TYPE)) {
+                        if (prePassConfiguration.previousBones[mesh.uniqueId]) {
+                            effect.setMatrices("mPreviousBones", prePassConfiguration.previousBones[mesh.uniqueId]);
+                        }
+
+                        MaterialHelper._CopyBonesTransformationMatrices(matrices, prePassConfiguration.previousBones[mesh.uniqueId]);
+                    }
                 }
             }
         }
     }
 
+    // Copies the bones transformation matrices into the target array and returns the target's reference
+    private static _CopyBonesTransformationMatrices(source: Float32Array, target: Float32Array): Float32Array {
+        target.set(source);
+
+        return target;
+    }
+
     /**
      * Binds the morph targets information from the mesh to the effect.
      * @param abstractMesh The mesh we are binding the information to render

+ 70 - 0
src/Materials/prePassConfiguration.ts

@@ -0,0 +1,70 @@
+import { Matrix } from "../Maths/math.vector";
+import { Mesh } from "../Meshes/mesh";
+import { Scene } from "../scene";
+import { Effect } from "../Materials/effect";
+import { Constants } from "../Engines/constants";
+
+/**
+ * Configuration needed for prepass-capable materials
+ */
+export class PrePassConfiguration {
+    /**
+     * Previous world matrices of meshes carrying this material
+     * Used for computing velocity
+     */
+    public previousWorldMatrices: { [index: number]: Matrix } = {};
+    /**
+     * Previous view project matrix
+     * Used for computing velocity
+     */
+    public previousViewProjection: Matrix;
+    /**
+     * Previous bones of meshes carrying this material
+     * Used for computing velocity
+     */
+    public previousBones: { [index: number]: Float32Array } = {};
+
+    /**
+     * Add the required uniforms to the current list.
+     * @param uniforms defines the current uniform list.
+     */
+    public static AddUniforms(uniforms: string[]): void {
+        uniforms.push("previousWorld", "previousViewProjection");
+    }
+
+    /**
+     * Add the required samplers to the current list.
+     * @param samplers defines the current sampler list.
+     */
+    public static AddSamplers(samplers: string[]): void {
+        // pass
+    }
+
+    /**
+     * Binds the material data.
+     * @param effect defines the effect to update
+     * @param scene defines the scene the material belongs to.
+     * @param mesh The mesh
+     * @param world World matrix of this mesh
+     * @param isFrozen Is the material frozen
+     */
+    public bindForSubMesh(effect: Effect, scene: Scene, mesh: Mesh, world: Matrix, isFrozen: boolean): void {
+        if (scene.prePassRenderer && scene.prePassRenderer.enabled) {
+            if (scene.prePassRenderer.getIndex(Constants.PREPASS_VELOCITY_TEXTURE_TYPE) !== -1) {
+                if (!this.previousWorldMatrices[mesh.uniqueId]) {
+                    this.previousWorldMatrices[mesh.uniqueId] = Matrix.Identity();
+                }
+
+                if (!this.previousViewProjection) {
+                    this.previousViewProjection = scene.getTransformMatrix();
+                }
+
+                effect.setMatrix("previousWorld", this.previousWorldMatrices[mesh.uniqueId]);
+                effect.setMatrix("previousViewProjection", this.previousViewProjection);
+
+                this.previousWorldMatrices[mesh.uniqueId] = world.clone();
+                this.previousViewProjection = scene.getTransformMatrix().clone();
+            }
+        }
+    }
+}

+ 20 - 0
src/Materials/standardMaterial.ts

@@ -11,6 +11,7 @@ import { VertexBuffer } from "../Meshes/buffer";
 import { SubMesh } from "../Meshes/subMesh";
 import { AbstractMesh } from "../Meshes/abstractMesh";
 import { Mesh } from "../Meshes/mesh";
+import { PrePassConfiguration } from "./prePassConfiguration";
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "./imageProcessingConfiguration";
 import { ColorCurves } from "./colorCurves";
@@ -86,6 +87,7 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr
     public NUM_BONE_INFLUENCERS = 0;
     public BonesPerMesh = 0;
     public BONETEXTURE = false;
+    public BONES_VELOCITY_ENABLED = false;
     public INSTANCES = false;
     public THIN_INSTANCES = false;
     public GLOSSINESS = false;
@@ -132,6 +134,12 @@ export class StandardMaterialDefines extends MaterialDefines implements IImagePr
     public PREPASS_ALBEDO_INDEX = -1;
     public PREPASS_DEPTHNORMAL = false;
     public PREPASS_DEPTHNORMAL_INDEX = -1;
+    public PREPASS_POSITION = false;
+    public PREPASS_POSITION_INDEX = -1;
+    public PREPASS_VELOCITY = false;
+    public PREPASS_VELOCITY_INDEX = -1;
+    public PREPASS_REFLECTIVITY = false;
+    public PREPASS_REFLECTIVITY_INDEX = -1;
     public SCENE_MRT_COUNT = 0;
 
     public RGBDLIGHTMAP = false;
@@ -581,6 +589,11 @@ export class StandardMaterial extends PushMaterial {
     }
 
     /**
+     * Defines additionnal PrePass parameters for the material.
+     */
+    public readonly prePassConfiguration: PrePassConfiguration;
+
+    /**
      * Gets wether the color curves effect is enabled.
      */
     public get cameraColorCurvesEnabled(): boolean {
@@ -713,6 +726,7 @@ export class StandardMaterial extends PushMaterial {
 
         // Setup the default processing configuration to the scene.
         this._attachImageProcessingConfiguration(null);
+        this.prePassConfiguration = new PrePassConfiguration();
 
         this.getRenderTargetTextures = (): SmartArray<RenderTargetTexture> => {
             this._renderTargets.reset();
@@ -1190,6 +1204,9 @@ export class StandardMaterial extends PushMaterial {
             DetailMapConfiguration.AddUniforms(uniforms);
             DetailMapConfiguration.AddSamplers(samplers);
 
+            PrePassConfiguration.AddUniforms(uniforms);
+            PrePassConfiguration.AddSamplers(uniforms);
+
             if (ImageProcessingConfiguration) {
                 ImageProcessingConfiguration.PrepareUniforms(uniforms, defines);
                 ImageProcessingConfiguration.PrepareSamplers(samplers, defines);
@@ -1362,6 +1379,9 @@ export class StandardMaterial extends PushMaterial {
             this.bindOnlyWorldMatrix(world);
         }
 
+        // PrePass
+        this.prePassConfiguration.bindForSubMesh(this._activeEffect, scene, mesh, world, this.isFrozen);
+
         // Normal Matrix
         if (defines.OBJECTSPACE_NORMALMAP) {
             world.toNormalMatrix(this._normalMatrix);

+ 4 - 21
src/PostProcesses/RenderPipeline/Pipelines/ssao2RenderingPipeline.ts

@@ -119,8 +119,6 @@ export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
     */
     private _sampleSphere: number[];
 
-    private _ssao2PrePassConfiguration: SSAO2Configuration;
-
     /**
     * Blur filter offsets
     */
@@ -212,7 +210,6 @@ export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
             scene.enableGeometryBufferRenderer();
         } else {
             this._prePassRenderer = <PrePassRenderer>scene.enablePrePassRenderer();
-            this._prePassRenderer.markAsDirty();
         }
 
         this._createRandomTexture();
@@ -235,7 +232,6 @@ export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
         if (cameras) {
             scene.postProcessRenderPipelineManager.attachCamerasToRenderPipeline(name, cameras);
         }
-
     }
 
     // Public Methods
@@ -269,7 +265,6 @@ export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
         }
 
         this._scene.postProcessRenderPipelineManager.detachCamerasFromRenderPipeline(this._name, this._scene.cameras);
-        this._ssao2PrePassConfiguration.enabled = false;
 
         super.dispose();
     }
@@ -450,6 +445,10 @@ export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
             effect.setTextureFromPostProcess("originalColor", this._originalColorPostProcess);
         };
         this._ssaoCombinePostProcess.samples = this.textureSamples;
+
+        if (!this._forceGeometryBuffer) {
+            this._ssaoCombinePostProcess._prePassEffectConfiguration = new SSAO2Configuration();
+        }
     }
 
     private _createRandomTexture(): void {
@@ -508,22 +507,6 @@ export class SSAO2RenderingPipeline extends PostProcessRenderPipeline {
     public static Parse(source: any, scene: Scene, rootUrl: string): SSAO2RenderingPipeline {
         return SerializationHelper.Parse(() => new SSAO2RenderingPipeline(source._name, scene, source._ratio), source, scene, rootUrl);
     }
-
-    /**
-     * Sets the required values to the prepass renderer.
-     * @param prePassRenderer defines the prepass renderer to setup
-     * @returns true if the pre pass is needed.
-     */
-    public setPrePassRenderer(prePassRenderer: PrePassRenderer): boolean {
-        let cfg = this._ssao2PrePassConfiguration;
-        if (!cfg) {
-            cfg = new SSAO2Configuration();
-        }
-
-        cfg.enabled = true;
-        this._ssao2PrePassConfiguration = prePassRenderer.addEffectConfiguration(cfg);
-        return true;
-    }
 }
 
 _TypeStore.RegisteredTypes["BABYLON.SSAO2RenderingPipeline"] = SSAO2RenderingPipeline;

+ 48 - 12
src/PostProcesses/motionBlurPostProcess.ts

@@ -7,6 +7,8 @@ import { PostProcess, PostProcessOptions } from "./postProcess";
 import { Constants } from "../Engines/constants";
 import { GeometryBufferRenderer } from "../Rendering/geometryBufferRenderer";
 import { AbstractMesh } from "../Meshes/abstractMesh";
+import { MotionBlurConfiguration } from "../Rendering/motionBlurConfiguration";
+import { PrePassRenderer } from "../Rendering/prePassRenderer";
 
 import "../Animations/animatable";
 import '../Rendering/geometryBufferRendererSceneComponent';
@@ -56,7 +58,10 @@ export class MotionBlurPostProcess extends PostProcess {
 
     @serialize("motionBlurSamples")
     private _motionBlurSamples: number = 32;
+
+    private _forceGeometryBuffer: boolean = false;
     private _geometryBufferRenderer: Nullable<GeometryBufferRenderer>;
+    private _prePassRenderer: PrePassRenderer;
 
     /**
      * Gets a string identifying the name of the class
@@ -77,20 +82,31 @@ export class MotionBlurPostProcess extends PostProcess {
      * @param reusable If the post process can be reused on the same frame. (default: false)
      * @param textureType Type of textures used when performing the post process. (default: 0)
      * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
+     * @param forceGeometryBuffer If this post process should use geometry buffer instead of prepass (default: false)
      */
-    constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+    constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false, forceGeometryBuffer = false) {
         super(name, "motionBlur", ["motionStrength", "motionScale", "screenSize"], ["velocitySampler"], options, camera, samplingMode, engine, reusable, "#define GEOMETRY_SUPPORTED\n#define SAMPLES 64.0", textureType, undefined, null, blockCompilation);
 
-        this._geometryBufferRenderer = scene.enableGeometryBufferRenderer();
+        this._forceGeometryBuffer = forceGeometryBuffer;
+
+        // Set up assets
+        if (this._forceGeometryBuffer) {
+            this._geometryBufferRenderer = scene.enableGeometryBufferRenderer();
+
+            if (this._geometryBufferRenderer) {
+                this._geometryBufferRenderer.enableVelocity = true;
+            }
+        } else {
+            this._prePassRenderer = <PrePassRenderer>scene.enablePrePassRenderer();
+            this._prePassRenderer.markAsDirty();
+            this._prePassEffectConfiguration = new MotionBlurConfiguration();
+        }
 
-        if (!this._geometryBufferRenderer) {
-            // Geometry buffer renderer is not supported. So, work as a passthrough.
+        if (!this._geometryBufferRenderer && !this._prePassRenderer) {
+            // We can't get a velocity texture. So, work as a passthrough.
             Logger.Warn("Multiple Render Target support needed to compute object based motion blur");
             this.updateEffect();
         } else {
-            // Geometry buffer renderer is supported.
-            this._geometryBufferRenderer.enableVelocity = true;
-
             this.onApply = (effect: Effect) => {
                 effect.setVector2("screenSize", new Vector2(this.width, this.height));
 
@@ -100,6 +116,9 @@ export class MotionBlurPostProcess extends PostProcess {
                 if (this._geometryBufferRenderer) {
                     const velocityIndex = this._geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.VELOCITY_TEXTURE_TYPE);
                     effect.setTexture("velocitySampler", this._geometryBufferRenderer.getGBuffer().textures[velocityIndex]);
+                } else {
+                    const velocityIndex = this._prePassRenderer.getIndex(Constants.PREPASS_VELOCITY_TEXTURE_TYPE);
+                    effect.setTexture("velocitySampler", this._prePassRenderer.prePassRT.textures[velocityIndex]);
                 }
             };
         }
@@ -111,8 +130,16 @@ export class MotionBlurPostProcess extends PostProcess {
      * @param skinnedMesh The mesh containing the skeleton to ignore when computing the velocity map.
      */
     public excludeSkinnedMesh(skinnedMesh: AbstractMesh): void {
-        if (this._geometryBufferRenderer && skinnedMesh.skeleton) {
-            this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity.push(skinnedMesh);
+        if (skinnedMesh.skeleton) {
+            let list;
+            if (this._geometryBufferRenderer) {
+                list = this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity;
+            } else if (this._prePassRenderer) {
+                list = this._prePassRenderer.excludedSkinnedMesh;
+            } else {
+                return;
+            }
+            list.push(skinnedMesh);
         }
     }
 
@@ -122,10 +149,19 @@ export class MotionBlurPostProcess extends PostProcess {
      * @see excludeSkinnedMesh to exclude a skinned mesh from bones velocity computation.
      */
     public removeExcludedSkinnedMesh(skinnedMesh: AbstractMesh): void {
-        if (this._geometryBufferRenderer && skinnedMesh.skeleton) {
-            const index = this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity.indexOf(skinnedMesh);
+        if (skinnedMesh.skeleton) {
+            let list;
+            if (this._geometryBufferRenderer) {
+                list = this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity;
+            } else if (this._prePassRenderer) {
+                list = this._prePassRenderer.excludedSkinnedMesh;
+            } else {
+                return;
+            }
+
+            const index = list.indexOf(skinnedMesh);
             if (index !== -1) {
-                this._geometryBufferRenderer.excludedSkinnedMeshesFromVelocity.splice(index, 1);
+                list.splice(index, 1);
             }
         }
     }

+ 23 - 0
src/PostProcesses/postProcess.ts

@@ -19,6 +19,8 @@ declare type Scene = import("../scene").Scene;
 declare type InternalTexture = import("../Materials/Textures/internalTexture").InternalTexture;
 declare type WebVRFreeCamera = import("../Cameras/VR/webVRCamera").WebVRFreeCamera;
 declare type Animation = import("../Animations/animation").Animation;
+declare type PrePassRenderer = import("../Rendering/prePassRenderer").PrePassRenderer;
+declare type PrePassEffectConfiguration = import("../Rendering/prePassEffectConfiguration").PrePassEffectConfiguration;
 
 /**
  * Size options for a post process
@@ -187,6 +189,12 @@ export class PostProcess {
     private _forcedOutputTexture: Nullable<InternalTexture>;
 
     /**
+    * Prepass configuration in case this post process needs a texture from prepass
+    * @hidden
+    */
+    public _prePassEffectConfiguration: PrePassEffectConfiguration;
+
+    /**
      * Returns the fragment url or shader name used in the post process.
      * @returns the fragment url or name in the shader store.
      */
@@ -679,6 +687,21 @@ export class PostProcess {
     }
 
     /**
+     * Sets the required values to the prepass renderer.
+     * @param prePassRenderer defines the prepass renderer to setup.
+     * @returns true if the pre pass is needed.
+     */
+    public setPrePassRenderer(prePassRenderer: PrePassRenderer): boolean {
+        if (this._prePassEffectConfiguration) {
+            this._prePassEffectConfiguration = prePassRenderer.addEffectConfiguration(this._prePassEffectConfiguration);
+            this._prePassEffectConfiguration.enabled = true;
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
      * Disposes the post process.
      * @param camera The camera to dispose the post process on.
      */

+ 47 - 17
src/PostProcesses/screenSpaceReflectionPostProcess.ts

@@ -5,6 +5,8 @@ import { PostProcess, PostProcessOptions } from "./postProcess";
 import { Constants } from "../Engines/constants";
 import { GeometryBufferRenderer } from '../Rendering/geometryBufferRenderer';
 import { serialize, SerializationHelper } from '../Misc/decorators';
+import { PrePassRenderer } from "../Rendering/prePassRenderer";
+import { ScreenSpaceReflectionsConfiguration } from "../Rendering/screenSpaceReflectionsConfiguration";
 
 import "../Shaders/screenSpaceReflection.fragment";
 import { _TypeStore } from '../Misc/typeStore';
@@ -43,7 +45,9 @@ export class ScreenSpaceReflectionPostProcess extends PostProcess {
     @serialize()
     public roughnessFactor: number = 0.2;
 
+    private _forceGeometryBuffer: boolean = false;
     private _geometryBufferRenderer: Nullable<GeometryBufferRenderer>;
+    private _prePassRenderer: PrePassRenderer;
     private _enableSmoothReflections: boolean = false;
     private _reflectionSamples: number = 64;
     private _smoothSteps: number = 5;
@@ -67,8 +71,9 @@ export class ScreenSpaceReflectionPostProcess extends PostProcess {
      * @param reusable If the post process can be reused on the same frame. (default: false)
      * @param textureType Type of textures used when performing the post process. (default: 0)
      * @param blockCompilation If compilation of the shader should not be done in the constructor. The updateEffect method can be used to compile the shader at a later time. (default: false)
+     * @param forceGeometryBuffer If this post process should use geometry buffer instead of prepass (default: false)
      */
-    constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false) {
+    constructor(name: string, scene: Scene, options: number | PostProcessOptions, camera: Nullable<Camera>, samplingMode?: number, engine?: Engine, reusable?: boolean, textureType: number = Constants.TEXTURETYPE_UNSIGNED_INT, blockCompilation = false, forceGeometryBuffer = false) {
         super(name, "screenSpaceReflection", [
             "projection", "view", "threshold", "reflectionSpecularFalloffExponent", "strength", "step", "roughnessFactor"
         ], [
@@ -77,31 +82,53 @@ export class ScreenSpaceReflectionPostProcess extends PostProcess {
         "#define SSR_SUPPORTED\n#define REFLECTION_SAMPLES 64\n#define SMOOTH_STEPS 5\n",
         textureType, undefined, null, blockCompilation);
 
-        // Get geometry buffer renderer and update effect
-        const geometryBufferRenderer = scene.enableGeometryBufferRenderer();
-        if (geometryBufferRenderer) {
-            if (geometryBufferRenderer.isSupported) {
-                geometryBufferRenderer.enablePosition = true;
-                geometryBufferRenderer.enableReflectivity = true;
-                this._geometryBufferRenderer = geometryBufferRenderer;
+        this._forceGeometryBuffer = forceGeometryBuffer;
+
+        if (this._forceGeometryBuffer) {
+            // Get geometry buffer renderer and update effect
+            const geometryBufferRenderer = scene.enableGeometryBufferRenderer();
+            if (geometryBufferRenderer) {
+                if (geometryBufferRenderer.isSupported) {
+                    geometryBufferRenderer.enablePosition = true;
+                    geometryBufferRenderer.enableReflectivity = true;
+                    this._geometryBufferRenderer = geometryBufferRenderer;
+                }
             }
+        } else {
+            this._prePassRenderer = <PrePassRenderer>scene.enablePrePassRenderer();
+            this._prePassRenderer.markAsDirty();
+            this._prePassEffectConfiguration = new ScreenSpaceReflectionsConfiguration();
         }
 
         this._updateEffectDefines();
 
         // On apply, send uniforms
         this.onApply = (effect: Effect) => {
-            if (!geometryBufferRenderer) {
+            const geometryBufferRenderer = this._geometryBufferRenderer;
+            const prePassRenderer = this._prePassRenderer;
+
+            if (!prePassRenderer && !geometryBufferRenderer) {
                 return;
             }
 
-            // Samplers
-            const positionIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE);
-            const roughnessIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.REFLECTIVITY_TEXTURE_TYPE);
-
-            effect.setTexture("normalSampler", geometryBufferRenderer.getGBuffer().textures[1]);
-            effect.setTexture("positionSampler", geometryBufferRenderer.getGBuffer().textures[positionIndex]);
-            effect.setTexture("reflectivitySampler", geometryBufferRenderer.getGBuffer().textures[roughnessIndex]);
+            if (geometryBufferRenderer) {
+                // Samplers
+                const positionIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.POSITION_TEXTURE_TYPE);
+                const roughnessIndex = geometryBufferRenderer.getTextureIndex(GeometryBufferRenderer.REFLECTIVITY_TEXTURE_TYPE);
+
+                effect.setTexture("normalSampler", geometryBufferRenderer.getGBuffer().textures[1]);
+                effect.setTexture("positionSampler", geometryBufferRenderer.getGBuffer().textures[positionIndex]);
+                effect.setTexture("reflectivitySampler", geometryBufferRenderer.getGBuffer().textures[roughnessIndex]);
+            } else {
+                // Samplers
+                const positionIndex = prePassRenderer.getIndex(Constants.PREPASS_POSITION_TEXTURE_TYPE);
+                const roughnessIndex = prePassRenderer.getIndex(Constants.PREPASS_REFLECTIVITY_TEXTURE_TYPE);
+                const normalIndex = prePassRenderer.getIndex(Constants.PREPASS_DEPTHNORMAL_TEXTURE_TYPE);
+
+                effect.setTexture("normalSampler", prePassRenderer.prePassRT.textures[normalIndex]);
+                effect.setTexture("positionSampler", prePassRenderer.prePassRT.textures[positionIndex]);
+                effect.setTexture("reflectivitySampler", prePassRenderer.prePassRT.textures[roughnessIndex]);
+            }
 
             // Uniforms
             const camera = scene.activeCamera;
@@ -192,8 +219,11 @@ export class ScreenSpaceReflectionPostProcess extends PostProcess {
 
     private _updateEffectDefines(): void {
         const defines: string[] = [];
-        if (this._geometryBufferRenderer) {
+        if (this._geometryBufferRenderer || this._prePassRenderer) {
             defines.push("#define SSR_SUPPORTED");
+            if (this._prePassRenderer) {
+                defines.push("#define PREPASS_LAYOUT");
+            }
         }
         if (this._enableSmoothReflections) {
             defines.push("#define ENABLE_SMOOTH_REFLECTIONS");

+ 26 - 0
src/Rendering/motionBlurConfiguration.ts

@@ -0,0 +1,26 @@
+import { Constants } from "../Engines/constants";
+import { PrePassEffectConfiguration } from "./prePassEffectConfiguration";
+import { _DevTools } from '../Misc/devTools';
+
+/**
+ * Contains all parameters needed for the prepass to perform
+ * motion blur
+ */
+export class MotionBlurConfiguration implements PrePassEffectConfiguration {
+    /**
+     * Is motion blur enabled
+     */
+    public enabled = false;
+
+    /**
+     * Name of the configuration
+     */
+    public name = "motionBlur";
+
+    /**
+     * Textures that should be present in the MRT for this effect to work
+     */
+    public readonly texturesRequired: number[] = [
+        Constants.PREPASS_VELOCITY_TEXTURE_TYPE
+    ];
+}

+ 1 - 1
src/Rendering/prePassEffectConfiguration.ts

@@ -23,7 +23,7 @@ export interface PrePassEffectConfiguration {
     /**
      * Disposes the effect configuration
      */
-    dispose(): void;
+    dispose?: () => void;
     /**
      * Creates the associated post process
      */

+ 36 - 35
src/Rendering/prePassRenderer.ts

@@ -5,10 +5,13 @@ import { Constants } from "../Engines/constants";
 import { ImageProcessingPostProcess } from "../PostProcesses/imageProcessingPostProcess";
 import { PostProcess } from "../PostProcesses/postProcess";
 import { Effect } from "../Materials/effect";
-import { Logger } from '../Misc/logger';
 import { _DevTools } from '../Misc/devTools';
 import { Color4 } from "../Maths/math.color";
 import { PrePassEffectConfiguration } from "./prePassEffectConfiguration";
+import { Nullable } from "../types";
+import { AbstractMesh } from '../Meshes/abstractMesh';
+import { Material } from '../Materials/material';
+
 /**
  * Renders a pre pass of the scene
  * This means every mesh in the scene will be rendered to a render target texture
@@ -50,7 +53,12 @@ export class PrePassRenderer {
             type: Constants.PREPASS_ALBEDO_TEXTURE_TYPE,
             format: Constants.TEXTURETYPE_UNSIGNED_INT,
         },
-];
+    ];
+
+    /**
+     * To save performance, we can excluded skinned meshes from the prepass
+     */
+    public excludedSkinnedMesh: AbstractMesh[] = [];
 
     private _textureIndices: number[] = [];
 
@@ -86,16 +94,6 @@ export class PrePassRenderer {
      */
     private _effectConfigurations: PrePassEffectConfiguration[] = [];
 
-    /**
-     * Should materials render their geometry on the MRT
-     */
-    public materialsShouldRenderGeometry: boolean = false;
-
-    /**
-     * Should materials render the irradiance information on the MRT
-     */
-    public materialsShouldRenderIrradiance: boolean = false;
-
     private _mrtFormats: number[] = [];
     private _mrtLayout: number[];
 
@@ -259,16 +257,6 @@ export class PrePassRenderer {
         }
     }
 
-    private _checkTextureType(type: number) : boolean {
-        if (type < 0 || type >= this._textureFormats.length) {
-            Logger.Error("PrePassRenderer : Unknown texture type");
-            return false;
-        }
-
-        return true;
-
-    }
-
     /**
      * Adds an effect configuration to the prepass.
      * If an effect has already been added, it won't add it twice and will return the configuration
@@ -294,10 +282,6 @@ export class PrePassRenderer {
      * @return The index
      */
     public getIndex(type: number) : number {
-        if (!this._checkTextureType(type)) {
-            return -1;
-        }
-
         return this._textureIndices[type];
     }
 
@@ -389,9 +373,6 @@ export class PrePassRenderer {
     private _enableTextures(types: number[]) {
         for (let i = 0; i < types.length; i++) {
             let type = types[i];
-            if (!this._checkTextureType(type)) {
-                return;
-            }
 
             if (this._textureIndices[type] === -1) {
                 this._textureIndices[type] = this._mrtLayout.length;
@@ -413,13 +394,22 @@ export class PrePassRenderer {
             }
         }
 
-        const pipelines = this._scene.postProcessRenderPipelineManager.supportedPipelines;
-        for (let i = 0; i < pipelines.length; i++) {
-            if (pipelines[i].setPrePassRenderer(this)) {
-                enablePrePass = true;
+        const camera = this._scene.activeCamera;
+        if (!camera) {
+            return;
+        }
+
+        const postProcesses = (<Nullable<PostProcess[]>>camera._postProcesses.filter((pp) => { return pp != null; }));
+
+        if (postProcesses) {
+            for (let i = 0; i < postProcesses.length; i++) {
+                if (postProcesses[i].setPrePassRenderer(this)) {
+                    enablePrePass = true;
+                }
             }
         }
 
+        this._markAllMaterialsAsPrePassDirty();
         this._isDirty = false;
 
         if (enablePrePass) {
@@ -427,7 +417,16 @@ export class PrePassRenderer {
         }
 
         if (!this.enabled) {
-            this._engine.bindAttachments(this._defaultAttachments);
+            // Prepass disabled, we render only on 1 color attachment
+            this._engine.bindAttachments([this._engine._gl.COLOR_ATTACHMENT0]);
+        }
+    }
+
+    private _markAllMaterialsAsPrePassDirty() {
+        const materials = this._scene.materials;
+
+        for (let i = 0; i < materials.length; i++) {
+            materials[i].markAsDirty(Material.PrePassDirtyFlag);
         }
     }
 
@@ -436,7 +435,9 @@ export class PrePassRenderer {
      */
     public dispose() {
         for (let i = 0; i < this._effectConfigurations.length; i++) {
-            this._effectConfigurations[i].dispose();
+            if (this._effectConfigurations[i].dispose) {
+                this._effectConfigurations[i].dispose!();
+            }
         }
 
         this.imageProcessingPostProcess.dispose();

+ 28 - 0
src/Rendering/screenSpaceReflectionsConfiguration.ts

@@ -0,0 +1,28 @@
+import { Constants } from "../Engines/constants";
+import { PrePassEffectConfiguration } from "./prePassEffectConfiguration";
+import { _DevTools } from '../Misc/devTools';
+
+/**
+ * Contains all parameters needed for the prepass to perform
+ * screen space reflections
+ */
+export class ScreenSpaceReflectionsConfiguration implements PrePassEffectConfiguration {
+    /**
+     * Is ssr enabled
+     */
+    public enabled = false;
+
+    /**
+     * Name of the configuration
+     */
+    public name = "screenSpaceReflections";
+
+    /**
+     * Textures that should be present in the MRT for this effect to work
+     */
+    public readonly texturesRequired: number[] = [
+        Constants.PREPASS_DEPTHNORMAL_TEXTURE_TYPE,
+        Constants.PREPASS_REFLECTIVITY_TEXTURE_TYPE,
+        Constants.PREPASS_POSITION_TEXTURE_TYPE,
+    ];
+}

+ 0 - 15
src/Rendering/ssao2Configuration.ts

@@ -23,19 +23,4 @@ export class SSAO2Configuration implements PrePassEffectConfiguration {
     public readonly texturesRequired: number[] = [
         Constants.PREPASS_DEPTHNORMAL_TEXTURE_TYPE
     ];
-
-    /**
-     * Builds a ssao2 configuration object
-     * @param scene The scene
-     */
-    constructor() {
-
-    }
-
-    /**
-     * Disposes the configuration
-     */
-    public dispose() {
-        // pass
-    }
 }

+ 4 - 1
src/Shaders/ShadersInclude/bonesDeclaration.fx

@@ -4,7 +4,10 @@
 		uniform float boneTextureWidth;
 	#else
 		uniform mat4 mBones[BonesPerMesh];
-	#endif	
+		#ifdef BONES_VELOCITY_ENABLED
+		    uniform mat4 mPreviousBones[BonesPerMesh];
+		#endif
+	#endif
 
 	attribute vec4 matricesIndices;
 	attribute vec4 matricesWeights;

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

@@ -128,7 +128,7 @@ struct clearcoatOutParams
         // clearCoatRoughness = mix(0.089, 0.6, clearCoatRoughness);
 
         // Remap F0 to account for the change of interface within the material.
-        #ifdef CLEARCOAT_REMAPP_F0
+        #ifdef CLEARCOAT_REMAP_F0
             vec3 specularEnvironmentR0Updated = getR0RemappedForClearCoat(specularEnvironmentR0);
         #else
             vec3 specularEnvironmentR0Updated = specularEnvironmentR0;

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

@@ -134,8 +134,10 @@ uniform mat4 view;
     uniform vec3 vDiffusionDistance;
     uniform vec4 vTintColor;
     uniform vec3 vSubSurfaceIntensity;
+#endif
 
-    #ifdef SS_SCATTERING
+#ifdef PREPASS
+    #ifdef PREPASS_IRRADIANCE
         uniform float scatteringDiffusionProfile;
     #endif
 #endif

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

@@ -27,8 +27,4 @@ varying vec3 vPositionW;
 
 #ifdef VERTEXCOLOR
     varying vec4 vColor;
-#endif
-
-#ifdef PREPASS
-	varying vec3 vViewPos;
-#endif
+#endif

+ 9 - 0
src/Shaders/ShadersInclude/prePassDeclaration.fx

@@ -2,4 +2,13 @@
 #extension GL_EXT_draw_buffers : require
 layout(location = 0) out vec4 glFragData[{X}];
 vec4 gl_FragColor;
+
+#ifdef PREPASS_DEPTHNORMAL
+    varying vec3 vViewPos;
+#endif
+#ifdef PREPASS_VELOCITY
+    varying vec4 vCurrentPosition;
+    varying vec4 vPreviousPosition;
+#endif
+
 #endif

+ 37 - 0
src/Shaders/ShadersInclude/prePassVertex.fx

@@ -0,0 +1,37 @@
+#ifdef PREPASS_DEPTHNORMAL
+    vViewPos = (view * worldPos).rgb;
+#endif
+
+#if defined(PREPASS_VELOCITY) && defined(BONES_VELOCITY_ENABLED)
+    vCurrentPosition = viewProjection * worldPos;
+
+#if NUM_BONE_INFLUENCERS > 0
+    mat4 previousInfluence;
+    previousInfluence = mPreviousBones[int(matricesIndices[0])] * matricesWeights[0];
+    #if NUM_BONE_INFLUENCERS > 1
+        previousInfluence += mPreviousBones[int(matricesIndices[1])] * matricesWeights[1];
+    #endif  
+    #if NUM_BONE_INFLUENCERS > 2
+        previousInfluence += mPreviousBones[int(matricesIndices[2])] * matricesWeights[2];
+    #endif  
+    #if NUM_BONE_INFLUENCERS > 3
+        previousInfluence += mPreviousBones[int(matricesIndices[3])] * matricesWeights[3];
+    #endif
+    #if NUM_BONE_INFLUENCERS > 4
+        previousInfluence += mPreviousBones[int(matricesIndicesExtra[0])] * matricesWeightsExtra[0];
+    #endif  
+    #if NUM_BONE_INFLUENCERS > 5
+        previousInfluence += mPreviousBones[int(matricesIndicesExtra[1])] * matricesWeightsExtra[1];
+    #endif  
+    #if NUM_BONE_INFLUENCERS > 6
+        previousInfluence += mPreviousBones[int(matricesIndicesExtra[2])] * matricesWeightsExtra[2];
+    #endif  
+    #if NUM_BONE_INFLUENCERS > 7
+        previousInfluence += mPreviousBones[int(matricesIndicesExtra[3])] * matricesWeightsExtra[3];
+    #endif
+
+    vPreviousPosition = previousViewProjection * previousWorld * previousInfluence * vec4(positionUpdated, 1.0);
+#else
+    vPreviousPosition = previousViewProjection * previousWorld * vec4(positionUpdated, 1.0);
+#endif
+#endif

+ 11 - 0
src/Shaders/ShadersInclude/prePassVertexDeclaration.fx

@@ -0,0 +1,11 @@
+#ifdef PREPASS
+#ifdef PREPASS_DEPTHNORMAL
+    varying vec3 vViewPos;
+#endif
+#ifdef PREPASS_VELOCITY
+    uniform mat4 previousWorld;
+    uniform mat4 previousViewProjection;
+    varying vec4 vCurrentPosition;
+    varying vec4 vPreviousPosition;
+#endif
+#endif

+ 6 - 0
src/Shaders/ShadersInclude/subSurfaceScatteringFunctions.fx

@@ -1,8 +1,14 @@
+#ifdef SS_SCATTERING
 vec3 tagLightingForSSS(vec3 color) {
     color.b = max(color.b, HALF_MIN);
 
     return color;
 }
+#else
+vec3 tagLightingForSSS(vec3 color) {
+    return vec3(0., 0., 0.);
+}
+#endif
 
 bool testLightingForSSS(vec3 color)
 {

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

@@ -37,10 +37,6 @@ varying vec4 vColor;
 	varying vec2 vMainUV2;
 #endif
 
-#ifdef PREPASS
-	varying vec3 vViewPos;
-#endif
-
 // Helper functions
 #include<helperFunctions>
 
@@ -482,6 +478,20 @@ color.rgb = max(color.rgb, 0.);
 #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR
 #ifdef PREPASS
     gl_FragData[0] = color; // We can't split irradiance on std material
+    
+    #ifdef PREPASS_POSITION
+    gl_FragData[PREPASS_POSITION_INDEX] = vec4(vPositionW, 1.0);
+    #endif
+
+    #ifdef PREPASS_VELOCITY
+    vec2 a = (vCurrentPosition.xy / vCurrentPosition.w) * 0.5 + 0.5;
+    vec2 b = (vPreviousPosition.xy / vPreviousPosition.w) * 0.5 + 0.5;
+
+    vec2 velocity = abs(a - b);
+    velocity = vec2(pow(velocity.x, 1.0 / 3.0), pow(velocity.y, 1.0 / 3.0)) * sign(a - b) * 0.5 + 0.5;
+
+    gl_FragData[PREPASS_VELOCITY_INDEX] = vec4(velocity, 0.0, 1.0);
+    #endif
 
     #ifdef PREPASS_IRRADIANCE
         gl_FragData[PREPASS_IRRADIANCE_INDEX] = vec4(0.0, 0.0, 0.0, 1.0); //  We can't split irradiance on std material
@@ -494,6 +504,13 @@ color.rgb = max(color.rgb, 0.);
     #ifdef PREPASS_ALBEDO
         gl_FragData[PREPASS_ALBEDO_INDEX] = vec4(0.0, 0.0, 0.0, 1.0); // We can't split albedo on std material
     #endif
+    #ifdef PREPASS_REFLECTIVITY
+        #if defined(SPECULAR)
+            gl_FragData[PREPASS_REFLECTIVITY_INDEX] = specularMapColor;
+        #else
+            gl_FragData[PREPASS_REFLECTIVITY_INDEX] = vec4(0.0, 0.0, 0.0, 1.0);
+        #endif
+    #endif
 #endif
 	gl_FragColor = color;
 

+ 10 - 7
src/Shaders/default.vertex.fx

@@ -26,10 +26,7 @@ attribute vec4 color;
 
 // Uniforms
 #include<instancesDeclaration>
-
-#ifdef PREPASS
-varying vec3 vViewPos;
-#endif
+#include<prePassVertexDeclaration>
 
 #ifdef MAINUV1
 	varying vec2 vMainUV1;
@@ -128,6 +125,13 @@ void main(void) {
 #define CUSTOM_VERTEX_UPDATE_NORMAL
 
 #include<instancesVertex>
+
+#if defined(PREPASS) && defined(PREPASS_VELOCITY) && !defined(BONES_VELOCITY_ENABLED)
+    // Compute velocity before bones computation
+    vCurrentPosition = viewProjection * finalWorld * vec4(positionUpdated, 1.0);
+    vPreviousPosition = previousViewProjection * previousWorld * vec4(positionUpdated, 1.0);
+#endif
+
 #include<bonesVertex>
 
 	vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0);
@@ -160,9 +164,8 @@ void main(void) {
 #endif	
 
 	vPositionW = vec3(worldPos);
-	#ifdef PREPASS
-	    vViewPos = (view * worldPos).rgb;
-	#endif
+	
+#include<prePassVertex>
 
 #if defined(REFLECTIONMAP_EQUIRECTANGULAR_FIXED) || defined(REFLECTIONMAP_MIRROREDEQUIRECTANGULAR_FIXED)
 	vDirectionW = normalize(vec3(finalWorld * vec4(positionUpdated, 0.0)));

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

@@ -143,6 +143,7 @@ void main(void) {
 
 #if defined(REFLECTIVITY)
     vec4 surfaceMetallicOrReflectivityColorMap = texture2D(reflectivitySampler, vReflectivityUV + uvOffset);
+    vec4 baseReflectivity = surfaceMetallicOrReflectivityColorMap;
     #ifndef METALLICWORKFLOW
         surfaceMetallicOrReflectivityColorMap = toLinearSpace(surfaceMetallicOrReflectivityColorMap);
         surfaceMetallicOrReflectivityColorMap.rgb *= vReflectivityInfos.y;
@@ -502,6 +503,20 @@ void main(void) {
     #define CUSTOM_FRAGMENT_BEFORE_FRAGCOLOR
 
 #ifdef PREPASS
+    #ifdef PREPASS_POSITION
+    gl_FragData[PREPASS_POSITION_INDEX] = vec4(vPositionW, 1.0);
+    #endif
+
+    #ifdef PREPASS_VELOCITY
+    vec2 a = (vCurrentPosition.xy / vCurrentPosition.w) * 0.5 + 0.5;
+    vec2 b = (vPreviousPosition.xy / vPreviousPosition.w) * 0.5 + 0.5;
+
+    vec2 velocity = abs(a - b);
+    velocity = vec2(pow(velocity.x, 1.0 / 3.0), pow(velocity.y, 1.0 / 3.0)) * sign(a - b) * 0.5 + 0.5;
+
+    gl_FragData[PREPASS_VELOCITY_INDEX] = vec4(velocity, 0.0, 1.0);
+    #endif
+
     #ifdef PREPASS_IRRADIANCE
         vec3 irradiance = finalDiffuse;
         #ifndef UNLIT
@@ -529,6 +544,14 @@ void main(void) {
     #ifdef PREPASS_ALBEDO
         gl_FragData[PREPASS_ALBEDO_INDEX] = vec4(sqAlbedo, 1.0); // albedo, for pre and post scatter
     #endif
+
+    #ifdef PREPASS_REFLECTIVITY
+        #if defined(REFLECTIVITY)
+            gl_FragData[PREPASS_REFLECTIVITY_INDEX] = vec4(baseReflectivity.rgb, 1.0);
+        #else
+            gl_FragData[PREPASS_REFLECTIVITY_INDEX] = vec4(0.0, 0.0, 0.0, 1.0);
+        #endif
+    #endif
 #endif
 
     gl_FragColor = finalColor;

+ 10 - 7
src/Shaders/pbr.vertex.fx

@@ -33,10 +33,7 @@ attribute vec4 color;
 
 // Uniforms
 #include<instancesDeclaration>
-
-#ifdef PREPASS
-varying vec3 vViewPos;
-#endif
+#include<prePassVertexDeclaration>
 
 #if defined(ALBEDO) && ALBEDODIRECTUV == 0
 varying vec2 vAlbedoUV;
@@ -173,13 +170,19 @@ void main(void) {
 #define CUSTOM_VERTEX_UPDATE_NORMAL
 
 #include<instancesVertex>
+
+#if defined(PREPASS) && defined(PREPASS_VELOCITY) && !defined(BONES_VELOCITY_ENABLED)
+    // Compute velocity before bones computation
+    vCurrentPosition = viewProjection * finalWorld * vec4(positionUpdated, 1.0);
+    vPreviousPosition = previousViewProjection * previousWorld * vec4(positionUpdated, 1.0);
+#endif
+
 #include<bonesVertex>
 
     vec4 worldPos = finalWorld * vec4(positionUpdated, 1.0);
     vPositionW = vec3(worldPos);
-#ifdef PREPASS
-    vViewPos = (view * worldPos).rgb;
-#endif
+
+#include<prePassVertex>
 
 #ifdef NORMAL
     mat3 normalWorld = mat3(finalWorld);

+ 4 - 0
src/Shaders/screenSpaceReflection.fragment.fx

@@ -135,7 +135,11 @@ void main()
         }
         
         // Get coordinates of the pixel to pick according to the pixel's position and normal.
+        #ifdef PREPASS_LAYOUT
+        vec3 normal = (texture2D(normalSampler, vUV)).gba;
+        #else
         vec3 normal = (texture2D(normalSampler, vUV)).xyz;
+        #endif
         vec3 position = (view * texture2D(positionSampler, vUV)).xyz;
         vec3 reflected = normalize(reflect(normalize(position), normalize(normal)));
 

+ 1 - 1
src/XR/webXRFeaturesManager.ts

@@ -342,7 +342,7 @@ export class WebXRFeaturesManager implements IDisposable {
 
             if (attachIfPossible) {
                 // if session started already, request and enable
-                if (this._xrSessionManager.session && !feature.featureImplementation.attached) {
+                if (this._xrSessionManager.session && !this._features[name].featureImplementation.attached) {
                     // enable feature
                     this.attachFeature(name);
                 }

BIN
tests/validation/ReferenceImages/geometrybufferrenderer.png


BIN
tests/validation/ReferenceImages/motionBlur.png


BIN
tests/validation/ReferenceImages/ssr.png


+ 21 - 0
tests/validation/config.json

@@ -908,6 +908,27 @@
             "playgroundId": "#EIJH8L#1",
             "referenceImage": "ktx2decoder.png",
             "excludeFromAutomaticTesting": true
+        },
+        {
+            "title": "Motion Blur",
+            "playgroundId": "#E5YGEL#2",
+            "referenceImage": "motionBlur.png",
+            "renderCount": 10,
+            "excludeFromAutomaticTesting": true
+        },
+        {
+            "title": "Screen space reflections",
+            "playgroundId": "#PIZ1GK#172",
+            "referenceImage": "ssr.png",
+            "renderCount": 10,
+            "excludeFromAutomaticTesting": true
+        },
+        {
+            "title": "Geometry buffer renderer",
+            "playgroundId": "#PIZ1GK#173",
+            "referenceImage": "geometrybufferrenderer.png",
+            "renderCount": 10,
+            "excludeFromAutomaticTesting": true
         }
     ]
 }