David Catuhe 6 лет назад
Родитель
Сommit
c08da92539
100 измененных файлов с 19670 добавлено и 25287 удалено
  1. 1147 719
      Playground/babylon.d.txt
  2. 21 6
      Playground/js/index.js
  3. 17 14
      Tools/Gulp/tasks/gulpTasks-whatsNew.js
  4. 2 1
      Viewer/src/helper/index.ts
  5. 118 0
      azure-pipelines.yml
  6. 1162 722
      dist/preview release/babylon.d.ts
  7. 2 2
      dist/preview release/babylon.js
  8. 1917 5614
      dist/preview release/babylon.max.js
  9. 1 1
      dist/preview release/babylon.max.js.map
  10. 5854 7901
      dist/preview release/babylon.module.d.ts
  11. 1 1
      dist/preview release/glTF2Interface/package.json
  12. 39 39
      dist/preview release/gui/babylon.gui.js
  13. 1 1
      dist/preview release/gui/babylon.gui.js.map
  14. 2 2
      dist/preview release/gui/package.json
  15. 6 6
      dist/preview release/inspector/babylon.inspector.bundle.js
  16. 21 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js
  17. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.max.js.map
  18. 6 6
      dist/preview release/inspector/package.json
  19. 11 0
      dist/preview release/loaders/babylon.glTF1FileLoader.js
  20. 1 1
      dist/preview release/loaders/babylon.glTF1FileLoader.js.map
  21. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  22. 11 0
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  23. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.js.map
  24. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  25. 11 0
      dist/preview release/loaders/babylon.glTFFileLoader.js
  26. 1 1
      dist/preview release/loaders/babylon.glTFFileLoader.js.map
  27. 2 2
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  28. 20 4
      dist/preview release/loaders/babylon.objFileLoader.js
  29. 1 1
      dist/preview release/loaders/babylon.objFileLoader.js.map
  30. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  31. 31 4
      dist/preview release/loaders/babylonjs.loaders.js
  32. 1 1
      dist/preview release/loaders/babylonjs.loaders.js.map
  33. 2 2
      dist/preview release/loaders/babylonjs.loaders.min.js
  34. 3 3
      dist/preview release/loaders/package.json
  35. 2 2
      dist/preview release/materialsLibrary/package.json
  36. 1 1
      dist/preview release/package.json
  37. 1 1
      dist/preview release/packagesSizeBaseLine.json
  38. 2 2
      dist/preview release/postProcessesLibrary/package.json
  39. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  40. 3 3
      dist/preview release/serializers/package.json
  41. 5854 7901
      dist/preview release/viewer/babylon.module.d.ts
  42. 348 472
      dist/preview release/viewer/babylon.viewer.js
  43. 3 3
      dist/preview release/viewer/babylon.viewer.max.js
  44. 19 7
      dist/preview release/what's new.md
  45. 36 1
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/pbrMaterialPropertyGridComponent.tsx
  46. 22 5
      loaders/src/OBJ/objFileLoader.ts
  47. 13 0
      loaders/src/glTF/glTFFileLoader.ts
  48. 1 1
      package.json
  49. 4 1
      src/Animations/runtimeAnimation.ts
  50. 20 4
      src/Cameras/Inputs/freeCameraMouseInput.ts
  51. 3 2
      src/Cameras/RigModes/vrRigMode.ts
  52. 2 2
      src/Cameras/VR/webVRCamera.ts
  53. 2 7
      src/Cameras/arcRotateCamera.ts
  54. 1241 1268
      src/Cameras/camera.ts
  55. 29 1
      src/Cameras/deviceOrientationCamera.ts
  56. 169 0
      src/Engines/Extensions/engine.multiview.ts
  57. 2 1
      src/Engines/Extensions/index.ts
  58. 42 78
      src/Engines/engine.ts
  59. 1 1
      src/Gamepads/Controllers/gearVRController.ts
  60. 50 11
      src/Loading/Plugins/babylonFileLoader.ts
  61. 1 7
      src/Materials/Background/backgroundMaterial.ts
  62. 65 151
      src/Materials/PBR/pbrBaseMaterial.ts
  63. 3 3
      src/Materials/PBR/pbrClearCoatConfiguration.ts
  64. 33 12
      src/Materials/PBR/pbrMaterial.ts
  65. 551 0
      src/Materials/PBR/pbrSubSurfaceConfiguration.ts
  66. 40 0
      src/Materials/Textures/MultiviewRenderTarget.ts
  67. 4 39
      src/Materials/Textures/renderTargetTexture.ts
  68. 1 1
      src/Materials/Textures/texture.ts
  69. 2 2
      src/Materials/material.ts
  70. 16 0
      src/Materials/materialFlags.ts
  71. 15 0
      src/Materials/materialHelper.ts
  72. 3 7
      src/Materials/standardMaterial.ts
  73. 46 51
      src/Meshes/transformNode.ts
  74. 94 1
      src/Misc/assetsManager.ts
  75. 7 0
      src/Misc/brdfTextureTools.ts
  76. 50 3
      src/Misc/tools.ts
  77. 4 4
      src/Misc/webRequest.ts
  78. 15 11
      src/Physics/Plugins/ammoJSPlugin.ts
  79. 10 1
      src/Physics/Plugins/cannonJSPlugin.ts
  80. 1 2
      src/Physics/physicsEngine.ts
  81. 74 21
      src/Physics/physicsHelper.ts
  82. 1 1
      src/Physics/physicsImpostor.ts
  83. 46 7
      src/PostProcesses/RenderPipeline/Pipelines/standardRenderingPipeline.ts
  84. 2 1
      src/PostProcesses/index.ts
  85. 1 29
      src/PostProcesses/vrDistortionCorrectionPostProcess.ts
  86. 34 0
      src/PostProcesses/vrMultiviewToSingleviewPostProcess.ts
  87. 2 0
      src/Shaders/ShadersInclude/lightFragment.fx
  88. 18 0
      src/Shaders/ShadersInclude/pbrBRDFFunctions.fx
  89. 7 1
      src/Shaders/ShadersInclude/pbrDebug.fx
  90. 16 0
      src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx
  91. 9 2
      src/Shaders/ShadersInclude/pbrDirectLightingSetupFunctions.fx
  92. 20 8
      src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx
  93. 40 27
      src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx
  94. 7 3
      src/Shaders/ShadersInclude/pbrHelperFunctions.fx
  95. 2 2
      src/Shaders/ShadersInclude/pbrIBLFunctions.fx
  96. 10 7
      src/Shaders/ShadersInclude/pbrUboDeclaration.fx
  97. 14 8
      src/Shaders/ShadersInclude/pbrVertexDeclaration.fx
  98. 97 37
      src/Shaders/pbr.fragment.fx
  99. 19 0
      src/Shaders/pbr.vertex.fx
  100. 0 0
      src/Shaders/standard.fragment.fx

Разница между файлами не показана из-за своего большого размера
+ 1147 - 719
Playground/babylon.d.txt


+ 21 - 6
Playground/js/index.js

@@ -626,8 +626,17 @@ function showError(errorMessage, errorEvent) {
 
                     if (scene) {
                         if (showInspector) {
-                            if (!scene.debugLayer.isVisible()) {
-                                scene.debugLayer.show({ embedMode: true });
+                            if(scene.then){
+                                // Handle if scene is a promise
+                                scene.then((s)=>{
+                                    if (!s.debugLayer.isVisible()) {
+                                        s.debugLayer.show({ embedMode: true });
+                                    }
+                                })
+                            }else{
+                                if (!scene.debugLayer.isVisible()) {
+                                    scene.debugLayer.show({ embedMode: true });
+                                }
                             }
                         }
                     }
@@ -694,7 +703,7 @@ function showError(errorMessage, errorEvent) {
 
         var addTexturesToZip = function(zip, index, textures, folder, then) {
 
-            if (index === textures.length) {
+            if (index === textures.length || !textures[index].name) {
                 then();
                 return;
             }
@@ -705,9 +714,15 @@ function showError(errorMessage, errorEvent) {
             }
 
             if (textures[index].isCube) {
-                if (textures[index]._extensions && textures[index].name.indexOf("dds") === -1) {
-                    for (var i = 0; i < 6; i++) {
-                        textures.push({ name: textures[index].name + textures[index]._extensions[i] });
+                if (textures[index].name.indexOf("dds") === -1) {
+                    if (textures[index]._extensions) {
+                        for (var i = 0; i < 6; i++) {
+                            textures.push({ name: textures[index].name + textures[index]._extensions[i] });
+                        }
+                    } else if (textures[index]._files) {
+                        for (var i = 0; i < 6; i++) {
+                            textures.push({ name: textures[index]._files[i] });
+                        }
                     }
                 }
                 else {

+ 17 - 14
Tools/Gulp/tasks/gulpTasks-whatsNew.js

@@ -6,22 +6,25 @@ var fs = require("fs");
  * Tests the whats new file to ensure changes have been made in the PR.
  */
 gulp.task("tests-whatsnew", function(done) {
-    // Only checks on Travis
-    if (!process.env.TRAVIS) {
-        done();
-        return;
-    }
+    // Check status on azure
+    if (!process.env["AZURE_PULLREQUESTID"]) {
+        // Only checks on Travis
+        if (!process.env.TRAVIS) {
+            done();
+            return;
+        }
 
-    // Only checks on Pull Requests
-    if (process.env.TRAVIS_PULL_REQUEST == "false") {
-        done();
-        return;
-    }
+        // Only checks on Pull Requests
+        if (process.env.TRAVIS_PULL_REQUEST == "false") {
+            done();
+            return;
+        }
 
-    // Do not check deploy
-    if (process.env.TRAVIS_BRANCH == "preview") {
-        done();
-        return;
+        // Do not check deploy
+        if (process.env.TRAVIS_BRANCH == "preview") {
+            done();
+            return;
+        }
     }
 
     // Compare what's new with the current one in the preview release folder.

+ 2 - 1
Viewer/src/helper/index.ts

@@ -45,7 +45,8 @@ export function extendClassWithConfig(object: any, config: any) {
         if (key in object && typeof object[key] !== 'function') {
             // if (typeof object[key] === 'function') return;
             // if it is an object, iterate internally until reaching basic types
-            if (typeof object[key] === 'object') {
+            // but null is an object so if its null and config[key] is not an object eg. number, the number should be set
+            if ((typeof object[key] === 'object') && (object[key] !== null || typeof config[key] === "object")) {
                 extendClassWithConfig(object[key], config[key]);
             } else {
                 if (config[key] !== undefined) {

+ 118 - 0
azure-pipelines.yml

@@ -0,0 +1,118 @@
+trigger:
+- master
+
+pr:
+  autoCancel: true
+  branches:
+    include:
+    - master
+
+jobs:
+- job: WhatsNewUpdate
+  displayName: 'What s New Update'
+  pool:
+    vmImage: 'Ubuntu-16.04'
+    demands: npm
+  steps:
+  - task: Npm@1
+    displayName: 'npm install'
+    inputs:
+      workingDir: Tools/Gulp
+      verbose: false
+  - script: 'gulp tests-whatsnew'
+    workingDirectory: Tools/Gulp
+    displayName: 'Whats new'
+    env:
+      AZURE_PULLREQUESTID: $(System.PullRequest.PullRequestId)
+
+- job: DocumentationCheck
+  displayName: 'Documentation Check'
+  pool:
+    vmImage: 'Ubuntu-16.04'
+    demands: npm
+  steps:
+  - task: Npm@1
+    displayName: 'npm install'
+    inputs:
+      workingDir: Tools/Gulp
+      verbose: false
+  - script: 'gulp typedoc-check'
+    workingDirectory: Tools/Gulp
+    displayName: 'Typedoc check'
+
+- job: Linting
+  pool:
+    vmImage: 'Ubuntu-16.04'
+    demands: npm
+  steps:
+  - task: Npm@1
+    displayName: 'npm install'
+    inputs:
+      workingDir: Tools/Gulp
+      verbose: false
+  - script: 'gulp fullLint'
+    workingDirectory: Tools/Gulp
+    displayName: 'Full Lint'
+
+- job: Build
+  pool:
+    vmImage: 'Ubuntu-16.04'
+    demands: npm
+  steps:
+  - task: Npm@1
+    displayName: 'npm install'
+    inputs:
+      workingDir: Tools/Gulp
+      verbose: false
+  - script: 'gulp typescript-all'
+    workingDirectory: Tools/Gulp
+    displayName: 'Typescript all'
+
+- job: Tests
+  pool:
+    vmImage: 'Ubuntu-16.04'
+    demands: npm
+  steps:
+  - task: Npm@1
+    displayName: 'npm install'
+    inputs:
+      workingDir: Tools/Gulp
+      verbose: false
+  - script: 'gulp typescript-all'
+    workingDirectory: Tools/Gulp
+    displayName: 'Typescript all'
+  - script: 'gulp tests-babylon-unit'
+    workingDirectory: Tools/Gulp
+    displayName: 'Unit Tests'
+  - script: |
+      export DISPLAY=:99
+      Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
+      sleep 3 # give xvfb some time to start
+      gulp tests-validation-virtualscreen
+    workingDirectory: Tools/Gulp
+    displayName: 'Visual Tests'
+
+- job: ViewerTests
+  displayName: 'Viewer Tests'
+  pool:
+    vmImage: 'Ubuntu-16.04'
+    demands: npm
+  steps:
+  - task: Npm@1
+    displayName: 'npm install'
+    inputs:
+      workingDir: Tools/Gulp
+      verbose: false
+  - script: 'gulp typescript-all'
+    workingDirectory: Tools/Gulp
+    displayName: 'Typescript all'
+  - script: 'gulp tests-viewer-unit'
+    workingDirectory: Tools/Gulp
+    displayName: 'Unit Tests'
+  - script: |
+      export DISPLAY=:99
+      Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &
+      sleep 3 # give xvfb some time to start
+      gulp tests-viewer-validation-virtualscreen
+    workingDirectory: Tools/Gulp
+    displayName: 'Visual Tests'

Разница между файлами не показана из-за своего большого размера
+ 1162 - 722
dist/preview release/babylon.d.ts


Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/babylon.js


Разница между файлами не показана из-за своего большого размера
+ 1917 - 5614
dist/preview release/babylon.max.js


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/babylon.max.js.map


Разница между файлами не показана из-за своего большого размера
+ 5854 - 7901
dist/preview release/babylon.module.d.ts


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

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

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

@@ -7,7 +7,7 @@
 		exports["babylonjs-gui"] = factory(require("babylonjs"));
 	else
 		root["BABYLON"] = root["BABYLON"] || {}, root["BABYLON"]["GUI"] = factory(root["BABYLON"]);
-})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__) {
+})((typeof self !== "undefined" ? self : typeof global !== "undefined" ? global : this), function(__WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_tools__) {
 return /******/ (function(modules) { // webpackBootstrap
 /******/ 	// The module cache
 /******/ 	var installedModules = {};
@@ -355,7 +355,7 @@ module.exports = g;
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTextureInstrumentation", function() { return AdvancedDynamicTextureInstrumentation; });
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_0__);
 
 /**
@@ -498,7 +498,7 @@ var AdvancedDynamicTextureInstrumentation = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AdvancedDynamicTexture", function() { return AdvancedDynamicTexture; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _controls_container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./controls/container */ "./2D/controls/container.ts");
 /* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./style */ "./2D/style.ts");
@@ -1619,7 +1619,7 @@ var Button = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Checkbox", function() { return Checkbox; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -1800,7 +1800,7 @@ var Checkbox = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ColorPicker", function() { return ColorPicker; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _inputText__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./inputText */ "./2D/controls/inputText.ts");
@@ -3247,7 +3247,7 @@ var ColorPicker = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container", function() { return Container; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/logger */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_logger__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -3652,7 +3652,7 @@ var Container = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control", function() { return Control; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _measure__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../measure */ "./2D/measure.ts");
@@ -6212,7 +6212,7 @@ var Grid = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Image", function() { return Image; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 
@@ -6987,7 +6987,7 @@ var InputPassword = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "InputText", function() { return InputText; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -7996,7 +7996,7 @@ var InputText = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Line", function() { return Line; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -8264,7 +8264,7 @@ var Line = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLine", function() { return MultiLine; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/abstractMesh */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_abstractMesh__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _multiLinePoint__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../multiLinePoint */ "./2D/multiLinePoint.ts");
@@ -8531,7 +8531,7 @@ var MultiLine = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RadioButton", function() { return RadioButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
@@ -8876,7 +8876,7 @@ var Rectangle = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScrollViewer", function() { return ScrollViewer; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Events/pointerEvents */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Events/pointerEvents */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Events_pointerEvents__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _rectangle__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../rectangle */ "./2D/controls/rectangle.ts");
 /* harmony import */ var _grid__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../grid */ "./2D/controls/grid.ts");
@@ -9967,7 +9967,7 @@ var SelectionPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "BaseSlider", function() { return BaseSlider; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../control */ "./2D/controls/control.ts");
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../valueAndUnit */ "./2D/valueAndUnit.ts");
@@ -10862,7 +10862,7 @@ var Slider = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel", function() { return StackPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container */ "./2D/controls/container.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -11120,7 +11120,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextWrapping", function() { return TextWrapping; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "TextBlock", function() { return TextBlock; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../valueAndUnit */ "./2D/valueAndUnit.ts");
 /* harmony import */ var _control__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./control */ "./2D/controls/control.ts");
@@ -11560,7 +11560,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "KeyPropertySet", function() { return KeyPropertySet; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VirtualKeyboard", function() { return VirtualKeyboard; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _stackPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./stackPanel */ "./2D/controls/stackPanel.ts");
 /* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./button */ "./2D/controls/button.ts");
@@ -11935,7 +11935,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector2WithInfo", function() { return Vector2WithInfo; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Matrix2D", function() { return Matrix2D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -12159,7 +12159,7 @@ var Matrix2D = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Measure", function() { return Measure; });
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__);
 
 
@@ -12292,7 +12292,7 @@ var Measure = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiLinePoint", function() { return MultiLinePoint; });
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -12435,7 +12435,7 @@ var MultiLinePoint = /** @class */ (function () {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Style", function() { return Style; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _valueAndUnit__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./valueAndUnit */ "./2D/valueAndUnit.ts");
 
@@ -12742,7 +12742,7 @@ var ValueAndUnit = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AbstractButton3D", function() { return AbstractButton3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -12785,7 +12785,7 @@ var AbstractButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Button3D", function() { return Button3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _abstractButton3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./abstractButton3D */ "./3D/controls/abstractButton3D.ts");
 /* harmony import */ var _2D_advancedDynamicTexture__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../../2D/advancedDynamicTexture */ "./2D/advancedDynamicTexture.ts");
@@ -12962,7 +12962,7 @@ var Button3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Container3D", function() { return Container3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Meshes/transformNode */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Meshes_transformNode__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _control3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./control3D */ "./3D/controls/control3D.ts");
 
@@ -13119,7 +13119,7 @@ var Container3D = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Control3D", function() { return Control3D; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _vector3WithInfo__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../vector3WithInfo */ "./3D/vector3WithInfo.ts");
 
@@ -13513,7 +13513,7 @@ var Control3D = /** @class */ (function () {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "CylinderPanel", function() { return CylinderPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -13598,7 +13598,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HolographicButton", function() { return HolographicButton; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
 /* harmony import */ var _button3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./button3D */ "./3D/controls/button3D.ts");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_2__);
 /* harmony import */ var _materials_fluentMaterial__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ../materials/fluentMaterial */ "./3D/materials/fluentMaterial.ts");
 /* harmony import */ var _2D_controls_stackPanel__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! ../../2D/controls/stackPanel */ "./2D/controls/stackPanel.ts");
@@ -14074,7 +14074,7 @@ var MeshButton3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "PlanePanel", function() { return PlanePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
@@ -14129,7 +14129,7 @@ var PlanePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "ScatterPanel", function() { return ScatterPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -14256,7 +14256,7 @@ var ScatterPanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "SpherePanel", function() { return SpherePanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _volumeBasedPanel__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./volumeBasedPanel */ "./3D/controls/volumeBasedPanel.ts");
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
@@ -14341,7 +14341,7 @@ var SpherePanel = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "StackPanel3D", function() { return StackPanel3D; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -14466,7 +14466,7 @@ var StackPanel3D = /** @class */ (function (_super) {
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "VolumeBasedPanel", function() { return VolumeBasedPanel; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/tools */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_tools__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _container3D__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./container3D */ "./3D/controls/container3D.ts");
 
@@ -14657,7 +14657,7 @@ var VolumeBasedPanel = /** @class */ (function (_super) {
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "GUI3DManager", function() { return GUI3DManager; });
-/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Misc/observable */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__);
 /* harmony import */ var _controls_container3D__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./controls/container3D */ "./3D/controls/container3D.ts");
 
@@ -14924,7 +14924,7 @@ __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterialDefines", function() { return FluentMaterialDefines; });
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FluentMaterial", function() { return FluentMaterial; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Misc/decorators */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Misc_decorators__WEBPACK_IMPORTED_MODULE_1__);
 /* harmony import */ var _shaders_fluent_vertex__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./shaders/fluent.vertex */ "./3D/materials/shaders/fluent.vertex.ts");
 /* harmony import */ var _shaders_fluent_fragment__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./shaders/fluent.fragment */ "./3D/materials/shaders/fluent.fragment.ts");
@@ -15246,7 +15246,7 @@ __webpack_require__.r(__webpack_exports__);
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentPixelShader", function() { return fluentPixelShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentPixelShader';
@@ -15268,7 +15268,7 @@ var fluentPixelShader = { name: name, shader: shader };
 "use strict";
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "fluentVertexShader", function() { return fluentVertexShader; });
-/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! babylonjs/Materials/effect */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Materials_effect__WEBPACK_IMPORTED_MODULE_0__);
 
 var name = 'fluentVertexShader';
@@ -15291,7 +15291,7 @@ var fluentVertexShader = { name: name, shader: shader };
 __webpack_require__.r(__webpack_exports__);
 /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "Vector3WithInfo", function() { return Vector3WithInfo; });
 /* harmony import */ var tslib__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! tslib */ "../../node_modules/tslib/tslib.es6.js");
-/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/observable");
+/* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! babylonjs/Maths/math */ "babylonjs/Misc/tools");
 /* harmony import */ var babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_1__);
 
 
@@ -15585,14 +15585,14 @@ if (typeof globalObject !== "undefined") {
 
 /***/ }),
 
-/***/ "babylonjs/Misc/observable":
+/***/ "babylonjs/Misc/tools":
 /*!****************************************************************************************************!*\
   !*** external {"root":"BABYLON","commonjs":"babylonjs","commonjs2":"babylonjs","amd":"babylonjs"} ***!
   \****************************************************************************************************/
 /*! no static exports found */
 /***/ (function(module, exports) {
 
-module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_observable__;
+module.exports = __WEBPACK_EXTERNAL_MODULE_babylonjs_Misc_tools__;
 
 /***/ })
 

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/gui/babylon.gui.js.map


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

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

Разница между файлами не показана из-за своего большого размера
+ 6 - 6
dist/preview release/inspector/babylon.inspector.bundle.js


+ 21 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js

@@ -37366,6 +37366,7 @@ var PBRMaterialPropertyGridComponent = /** @class */ (function (_super) {
             { label: "ClearCoat Tint Map", value: 28 },
             { label: "Sheen Map", value: 29 },
             { label: "Anisotropic Map", value: 30 },
+            { label: "Thickness Map", value: 31 },
             // Env
             { label: "Env Refraction", value: 40 },
             { label: "Env Reflection", value: 41 },
@@ -37385,6 +37386,8 @@ var PBRMaterialPropertyGridComponent = /** @class */ (function (_super) {
             { label: "ClearCoat Color", value: 65 },
             { label: "ClearCoat Roughness", value: 66 },
             { label: "ClearCoat NdotV", value: 67 },
+            { label: "Transmittance", value: 68 },
+            { label: "Refraction Transmittance", value: 69 },
             // Misc
             { label: "SEO", value: 70 },
             { label: "EHO", value: 71 },
@@ -37443,6 +37446,24 @@ var PBRMaterialPropertyGridComponent = /** @class */ (function (_super) {
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Intensity", target: material.sheen, propertyName: "intensity", minimum: 0, maximum: 1, step: 0.01, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_color3LineComponent__WEBPACK_IMPORTED_MODULE_4__["Color3LineComponent"], { label: "Color", target: material.sheen, propertyName: "color", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                         react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textureLinkLineComponent__WEBPACK_IMPORTED_MODULE_10__["TextureLinkLineComponent"], { label: "Texture", texture: material.sheen.texture, material: material, onSelectionChangedObservable: this.props.onSelectionChangedObservable, onDebugSelectionChangeObservable: this._onDebugSelectionChangeObservable }))),
+            react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "SUBSURFACE" },
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_textureLinkLineComponent__WEBPACK_IMPORTED_MODULE_10__["TextureLinkLineComponent"], { label: "Thickness", texture: material.subSurface.thicknessTexture, material: material, onSelectionChangedObservable: this.props.onSelectionChangedObservable, onDebugSelectionChangeObservable: this._onDebugSelectionChangeObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Min Thickness", target: material.subSurface, propertyName: "minimumThickness", minimum: 0, maximum: 10, step: 0.1, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Max Thickness", target: material.subSurface, propertyName: "maximumThickness", minimum: 0, maximum: 10, step: 0.1, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Mask From Thickness", target: material.subSurface, propertyName: "useMaskFromThicknessTexture", onValueChanged: function () { return _this.forceUpdate(); }, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_color3LineComponent__WEBPACK_IMPORTED_MODULE_4__["Color3LineComponent"], { label: "Tint Color", target: material.subSurface, propertyName: "tintColor", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Refraction Enabled", target: material.subSurface, propertyName: "isRefractionEnabled", onValueChanged: function () { return _this.forceUpdate(); }, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                material.subSurface.isRefractionEnabled &&
+                    react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "fragment" },
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Intensity", target: material.subSurface, propertyName: "refractionIntensity", minimum: 0, maximum: 1, step: 0.01, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Index of Refraction", target: material.subSurface, propertyName: "indexOfRefraction", minimum: 1, maximum: 2, step: 0.01, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Tint at Distance", target: material.subSurface, propertyName: "tintColorAtDistance", minimum: 0, maximum: 10, step: 0.1, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Link refraction with transparency", target: material.subSurface, propertyName: "linkRefractionWithTransparency", onPropertyChangedObservable: this.props.onPropertyChangedObservable })),
+                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Transluency Enabled", target: material.subSurface, propertyName: "isTranslucencyEnabled", onValueChanged: function () { return _this.forceUpdate(); }, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                material.subSurface.isTranslucencyEnabled &&
+                    react__WEBPACK_IMPORTED_MODULE_1__["createElement"]("div", { className: "fragment" },
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Intensity", target: material.subSurface, propertyName: "translucencyIntensity", minimum: 0, maximum: 1, step: 0.01, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
+                        react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_color3LineComponent__WEBPACK_IMPORTED_MODULE_4__["Color3LineComponent"], { label: "Diffusion Distance", target: material.subSurface, propertyName: "diffusionDistance", onPropertyChangedObservable: this.props.onPropertyChangedObservable }))),
             react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lineContainerComponent__WEBPACK_IMPORTED_MODULE_3__["LineContainerComponent"], { globalState: this.props.globalState, title: "LEVELS", closed: true },
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Environment", target: material, propertyName: "environmentIntensity", minimum: 0, maximum: 1, step: 0.01, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_sliderLineComponent__WEBPACK_IMPORTED_MODULE_7__["SliderLineComponent"], { label: "Specular", target: material, propertyName: "specularIntensity", minimum: 0, maximum: 1, step: 0.01, onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
@@ -37458,7 +37479,6 @@ var PBRMaterialPropertyGridComponent = /** @class */ (function (_super) {
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Alpha from albedo", target: material, propertyName: "useAlphaFromAlbedoTexture", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Ambient in grayscale", target: material, propertyName: "useAmbientInGrayScale", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Radiance over alpha", target: material, propertyName: "useRadianceOverAlpha", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
-                react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Link refraction with transparency", target: material, propertyName: "linkRefractionWithTransparency", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Micro-surface from ref. map alpha", target: material, propertyName: "useMicroSurfaceFromReflectivityMapAlpha", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Specular over alpha", target: material, propertyName: "useSpecularOverAlpha", onPropertyChangedObservable: this.props.onPropertyChangedObservable }),
                 react__WEBPACK_IMPORTED_MODULE_1__["createElement"](_lines_checkBoxLineComponent__WEBPACK_IMPORTED_MODULE_5__["CheckBoxLineComponent"], { label: "Specular anti-aliasing", target: material, propertyName: "enableSpecularAntiAliasing", onPropertyChangedObservable: this.props.onPropertyChangedObservable })),

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.max.js.map


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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.32",
+    "version": "4.0.0-beta.2",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -31,11 +31,11 @@
     "dependencies": {
         "@types/react": "~16.7.3",
         "@types/react-dom": "~16.0.9",
-        "babylonjs": "4.0.0-alpha.32",
-        "babylonjs-gui": "4.0.0-alpha.32",
-        "babylonjs-loaders": "4.0.0-alpha.32",
-        "babylonjs-serializers": "4.0.0-alpha.32",
-        "babylonjs-gltf2interface": "4.0.0-alpha.32"
+        "babylonjs": "4.0.0-beta.2",
+        "babylonjs-gui": "4.0.0-beta.2",
+        "babylonjs-loaders": "4.0.0-beta.2",
+        "babylonjs-serializers": "4.0.0-beta.2",
+        "babylonjs-gltf2interface": "4.0.0-beta.2"
     },
     "engines": {
         "node": "*"

+ 11 - 0
dist/preview release/loaders/babylon.glTF1FileLoader.js

@@ -3104,12 +3104,23 @@ var GLTFFileLoader = /** @class */ (function () {
         return this._parseAsync(scene, data, rootUrl, fileName).then(function (loaderData) {
             _this._log("Loading " + (fileName || ""));
             _this._loader = _this._getLoader(loaderData);
+            // Get materials/textures when loading to add to container
+            var materials = [];
+            _this.onMaterialLoadedObservable.add(function (material) {
+                materials.push(material);
+            });
+            var textures = [];
+            _this.onTextureLoadedObservable.add(function (texture) {
+                textures.push(texture);
+            });
             return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fileName).then(function (result) {
                 var container = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["AssetContainer"](scene);
                 Array.prototype.push.apply(container.meshes, result.meshes);
                 Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                 Array.prototype.push.apply(container.skeletons, result.skeletons);
                 Array.prototype.push.apply(container.animationGroups, result.animationGroups);
+                Array.prototype.push.apply(container.materials, materials);
+                Array.prototype.push.apply(container.textures, textures);
                 container.removeAllFromScene();
                 return container;
             });

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylon.glTF1FileLoader.js.map


Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


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

@@ -3763,12 +3763,23 @@ var GLTFFileLoader = /** @class */ (function () {
         return this._parseAsync(scene, data, rootUrl, fileName).then(function (loaderData) {
             _this._log("Loading " + (fileName || ""));
             _this._loader = _this._getLoader(loaderData);
+            // Get materials/textures when loading to add to container
+            var materials = [];
+            _this.onMaterialLoadedObservable.add(function (material) {
+                materials.push(material);
+            });
+            var textures = [];
+            _this.onTextureLoadedObservable.add(function (texture) {
+                textures.push(texture);
+            });
             return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fileName).then(function (result) {
                 var container = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["AssetContainer"](scene);
                 Array.prototype.push.apply(container.meshes, result.meshes);
                 Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                 Array.prototype.push.apply(container.skeletons, result.skeletons);
                 Array.prototype.push.apply(container.animationGroups, result.animationGroups);
+                Array.prototype.push.apply(container.materials, materials);
+                Array.prototype.push.apply(container.textures, textures);
                 container.removeAllFromScene();
                 return container;
             });

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.js.map


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


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

@@ -6312,12 +6312,23 @@ var GLTFFileLoader = /** @class */ (function () {
         return this._parseAsync(scene, data, rootUrl, fileName).then(function (loaderData) {
             _this._log("Loading " + (fileName || ""));
             _this._loader = _this._getLoader(loaderData);
+            // Get materials/textures when loading to add to container
+            var materials = [];
+            _this.onMaterialLoadedObservable.add(function (material) {
+                materials.push(material);
+            });
+            var textures = [];
+            _this.onTextureLoadedObservable.add(function (texture) {
+                textures.push(texture);
+            });
             return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fileName).then(function (result) {
                 var container = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["AssetContainer"](scene);
                 Array.prototype.push.apply(container.meshes, result.meshes);
                 Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                 Array.prototype.push.apply(container.skeletons, result.skeletons);
                 Array.prototype.push.apply(container.animationGroups, result.animationGroups);
+                Array.prototype.push.apply(container.materials, materials);
+                Array.prototype.push.apply(container.textures, textures);
                 container.removeAllFromScene();
                 return container;
             });

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylon.glTFFileLoader.js.map


Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/loaders/babylon.glTFFileLoader.min.js


+ 20 - 4
dist/preview release/loaders/babylon.objFileLoader.js

@@ -530,6 +530,22 @@ var OBJFileLoader = /** @class */ (function () {
         return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
             var container = new babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["AssetContainer"](scene);
             result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });
+            result.meshes.forEach(function (mesh) {
+                var material = mesh.material;
+                if (material) {
+                    // Materials
+                    if (container.materials.indexOf(material) == -1) {
+                        container.materials.push(material);
+                        // Textures
+                        var textures = material.getActiveTextures();
+                        textures.forEach(function (t) {
+                            if (container.textures.indexOf(t) == -1) {
+                                container.textures.push(t);
+                            }
+                        });
+                    }
+                }
+            });
             container.removeAllFromScene();
             return container;
         });
@@ -595,7 +611,7 @@ var OBJFileLoader = /** @class */ (function () {
                 arr[obj[0]] = { normals: [], idx: [], uv: [] };
             }
             var idx = arr[obj[0]].normals.indexOf(obj[1]);
-            if (idx != 1 && (obj[2] == arr[obj[0]].uv[idx])) {
+            if (idx != 1 && (obj[2] === arr[obj[0]].uv[idx])) {
                 return arr[obj[0]].idx[idx];
             }
             return -1;
@@ -630,7 +646,7 @@ var OBJFileLoader = /** @class */ (function () {
                 ]);
             }
             //If it not exists
-            if (_index == -1) {
+            if (_index === -1) {
                 //Add an new indice.
                 //The array of indices is only an array with his length equal to the number of triangles - 1.
                 //We add vertices data in this order
@@ -1054,7 +1070,7 @@ var OBJFileLoader = /** @class */ (function () {
             //check meshesNames (stlFileLoader)
             if (meshesNames && meshesFromObj[j].name) {
                 if (meshesNames instanceof Array) {
-                    if (meshesNames.indexOf(meshesFromObj[j].name) == -1) {
+                    if (meshesNames.indexOf(meshesFromObj[j].name) === -1) {
                         continue;
                     }
                 }
@@ -1120,7 +1136,7 @@ var OBJFileLoader = /** @class */ (function () {
                                 startIndex = _index + 1;
                             }
                             //If the material is not used dispose it
-                            if (_index == -1 && _indices.length == 0) {
+                            if (_index === -1 && _indices.length === 0) {
                                 //If the material is not needed, remove it
                                 materialsFromMTLFile.materials[n].dispose();
                             }

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.js.map


Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


+ 31 - 4
dist/preview release/loaders/babylonjs.loaders.js

@@ -748,6 +748,22 @@ var OBJFileLoader = /** @class */ (function () {
         return this.importMeshAsync(null, scene, data, rootUrl).then(function (result) {
             var container = new babylonjs_Maths_math__WEBPACK_IMPORTED_MODULE_0__["AssetContainer"](scene);
             result.meshes.forEach(function (mesh) { return container.meshes.push(mesh); });
+            result.meshes.forEach(function (mesh) {
+                var material = mesh.material;
+                if (material) {
+                    // Materials
+                    if (container.materials.indexOf(material) == -1) {
+                        container.materials.push(material);
+                        // Textures
+                        var textures = material.getActiveTextures();
+                        textures.forEach(function (t) {
+                            if (container.textures.indexOf(t) == -1) {
+                                container.textures.push(t);
+                            }
+                        });
+                    }
+                }
+            });
             container.removeAllFromScene();
             return container;
         });
@@ -813,7 +829,7 @@ var OBJFileLoader = /** @class */ (function () {
                 arr[obj[0]] = { normals: [], idx: [], uv: [] };
             }
             var idx = arr[obj[0]].normals.indexOf(obj[1]);
-            if (idx != 1 && (obj[2] == arr[obj[0]].uv[idx])) {
+            if (idx != 1 && (obj[2] === arr[obj[0]].uv[idx])) {
                 return arr[obj[0]].idx[idx];
             }
             return -1;
@@ -848,7 +864,7 @@ var OBJFileLoader = /** @class */ (function () {
                 ]);
             }
             //If it not exists
-            if (_index == -1) {
+            if (_index === -1) {
                 //Add an new indice.
                 //The array of indices is only an array with his length equal to the number of triangles - 1.
                 //We add vertices data in this order
@@ -1272,7 +1288,7 @@ var OBJFileLoader = /** @class */ (function () {
             //check meshesNames (stlFileLoader)
             if (meshesNames && meshesFromObj[j].name) {
                 if (meshesNames instanceof Array) {
-                    if (meshesNames.indexOf(meshesFromObj[j].name) == -1) {
+                    if (meshesNames.indexOf(meshesFromObj[j].name) === -1) {
                         continue;
                     }
                 }
@@ -1338,7 +1354,7 @@ var OBJFileLoader = /** @class */ (function () {
                                 startIndex = _index + 1;
                             }
                             //If the material is not used dispose it
-                            if (_index == -1 && _indices.length == 0) {
+                            if (_index === -1 && _indices.length === 0) {
                                 //If the material is not needed, remove it
                                 materialsFromMTLFile.materials[n].dispose();
                             }
@@ -7628,12 +7644,23 @@ var GLTFFileLoader = /** @class */ (function () {
         return this._parseAsync(scene, data, rootUrl, fileName).then(function (loaderData) {
             _this._log("Loading " + (fileName || ""));
             _this._loader = _this._getLoader(loaderData);
+            // Get materials/textures when loading to add to container
+            var materials = [];
+            _this.onMaterialLoadedObservable.add(function (material) {
+                materials.push(material);
+            });
+            var textures = [];
+            _this.onTextureLoadedObservable.add(function (texture) {
+                textures.push(texture);
+            });
             return _this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fileName).then(function (result) {
                 var container = new babylonjs_Misc_observable__WEBPACK_IMPORTED_MODULE_0__["AssetContainer"](scene);
                 Array.prototype.push.apply(container.meshes, result.meshes);
                 Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                 Array.prototype.push.apply(container.skeletons, result.skeletons);
                 Array.prototype.push.apply(container.animationGroups, result.animationGroups);
+                Array.prototype.push.apply(container.materials, materials);
+                Array.prototype.push.apply(container.textures, textures);
                 container.removeAllFromScene();
                 return container;
             });

Разница между файлами не показана из-за своего большого размера
+ 1 - 1
dist/preview release/loaders/babylonjs.loaders.js.map


Разница между файлами не показана из-за своего большого размера
+ 2 - 2
dist/preview release/loaders/babylonjs.loaders.min.js


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

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

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

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

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

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

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

@@ -1 +1 @@
-{"engineOnly":309612,"sceneOnly":551979,"minGridMaterial":675195,"minStandardMaterial":774050}
+{"engineOnly":308950,"sceneOnly":510511,"minGridMaterial":634280,"minStandardMaterial":758099}

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

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

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

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

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

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

Разница между файлами не показана из-за своего большого размера
+ 5854 - 7901
dist/preview release/viewer/babylon.module.d.ts


Разница между файлами не показана из-за своего большого размера
+ 348 - 472
dist/preview release/viewer/babylon.viewer.js


Разница между файлами не показана из-за своего большого размера
+ 3 - 3
dist/preview release/viewer/babylon.viewer.max.js


+ 19 - 7
dist/preview release/what's new.md

@@ -8,8 +8,7 @@
 - Added [support for AmmoJS](https://doc.babylonjs.com/how_to/using_the_physics_engine) as a physics plugin (Composite objects, motors, joints) ([TrevorDev](https://github.com/TrevorDev))
   - Added support for soft bodies, which are 3D softbody, 2D cloth and 1D rope, in Ammo physics plugin. [Doc](https://doc.babylonjs.com/how_to/soft_bodies) ([JohnK](https://github.com/BabylonJSGuide))
   - Added support for [Convex Hull Impostor][https://github.com/kripken/ammo.js/blob/master/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h] using Ammo.js plugin ([MackeyK24](https://github.com/mackeyk24))
-  - Added customShaderNameResolve to PBRMaterialBase to allow subclasses to specify custom shader information [MackeyK24](https://github.com/mackeyk24))
-  - Added PBRCustomMaterial to material library to allow easy subclassing of PBR materials [MackeyK24](https://github.com/mackeyk24))  
+  - Added AmmoJSPlugin scene file loader [MackeyK24](https://github.com/mackeyk24))  
 - Added support for [WebXR](https://doc.babylonjs.com/how_to/webxr) ([TrevorDev](https://github.com/TrevorDev))
   - Add customAnimationFrameRequester to allow sessions to hook into engine's render loop ([TrevorDev](https://github.com/TrevorDev))
   - camera customDefaultRenderTarget to allow cameras to render to a custom render target (eg. xr framebuffer) instead of the canvas ([TrevorDev](https://github.com/TrevorDev))
@@ -29,7 +28,7 @@
   - InvalidateRect added to AdvancedDynamicTexture to improve perf for heavily populated GUIs, works with shadows ([TrevorDev](https://github.com/TrevorDev)) **** NEED DEMO or DOC LINK)
 - Migrated the code to modules and deploy [ES6 npm packages](https://doc.babylonjs.com/features/es6_support) ([Sebavan](https://github.com/Sebavan))
 - Added `TrailMesh` class. Credit to furcatomasz ([danjpar](https://github.com/danjpar)) **** NEED DEMO or DOC LINK)
-- Support rendering to a Multiview outputRenderTargetTexture to improve performance for XR scenarios ([TrevorDev](https://github.com/TrevorDev))
+- Support rendering to a Multiview outputRenderTargetTexture with multiview engine component to improve performance for XR scenarios ([TrevorDev](https://github.com/TrevorDev))
 - PBR:
   - Added Inspector Debug Mode ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
   - Added Smith Height Correlated Visibility term to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
@@ -37,6 +36,7 @@
   - Added clear coat support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
   - Added anisotropy support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
   - Added sheen support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
+  - Added sub-surface support to PBR ([Sebavan](https://github.com/Sebavan)) **** NEED DEMO or DOC LINK)
 - Added a STL exporter ([pryme8](https://github.com/pryme8))
 
 ## Optimizations
@@ -131,6 +131,10 @@
 - Observables can now make observers top or bottom priority ([TrevorDev](https://github.com/TrevorDev))
 - Mesh outline no longer is shown through the mesh when it's transparent ([TrevorDev](https://github.com/TrevorDev))
 - DeviceOrientationCamera will no longer be modified by mouse input if the orientation sensor is active ([TrevorDev](https://github.com/TrevorDev))
+- Added LoadScriptAsync tools helper function [MackeyK24](https://github.com/mackeyk24))  
+- Added customShaderNameResolve to PBRMaterialBase to allow subclasses to specify custom shader information [MackeyK24](https://github.com/mackeyk24))
+- Added PBRCustomMaterial to material library to allow easy subclassing of PBR materials [MackeyK24](https://github.com/mackeyk24))
+- Added `auto-exposure` support in `StandardRenderingPipeline` when `HDR` is enabled ([julien-moreau](https://github.com/julien-moreau))
 
 ### OBJ Loader
 - Add color vertex support (not part of standard) ([brianzinn](https://github.com/brianzinn))
@@ -147,13 +151,12 @@
   - Skinned meshes now set an override mesh instead of reparenting to the `__root__` transform node
   - Loaded bones are linked with the transform node created for the corresponding glTF node
 - Add `EquiRectangularCubeTexture` class to enable the usage of browser-canvas supported images as `CubeTexture`'s ([Dennis Dervisis](https://github.com/ddervisis))
+- Add `EquiRectangularCubeTextureAssetTask` to be able to load `EquiRectangularCubeTextures`s via Asset Manager ([Dennis Dervisis](https://github.com/ddervisis))
 
 ### glTF Serializer
 
 - Added support for exporting `KHR_lights_punctual`
 
-### Viewer
-
 ### Post-Processes Library
 - Added the [Ocean](https://doc.babylonjs.com/extensions/oceanpostprocess) post-process ([julien-moreau](https://github.com/julien-moreau))
 
@@ -164,6 +167,12 @@
 - Fixed `TerrainMaterial.isReadyForSubMesh` to remove WebGL warnings ([julien-moreau](https://github.com/julien-moreau))
 - Fixed `MixMaterial.isReadyForSubMesh` to remove WebGL warnings ([dad72](https://github.com/dad72))
 
+### Infrastructure
+
+- Adding Azure DevOps Build ([Sebavan](https://github.com/Sebavan))
+
+### Viewer
+
 ## Bug fixes
 - Fixed ArcRotateCamera.setTarget (position was sometimes wrong) ([Deltakosh](https://github.com/deltakosh))
 - Fixed TransformNode.setDirection (orientation was wrong) ([Deltakosh](https://github.com/deltakosh))
@@ -196,7 +205,7 @@
 - Fix case sensitive paths ([mrdunk](https://github.com))
 - Fix more case sensitive paths ([mrdunk](https://github.com))
 - Attaching a BoundingBoxGizmo on a child should not remove its parent ([TrevorDev](https://github.com/TrevorDev))
-- AmmoJS fix include issue caused after modules update and use world contact point to be consistent with oimo and cannon ([TrevorDev](https://github.com/TrevorDev))
+- AmmoJS fix include issue caused after modules update and use world contact point to be consistent with Oimo and Cannon ([TrevorDev](https://github.com/TrevorDev))
 - Warn of motor with maxForce in Oimo plugin and set default force to be consistent with others, cannonJS support no impostor, cannonJS cylinder axis, ammoJS wake up impostor when apply force/impulse ([TrevorDev](https://github.com/TrevorDev))
 - Utility layer should render on last active camera ([TrevorDev](https://github.com/TrevorDev))
 - PointerDragBehavior should not let the drag plane get out of sync when rotating the object during dragging ([TrevorDev](https://github.com/TrevorDev))
@@ -205,8 +214,10 @@
 - Tools.CreateScreenshot stopped working ([TrevorDev](https://github.com/TrevorDev))
 - Inspector showing duplicate nodes when attached to gizmo ([TrevorDev](https://github.com/TrevorDev))
 - Add missing dependencies for files to support including them from a direct path (eg. import "@babylonjs/core/Helpers/sceneHelpers";) ([TrevorDev](https://github.com/TrevorDev))
-- AssetContainer should not dispose objects it doesn't contain, Support for environmentTexture ([TrevorDev](https://github.com/TrevorDev))
+- AssetContainer should not dispose objects it doesn't contain. Support for environmentTexture add/remove ([TrevorDev](https://github.com/TrevorDev))
 - Fix `mesh.visibility` not working properly when certain material properties are set that changes the interpretation of alpha (e.g. refraction, specular over alpha, etc.) ([bghgary](https://github.com/bghgary))
+- Fix material and texture leak when loading/removing GLTF/obj/babylon files with AssetContainer ([TrevorDev](https://github.com/TrevorDev))
+- Avoid exception when removing impostor during cannon world step ([TrevorDev](https://github.com/TrevorDev))
 
 ### Core Engine
 - Fixed a bug with `mesh.alwaysSelectAsActiveMesh` preventing layerMask to be taken in account ([Deltakosh](https://github.com/deltakosh))
@@ -231,6 +242,7 @@
 - PointerDragBehavior validateDrag predicate to stop dragging to specific points ([TrevorDev](https://github.com/TrevorDev))
 - Auto Update Touch Action [#5674](https://github.com/BabylonJS/Babylon.js/issues/5674)([Sebavan](https://github.com/Sebavan))
 - Add hemispheric lighting to gizmos to avoid flat look ([TrevorDev](https://github.com/TrevorDev))
+- Fix a bug causing `WebRequest.open` to crash if `WebRequest.CustomRequestHeaders` are set [#6055](https://github.com/BabylonJS/Babylon.js/issues/6055)([susares](https://github.com/susares))
 
 ### Viewer
 

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

@@ -84,6 +84,7 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "ClearCoat Tint Map", value: 28 },
             { label: "Sheen Map", value: 29 },
             { label: "Anisotropic Map", value: 30 },
+            { label: "Thickness Map", value: 31 },
             // Env
             { label: "Env Refraction", value: 40 },
             { label: "Env Reflection", value: 41 },
@@ -103,6 +104,8 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
             { label: "ClearCoat Color", value: 65 },
             { label: "ClearCoat Roughness", value: 66 },
             { label: "ClearCoat NdotV", value: 67 },
+            { label: "Transmittance", value: 68 },
+            { label: "Refraction Transmittance", value: 69 },
             // Misc
             { label: "SEO", value: 70 },
             { label: "EHO", value: 71 },
@@ -193,6 +196,39 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                         </div>
                     }
                 </LineContainerComponent>
+                <LineContainerComponent globalState={this.props.globalState} title="SUBSURFACE">
+                    <TextureLinkLineComponent label="Thickness" texture={material.subSurface.thicknessTexture} material={material} onSelectionChangedObservable={this.props.onSelectionChangedObservable} onDebugSelectionChangeObservable={this._onDebugSelectionChangeObservable} />
+                    <SliderLineComponent label="Min Thickness" target={material.subSurface} propertyName="minimumThickness" minimum={0} maximum={10} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <SliderLineComponent label="Max Thickness" target={material.subSurface} propertyName="maximumThickness" minimum={0} maximum={10} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Mask From Thickness" target={material.subSurface} propertyName="useMaskFromThicknessTexture"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <Color3LineComponent label="Tint Color" target={material.subSurface} propertyName="tintColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+
+                    <CheckBoxLineComponent label="Refraction Enabled" target={material.subSurface} propertyName="isRefractionEnabled"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        material.subSurface.isRefractionEnabled &&
+                        <div className="fragment">
+                            <SliderLineComponent label="Intensity" target={material.subSurface} propertyName="refractionIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Index of Refraction" target={material.subSurface} propertyName="indexOfRefraction" minimum={1} maximum={2} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <SliderLineComponent label="Tint at Distance" target={material.subSurface} propertyName="tintColorAtDistance" minimum={0} maximum={10} step={0.1} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <CheckBoxLineComponent label="Link refraction with transparency" target={material.subSurface} propertyName="linkRefractionWithTransparency" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        </div>
+                    }
+
+                    <CheckBoxLineComponent label="Transluency Enabled" target={material.subSurface} propertyName="isTranslucencyEnabled"
+                        onValueChanged={() => this.forceUpdate()}
+                        onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        material.subSurface.isTranslucencyEnabled &&
+                        <div className="fragment">
+                            <SliderLineComponent label="Intensity" target={material.subSurface} propertyName="translucencyIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                            <Color3LineComponent label="Diffusion Distance" target={material.subSurface} propertyName="diffusionDistance" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        </div>
+                    }
+                </LineContainerComponent>
                 <LineContainerComponent globalState={this.props.globalState} title="LEVELS" closed={true}>
                     <SliderLineComponent label="Environment" target={material} propertyName="environmentIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <SliderLineComponent label="Specular" target={material} propertyName="specularIntensity" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -215,7 +251,6 @@ export class PBRMaterialPropertyGridComponent extends React.Component<IPBRMateri
                     <CheckBoxLineComponent label="Alpha from albedo" target={material} propertyName="useAlphaFromAlbedoTexture" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Ambient in grayscale" target={material} propertyName="useAmbientInGrayScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Radiance over alpha" target={material} propertyName="useRadianceOverAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <CheckBoxLineComponent label="Link refraction with transparency" target={material} propertyName="linkRefractionWithTransparency" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Micro-surface from ref. map alpha" target={material} propertyName="useMicroSurfaceFromReflectivityMapAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Specular over alpha" target={material} propertyName="useSpecularOverAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <CheckBoxLineComponent label="Specular anti-aliasing" target={material} propertyName="enableSpecularAntiAliasing" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />

+ 22 - 5
loaders/src/OBJ/objFileLoader.ts

@@ -451,6 +451,23 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
         return this.importMeshAsync(null, scene, data, rootUrl).then((result) => {
             var container = new AssetContainer(scene);
             result.meshes.forEach((mesh) => container.meshes.push(mesh));
+            result.meshes.forEach((mesh) => {
+                var material = mesh.material;
+                if (material) {
+                    // Materials
+                    if (container.materials.indexOf(material) == -1) {
+                        container.materials.push(material);
+
+                        // Textures
+                        var textures = material.getActiveTextures();
+                        textures.forEach((t) => {
+                            if (container.textures.indexOf(t) == -1) {
+                                container.textures.push(t);
+                            }
+                        });
+                    }
+                }
+            });
             container.removeAllFromScene();
             return container;
         });
@@ -515,7 +532,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
             if (!arr[obj[0]]) { arr[obj[0]] = { normals: [], idx: [], uv: [] }; }
             var idx = arr[obj[0]].normals.indexOf(obj[1]);
 
-            if (idx != 1 && (obj[2] == arr[obj[0]].uv[idx])) {
+            if (idx != 1 && (obj[2] === arr[obj[0]].uv[idx])) {
                 return arr[obj[0]].idx[idx];
             }
             return -1;
@@ -558,7 +575,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
             }
 
             //If it not exists
-            if (_index == -1) {
+            if (_index === -1) {
                 //Add an new indice.
                 //The array of indices is only an array with his length equal to the number of triangles - 1.
                 //We add vertices data in this order
@@ -847,7 +864,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
             if (line.length === 0 || line.charAt(0) === '#') {
                 continue;
 
-            //Get information about one position possible for the vertices
+                //Get information about one position possible for the vertices
             } else if (this.vertexPattern.test(line)) {
                 result = line.match(/[^ ]+/g)!;  // match will return non-null due to passing regex pattern
 
@@ -1068,7 +1085,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
             //check meshesNames (stlFileLoader)
             if (meshesNames && meshesFromObj[j].name) {
                 if (meshesNames instanceof Array) {
-                    if (meshesNames.indexOf(meshesFromObj[j].name) == -1) {
+                    if (meshesNames.indexOf(meshesFromObj[j].name) === -1) {
                         continue;
                     }
                 }
@@ -1138,7 +1155,7 @@ export class OBJFileLoader implements ISceneLoaderPluginAsync, ISceneLoaderPlugi
                                 startIndex = _index + 1;
                             }
                             //If the material is not used dispose it
-                            if (_index == -1 && _indices.length == 0) {
+                            if (_index === -1 && _indices.length === 0) {
                                 //If the material is not needed, remove it
                                 materialsFromMTLFile.materials[n].dispose();
                             } else {

+ 13 - 0
loaders/src/glTF/glTFFileLoader.ts

@@ -503,12 +503,25 @@ export class GLTFFileLoader implements IDisposable, ISceneLoaderPluginAsync, ISc
         return this._parseAsync(scene, data, rootUrl, fileName).then((loaderData) => {
             this._log(`Loading ${fileName || ""}`);
             this._loader = this._getLoader(loaderData);
+
+            // Get materials/textures when loading to add to container
+            let materials: Array<Material> = [];
+            this.onMaterialLoadedObservable.add((material) => {
+                materials.push(material);
+            });
+            let textures: Array<BaseTexture> = [];
+            this.onTextureLoadedObservable.add((texture) => {
+                textures.push(texture);
+            });
+
             return this._loader.importMeshAsync(null, scene, loaderData, rootUrl, onProgress, fileName).then((result) => {
                 const container = new AssetContainer(scene);
                 Array.prototype.push.apply(container.meshes, result.meshes);
                 Array.prototype.push.apply(container.particleSystems, result.particleSystems);
                 Array.prototype.push.apply(container.skeletons, result.skeletons);
                 Array.prototype.push.apply(container.animationGroups, result.animationGroups);
+                Array.prototype.push.apply(container.materials, materials);
+                Array.prototype.push.apply(container.textures, textures);
                 container.removeAllFromScene();
                 return container;
             });

+ 1 - 1
package.json

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

+ 4 - 1
src/Animations/runtimeAnimation.ts

@@ -455,9 +455,12 @@ export class RuntimeAnimation {
         this._previousDelay = delay;
         this._previousRatio = ratio;
 
-        if (((to > from && ratio >= range) || (from > to && ratio <= range)) && !loop) { // If we are out of range and not looping get back to caller
+        if ((to > from && ratio >= range) && !loop) { // If we are out of range and not looping get back to caller
             returnValue = false;
             highLimitValue = this._animation._getKeyValue(keys[keys.length - 1].value);
+        } else if ((from > to && ratio <= range) && !loop) {
+            returnValue = false;
+            highLimitValue = this._animation._getKeyValue(keys[0].value);
         } else {
             // Get max value if required
 

+ 20 - 4
src/Cameras/Inputs/freeCameraMouseInput.ts

@@ -1,4 +1,4 @@
-import { Observer, EventState } from "../../Misc/observable";
+import { Observer, EventState, Observable } from "../../Misc/observable";
 import { serialize } from "../../Misc/decorators";
 import { Nullable } from "../../types";
 import { ICameraInput, CameraInputTypes } from "../../Cameras/cameraInputsManager";
@@ -32,6 +32,15 @@ export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
     private previousPosition: Nullable<{ x: number, y: number }> = null;
 
     /**
+     * Observable for when a pointer move event occurs containing the move offset
+     */
+    public onPointerMovedObservable = new Observable<{ offsetX: number, offsetY: number }>();
+    /**
+     * @hidden
+     * If the camera should be rotated automatically based on pointer movement
+     */
+    public _allowCameraRotation = true;
+    /**
      * Manage the mouse inputs to control the movement of a free camera.
      * @see http://doc.babylonjs.com/how_to/customizing_camera_inputs
      * @param touchEnabled Defines if touch is enabled or not
@@ -105,12 +114,15 @@ export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
                     }
 
                     var offsetX = evt.clientX - this.previousPosition.x;
+                    var offsetY = evt.clientY - this.previousPosition.y;
                     if (this.camera.getScene().useRightHandedSystem) { offsetX *= -1; }
                     if (this.camera.parent && this.camera.parent._getWorldMatrixDeterminant() < 0) { offsetX *= -1; }
-                    this.camera.cameraRotation.y += offsetX / this.angularSensibility;
 
-                    var offsetY = evt.clientY - this.previousPosition.y;
-                    this.camera.cameraRotation.x += offsetY / this.angularSensibility;
+                    if (this._allowCameraRotation) {
+                        this.camera.cameraRotation.y += offsetX / this.angularSensibility;
+                        this.camera.cameraRotation.x += offsetY / this.angularSensibility;
+                    }
+                    this.onPointerMovedObservable.notifyObservers({offsetX: offsetX, offsetY: offsetY});
 
                     this.previousPosition = {
                         x: evt.clientX,
@@ -179,6 +191,10 @@ export class FreeCameraMouseInput implements ICameraInput<FreeCamera> {
                 element.removeEventListener("contextmenu", <EventListener>this.onContextMenu);
             }
 
+            if (this.onPointerMovedObservable) {
+                this.onPointerMovedObservable.clear();
+            }
+
             this._observer = null;
             this._onMouseMove = null;
             this.previousPosition = null;

+ 3 - 2
src/Cameras/RigModes/vrRigMode.ts

@@ -1,6 +1,7 @@
 import { Camera } from "../camera";
 import { Matrix, Viewport } from "../../Maths/math";
-import { VRDistortionCorrectionPostProcess, VRMultiviewToSingleview } from "../../PostProcesses/vrDistortionCorrectionPostProcess";
+import { VRDistortionCorrectionPostProcess } from "../../PostProcesses/vrDistortionCorrectionPostProcess";
+import { VRMultiviewToSingleviewPostProcess } from '../../PostProcesses/vrMultiviewToSingleviewPostProcess';
 import { VRCameraMetrics } from "../VR/vrCameraMetrics";
 import { Logger } from '../../Misc/logger';
 
@@ -30,7 +31,7 @@ Camera._setVRRigMode = function(camera: Camera, rigParams: any) {
             metrics.multiviewEnabled = false;
         }else {
             camera._useMultiviewToSingleView = true;
-            camera._rigPostProcess = new VRMultiviewToSingleview("VRMultiviewToSingleview", camera, metrics.postProcessScaleFactor);
+            camera._rigPostProcess = new VRMultiviewToSingleviewPostProcess("VRMultiviewToSingleview", camera, metrics.postProcessScaleFactor);
         }
     }
 

+ 2 - 2
src/Cameras/VR/webVRCamera.ts

@@ -14,7 +14,7 @@ import { AbstractMesh } from "../../Meshes/abstractMesh";
 import { Ray } from "../../Culling/ray";
 import { HemisphericLight } from "../../Lights/hemisphericLight";
 import { Logger } from '../../Misc/logger';
-import { VRMultiviewToSingleview } from '../../PostProcesses/vrDistortionCorrectionPostProcess';
+import { VRMultiviewToSingleviewPostProcess } from '../../PostProcesses/vrMultiviewToSingleviewPostProcess';
 
 // Side effect import to define the stereoscopic mode.
 import "../RigModes/webVRRigMode";
@@ -289,7 +289,7 @@ export class WebVRFreeCamera extends FreeCamera implements PoseControlled {
                 this._useMultiviewToSingleView = false;
             }else {
                 this._useMultiviewToSingleView = true;
-                this._rigPostProcess = new VRMultiviewToSingleview("VRMultiviewToSingleview", this, 1.0);
+                this._rigPostProcess = new VRMultiviewToSingleviewPostProcess("VRMultiviewToSingleview", this, 1.0);
             }
         }
 

+ 2 - 7
src/Cameras/arcRotateCamera.ts

@@ -856,13 +856,8 @@ export class ArcRotateCamera extends TargetCamera {
 
     /**
      * Rebuilds angles (alpha, beta) and radius from the give position and target
-     * @param updateView defines a boolean forcing the camera to update its position with a view matrix computation first (default is true)
      */
-    public rebuildAnglesAndRadius(updateView = true): void {
-        if (updateView) {
-            this.getViewMatrix(); // Force position update
-        }
-
+    public rebuildAnglesAndRadius(): void {
         this._position.subtractToRef(this._getTargetPosition(), this._computationVector);
         this.radius = this._computationVector.length();
 
@@ -893,7 +888,7 @@ export class ArcRotateCamera extends TargetCamera {
         }
         this._position.copyFrom(position);
 
-        this.rebuildAnglesAndRadius(false);
+        this.rebuildAnglesAndRadius();
     }
 
     /**

Разница между файлами не показана из-за своего большого размера
+ 1241 - 1268
src/Cameras/camera.ts


+ 29 - 1
src/Cameras/deviceOrientationCamera.ts

@@ -18,6 +18,7 @@ export class DeviceOrientationCamera extends FreeCamera {
 
     private _initialQuaternion: Quaternion;
     private _quaternionCache: Quaternion;
+    private _tmpDragQuaternion = new Quaternion();
 
     /**
      * Creates a new device orientation camera
@@ -33,12 +34,39 @@ export class DeviceOrientationCamera extends FreeCamera {
         // When the orientation sensor fires it's first event, disable mouse input
         if (this.inputs._deviceOrientationInput) {
             this.inputs._deviceOrientationInput._onDeviceOrientationChangedObservable.addOnce(() => {
-                this.inputs.removeMouse();
+                if (this.disablePointerInputWhenUsingDeviceOrientation) {
+                    if (this.inputs._mouseInput) {
+                        this.inputs._mouseInput._allowCameraRotation = false;
+                        this.inputs._mouseInput.onPointerMovedObservable.add((e) => {
+                            if (this._dragFactor != 0) {
+                                if (!this._initialQuaternion) {
+                                    this._initialQuaternion = new Quaternion();
+                                }
+                                // Rotate the initial space around the y axis to allow users to "turn around" via touch/mouse
+                                Quaternion.FromEulerAnglesToRef(0, e.offsetX * this._dragFactor, 0, this._tmpDragQuaternion);
+                                this._initialQuaternion.multiplyToRef(this._tmpDragQuaternion, this._initialQuaternion);
+                            }
+                        });
+                    }
+                }
             });
         }
     }
 
     /**
+     * Disabled pointer input on first orientation sensor update (Default: true)
+     */
+    public disablePointerInputWhenUsingDeviceOrientation = true;
+    private _dragFactor = 0;
+    /**
+     * Enabled turning on the y axis when the orientation sensor is active
+     * @param dragFactor the factor that controls the turn speed (default: 1/300)
+     */
+    public enableHorizontalDragging(dragFactor= 1 / 300) {
+        this._dragFactor = dragFactor;
+    }
+
+    /**
      * Gets the current instance class name ("DeviceOrientationCamera").
      * This helps avoiding instanceof at run time.
      * @returns the class name

+ 169 - 0
src/Engines/Extensions/engine.multiview.ts

@@ -0,0 +1,169 @@
+import { Camera } from "../../Cameras/camera";
+import { Engine } from "../../Engines/engine";
+import { Scene } from "../../scene";
+import { InternalTexture } from '../../Materials/Textures/internalTexture';
+import { Nullable } from '../../types';
+import { RenderTargetTexture } from '../../Materials/Textures/renderTargetTexture';
+import { Matrix, Tmp, Frustum } from '../../Maths/math';
+import { UniformBuffer } from '../../Materials/uniformBuffer';
+import { MultiviewRenderTarget } from '../../Materials/Textures/MultiviewRenderTarget';
+
+declare module "../../Engines/engine" {
+    export interface Engine {
+        /**
+         * Creates a new multiview render target
+         * @param width defines the width of the texture
+         * @param height defines the height of the texture
+         * @returns the created multiview texture
+         */
+        createMultiviewRenderTargetTexture(width: number, height: number): InternalTexture;
+
+        /**
+         * Binds a multiview framebuffer to be drawn to
+         * @param multiviewTexture texture to bind
+         */
+        bindMultiviewFramebuffer(multiviewTexture: InternalTexture): void;
+    }
+}
+
+Engine.prototype.createMultiviewRenderTargetTexture = function(width: number, height: number) {
+    var gl = this._gl;
+
+    if (!this.getCaps().multiview) {
+        throw "Multiview is not supported";
+    }
+
+    var internalTexture = new InternalTexture(this, InternalTexture.DATASOURCE_UNKNOWN, true);
+    internalTexture.width = width;
+    internalTexture.height = height;
+    internalTexture._framebuffer = gl.createFramebuffer();
+
+    internalTexture._colorTextureArray = gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._colorTextureArray);
+    (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, 2);
+
+    internalTexture._depthStencilTextureArray = gl.createTexture();
+    gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._depthStencilTextureArray);
+    (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, (gl as any).DEPTH32F_STENCIL8, width, height, 2);
+    internalTexture.isReady = true;
+    return internalTexture;
+};
+
+Engine.prototype.bindMultiviewFramebuffer = function(multiviewTexture: InternalTexture) {
+    var gl: any = this._gl;
+    var ext = this.getCaps().multiview;
+
+    this.bindFramebuffer(multiviewTexture, undefined, undefined, undefined, true);
+    gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, multiviewTexture._framebuffer);
+    if (multiviewTexture._colorTextureArray && multiviewTexture._depthStencilTextureArray) {
+        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, multiviewTexture._colorTextureArray, 0, 0, 2);
+        ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, multiviewTexture._depthStencilTextureArray, 0, 0, 2);
+    } else {
+        throw "Invalid multiview frame buffer";
+    }
+};
+
+declare module "../../Cameras/camera" {
+    export interface Camera {
+        /**
+         * @hidden
+         * For cameras that cannot use multiview images to display directly. (e.g. webVR camera will render to multiview texture, then copy to each eye texture and go from there)
+         */
+        _useMultiviewToSingleView: boolean;
+        /**
+         * @hidden
+         * For cameras that cannot use multiview images to display directly. (e.g. webVR camera will render to multiview texture, then copy to each eye texture and go from there)
+         */
+        _multiviewTexture: Nullable<RenderTargetTexture>;
+
+        /**
+         * @hidden
+         * ensures the multiview texture of the camera exists and has the specified width/height
+         * @param width height to set on the multiview texture
+         * @param height width to set on the multiview texture
+         */
+        _resizeOrCreateMultiviewTexture(width: number, height: number): void;
+    }
+}
+
+Camera.prototype._useMultiviewToSingleView = false;
+
+Camera.prototype._multiviewTexture = null;
+
+Camera.prototype._resizeOrCreateMultiviewTexture = function(width: number, height: number) {
+    if (!this._multiviewTexture) {
+        this._multiviewTexture = new MultiviewRenderTarget(this.getScene(), {width: width, height: height});
+    }else if (this._multiviewTexture.getRenderWidth() != width || this._multiviewTexture.getRenderHeight() != height) {
+        this._multiviewTexture.dispose();
+        this._multiviewTexture = new MultiviewRenderTarget(this.getScene(), {width: width, height: height});
+    }
+};
+
+declare module "../../scene" {
+    export interface Scene {
+        /** @hidden */
+        _transformMatrixR: Matrix;
+        /** @hidden */
+        _multiviewSceneUbo: Nullable<UniformBuffer>;
+        /** @hidden */
+        _createMultiviewUbo(): void;
+        /** @hidden */
+        _updateMultiviewUbo(viewR?: Matrix, projectionR?: Matrix): void;
+        /** @hidden */
+        _renderMultiviewToSingleView(camera: Camera): void;
+    }
+}
+
+Scene.prototype._transformMatrixR = Matrix.Zero();
+Scene.prototype._multiviewSceneUbo = null;
+Scene.prototype._createMultiviewUbo = function() {
+    this._multiviewSceneUbo = new UniformBuffer(this.getEngine(), undefined, true);
+    this._multiviewSceneUbo.addUniform("viewProjection", 16);
+    this._multiviewSceneUbo.addUniform("viewProjectionR", 16);
+    this._multiviewSceneUbo.addUniform("view", 16);
+};
+Scene.prototype._updateMultiviewUbo = function(viewR?: Matrix, projectionR?: Matrix) {
+    if (viewR && projectionR) {
+        viewR.multiplyToRef(projectionR, this._transformMatrixR);
+    }
+
+    if (viewR && projectionR) {
+        viewR.multiplyToRef(projectionR, Tmp.Matrix[0]);
+        Frustum.GetRightPlaneToRef(Tmp.Matrix[0], this._frustumPlanes[3]); // Replace right plane by second camera right plane
+    }
+
+    if (this._multiviewSceneUbo) {
+        this._multiviewSceneUbo.updateMatrix("viewProjection", this.getTransformMatrix());
+        this._multiviewSceneUbo.updateMatrix("viewProjectionR", this._transformMatrixR);
+        this._multiviewSceneUbo.updateMatrix("view", this._viewMatrix);
+        this._multiviewSceneUbo.update();
+    }
+};
+Scene.prototype._renderMultiviewToSingleView = function(camera: Camera) {
+    // Multiview is only able to be displayed directly for API's such as webXR
+    // This displays a multiview image by rendering to the multiview image and then
+    // copying the result into the sub cameras instead of rendering them and proceeding as normal from there
+
+    // Render to a multiview texture
+    camera._resizeOrCreateMultiviewTexture(
+        (camera._rigPostProcess && camera._rigPostProcess && camera._rigPostProcess.width > 0) ? camera._rigPostProcess.width / 2 : this.getEngine().getRenderWidth(true) / 2,
+        (camera._rigPostProcess && camera._rigPostProcess && camera._rigPostProcess.height > 0) ? camera._rigPostProcess.height : this.getEngine().getRenderHeight(true)
+    );
+    if (!this._multiviewSceneUbo) {
+        this._createMultiviewUbo();
+    }
+    camera.outputRenderTarget = camera._multiviewTexture;
+    this._renderForCamera(camera);
+    camera.outputRenderTarget = null;
+
+    // Consume the multiview texture through a shader for each eye
+    for (var index = 0; index < camera._rigCameras.length; index++) {
+        var engine = this.getEngine();
+        this._activeCamera = camera._rigCameras[index];
+        engine.setViewport(this._activeCamera.viewport);
+        if (this.postProcessManager) {
+            this.postProcessManager._prepareFrame();
+            this.postProcessManager._finalizeFrame(this._activeCamera.isIntermediate);
+        }
+    }
+};

+ 2 - 1
src/Engines/Extensions/index.ts

@@ -1,2 +1,3 @@
 export * from "./engine.occlusionQuery";
-export * from "./engine.transformFeedback";
+export * from "./engine.transformFeedback";
+export * from "./engine.multiview";

+ 42 - 78
src/Engines/engine.ts

@@ -243,12 +243,17 @@ export interface IDisplayChangedEventArgs {
 export class Engine {
     /** Use this array to turn off some WebGL2 features on known buggy browsers version */
     public static ExceptionList = [
-        { key: "Chrome/63.0", capture: "63\\.0\\.3239\\.(\\d+)", captureConstraint: 108, targets: ["uniformBuffer"] },
-        { key: "Firefox/58", capture: null, captureConstraint: null, targets: ["uniformBuffer"] },
-        { key: "Firefox/59", capture: null, captureConstraint: null, targets: ["uniformBuffer"] },
+        { key: "Chrome\/63\.0", capture: "63\\.0\\.3239\\.(\\d+)", captureConstraint: 108, targets: ["uniformBuffer"] },
+        { key: "Firefox\/58", capture: null, captureConstraint: null, targets: ["uniformBuffer"] },
+        { key: "Firefox\/59", capture: null, captureConstraint: null, targets: ["uniformBuffer"] },
         { key: "Macintosh", capture: null, captureConstraint: null, targets: ["textureBindingOptimization"] },
         { key: "iPhone", capture: null, captureConstraint: null, targets: ["textureBindingOptimization"] },
-        { key: "iPad", capture: null, captureConstraint: null, targets: ["textureBindingOptimization"] }
+        { key: "iPad", capture: null, captureConstraint: null, targets: ["textureBindingOptimization"] },
+        { key: "Chrome\/72.+?Mobile", capture: null, captureConstraint: null, targets: ["vao"] },
+        { key: "Chrome\/73.+?Mobile", capture: null, captureConstraint: null, targets: ["vao"] },
+        { key: "Chrome\/74.+?Mobile", capture: null, captureConstraint: null, targets: ["vao"] },
+        { key: "Mac OS.+Chrome\/71", capture: null, captureConstraint: null, targets: ["vao"] },
+        { key: "Mac OS.+Chrome\/72", capture: null, captureConstraint: null, targets: ["vao"] }
     ];
 
     /** Gets the list of created engines */
@@ -497,14 +502,14 @@ export class Engine {
      */
     // Not mixed with Version for tooling purpose.
     public static get NpmPackage(): string {
-        return "babylonjs@4.0.0-alpha.32";
+        return "babylonjs@4.0.0-beta.2";
     }
 
     /**
      * Returns the current version of the framework
      */
     public static get Version(): string {
-        return "4.0.0-alpha.32";
+        return "4.0.0-beta.2";
     }
 
     /**
@@ -856,6 +861,11 @@ export class Engine {
         return this._performanceMonitor;
     }
 
+    /**
+     * Gets or sets a boolean indicating that vertex array object must be disabled even if they are supported
+     */
+    public disableVertexArrayObjects = false;
+
     // States
     /** @hidden */
     protected _depthCullingState = new _DepthCullingState();
@@ -1054,8 +1064,9 @@ export class Engine {
                 for (var exception of Engine.ExceptionList) {
                     let key = exception.key;
                     let targets = exception.targets;
+                    let check = new RegExp(key);
 
-                    if (ua.indexOf(key) > -1) {
+                    if (check.test(ua)) {
                         if (exception.capture && exception.captureConstraint) {
                             let capture = exception.capture;
                             let constraint = exception.captureConstraint;
@@ -1076,6 +1087,9 @@ export class Engine {
                                 case "uniformBuffer":
                                     this.disableUniformBuffers = true;
                                     break;
+                                case "vao":
+                                    this.disableVertexArrayObjects = true;
+                                    break;
                                 case "textureBindingOptimization":
                                     this.disableTextureBindingOptimization = true;
                                     break;
@@ -1491,7 +1505,9 @@ export class Engine {
         }
 
         // Vertex array object
-        if (this._webGLVersion > 1) {
+        if (this.disableVertexArrayObjects) {
+            this._caps.vertexArrayObject = false;
+        } else if (this._webGLVersion > 1) {
             this._caps.vertexArrayObject = true;
         } else {
             var vertexArrayObjectExtension = this._gl.getExtension('OES_vertex_array_object');
@@ -3411,13 +3427,6 @@ export class Engine {
         gl.shaderSource(shader, source);
         gl.compileShader(shader);
 
-        if (!this._caps.parallelShaderCompile && !gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
-            let log = gl.getShaderInfoLog(shader);
-            if (log) {
-                throw new Error(log);
-            }
-        }
-
         return shader;
     }
 
@@ -3509,23 +3518,21 @@ export class Engine {
 
         var linked = context.getProgramParameter(shaderProgram, context.LINK_STATUS);
 
-        if (!linked) {
-            if (this._caps.parallelShaderCompile) { // Get more info
+        if (!linked) { // Get more info
 
-                // Vertex
-                if (!this._gl.getShaderParameter(vertexShader, this._gl.COMPILE_STATUS)) {
-                    let log = this._gl.getShaderInfoLog(vertexShader);
-                    if (log) {
-                        throw new Error(log);
-                    }
+            // Vertex
+            if (!this._gl.getShaderParameter(vertexShader, this._gl.COMPILE_STATUS)) {
+                let log = this._gl.getShaderInfoLog(vertexShader);
+                if (log) {
+                    throw new Error(log);
                 }
+            }
 
-                // Fragment
-                if (!this._gl.getShaderParameter(fragmentShader, this._gl.COMPILE_STATUS)) {
-                    let log = this._gl.getShaderInfoLog(fragmentShader);
-                    if (log) {
-                        throw new Error(log);
-                    }
+            // Fragment
+            if (!this._gl.getShaderParameter(fragmentShader, this._gl.COMPILE_STATUS)) {
+                let log = this._gl.getShaderInfoLog(fragmentShader);
+                if (log) {
+                    throw new Error(log);
                 }
             }
 
@@ -5623,53 +5630,6 @@ export class Engine {
     }
 
     /**
-     * Creates a new multiview render target
-     * @param width defines the width of the texture
-     * @param height defines the height of the texture
-     * @returns the created multiview texture
-     */
-    public createMultiviewRenderTargetTexture(width: number, height: number) {
-        var gl = this._gl;
-
-        if (!this.getCaps().multiview) {
-            throw "Multiview is not supported";
-        }
-
-        var internalTexture = new InternalTexture(this, InternalTexture.DATASOURCE_UNKNOWN, true);
-        internalTexture.width = width;
-        internalTexture.height = height;
-        internalTexture._framebuffer = gl.createFramebuffer();
-
-        internalTexture._colorTextureArray = gl.createTexture();
-        gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._colorTextureArray);
-        (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, gl.RGBA8, width, height, 2);
-
-        internalTexture._depthStencilTextureArray = gl.createTexture();
-        gl.bindTexture(gl.TEXTURE_2D_ARRAY, internalTexture._depthStencilTextureArray);
-        (gl as any).texStorage3D(gl.TEXTURE_2D_ARRAY, 1, (gl as any).DEPTH32F_STENCIL8, width, height, 2);
-        internalTexture.isReady = true;
-        return internalTexture;
-    }
-
-    /**
-     * Binds a multiview framebuffer to be drawn to
-     * @param multiviewTexture texture to bind
-     */
-    public bindMultiviewFramebuffer(multiviewTexture: InternalTexture) {
-        var gl: any = this._gl;
-        var ext = this.getCaps().multiview;
-
-        this.bindFramebuffer(multiviewTexture, undefined, undefined, undefined, true);
-        gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, multiviewTexture._framebuffer);
-        if (multiviewTexture._colorTextureArray && multiviewTexture._depthStencilTextureArray) {
-            ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.COLOR_ATTACHMENT0, multiviewTexture._colorTextureArray, 0, 0, 2);
-            ext.framebufferTextureMultiviewWEBGL(gl.DRAW_FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, multiviewTexture._depthStencilTextureArray, 0, 0, 2);
-        } else {
-            throw "Invalid multiview frame buffer";
-        }
-    }
-
-    /**
      * Creates a new render target cube texture
      * @param size defines the size of the texture
      * @param options defines the options used to create the texture
@@ -5820,8 +5780,12 @@ export class Engine {
                 if (loader.supportCascades) {
                     this._cascadeLoadFiles(scene, onloaddata, files, onError);
                 }
-                else if (onError) {
-                    onError("Textures type does not support cascades.");
+                else {
+                    if (onError) {
+                        onError("Textures type does not support cascades.");
+                    } else {
+                        Logger.Warn("Texture loader does not support cascades.");
+                    }
                 }
             }
             else {

+ 1 - 1
src/Gamepads/Controllers/gearVRController.ts

@@ -29,7 +29,7 @@ export class GearVRController extends WebVRController {
     public static readonly GAMEPAD_ID_PREFIX: string = 'Gear VR'; // id is 'Gear VR Controller'
 
     private readonly _buttonIndexToObservableNameMap = [
-        'onTrackpadChangedObservable', // Trackpad
+        'onPadStateChangedObservable', // Pad
         'onTriggerStateChangedObservable' // Trigger
     ];
 

+ 50 - 11
src/Loading/Plugins/babylonFileLoader.ts

@@ -25,8 +25,10 @@ import { Skeleton } from "../../Bones/skeleton";
 import { MorphTargetManager } from "../../Morph/morphTargetManager";
 import { CannonJSPlugin } from "../../Physics/Plugins/cannonJSPlugin";
 import { OimoJSPlugin } from "../../Physics/Plugins/oimoJSPlugin";
+import { AmmoJSPlugin } from "../../Physics/Plugins/ammoJSPlugin";
 import { ReflectionProbe } from "../../Probes/reflectionProbe";
 import { _TypeStore } from '../../Misc/typeStore';
+import { Tools } from '../../Misc/tools';
 
 /** @hidden */
 export var _BabylonLoaderRegistered = true;
@@ -87,11 +89,19 @@ var loadAssetContainer = (scene: Scene, data: string, rootUrl: string, onError?:
                 }
                 scene.environmentTexture = hdrTexture;
             } else {
-                var cubeTexture = CubeTexture.CreateFromPrefilteredData((parsedData.environmentTexture.match(/https?:\/\//g) ? "" : rootUrl) + parsedData.environmentTexture, scene);
-                if (parsedData.environmentTextureRotationY) {
-                    cubeTexture.rotationY = parsedData.environmentTextureRotationY;
+                if (Tools.EndsWith(parsedData.environmentTexture, ".env")) {
+                    var compressedTexture = new CubeTexture((parsedData.environmentTexture.match(/https?:\/\//g) ? "" : rootUrl) + parsedData.environmentTexture, scene);
+                    if (parsedData.environmentTextureRotationY) {
+                        compressedTexture.rotationY = parsedData.environmentTextureRotationY;
+                    }
+                    scene.environmentTexture = compressedTexture;
+                } else {
+                    var cubeTexture = CubeTexture.CreateFromPrefilteredData((parsedData.environmentTexture.match(/https?:\/\//g) ? "" : rootUrl) + parsedData.environmentTexture, scene);
+                    if (parsedData.environmentTextureRotationY) {
+                        cubeTexture.rotationY = parsedData.environmentTextureRotationY;
+                    }
+                    scene.environmentTexture = cubeTexture;
                 }
-                scene.environmentTexture = cubeTexture;
             }
             if (parsedData.createDefaultSkybox === true) {
                 var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;
@@ -147,9 +157,19 @@ var loadAssetContainer = (scene: Scene, data: string, rootUrl: string, onError?:
             for (index = 0, cache = parsedData.materials.length; index < cache; index++) {
                 var parsedMaterial = parsedData.materials[index];
                 var mat = Material.Parse(parsedMaterial, scene, rootUrl);
-                container.materials.push(mat);
-                log += (index === 0 ? "\n\tMaterials:" : "");
-                log += "\n\t\t" + mat.toString(fullDetails);
+                if (mat) {
+                    container.materials.push(mat);
+                    log += (index === 0 ? "\n\tMaterials:" : "");
+                    log += "\n\t\t" + mat.toString(fullDetails);
+
+                    // Textures
+                    var textures = mat.getActiveTextures();
+                    textures.forEach((t) => {
+                        if (container.textures.indexOf(t) == -1) {
+                            container.textures.push(t);
+                        }
+                    });
+                }
             }
         }
 
@@ -158,8 +178,17 @@ var loadAssetContainer = (scene: Scene, data: string, rootUrl: string, onError?:
                 var parsedMultiMaterial = parsedData.multiMaterials[index];
                 var mmat = MultiMaterial.ParseMultiMaterial(parsedMultiMaterial, scene);
                 container.multiMaterials.push(mmat);
+
                 log += (index === 0 ? "\n\tMultiMaterials:" : "");
                 log += "\n\t\t" + mmat.toString(fullDetails);
+
+                // Textures
+                var textures = mmat.getActiveTextures();
+                textures.forEach((t) => {
+                    if (container.textures.indexOf(t) == -1) {
+                        container.textures.push(t);
+                    }
+                });
             }
         }
 
@@ -591,6 +620,8 @@ SceneLoader.RegisterPlugin({
                     physicsPlugin = new CannonJSPlugin();
                 } else if (parsedData.physicsEngine === "oimo") {
                     physicsPlugin = new OimoJSPlugin();
+                } else if (parsedData.physicsEngine === "ammo") {
+                    physicsPlugin = new AmmoJSPlugin();
                 }
                 log = "\tPhysics engine " + (parsedData.physicsEngine ? parsedData.physicsEngine : "oimo") + " enabled\n";
                 //else - default engine, which is currently oimo
@@ -633,11 +664,19 @@ SceneLoader.RegisterPlugin({
                     }
                     scene.environmentTexture = hdrTexture;
                 } else {
-                    var cubeTexture = CubeTexture.CreateFromPrefilteredData(rootUrl + parsedData.environmentTexture, scene);
-                    if (parsedData.environmentTextureRotationY) {
-                        cubeTexture.rotationY = parsedData.environmentTextureRotationY;
+                    if (Tools.EndsWith(parsedData.environmentTexture, ".env")) {
+                        var compressedTexture = new CubeTexture(rootUrl + parsedData.environmentTexture, scene);
+                        if (parsedData.environmentTextureRotationY) {
+                            compressedTexture.rotationY = parsedData.environmentTextureRotationY;
+                        }
+                        scene.environmentTexture = compressedTexture;
+                    } else {
+                        var cubeTexture = CubeTexture.CreateFromPrefilteredData(rootUrl + parsedData.environmentTexture, scene);
+                        if (parsedData.environmentTextureRotationY) {
+                            cubeTexture.rotationY = parsedData.environmentTextureRotationY;
+                        }
+                        scene.environmentTexture = cubeTexture;
                     }
-                    scene.environmentTexture = cubeTexture;
                 }
                 if (parsedData.createDefaultSkybox === true) {
                     var skyboxScale = (scene.activeCamera !== undefined && scene.activeCamera !== null) ? (scene.activeCamera.maxZ - scene.activeCamera.minZ) / 2 : 1000;

+ 1 - 7
src/Materials/Background/backgroundMaterial.ts

@@ -677,13 +677,7 @@ export class BackgroundMaterial extends PushMaterial {
         defines._needNormals = true;
 
         // Multiview
-        if (scene.activeCamera) {
-            var previousMultiview = defines.MULTIVIEW;
-            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
-            if (defines.MULTIVIEW != previousMultiview) {
-                defines.markAsUnprocessed();
-            }
-        }
+        MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
         // Textures
         if (defines._areTexturesDirty) {

+ 65 - 151
src/Materials/PBR/pbrBaseMaterial.ts

@@ -18,6 +18,7 @@ import { IMaterialClearCoatDefines, PBRClearCoatConfiguration } from "./pbrClear
 import { IMaterialAnisotropicDefines, PBRAnisotropicConfiguration } from "./pbrAnisotropicConfiguration";
 import { IMaterialBRDFDefines, PBRBRDFConfiguration } from "./pbrBRDFConfiguration";
 import { IMaterialSheenDefines, PBRSheenConfiguration } from "./pbrSheenConfiguration";
+import { IMaterialSubSurfaceDefines, PBRSubSurfaceConfiguration } from "./pbrSubSurfaceConfiguration";
 
 import { ImageProcessingConfiguration, IImageProcessingConfigurationDefines } from "../../Materials/imageProcessingConfiguration";
 import { Effect, EffectFallbacks, EffectCreationOptions } from "../../Materials/effect";
@@ -46,7 +47,8 @@ export class PBRMaterialDefines extends MaterialDefines
     IMaterialClearCoatDefines,
     IMaterialAnisotropicDefines,
     IMaterialBRDFDefines,
-    IMaterialSheenDefines {
+    IMaterialSheenDefines,
+    IMaterialSubSurfaceDefines {
     public PBR = true;
 
     public MAINUV1 = false;
@@ -135,14 +137,6 @@ export class PBRMaterialDefines extends MaterialDefines
     public RADIANCEOCCLUSION = false;
     public HORIZONOCCLUSION = false;
 
-    public REFRACTION = false;
-    public REFRACTIONMAP_3D = false;
-    public REFRACTIONMAP_OPPOSITEZ = false;
-    public LODINREFRACTIONALPHA = false;
-    public GAMMAREFRACTION = false;
-    public RGBDREFRACTION = false;
-    public LINKREFRACTIONTOTRANSPARENCY = false;
-
     public INSTANCES = false;
 
     public NUM_BONE_INFLUENCERS = 0;
@@ -210,6 +204,24 @@ export class PBRMaterialDefines extends MaterialDefines
     public SHEEN_TEXTUREDIRECTUV = 0;
     public SHEEN_LINKWITHALBEDO = false;
 
+    public SUBSURFACE = false;
+
+    public SS_REFRACTION = false;
+    public SS_TRANSLUCENCY = false;
+    public SS_SCATERRING = false;
+
+    public SS_THICKNESSANDMASK_TEXTURE = false;
+    public SS_THICKNESSANDMASK_TEXTUREDIRECTUV = 0;
+
+    public SS_REFRACTIONMAP_3D = false;
+    public SS_REFRACTIONMAP_OPPOSITEZ = false;
+    public SS_LODINREFRACTIONALPHA = false;
+    public SS_GAMMAREFRACTION = false;
+    public SS_RGBDREFRACTION = false;
+    public SS_LINKREFRACTIONTOTRANSPARENCY = false;
+
+    public SS_MASK_FROM_THICKNESS_TEXTURE = false;
+
     public UNLIT = false;
 
     public DEBUGMODE = 0;
@@ -351,11 +363,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _reflectionTexture: BaseTexture;
 
     /**
-     * Stores the refraction values in a texture.
-     */
-    protected _refractionTexture: BaseTexture;
-
-    /**
      * Stores the emissive values in a texture.
      */
     protected _emissiveTexture: BaseTexture;
@@ -429,22 +436,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     protected _microSurface = 0.9;
 
     /**
-     * source material index of refraction (IOR)' / 'destination material IOR.
-     */
-    protected _indexOfRefraction = 0.66;
-
-    /**
-     * Controls if refraction needs to be inverted on Y. This could be useful for procedural texture.
-     */
-    protected _invertRefractionY = false;
-
-    /**
-     * This parameters will make the material used its opacity to control how much it is refracting aginst not.
-     * Materials half opaque for instance using refraction could benefit from this control.
-     */
-    protected _linkRefractionWithTransparency = false;
-
-    /**
      * Specifies that the material will use the light map as a show map.
      */
     protected _useLightmapAsShadowmap = false;
@@ -726,6 +717,11 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     public readonly sheen = new PBRSheenConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
 
     /**
+     * Defines the SubSurface parameters for the material.
+     */
+    public readonly subSurface = new PBRSubSurfaceConfiguration(this._markAllSubMeshesAsTexturesDirty.bind(this));
+
+    /**
      * Custom callback helping to override the default shader used in the material.
      */
     public customShaderNameResolve: (shaderName: string, uniforms: string[], uniformBuffers: string[], samplers: string[], defines: PBRMaterialDefines) => string;
@@ -749,9 +745,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 this._renderTargets.push(<RenderTargetTexture>this._reflectionTexture);
             }
 
-            if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
-                this._renderTargets.push(<RenderTargetTexture>this._refractionTexture);
-            }
+            this.subSurface.fillRenderTargetTextures(this._renderTargets);
 
             return this._renderTargets;
         };
@@ -767,11 +761,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
-        if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
-            return true;
-        }
-
-        return false;
+        return this.subSurface.hasRenderTargetTextures();
     }
 
     /**
@@ -831,7 +821,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
      * Returns true if alpha blending should be disabled.
      */
     private get _disableAlphaBlending(): boolean {
-        return (this._linkRefractionWithTransparency ||
+        return (this.subSurface.disableAlphaBlending ||
             this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_OPAQUE ||
             this._transparencyMode === PBRBaseMaterial.PBRMATERIAL_ALPHATEST);
     }
@@ -867,7 +857,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
-        if (this._linkRefractionWithTransparency) {
+        if (this.subSurface.disableAlphaBlending) {
             return false;
         }
 
@@ -981,13 +971,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     }
                 }
 
-                var refractionTexture = this._getRefractionTexture();
-                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                    if (!refractionTexture.isReadyOrNotBlocking()) {
-                        return false;
-                    }
-                }
-
                 if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) {
                     // This is blocking.
                     if (!this._environmentBRDFTexture.isReady()) {
@@ -997,15 +980,10 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             }
         }
 
-        if (!this.clearCoat.isReadyForSubMesh(defines, scene, engine, this._disableBumpMap)) {
-            return false;
-        }
-
-        if (!this.sheen.isReadyForSubMesh(defines, scene)) {
-            return false;
-        }
-
-        if (!this.anisotropy.isReadyForSubMesh(defines, scene)) {
+        if (!this.subSurface.isReadyForSubMesh(defines, scene) ||
+            !this.clearCoat.isReadyForSubMesh(defines, scene, engine, this._disableBumpMap) ||
+            !this.sheen.isReadyForSubMesh(defines, scene) ||
+            !this.anisotropy.isReadyForSubMesh(defines, scene)) {
             return false;
         }
 
@@ -1095,7 +1073,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         }
 
         fallbackRank = PBRAnisotropicConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
-        fallbackRank = PBRClearCoatConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
+        fallbackRank = PBRAnisotropicConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
+        fallbackRank = PBRSubSurfaceConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
         fallbackRank = PBRSheenConfiguration.AddFallbacks(defines, fallbacks, fallbackRank);
 
         if (defines.ENVIRONMENTBRDF) {
@@ -1184,27 +1163,29 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         var uniforms = ["world", "view", "viewProjection", "vEyePosition", "vLightsType", "vAmbientColor", "vAlbedoColor", "vReflectivityColor", "vEmissiveColor", "visibility", "vReflectionColor",
             "vFogInfos", "vFogColor", "pointSize",
             "vAlbedoInfos", "vAmbientInfos", "vOpacityInfos", "vReflectionInfos", "vReflectionPosition", "vReflectionSize", "vEmissiveInfos", "vReflectivityInfos",
-            "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos", "vRefractionInfos",
+            "vMicroSurfaceSamplerInfos", "vBumpInfos", "vLightmapInfos",
             "mBones",
-            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix", "refractionMatrix",
+            "vClipPlane", "vClipPlane2", "vClipPlane3", "vClipPlane4", "albedoMatrix", "ambientMatrix", "opacityMatrix", "reflectionMatrix", "emissiveMatrix", "reflectivityMatrix", "normalMatrix", "microSurfaceSamplerMatrix", "bumpMatrix", "lightmapMatrix",
             "vLightingIntensity",
             "logarithmicDepthConstant",
             "vSphericalX", "vSphericalY", "vSphericalZ",
             "vSphericalXX", "vSphericalYY", "vSphericalZZ",
             "vSphericalXY", "vSphericalYZ", "vSphericalZX",
-            "vReflectionMicrosurfaceInfos", "vRefractionMicrosurfaceInfos",
+            "vReflectionMicrosurfaceInfos",
             "vTangentSpaceParams", "boneTextureWidth",
             "vDebugMode"
         ];
 
         var samplers = ["albedoSampler", "reflectivitySampler", "ambientSampler", "emissiveSampler",
             "bumpSampler", "lightmapSampler", "opacitySampler",
-            "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh",
             "reflectionSampler", "reflectionSamplerLow", "reflectionSamplerHigh",
             "microSurfaceSampler", "environmentBrdfSampler", "boneSampler"];
 
         var uniformBuffers = ["Material", "Scene"];
 
+        PBRSubSurfaceConfiguration.AddUniforms(uniforms);
+        PBRSubSurfaceConfiguration.AddSamplers(samplers);
+
         PBRClearCoatConfiguration.AddUniforms(uniforms);
         PBRClearCoatConfiguration.AddSamplers(samplers);
 
@@ -1254,13 +1235,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         defines._needNormals = true;
 
         // Multiview
-        if (scene.activeCamera) {
-            var previousMultiview = defines.MULTIVIEW;
-            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
-            if (defines.MULTIVIEW != previousMultiview) {
-                defines.markAsUnprocessed();
-            }
-        }
+        MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
         // Textures
         defines.METALLICWORKFLOW = this.isMetallicWorkflow();
@@ -1442,22 +1417,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     defines.BUMP = false;
                 }
 
-                var refractionTexture = this._getRefractionTexture();
-                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                    defines.REFRACTION = true;
-                    defines.REFRACTIONMAP_3D = refractionTexture.isCube;
-                    defines.GAMMAREFRACTION = refractionTexture.gammaSpace;
-                    defines.RGBDREFRACTION = refractionTexture.isRGBD;
-                    defines.REFRACTIONMAP_OPPOSITEZ = refractionTexture.invertZ;
-                    defines.LODINREFRACTIONALPHA = refractionTexture.lodLevelInAlpha;
-
-                    if (this._linkRefractionWithTransparency) {
-                        defines.LINKREFRACTIONTOTRANSPARENCY = true;
-                    }
-                } else {
-                    defines.REFRACTION = false;
-                }
-
                 if (this._environmentBRDFTexture && MaterialFlags.ReflectionTextureEnabled) {
                     defines.ENVIRONMENTBRDF = true;
                     // Not actual true RGBD, only the B chanel is encoded as RGBD for sheen.
@@ -1524,6 +1483,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         }
 
         // External config
+        this.subSurface.prepareDefines(defines, scene);
         this.clearCoat.prepareDefines(defines, scene);
         this.anisotropy.prepareDefines(defines, mesh, scene);
         this.brdf.prepareDefines(defines);
@@ -1573,7 +1533,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         this._uniformBuffer.addUniform("vLightmapInfos", 2);
         this._uniformBuffer.addUniform("vReflectivityInfos", 3);
         this._uniformBuffer.addUniform("vMicroSurfaceSamplerInfos", 2);
-        this._uniformBuffer.addUniform("vRefractionInfos", 4);
         this._uniformBuffer.addUniform("vReflectionInfos", 2);
         this._uniformBuffer.addUniform("vReflectionPosition", 3);
         this._uniformBuffer.addUniform("vReflectionSize", 3);
@@ -1587,14 +1546,12 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         this._uniformBuffer.addUniform("microSurfaceSamplerMatrix", 16);
         this._uniformBuffer.addUniform("bumpMatrix", 16);
         this._uniformBuffer.addUniform("vTangentSpaceParams", 2);
-        this._uniformBuffer.addUniform("refractionMatrix", 16);
         this._uniformBuffer.addUniform("reflectionMatrix", 16);
 
         this._uniformBuffer.addUniform("vReflectionColor", 3);
         this._uniformBuffer.addUniform("vAlbedoColor", 4);
         this._uniformBuffer.addUniform("vLightingIntensity", 4);
 
-        this._uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
         this._uniformBuffer.addUniform("vReflectionMicrosurfaceInfos", 3);
         this._uniformBuffer.addUniform("pointSize", 1);
         this._uniformBuffer.addUniform("vReflectivityColor", 4);
@@ -1604,20 +1561,29 @@ export abstract class PBRBaseMaterial extends PushMaterial {
         PBRClearCoatConfiguration.PrepareUniformBuffer(this._uniformBuffer);
         PBRAnisotropicConfiguration.PrepareUniformBuffer(this._uniformBuffer);
         PBRSheenConfiguration.PrepareUniformBuffer(this._uniformBuffer);
+        PBRSubSurfaceConfiguration.PrepareUniformBuffer(this._uniformBuffer);
 
         this._uniformBuffer.create();
     }
 
     /**
-     * Unbinds the textures.
+     * Unbinds the material from the mesh
      */
     public unbind(): void {
-        if (this._reflectionTexture && this._reflectionTexture.isRenderTarget) {
-            this._uniformBuffer.setTexture("reflectionSampler", null);
-        }
+        if (this._activeEffect) {
+            let needFlag = false;
+            if (this._reflectionTexture && this._reflectionTexture.isRenderTarget) {
+                this._activeEffect.setTexture("reflection2DSampler", null);
+                needFlag = true;
+            }
 
-        if (this._refractionTexture && this._refractionTexture.isRenderTarget) {
-            this._uniformBuffer.setTexture("refractionSampler", null);
+            if (this.subSurface.unbind(this._activeEffect)) {
+                needFlag = true;
+            }
+
+            if (needFlag) {
+                this._markAllSubMeshesAsTexturesDirty();
+            }
         }
 
         super.unbind();
@@ -1666,7 +1632,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
 
             this.bindViewProjection(effect);
             reflectionTexture = this._getReflectionTexture();
-            var refractionTexture = this._getRefractionTexture();
 
             if (!this._uniformBuffer.useUbo || !this.isFrozen || !this._uniformBuffer.isSync) {
 
@@ -1757,22 +1722,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                             this._uniformBuffer.updateFloat2("vTangentSpaceParams", this._invertNormalMapX ? -1.0 : 1.0, this._invertNormalMapY ? -1.0 : 1.0);
                         }
                     }
-
-                    if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                        this._uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
-
-                        var depth = 1.0;
-                        if (!refractionTexture.isCube) {
-                            if ((<any>refractionTexture).depth) {
-                                depth = (<any>refractionTexture).depth;
-                            }
-                        }
-                        this._uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
-                        this._uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos",
-                            refractionTexture.getSize().width,
-                            refractionTexture.lodGenerationScale,
-                            refractionTexture.lodGenerationOffset);
-                    }
                 }
 
                 // Point size
@@ -1835,17 +1784,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                     this._uniformBuffer.setTexture("environmentBrdfSampler", this._environmentBRDFTexture);
                 }
 
-                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
-                    if (defines.LODBASEDMICROSFURACE) {
-                        this._uniformBuffer.setTexture("refractionSampler", refractionTexture);
-                    }
-                    else {
-                        this._uniformBuffer.setTexture("refractionSampler", refractionTexture._lodTextureMid || refractionTexture);
-                        this._uniformBuffer.setTexture("refractionSamplerLow", refractionTexture._lodTextureLow || refractionTexture);
-                        this._uniformBuffer.setTexture("refractionSamplerHigh", refractionTexture._lodTextureHigh || refractionTexture);
-                    }
-                }
-
                 if (this._emissiveTexture && MaterialFlags.EmissiveTextureEnabled) {
                     this._uniformBuffer.setTexture("emissiveSampler", this._emissiveTexture);
                 }
@@ -1872,6 +1810,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
                 }
             }
 
+            this.subSurface.bindForSubMesh(this._uniformBuffer, scene, engine, this.isFrozen, defines.LODBASEDMICROSFURACE);
             this.clearCoat.bindForSubMesh(this._uniformBuffer, scene, engine, this._disableBumpMap, this.isFrozen, this._invertNormalMapX, this._invertNormalMapY);
             this.anisotropy.bindForSubMesh(this._uniformBuffer, scene, this.isFrozen);
             this.sheen.bindForSubMesh(this._uniformBuffer, scene, this.isFrozen);
@@ -1967,10 +1906,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             results.push(this._lightmapTexture);
         }
 
-        if (this._refractionTexture && this._refractionTexture.animations && this._refractionTexture.animations.length > 0) {
-            results.push(this._refractionTexture);
-        }
-
+        this.subSurface.getAnimatables(results);
         this.clearCoat.getAnimatables(results);
         this.sheen.getAnimatables(results);
         this.anisotropy.getAnimatables(results);
@@ -1991,23 +1927,6 @@ export abstract class PBRBaseMaterial extends PushMaterial {
     }
 
     /**
-     * Returns the texture used for refraction or null if none is used.
-     * @returns - Refection texture if present.  If no refraction texture and refraction
-     * is linked with transparency, returns environment texture.  Otherwise, returns null.
-     */
-    private _getRefractionTexture(): Nullable<BaseTexture> {
-        if (this._refractionTexture) {
-            return this._refractionTexture;
-        }
-
-        if (this._linkRefractionWithTransparency) {
-            return this.getScene().environmentTexture;
-        }
-
-        return null;
-    }
-
-    /**
      * Returns an array of the actively used textures.
      * @returns - Array of BaseTextures
      */
@@ -2054,10 +1973,7 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             activeTextures.push(this._lightmapTexture);
         }
 
-        if (this._refractionTexture) {
-            activeTextures.push(this._refractionTexture);
-        }
-
+        this.subSurface.getActiveTextures(activeTextures);
         this.clearCoat.getActiveTextures(activeTextures);
         this.sheen.getActiveTextures(activeTextures);
         this.anisotropy.getActiveTextures(activeTextures);
@@ -2111,7 +2027,8 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             return true;
         }
 
-        return this.clearCoat.hasTexture(texture) ||
+        return this.subSurface.hasTexture(texture) ||
+            this.clearCoat.hasTexture(texture) ||
             this.sheen.hasTexture(texture) ||
             this.anisotropy.hasTexture(texture);
     }
@@ -2162,12 +2079,9 @@ export abstract class PBRBaseMaterial extends PushMaterial {
             if (this._lightmapTexture) {
                 this._lightmapTexture.dispose();
             }
-
-            if (this._refractionTexture) {
-                this._refractionTexture.dispose();
-            }
         }
 
+        this.subSurface.dispose(forceDisposeTextures);
         this.clearCoat.dispose(forceDisposeTextures);
         this.sheen.dispose(forceDisposeTextures);
         this.anisotropy.dispose(forceDisposeTextures);

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

@@ -96,14 +96,14 @@ export class PBRClearCoatConfiguration {
     public isTintEnabled = false;
 
     /**
-     * Defines if the clear coat tint is enabled in the material.
+     * Defines the clear coat tint of the material.
      * This is only use if tint is enabled
      */
     @serializeAsColor3()
     public tintColor = Color3.White();
 
     /**
-     * Defines if the distance at which the tint color should be found in the
+     * Defines the distance at which the tint color should be found in the
      * clear coat media.
      * This is only use if tint is enabled
      */
@@ -144,7 +144,7 @@ export class PBRClearCoatConfiguration {
     }
 
     /**
-     * Specifies that the submesh is ready to be used.
+     * Gets wehter the submesh is ready to be used or not.
      * @param defines the list of "defines" to update.
      * @param scene defines the scene the material belongs to.
      * @param engine defines the engine the material belongs to.

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

@@ -191,9 +191,18 @@ export class PBRMaterial extends PBRBaseMaterial {
     /**
      * Stores the refracted light information in a texture.
      */
-    @serializeAsTexture()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public refractionTexture: BaseTexture;
+    public get refractionTexture(): Nullable<BaseTexture> {
+        return this.subSurface.refractionTexture;
+    }
+    public set refractionTexture(value: Nullable<BaseTexture>) {
+        this.subSurface.refractionTexture = value;
+        if (value) {
+            this.subSurface.isRefractionEnabled = true;
+        }
+        else if (!this.subSurface.linkRefractionWithTransparency) {
+            this.subSurface.isRefractionEnabled = false;
+        }
+    }
 
     /**
      * The color of a material in ambient lighting.
@@ -240,24 +249,36 @@ export class PBRMaterial extends PBRBaseMaterial {
     /**
      * source material index of refraction (IOR)' / 'destination material IOR.
      */
-    @serialize()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public indexOfRefraction = 0.66;
+    public get indexOfRefraction(): number {
+        return  1 / this.subSurface.indexOfRefraction;
+    }
+    public set indexOfRefraction(value: number) {
+        this.subSurface.indexOfRefraction =  1 / value;
+    }
 
     /**
      * Controls if refraction needs to be inverted on Y. This could be useful for procedural texture.
      */
-    @serialize()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public invertRefractionY = false;
+    public get invertRefractionY(): boolean {
+        return this.subSurface.invertRefractionY;
+    }
+    public set invertRefractionY(value: boolean) {
+        this.subSurface.invertRefractionY = value;
+    }
 
     /**
      * This parameters will make the material used its opacity to control how much it is refracting aginst not.
      * Materials half opaque for instance using refraction could benefit from this control.
      */
-    @serialize()
-    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
-    public linkRefractionWithTransparency = false;
+    public get linkRefractionWithTransparency(): boolean {
+        return this.subSurface.linkRefractionWithTransparency;
+    }
+    public set linkRefractionWithTransparency(value: boolean) {
+        this.subSurface.linkRefractionWithTransparency = value;
+        if (value) {
+            this.subSurface.isRefractionEnabled = true;
+        }
+    }
 
     /**
      * If true, the light map contains occlusion information instead of lighting info.

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

@@ -0,0 +1,551 @@
+import { Nullable } from "../../types";
+import { IAnimatable } from "../../Misc/tools";
+import { SerializationHelper, serialize, serializeAsTexture, expandToProperty, serializeAsColor3 } from "../../Misc/decorators";
+import { Color3 } from "../../Maths/math";
+import { SmartArray } from "../../Misc/smartArray";
+import { BaseTexture } from "../../Materials/Textures/baseTexture";
+import { RenderTargetTexture } from "../../Materials/Textures/renderTargetTexture";
+import { Effect, EffectFallbacks } from "../../Materials/effect";
+import { MaterialFlags } from "../materialFlags";
+import { UniformBuffer } from "../../Materials/uniformBuffer";
+import { MaterialHelper } from "../../Materials/materialHelper";
+
+declare type Engine = import("../../Engines/engine").Engine;
+declare type Scene = import("../../scene").Scene;
+
+/**
+ * @hidden
+ */
+export interface IMaterialSubSurfaceDefines {
+    SUBSURFACE: boolean;
+
+    SS_REFRACTION: boolean;
+    SS_TRANSLUCENCY: boolean;
+    SS_SCATERRING: boolean;
+
+    SS_THICKNESSANDMASK_TEXTURE: boolean;
+    SS_THICKNESSANDMASK_TEXTUREDIRECTUV: number;
+
+    SS_REFRACTIONMAP_3D: boolean;
+    SS_REFRACTIONMAP_OPPOSITEZ: boolean;
+    SS_LODINREFRACTIONALPHA: boolean;
+    SS_GAMMAREFRACTION: boolean;
+    SS_RGBDREFRACTION: boolean;
+    SS_LINKREFRACTIONTOTRANSPARENCY: boolean;
+
+    SS_MASK_FROM_THICKNESS_TEXTURE: boolean;
+
+    /** @hidden */
+    _areTexturesDirty: boolean;
+}
+
+/**
+ * Define the code related to the sub surface parameters of the pbr material.
+ */
+export class PBRSubSurfaceConfiguration {
+    @serialize()
+    private _isRefractionEnabled = false;
+    /**
+     * Defines if the refraction is enabled in the material.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public isRefractionEnabled = false;
+
+    @serialize()
+    private _isTranslucencyEnabled = false;
+    /**
+     * Defines if the translucency is enabled in the material.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public isTranslucencyEnabled = false;
+
+    @serialize()
+    private _isScatteringEnabled = false;
+    // /**
+    //  * Defines if the sub surface scattering is enabled in the material.
+    //  */
+    // @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    // public isScatteringEnabled = false;
+
+    /**
+     * Defines the refraction intensity of the material.
+     * The refraction when enabled replaces the Diffuse part of the material.
+     * The intensity helps transitionning between diffuse and refraction.
+     */
+    @serialize()
+    public refractionIntensity: number = 1;
+
+    /**
+     * Defines the translucency intensity of the material.
+     * When translucency has been enabled, this defines how much of the "translucency"
+     * is addded to the diffuse part of the material.
+     */
+    @serialize()
+    public translucencyIntensity: number = 1;
+
+    /**
+     * Defines the scattering intensity of the material.
+     * When scattering has been enabled, this defines how much of the "scattered light"
+     * is addded to the diffuse part of the material.
+     */
+    @serialize()
+    public scatteringIntensity: number = 1;
+
+    @serializeAsTexture()
+    private _thicknessTexture: Nullable<BaseTexture> = null;
+    /**
+     * Stores the average thickness of a mesh in a texture (The texture is holding the values linearly).
+     * The red channel of the texture should contain the thickness remapped between 0 and 1.
+     * 0 would mean minimumThickness
+     * 1 would mean maximumThickness
+     * The other channels might be use as a mask to vary the different effects intensity.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public thicknessTexture: Nullable<BaseTexture> = null;
+
+    private _refractionTexture: Nullable<BaseTexture> = null;
+    /**
+     * Defines the texture to use for refraction.
+     */
+    @serializeAsTexture()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public refractionTexture: Nullable<BaseTexture> = null;
+
+    private _indexOfRefraction = 1;
+    /**
+     * Defines the indice of refraction used in the material.
+     * https://en.wikipedia.org/wiki/List_of_refractive_indices
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public indexOfRefraction = 1;
+
+    private _invertRefractionY = false;
+    /**
+     * Controls if refraction needs to be inverted on Y. This could be useful for procedural texture.
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public invertRefractionY = false;
+
+    private _linkRefractionWithTransparency = false;
+    /**
+     * This parameters will make the material used its opacity to control how much it is refracting aginst not.
+     * Materials half opaque for instance using refraction could benefit from this control.
+     */
+    @serialize()
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public linkRefractionWithTransparency = false;
+
+    /**
+     * Defines the minimum thickness stored in the thickness map.
+     * If no thickness map is defined, this value will be used to simulate thickness.
+     */
+    @serialize()
+    public minimumThickness: number = 0;
+
+    /**
+     * Defines the maximum thickness stored in the thickness map.
+     */
+    @serialize()
+    public maximumThickness: number = 1;
+
+    /**
+     * Defines the volume tint of the material.
+     * This is used for both translucency and scattering.
+     */
+    @serializeAsColor3()
+    public tintColor = Color3.White();
+
+    /**
+     * Defines the distance at which the tint color should be found in the media.
+     * This is used for refraction only.
+     */
+    @serialize()
+    public tintColorAtDistance = 1;
+
+    /**
+     * Defines how far each channel transmit through the media.
+     * It is defined as a color to simplify it selection.
+     */
+    @serializeAsColor3()
+    public diffusionDistance = Color3.White();
+
+    @serialize()
+    private _useMaskFromThicknessTexture = false;
+    /**
+     * Stores the intensity of the different subsurface effects in the thickness texture.
+     * * the green channel is the translucency intensity.
+     * * the blue channel is the scattering intensity.
+     * * the alpha channel is the refraction intensity.
+     */
+    @expandToProperty("_markAllSubMeshesAsTexturesDirty")
+    public useMaskFromThicknessTexture: boolean = false;
+
+    /** @hidden */
+    private _internalMarkAllSubMeshesAsTexturesDirty: () => void;
+
+    /** @hidden */
+    public _markAllSubMeshesAsTexturesDirty(): void {
+        this._internalMarkAllSubMeshesAsTexturesDirty();
+    }
+
+    /**
+     * Instantiate a new istance of sub surface configuration.
+     * @param markAllSubMeshesAsTexturesDirty Callback to flag the material to dirty
+     */
+    constructor(markAllSubMeshesAsTexturesDirty: () => void) {
+        this._internalMarkAllSubMeshesAsTexturesDirty = markAllSubMeshesAsTexturesDirty;
+    }
+
+    /**
+     * Gets wehter the submesh is ready to be used or not.
+     * @param defines the list of "defines" to update.
+     * @param scene defines the scene the material belongs to.
+     * @returns - boolean indicating that the submesh is ready or not.
+     */
+    public isReadyForSubMesh(defines: IMaterialSubSurfaceDefines, scene: Scene): boolean {
+        if (defines._areTexturesDirty) {
+            if (scene.texturesEnabled) {
+                if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                    if (!this._thicknessTexture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
+
+                var refractionTexture = this._getRefractionTexture(scene);
+                if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                    if (!refractionTexture.isReadyOrNotBlocking()) {
+                        return false;
+                    }
+                }
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param defines the list of "defines" to update.
+     * @param scene defines the scene to the material belongs to.
+     */
+    public prepareDefines(defines: IMaterialSubSurfaceDefines, scene: Scene): void {
+        if (defines._areTexturesDirty) {
+            defines.SUBSURFACE = false;
+
+            defines.SS_TRANSLUCENCY = this._isTranslucencyEnabled;
+            defines.SS_SCATERRING = this._isScatteringEnabled;
+            defines.SS_THICKNESSANDMASK_TEXTURE = false;
+            defines.SS_MASK_FROM_THICKNESS_TEXTURE = false;
+            defines.SS_REFRACTION = false;
+            defines.SS_REFRACTIONMAP_3D = false;
+            defines.SS_GAMMAREFRACTION = false;
+            defines.SS_RGBDREFRACTION = false;
+            defines.SS_REFRACTIONMAP_OPPOSITEZ = false;
+            defines.SS_LODINREFRACTIONALPHA = false;
+            defines.SS_LINKREFRACTIONTOTRANSPARENCY = false;
+
+            if (this._isRefractionEnabled || this._isTranslucencyEnabled || this._isScatteringEnabled) {
+                defines.SUBSURFACE = true;
+
+                if (defines._areTexturesDirty) {
+                    if (scene.texturesEnabled) {
+                        if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                            MaterialHelper.PrepareDefinesForMergedUV(this._thicknessTexture, defines, "SS_THICKNESSANDMASK_TEXTURE");
+                        }
+                    }
+                }
+
+                defines.SS_MASK_FROM_THICKNESS_TEXTURE = this._useMaskFromThicknessTexture;
+            }
+
+            if (this._isRefractionEnabled) {
+                if (scene.texturesEnabled) {
+                    var refractionTexture = this._getRefractionTexture(scene);
+                    if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                        defines.SS_REFRACTION = true;
+                        defines.SS_REFRACTIONMAP_3D = refractionTexture.isCube;
+                        defines.SS_GAMMAREFRACTION = refractionTexture.gammaSpace;
+                        defines.SS_RGBDREFRACTION = refractionTexture.isRGBD;
+                        defines.SS_REFRACTIONMAP_OPPOSITEZ = refractionTexture.invertZ;
+                        defines.SS_LODINREFRACTIONALPHA = refractionTexture.lodLevelInAlpha;
+                        defines.SS_LINKREFRACTIONTOTRANSPARENCY = this._linkRefractionWithTransparency;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Binds the material data.
+     * @param uniformBuffer defines the Uniform buffer to fill in.
+     * @param scene defines the scene the material belongs to.
+     * @param engine defines the engine the material belongs to.
+     * @param isFrozen defines wether the material is frozen or not.
+     * @param lodBasedMicrosurface defines wether the material relies on lod based microsurface or not.
+     */
+    public bindForSubMesh(uniformBuffer: UniformBuffer, scene: Scene, engine: Engine, isFrozen: boolean, lodBasedMicrosurface: boolean): void {
+        var refractionTexture = this._getRefractionTexture(scene);
+
+        if (!uniformBuffer.useUbo || !isFrozen || !uniformBuffer.isSync) {
+            if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                uniformBuffer.updateFloat2("vThicknessInfos", this._thicknessTexture.coordinatesIndex, this._thicknessTexture.level);
+                MaterialHelper.BindTextureMatrix(this._thicknessTexture, uniformBuffer, "thickness");
+            }
+
+            uniformBuffer.updateFloat2("vThicknessParam", this.minimumThickness, this.maximumThickness - this.minimumThickness);
+
+            if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                uniformBuffer.updateMatrix("refractionMatrix", refractionTexture.getReflectionTextureMatrix());
+
+                var depth = 1.0;
+                if (!refractionTexture.isCube) {
+                    if ((<any>refractionTexture).depth) {
+                        depth = (<any>refractionTexture).depth;
+                    }
+                }
+                uniformBuffer.updateFloat4("vRefractionInfos", refractionTexture.level, 1 / this._indexOfRefraction, depth, this._invertRefractionY ? -1 : 1);
+                uniformBuffer.updateFloat3("vRefractionMicrosurfaceInfos",
+                    refractionTexture.getSize().width,
+                    refractionTexture.lodGenerationScale,
+                    refractionTexture.lodGenerationOffset);
+            }
+
+            uniformBuffer.updateColor3("vDiffusionDistance", this.diffusionDistance);
+
+            uniformBuffer.updateFloat4("vTintColor", this.tintColor.r,
+                this.tintColor.g,
+                this.tintColor.b,
+                this.tintColorAtDistance);
+
+            uniformBuffer.updateFloat3("vSubSurfaceIntensity", this.refractionIntensity, this.translucencyIntensity, this.scatteringIntensity);
+        }
+
+        // Textures
+        if (scene.texturesEnabled) {
+            if (this._thicknessTexture && MaterialFlags.ThicknessTextureEnabled) {
+                uniformBuffer.setTexture("thicknessSampler", this._thicknessTexture);
+            }
+
+            if (refractionTexture && MaterialFlags.RefractionTextureEnabled) {
+                if (lodBasedMicrosurface) {
+                    uniformBuffer.setTexture("refractionSampler", refractionTexture);
+                }
+                else {
+                    uniformBuffer.setTexture("refractionSampler", refractionTexture._lodTextureMid || refractionTexture);
+                    uniformBuffer.setTexture("refractionSamplerLow", refractionTexture._lodTextureLow || refractionTexture);
+                    uniformBuffer.setTexture("refractionSamplerHigh", refractionTexture._lodTextureHigh || refractionTexture);
+                }
+            }
+        }
+    }
+
+    /**
+     * Unbinds the material from the mesh.
+     * @param activeEffect defines the effect that should be unbound from.
+     * @returns true if unbound, otherwise false
+     */
+    public unbind(activeEffect: Effect): boolean {
+        if (this._refractionTexture && this._refractionTexture.isRenderTarget) {
+            activeEffect.setTexture("refractionSampler", null);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the texture used for refraction or null if none is used.
+     * @param scene defines the scene the material belongs to.
+     * @returns - Refraction texture if present.  If no refraction texture and refraction
+     * is linked with transparency, returns environment texture.  Otherwise, returns null.
+     */
+    private _getRefractionTexture(scene: Scene): Nullable<BaseTexture> {
+        if (this._refractionTexture) {
+            return this._refractionTexture;
+        }
+
+        if (this._isRefractionEnabled) {
+            return scene.environmentTexture;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns true if alpha blending should be disabled.
+     */
+    public get disableAlphaBlending(): boolean {
+        return this.isRefractionEnabled && this._linkRefractionWithTransparency;
+    }
+
+    /**
+     * Fills the list of render target textures.
+     * @param renderTargets the list of render targets to update
+     */
+    public fillRenderTargetTextures(renderTargets: SmartArray<RenderTargetTexture>): void {
+        if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
+            renderTargets.push(<RenderTargetTexture>this._refractionTexture);
+        }
+    }
+
+    /**
+     * Checks to see if a texture is used in the material.
+     * @param texture - Base texture to use.
+     * @returns - Boolean specifying if a texture is used in the material.
+     */
+    public hasTexture(texture: BaseTexture): boolean {
+        if (this._thicknessTexture === texture) {
+            return true;
+        }
+
+        if (this._refractionTexture === texture) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Gets a boolean indicating that current material needs to register RTT
+     * @returns true if this uses a render target otherwise false.
+     */
+    public hasRenderTargetTextures(): boolean {
+        if (MaterialFlags.RefractionTextureEnabled && this._refractionTexture && this._refractionTexture.isRenderTarget) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns an array of the actively used textures.
+     * @param activeTextures Array of BaseTextures
+     */
+    public getActiveTextures(activeTextures: BaseTexture[]): void {
+        if (this._thicknessTexture) {
+            activeTextures.push(this._thicknessTexture);
+        }
+
+        if (this._refractionTexture) {
+            activeTextures.push(this._refractionTexture);
+        }
+    }
+
+    /**
+     * Returns the animatable textures.
+     * @param animatables Array of animatable textures.
+     */
+    public getAnimatables(animatables: IAnimatable[]): void {
+        if (this._thicknessTexture && this._thicknessTexture.animations && this._thicknessTexture.animations.length > 0) {
+            animatables.push(this._thicknessTexture);
+        }
+
+        if (this._refractionTexture && this._refractionTexture.animations && this._refractionTexture.animations.length > 0) {
+            animatables.push(this._refractionTexture);
+        }
+    }
+
+    /**
+     * Disposes the resources of the material.
+     * @param forceDisposeTextures - Forces the disposal of all textures.
+     */
+    public dispose(forceDisposeTextures?: boolean): void {
+        if (forceDisposeTextures) {
+            if (this._thicknessTexture) {
+                this._thicknessTexture.dispose();
+            }
+
+            if (this._refractionTexture) {
+                this._refractionTexture.dispose();
+            }
+        }
+    }
+
+    /**
+    * Get the current class name of the texture useful for serialization or dynamic coding.
+    * @returns "PBRSubSurfaceConfiguration"
+    */
+    public getClassName(): string {
+        return "PBRSubSurfaceConfiguration";
+    }
+
+    /**
+     * Add fallbacks to the effect fallbacks list.
+     * @param defines defines the Base texture to use.
+     * @param fallbacks defines the current fallback list.
+     * @param currentRank defines the current fallback rank.
+     * @returns the new fallback rank.
+     */
+    public static AddFallbacks(defines: IMaterialSubSurfaceDefines, fallbacks: EffectFallbacks, currentRank: number): number {
+        if (defines.SS_SCATERRING) {
+            fallbacks.addFallback(currentRank++, "SS_SCATERRING");
+        }
+        if (defines.SS_TRANSLUCENCY) {
+            fallbacks.addFallback(currentRank++, "SS_TRANSLUCENCY");
+        }
+        return currentRank;
+    }
+
+    /**
+     * Add the required uniforms to the current list.
+     * @param uniforms defines the current uniform list.
+     */
+    public static AddUniforms(uniforms: string[]): void {
+        uniforms.push(
+            "vDiffusionDistance", "vTintColor", "vSubSurfaceIntensity",
+            "vRefractionMicrosurfaceInfos",
+            "vRefractionInfos", "vThicknessInfos", "vThicknessParam",
+            "refractionMatrix", "thicknessMatrix");
+    }
+
+    /**
+     * Add the required samplers to the current list.
+     * @param samplers defines the current sampler list.
+     */
+    public static AddSamplers(samplers: string[]): void {
+        samplers.push("thicknessSampler",
+            "refractionSampler", "refractionSamplerLow", "refractionSamplerHigh");
+    }
+
+    /**
+     * Add the required uniforms to the current buffer.
+     * @param uniformBuffer defines the current uniform buffer.
+     */
+    public static PrepareUniformBuffer(uniformBuffer: UniformBuffer): void {
+        uniformBuffer.addUniform("vRefractionMicrosurfaceInfos", 3);
+        uniformBuffer.addUniform("vRefractionInfos", 4);
+        uniformBuffer.addUniform("refractionMatrix", 16);
+        uniformBuffer.addUniform("vThicknessInfos", 2);
+        uniformBuffer.addUniform("thicknessMatrix", 16);
+        uniformBuffer.addUniform("vThicknessParam", 2);
+        uniformBuffer.addUniform("vDiffusionDistance", 3);
+        uniformBuffer.addUniform("vTintColor", 4);
+        uniformBuffer.addUniform("vSubSurfaceIntensity", 3);
+    }
+
+    /**
+     * Makes a duplicate of the current configuration into another one.
+     * @param configuration define the config where to copy the info
+     */
+    public copyTo(configuration: PBRSubSurfaceConfiguration): void {
+        SerializationHelper.Clone(() => configuration, this);
+    }
+
+    /**
+     * Serializes this Sub Surface configuration.
+     * @returns - An object with the serialized config.
+     */
+    public serialize(): any {
+        return SerializationHelper.Serialize(this);
+    }
+
+    /**
+     * Parses a Sub Surface Configuration from a serialized object.
+     * @param source - Serialized object.
+     */
+    public parse(source: any): void {
+        SerializationHelper.Parse(() => this, source, null);
+    }
+}

+ 40 - 0
src/Materials/Textures/MultiviewRenderTarget.ts

@@ -0,0 +1,40 @@
+import { RenderTargetTexture } from '../Textures/renderTargetTexture';
+import { Scene } from '../../scene';
+import { InternalTexture } from '../Textures/internalTexture';
+
+/**
+ * Renders to multiple views with a single draw call
+ * @see https://www.khronos.org/registry/webgl/extensions/WEBGL_multiview/
+ */
+export class MultiviewRenderTarget extends RenderTargetTexture {
+    /**
+     * Creates a multiview render target
+     * @param scene scene used with the render target
+     * @param size the size of the render target (used for each view)
+     */
+    constructor(scene: Scene, size: number | { width: number, height: number } | { ratio: number } = 512) {
+        super("multiview rtt", size, scene, false, true, InternalTexture.DATASOURCE_UNKNOWN, false, undefined, false, false, true, undefined, true);
+        var internalTexture = scene.getEngine().createMultiviewRenderTargetTexture(this.getRenderWidth(), this.getRenderHeight());
+        internalTexture.isMultiview = true;
+        this._texture = internalTexture;
+    }
+
+    /**
+     * @hidden
+     * @param faceIndex the face index, if its a cube texture
+     */
+    public _bindFrameBuffer(faceIndex: number = 0) {
+        if (!this._texture) {
+            return;
+        }
+        this.getScene()!.getEngine().bindMultiviewFramebuffer(this._texture);
+    }
+
+    /**
+     * Gets the number of views the corresponding to the texture (eg. a MultiviewRenderTarget will have > 1)
+     * @returns the view count
+     */
+    public getViewCount() {
+        return 2;
+    }
+}

+ 4 - 39
src/Materials/Textures/renderTargetTexture.ts

@@ -556,6 +556,8 @@ export class RenderTargetTexture extends Texture {
      *   - or an object containing a ratio { ratio: number }
      */
     public resize(size: number | { width: number, height: number } | { ratio: number }): void {
+        var wasCube = this.isCube;
+
         this.releaseInternalTexture();
         let scene = this.getScene();
 
@@ -565,7 +567,7 @@ export class RenderTargetTexture extends Texture {
 
         this._processSizeParameter(size);
 
-        if (this.isCube) {
+        if (wasCube) {
             this._texture = scene.getEngine().createRenderTargetCubeTexture(this.getRenderSize(), this._renderTargetOptions);
         } else {
             this._texture = scene.getEngine().createRenderTargetTexture(this._size, this._renderTargetOptions);
@@ -1001,41 +1003,4 @@ export class RenderTargetTexture extends Texture {
 
 Texture._CreateRenderTargetTexture = (name: string, renderTargetSize: number, scene: Scene, generateMipMaps: boolean) => {
     return new RenderTargetTexture(name, renderTargetSize, scene, generateMipMaps);
-};
-
-/**
- * Renders to multiple views with a single draw call
- * @see https://www.khronos.org/registry/webgl/extensions/WEBGL_multiview/
- */
-export class MultiviewRenderTarget extends RenderTargetTexture {
-    /**
-     * Creates a multiview render target
-     * @param scene scene used with the render target
-     * @param size the size of the render target (used for each view)
-     */
-    constructor(scene: Scene, size: number | { width: number, height: number } | { ratio: number } = 512) {
-        super("multiview rtt", size, scene, false, true, InternalTexture.DATASOURCE_UNKNOWN, false, undefined, false, false, true, undefined, true);
-        var internalTexture = scene.getEngine().createMultiviewRenderTargetTexture(this.getRenderWidth(), this.getRenderHeight());
-        internalTexture.isMultiview = true;
-        this._texture = internalTexture;
-    }
-
-    /**
-     * @hidden
-     * @param faceIndex the face index, if its a cube texture
-     */
-    public _bindFrameBuffer(faceIndex: number = 0) {
-        if (!this._texture) {
-            return;
-        }
-        this.getScene()!.getEngine().bindMultiviewFramebuffer(this._texture);
-    }
-
-    /**
-     * Gets the number of views the corresponding to the texture (eg. a MultiviewRenderTarget will have > 1)
-     * @returns the view count
-     */
-    public getViewCount() {
-        return 2;
-    }
-}
+};

+ 1 - 1
src/Materials/Textures/texture.ts

@@ -548,7 +548,7 @@ export class Texture extends BaseTexture {
      */
     public clone(): Texture {
         return SerializationHelper.Clone(() => {
-            return new Texture(this._texture ? this._texture.url : null, this.getScene(), this._noMipmap, this._invertY, this.samplingMode);
+            return new Texture(this._texture ? this._texture.url : null, this.getScene(), this._noMipmap, this._invertY, this.samplingMode, undefined, undefined, this._texture ? this._texture._buffer : undefined);
         }, this);
     }
 

+ 2 - 2
src/Materials/material.ts

@@ -1156,7 +1156,7 @@ export class Material implements IAnimatable {
      * @param rootUrl defines the root URL to use to load textures
      * @returns a new material
      */
-    public static Parse(parsedMaterial: any, scene: Scene, rootUrl: string): any {
+    public static Parse(parsedMaterial: any, scene: Scene, rootUrl: string): Nullable<Material> {
         if (!parsedMaterial.customType) {
             parsedMaterial.customType = "BABYLON.StandardMaterial";
         }
@@ -1164,7 +1164,7 @@ export class Material implements IAnimatable {
             parsedMaterial.customType = "BABYLON.LegacyPBRMaterial";
             if (!BABYLON.LegacyPBRMaterial) {
                 Logger.Error("Your scene is trying to load a legacy version of the PBRMaterial, please, include it from the materials library.");
-                return;
+                return null;
             }
         }
 

+ 16 - 0
src/Materials/materialFlags.ts

@@ -261,4 +261,20 @@ export class MaterialFlags {
         this._AnisotropicTextureEnabled = value;
         Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
     }
+
+    private static _ThicknessTextureEnabled = true;
+    /**
+     * Are thickness textures enabled in the application.
+     */
+    public static get ThicknessTextureEnabled(): boolean {
+        return this._ThicknessTextureEnabled;
+    }
+    public static set ThicknessTextureEnabled(value: boolean) {
+        if (this._ThicknessTextureEnabled === value) {
+            return;
+        }
+
+        this._ThicknessTextureEnabled = value;
+        Engine.MarkAllMaterialsAsDirty(Constants.MATERIAL_TextureDirtyFlag);
+    }
 }

+ 15 - 0
src/Materials/materialHelper.ts

@@ -257,6 +257,21 @@ export class MaterialHelper {
     }
 
     /**
+     * Prepares the defines related to multiview
+     * @param scene The scene we are intending to draw
+     * @param defines The defines to update
+     */
+    public static PrepareDefinesForMultiview(scene: Scene, defines: any) {
+        if (scene.activeCamera) {
+            var previousMultiview = defines.MULTIVIEW;
+            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
+            if (defines.MULTIVIEW != previousMultiview) {
+                defines.markAsUnprocessed();
+            }
+        }
+    }
+
+    /**
      * Prepares the defines related to the light information passed in parameter
      * @param scene The scene we are intending to draw
      * @param mesh The mesh the effect is compiling for

+ 3 - 7
src/Materials/standardMaterial.ts

@@ -793,13 +793,9 @@ export class StandardMaterial extends PushMaterial {
 
         // Lights
         defines._needNormals = MaterialHelper.PrepareDefinesForLights(scene, mesh, defines, true, this._maxSimultaneousLights, this._disableLighting);
-        if (scene.activeCamera) {
-            var previousMultiview = defines.MULTIVIEW;
-            defines.MULTIVIEW = (scene.activeCamera.outputRenderTarget !== null && scene.activeCamera.outputRenderTarget.getViewCount() > 1);
-            if (defines.MULTIVIEW != previousMultiview) {
-                defines.markAsUnprocessed();
-            }
-        }
+
+        // Multiview
+        MaterialHelper.PrepareDefinesForMultiview(scene, defines);
 
         // Textures
         if (defines._areTexturesDirty) {

+ 46 - 51
src/Meshes/transformNode.ts

@@ -72,6 +72,12 @@ export class TransformNode extends Node {
     public billboardMode = TransformNode.BILLBOARDMODE_NONE;
 
     /**
+     * Gets or sets a boolean indicating that parent rotation should be preserved when using billboards.
+     * This could be useful for glTF objects where parent rotation helps converting from right handed to left handed
+     */
+    public preserveParentRotationForBillboard = false;
+
+    /**
      * Multiplication factor on scale x/y/z when computing the world matrix. Eg. for a 1x1x1 cube setting this to 2 will make it a 2x2x2 cube
      */
     @serialize()
@@ -916,54 +922,6 @@ export class TransformNode extends Node {
         this._pivotMatrix.multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[4]);
         Tmp.Matrix[4].multiplyToRef(Tmp.Matrix[0], this._tempMatrix);
 
-        // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
-        if (this.billboardMode !== TransformNode.BILLBOARDMODE_NONE && camera) {
-            if ((this.billboardMode & TransformNode.BILLBOARDMODE_ALL) !== TransformNode.BILLBOARDMODE_ALL) {
-                // Need to decompose each rotation here
-                var currentPosition = Tmp.Vector3[3];
-
-                if (this.parent && this.parent.getWorldMatrix) {
-                    if (this._transformToBoneReferal) {
-                        this.parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[6]);
-                        Vector3.TransformCoordinatesToRef(this.position, Tmp.Matrix[6], currentPosition);
-                    } else {
-                        Vector3.TransformCoordinatesToRef(this.position, this.parent.getWorldMatrix(), currentPosition);
-                    }
-                } else {
-                    currentPosition.copyFrom(this.position);
-                }
-
-                currentPosition.subtractInPlace(camera.globalPosition);
-
-                var finalEuler = Tmp.Vector3[4].copyFromFloats(0, 0, 0);
-                if ((this.billboardMode & TransformNode.BILLBOARDMODE_X) === TransformNode.BILLBOARDMODE_X) {
-                    finalEuler.x = Math.atan2(-currentPosition.y, currentPosition.z);
-                }
-
-                if ((this.billboardMode & TransformNode.BILLBOARDMODE_Y) === TransformNode.BILLBOARDMODE_Y) {
-                    finalEuler.y = Math.atan2(currentPosition.x, currentPosition.z);
-                }
-
-                if ((this.billboardMode & TransformNode.BILLBOARDMODE_Z) === TransformNode.BILLBOARDMODE_Z) {
-                    finalEuler.z = Math.atan2(currentPosition.y, currentPosition.x);
-                }
-
-                Matrix.RotationYawPitchRollToRef(finalEuler.y, finalEuler.x, finalEuler.z, Tmp.Matrix[0]);
-            } else {
-                Tmp.Matrix[1].copyFrom(camera.getViewMatrix());
-
-                Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
-                Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
-            }
-
-            if (this._scene.useRightHandedSystem) {
-                Matrix.ScalingToRef(1, 1, -1, Tmp.Matrix[1]);
-                Tmp.Matrix[0].multiplyToRef(Tmp.Matrix[1], Tmp.Matrix[0]);
-            }
-
-            this._tempMatrix.multiplyToRef(Tmp.Matrix[0], this._tempMatrix);
-        }
-
         // Post multiply inverse of pivotMatrix
         if (this._postMultiplyPivotMatrix) {
             this._tempMatrix.multiplyToRef(this._pivotMatrixInverse, this._tempMatrix);
@@ -974,7 +932,8 @@ export class TransformNode extends Node {
 
         // Parent
         if (this.parent && this.parent.getWorldMatrix) {
-            if (this.billboardMode !== TransformNode.BILLBOARDMODE_NONE) {
+            // We do not want parent rotation
+            if (this.billboardMode !== TransformNode.BILLBOARDMODE_NONE && !this.preserveParentRotationForBillboard) {
                 if (this._transformToBoneReferal) {
                     this.parent.getWorldMatrix().multiplyToRef(this._transformToBoneReferal.getWorldMatrix(), Tmp.Matrix[7]);
                 } else {
@@ -989,7 +948,6 @@ export class TransformNode extends Node {
                 Tmp.Matrix[7].setTranslation(translation);
 
                 this._localMatrix.multiplyToRef(Tmp.Matrix[7], this._worldMatrix);
-
             } else {
                 if (this._transformToBoneReferal) {
                     this._localMatrix.multiplyToRef(this.parent.getWorldMatrix(), Tmp.Matrix[6]);
@@ -998,11 +956,48 @@ export class TransformNode extends Node {
                     this._localMatrix.multiplyToRef(this.parent.getWorldMatrix(), this._worldMatrix);
                 }
             }
+
             this._markSyncedWithParent();
         } else {
             this._worldMatrix.copyFrom(this._localMatrix);
         }
 
+        // Billboarding (testing PG:http://www.babylonjs-playground.com/#UJEIL#13)
+        if (this.billboardMode !== TransformNode.BILLBOARDMODE_NONE && camera) {
+            let storedTranslation = Tmp.Vector3[0];
+            this._worldMatrix.getTranslationToRef(storedTranslation); // Save translation
+
+            // Cancel camera rotation
+            Tmp.Matrix[1].copyFrom(camera.getViewMatrix());
+            Tmp.Matrix[1].setTranslationFromFloats(0, 0, 0);
+            Tmp.Matrix[1].invertToRef(Tmp.Matrix[0]);
+
+            if ((this.billboardMode & TransformNode.BILLBOARDMODE_ALL) !== TransformNode.BILLBOARDMODE_ALL) {
+                Tmp.Matrix[0].decompose(undefined, Tmp.Quaternion[0], undefined);
+                let eulerAngles = Tmp.Vector3[1];
+                Tmp.Quaternion[0].toEulerAnglesToRef(eulerAngles);
+
+                if ((this.billboardMode & TransformNode.BILLBOARDMODE_X) !== TransformNode.BILLBOARDMODE_X) {
+                    eulerAngles.x = 0;
+                }
+
+                if ((this.billboardMode & TransformNode.BILLBOARDMODE_Y) !== TransformNode.BILLBOARDMODE_Y) {
+                    eulerAngles.y = 0;
+                }
+
+                if ((this.billboardMode & TransformNode.BILLBOARDMODE_Z) !== TransformNode.BILLBOARDMODE_Z) {
+                    eulerAngles.z = 0;
+                }
+
+                Matrix.RotationYawPitchRollToRef(eulerAngles.y, eulerAngles.x, eulerAngles.z, Tmp.Matrix[0]);
+            }
+            this._worldMatrix.setTranslationFromFloats(0, 0, 0);
+            this._worldMatrix.multiplyToRef(Tmp.Matrix[0], this._worldMatrix);
+
+            // Restore translation
+            this._worldMatrix.setTranslation(Tmp.Vector3[0]);
+        }
+
         // Normal matrix
         if (!this.ignoreNonUniformScaling) {
             if (this.scaling.isNonUniform) {
@@ -1080,7 +1075,7 @@ export class TransformNode extends Node {
         if (!camera) {
             camera = (<Camera>this.getScene().activeCamera);
         }
-        return this.absolutePosition.subtract(camera.position).length();
+        return this.absolutePosition.subtract(camera.globalPosition).length();
     }
 
     /**

+ 94 - 1
src/Misc/assetsManager.ts

@@ -9,6 +9,7 @@ import { BaseTexture } from "../Materials/Textures/baseTexture";
 import { Texture } from "../Materials/Textures/texture";
 import { CubeTexture } from "../Materials/Textures/cubeTexture";
 import { HDRCubeTexture } from "../Materials/Textures/hdrCubeTexture";
+import { EquiRectangularCubeTexture } from "../Materials/Textures/equiRectangularCubeTexture";
 import { Logger } from "../Misc/logger";
 import { AnimationGroup } from '../Animations/animationGroup';
 
@@ -672,7 +673,7 @@ export class HDRCubeTextureAssetTask extends AbstractAssetTask implements ITextu
      * @param onSuccess is a callback called when the task is successfully executed
      * @param onError is a callback called if an error occurs
      */
-    public run(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
+    public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void) {
 
         var onload = () => {
             onSuccess();
@@ -687,6 +688,80 @@ export class HDRCubeTextureAssetTask extends AbstractAssetTask implements ITextu
 }
 
 /**
+ * Define a task used by AssetsManager to load Equirectangular cube textures
+ */
+export class EquiRectangularCubeTextureAssetTask extends AbstractAssetTask implements ITextureAssetTask<EquiRectangularCubeTexture> {
+    /**
+     * Gets the loaded texture
+     */
+    public texture: EquiRectangularCubeTexture;
+
+    /**
+     * Callback called when the task is successful
+     */
+    public onSuccess: (task: EquiRectangularCubeTextureAssetTask) => void;
+    /**
+     * Callback called when the task is successful
+     */
+    public onError: (task: EquiRectangularCubeTextureAssetTask, message?: string, exception?: any) => void;
+
+    /**
+     * Creates a new EquiRectangularCubeTextureAssetTask object
+     * @param name defines the name of the task
+     * @param url defines the location of the file to load
+     * @param size defines the desired size (the more it increases the longer the generation will be)
+     * If the size is omitted this implies you are using a preprocessed cubemap.
+     * @param noMipmap defines if mipmaps should not be generated (default is false)
+     * @param gammaSpace specifies if the texture will be used in gamma or linear space
+     * (the PBR material requires those texture in linear space, but the standard material would require them in Gamma space)
+     * (default is true)
+     */
+    constructor(
+        /**
+         * Defines the name of the task
+         */
+        public name: string,
+        /**
+         * Defines the location of the file to load
+         */
+        public url: string,
+        /**
+         * Defines the desired size (the more it increases the longer the generation will be)
+         */
+        public size: number,
+        /**
+         * Defines if mipmaps should not be generated (default is false)
+         */
+        public noMipmap: boolean = false,
+        /**
+         * Specifies if the texture will be use in gamma or linear space (the PBR material requires those texture in linear space,
+         * but the standard material would require them in Gamma space) (default is true)
+         */
+        public gammaSpace: boolean = true) {
+        super(name);
+    }
+
+    /**
+     * Execute the current task
+     * @param scene defines the scene where you want your assets to be loaded
+     * @param onSuccess is a callback called when the task is successfully executed
+     * @param onError is a callback called if an error occurs
+     */
+    public runTask(scene: Scene, onSuccess: () => void, onError: (message?: string, exception?: any) => void): void {
+
+        const onload = () => {
+            onSuccess();
+        };
+
+        const onerror = (message?: string, exception?: any) => {
+            onError(message, exception);
+        };
+
+        this.texture = new EquiRectangularCubeTexture(this.url, scene, this.size, this.noMipmap, this.gammaSpace, onload, onerror);
+    }
+}
+
+/**
  * This class can be used to easily import assets into a scene
  * @see http://doc.babylonjs.com/how_to/how_to_use_assetsmanager
  */
@@ -858,6 +933,24 @@ export class AssetsManager {
     }
 
     /**
+     *
+     * Add a EquiRectangularCubeTextureAssetTask to the list of active tasks
+     * @param taskName defines the name of the new task
+     * @param url defines the url of the file to load
+     * @param size defines the size you want for the cubemap (can be null)
+     * @param noMipmap defines if the texture must not receive mipmaps (false by default)
+     * @param gammaSpace Specifies if the texture will be used in gamma or linear space
+     * (the PBR material requires those textures in linear space, but the standard material would require them in Gamma space)
+     * @returns a new EquiRectangularCubeTextureAssetTask object
+     */
+    public addEquiRectangularCubeTextureAssetTask(taskName: string, url: string, size: number, noMipmap = false, gammaSpace = true): EquiRectangularCubeTextureAssetTask {
+        const task = new EquiRectangularCubeTextureAssetTask(taskName, url, size, noMipmap, gammaSpace);
+        this._tasks.push(task);
+
+        return task;
+    }
+
+    /**
      * Remove a task from the assets manager.
      * @param task the task to remove
      */

+ 7 - 0
src/Misc/brdfTextureTools.ts

@@ -4,6 +4,7 @@ import { Texture } from "../Materials/Textures/texture";
 import { Constants } from "../Engines/constants";
 import { Scene } from "../scene";
 import { PostProcess } from "../PostProcesses/postProcess";
+import "../Shaders/rgbdDecode.fragment";
 
 /**
  * Class used to host texture specific utilities
@@ -83,12 +84,18 @@ export class BRDFTextureTools {
      */
     public static GetEnvironmentBRDFTexture(scene: Scene): BaseTexture {
         if (!scene.environmentBRDFTexture) {
+            // Forces Delayed Texture Loading to prevent undefined error whilst setting RGBD values.
+            var useDelayedTextureLoading = scene.useDelayedTextureLoading;
+            scene.useDelayedTextureLoading = false;
+
             var texture = Texture.CreateFromBase64String(this._environmentBRDFBase64Texture, "EnvironmentBRDFTexture", scene, true, true, Texture.BILINEAR_SAMPLINGMODE);
             texture._texture!._isRGBD = true;
             texture.wrapU = Texture.CLAMP_ADDRESSMODE;
             texture.wrapV = Texture.CLAMP_ADDRESSMODE;
             scene.environmentBRDFTexture = texture;
 
+            scene.useDelayedTextureLoading = useDelayedTextureLoading;
+
             texture.onLoadObservable.addOnce(() => {
                 this._ExpandDefaultBRDFTexture(texture._texture!);
             });

+ 50 - 3
src/Misc/tools.ts

@@ -995,15 +995,19 @@ export class Tools {
      * @param scriptUrl defines the url of the script to laod
      * @param onSuccess defines the callback called when the script is loaded
      * @param onError defines the callback to call if an error occurs
+     * @param scriptId defines the id of the script element
      */
-    public static LoadScript(scriptUrl: string, onSuccess: () => void, onError?: (message?: string, exception?: any) => void) {
+    public static LoadScript(scriptUrl: string, onSuccess: () => void, onError?: (message?: string, exception?: any) => void, scriptId?: string) {
         if (!DomManagement.IsWindowObjectExist()) {
             return;
         }
         var head = document.getElementsByTagName('head')[0];
         var script = document.createElement('script');
-        script.type = 'text/javascript';
-        script.src = scriptUrl;
+        script.setAttribute('type', 'text/javascript');
+        script.setAttribute('src', scriptUrl);
+        if (scriptId) {
+            script.id = scriptId;
+        }
 
         script.onload = () => {
             if (onSuccess) {
@@ -1021,6 +1025,39 @@ export class Tools {
     }
 
     /**
+     * Load an asynchronous script (identified by an url). When the url returns, the
+     * content of this file is added into a new script element, attached to the DOM (body element)
+     * @param scriptUrl defines the url of the script to laod
+     * @param scriptId defines the id of the script element
+     * @returns a promise request object
+     */
+    public static LoadScriptAsync(scriptUrl: string, scriptId?: string): Nullable<Promise<boolean>> {
+        return new Promise<boolean>((resolve, reject) => {
+            if (!DomManagement.IsWindowObjectExist()) {
+                resolve(false);
+                return;
+            }
+            var head = document.getElementsByTagName('head')[0];
+            var script = document.createElement('script');
+            script.setAttribute('type', 'text/javascript');
+            script.setAttribute('src', scriptUrl);
+            if (scriptId) {
+                script.id = scriptId;
+            }
+
+            script.onload = () => {
+                resolve(true);
+            };
+
+            script.onerror = (e) => {
+                resolve(false);
+            };
+
+            head.appendChild(script);
+        });
+    }
+
+    /**
      * Loads a file from a blob
      * @param fileToLoad defines the blob to use
      * @param callback defines the callback to call when data is loaded
@@ -1148,6 +1185,16 @@ export class Tools {
     }
 
     /**
+     * Checks for a matching suffix at the end of a string (for ES5 and lower)
+     * @param str Source string
+     * @param suffix Suffix to search for in the source string
+     * @returns Boolean indicating whether the suffix was found (true) or not (false)
+     */
+    public static EndsWith(str: string, suffix: string): boolean {
+        return str.indexOf(suffix, str.length - suffix.length) !== -1;
+    }
+
+    /**
      * Function used to register events at window level
      * @param events defines the events to register
      */

+ 4 - 4
src/Misc/webRequest.ts

@@ -112,6 +112,10 @@ export class WebRequest {
      * @param body defines an optional request body
      */
     public send(body?: Document | BodyInit | null): void {
+        if (WebRequest.CustomRequestHeaders) {
+            this._injectCustomRequestHeaders();
+        }
+
         this._xhr.send(body);
     }
 
@@ -121,10 +125,6 @@ export class WebRequest {
      * @param url defines the url to connect with
      */
     public open(method: string, url: string): void {
-        if (WebRequest.CustomRequestHeaders) {
-            this._injectCustomRequestHeaders();
-        }
-
         for (var update of WebRequest.CustomRequestModifiers) {
             update(this._xhr);
         }

+ 15 - 11
src/Physics/Plugins/ammoJSPlugin.ts

@@ -384,7 +384,9 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
      * @param impostor the imposter to create the physics body on
      */
     public generatePhysicsBody(impostor: PhysicsImpostor) {
-        impostor._pluginData = { toDispose: [] };
+        // Note: this method will not be called on child imposotrs for compound impostors
+
+        impostor._pluginData.toDispose = [];
 
         //parent-child relationship
         if (impostor.parent) {
@@ -458,9 +460,11 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
         if (this.world) {
             this.world.removeRigidBody(impostor.physicsBody);
 
-            impostor._pluginData.toDispose.forEach((d: any) => {
-                this.bjsAMMO.destroy(d);
-            });
+            if (impostor._pluginData) {
+                impostor._pluginData.toDispose.forEach((d: any) => {
+                    this.bjsAMMO.destroy(d);
+                });
+            }
         }
     }
 
@@ -1102,7 +1106,7 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
      * @returns mass
      */
     public getBodyMass(impostor: PhysicsImpostor): number {
-        return impostor._pluginData.mass;
+        return impostor._pluginData.mass || 0;
     }
 
     /**
@@ -1111,7 +1115,7 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
      * @returns friction value
      */
     public getBodyFriction(impostor: PhysicsImpostor): number {
-        return impostor._pluginData.friction;
+        return impostor._pluginData.friction || 0;
     }
 
     /**
@@ -1135,7 +1139,7 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
      * @returns restitution value
      */
     public getBodyRestitution(impostor: PhysicsImpostor): number {
-        return impostor._pluginData.restitution;
+        return impostor._pluginData.restitution || 0;
     }
 
     /**
@@ -1158,7 +1162,7 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
             Logger.Warn("Pressure is not a property of a rigid body");
             return 0;
         }
-        return impostor._pluginData.pressure;
+        return impostor._pluginData.pressure || 0;
     }
 
     /**
@@ -1193,7 +1197,7 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
             Logger.Warn("Stiffness is not a property of a rigid body");
             return 0;
         }
-        return impostor._pluginData.stiffness;
+        return impostor._pluginData.stiffness || 0;
     }
 
     /**
@@ -1223,7 +1227,7 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
             Logger.Warn("Velocity iterations is not a property of a rigid body");
             return 0;
         }
-        return impostor._pluginData.velocityIterations;
+        return impostor._pluginData.velocityIterations || 0;
     }
 
     /**
@@ -1252,7 +1256,7 @@ export class AmmoJSPlugin implements IPhysicsEnginePlugin {
             Logger.Warn("Position iterations is not a property of a rigid body");
             return 0;
         }
-        return impostor._pluginData.positionIterations;
+        return impostor._pluginData.positionIterations || 0;
     }
 
     /**

+ 10 - 1
src/Physics/Plugins/cannonJSPlugin.ts

@@ -21,6 +21,7 @@ export class CannonJSPlugin implements IPhysicsEnginePlugin {
     private _fixedTimeStep: number = 1 / 60;
     private _cannonRaycastResult: any;
     private _raycastResult: PhysicsRaycastResult;
+    private _removeAfterStep = new Array<PhysicsImpostor>();
     //See https://github.com/schteppe/CANNON.js/blob/gh-pages/demos/collisionFilter.html
     public BJSCANNON: any;
 
@@ -54,6 +55,12 @@ export class CannonJSPlugin implements IPhysicsEnginePlugin {
 
     public executeStep(delta: number): void {
         this.world.step(this._fixedTimeStep, this._useDeltaForWorldStep ? delta : 0, 3);
+        if (this._removeAfterStep.length > 0) {
+            this._removeAfterStep.forEach((impostor) => {
+                this.world.remove(impostor.physicsBody);
+            });
+            this._removeAfterStep = [];
+        }
     }
 
     public applyImpulse(impostor: PhysicsImpostor, force: Vector3, contactPoint: Vector3) {
@@ -165,7 +172,9 @@ export class CannonJSPlugin implements IPhysicsEnginePlugin {
         impostor.physicsBody.removeEventListener("collide", impostor.onCollide);
         this.world.removeEventListener("preStep", impostor.beforeStep);
         this.world.removeEventListener("postStep", impostor.afterStep);
-        this.world.remove(impostor.physicsBody);
+
+        // Only remove the physics body after the physics step to avoid disrupting cannon's internal state
+        this._removeAfterStep.push(impostor);
     }
 
     public generateJoint(impostorJoint: PhysicsImpostorJoint) {

+ 1 - 2
src/Physics/physicsEngine.ts

@@ -117,8 +117,7 @@ export class PhysicsEngine implements IPhysicsEngine {
             var removed = this._impostors.splice(index, 1);
             //Is it needed?
             if (removed.length) {
-                //this will also remove it from the world.
-                removed[0].physicsBody = null;
+                this.getPhysicsPlugin().removePhysicsBody(impostor);
             }
         }
     }

+ 74 - 21
src/Physics/physicsHelper.ts

@@ -61,16 +61,24 @@ export class PhysicsHelper {
         }
 
         var event = new PhysicsRadialExplosionEvent(this._scene, radiusOrEventOptions);
+        var affectedImpostorsWithData = Array<PhysicsAffectedImpostorWithData>();
 
         impostors.forEach((impostor) => {
-            var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin);
-            if (!impostorForceAndContactPoint) {
+            var impostorHitData = event.getImpostorHitData(impostor, origin);
+            if (!impostorHitData) {
                 return;
             }
 
-            impostor.applyImpulse(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            impostor.applyImpulse(impostorHitData.force, impostorHitData.contactPoint);
+
+            affectedImpostorsWithData.push({
+                impostor: impostor,
+                hitData: impostorHitData,
+            });
         });
 
+        event.triggerAffectedImpostorsCallback(affectedImpostorsWithData);
+
         event.dispose(false);
 
         return event;
@@ -103,16 +111,24 @@ export class PhysicsHelper {
         }
 
         var event = new PhysicsRadialExplosionEvent(this._scene, radiusOrEventOptions);
+        var affectedImpostorsWithData = Array<PhysicsAffectedImpostorWithData>();
 
         impostors.forEach((impostor) => {
-            var impostorForceAndContactPoint = event.getImpostorForceAndContactPoint(impostor, origin);
-            if (!impostorForceAndContactPoint) {
+            var impostorHitData = event.getImpostorHitData(impostor, origin);
+            if (!impostorHitData) {
                 return;
             }
 
-            impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            impostor.applyForce(impostorHitData.force, impostorHitData.contactPoint);
+
+            affectedImpostorsWithData.push({
+                impostor: impostor,
+                hitData: impostorHitData,
+            });
         });
 
+        event.triggerAffectedImpostorsCallback(affectedImpostorsWithData);
+
         event.dispose(false);
 
         return event;
@@ -252,9 +268,9 @@ class PhysicsRadialExplosionEvent {
      * Returns the force and contact point of the impostor or false, if the impostor is not affected by the force/impulse.
      * @param impostor A physics imposter
      * @param origin the origin of the explosion
-     * @returns {Nullable<PhysicsForceAndContactPoint>} A physics force and contact point, or null
+     * @returns {Nullable<PhysicsHitData>} A physics force and contact point, or null
      */
-    public getImpostorForceAndContactPoint(impostor: PhysicsImpostor, origin: Vector3): Nullable<PhysicsForceAndContactPoint> {
+    public getImpostorHitData(impostor: PhysicsImpostor, origin: Vector3): Nullable<PhysicsHitData> {
         if (impostor.mass === 0) {
             return null;
         }
@@ -290,7 +306,17 @@ class PhysicsRadialExplosionEvent {
 
         var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
 
-        return { force: force, contactPoint: contactPoint };
+        return { force: force, contactPoint: contactPoint, distanceFromOrigin: distanceFromOrigin };
+    }
+
+    /**
+     * Triggers affecterd impostors callbacks
+     * @param affectedImpostorsWithData defines the list of affected impostors (including associated data)
+     */
+    public triggerAffectedImpostorsCallback(affectedImpostorsWithData: Array<PhysicsAffectedImpostorWithData>) {
+        if (this._options.affectedImpostorsCallback) {
+            this._options.affectedImpostorsCallback(affectedImpostorsWithData);
+        }
     }
 
     /**
@@ -495,7 +521,7 @@ class PhysicsUpdraftEvent {
         }
     }
 
-    private getImpostorForceAndContactPoint(impostor: PhysicsImpostor): Nullable<PhysicsForceAndContactPoint> {
+    private getImpostorHitData(impostor: PhysicsImpostor): Nullable<PhysicsHitData> {
         if (impostor.mass === 0) {
             return null;
         }
@@ -512,21 +538,23 @@ class PhysicsUpdraftEvent {
             var direction = impostorObjectCenter.subtract(this._originTop);
         }
 
+        var distanceFromOrigin = Vector3.Distance(this._origin, impostorObjectCenter);
+
         var multiplier = this._options.strength * -1;
 
         var force = direction.multiplyByFloats(multiplier, multiplier, multiplier);
 
-        return { force: force, contactPoint: impostorObjectCenter };
+        return { force: force, contactPoint: impostorObjectCenter, distanceFromOrigin: distanceFromOrigin };
     }
 
     private _tick() {
         this._physicsEngine.getImpostors().forEach((impostor) => {
-            var impostorForceAndContactPoint = this.getImpostorForceAndContactPoint(impostor);
-            if (!impostorForceAndContactPoint) {
+            var impostorHitData = this.getImpostorHitData(impostor);
+            if (!impostorHitData) {
                 return;
             }
 
-            impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            impostor.applyForce(impostorHitData.force, impostorHitData.contactPoint);
         });
     }
 
@@ -625,7 +653,7 @@ class PhysicsVortexEvent {
         }
     }
 
-    private getImpostorForceAndContactPoint(impostor: PhysicsImpostor): Nullable<PhysicsForceAndContactPoint> {
+    private getImpostorHitData(impostor: PhysicsImpostor): Nullable<PhysicsHitData> {
         if (impostor.mass === 0) {
             return null;
         }
@@ -670,17 +698,17 @@ class PhysicsVortexEvent {
         var force = new Vector3(forceX, forceY, forceZ);
         force = force.multiplyByFloats(this._options.strength, this._options.strength, this._options.strength);
 
-        return { force: force, contactPoint: impostorObjectCenter };
+        return { force: force, contactPoint: impostorObjectCenter, distanceFromOrigin: absoluteDistanceFromOrigin };
     }
 
     private _tick() {
         this._physicsEngine.getImpostors().forEach((impostor) => {
-            var impostorForceAndContactPoint = this.getImpostorForceAndContactPoint(impostor);
-            if (!impostorForceAndContactPoint) {
+            var impostorHitData = this.getImpostorHitData(impostor);
+            if (!impostorHitData) {
                 return;
             }
 
-            impostor.applyForce(impostorForceAndContactPoint.force, impostorForceAndContactPoint.contactPoint);
+            impostor.applyForce(impostorHitData.force, impostorHitData.contactPoint);
         });
     }
 
@@ -730,6 +758,11 @@ export class PhysicsRadialExplosionEventOptions {
      * Sphere options for the radial explosion.
      */
     sphere: { segments: number, diameter: number } = { segments: 32, diameter: 1 };
+
+    /**
+     * Sphere options for the radial explosion.
+     */
+    affectedImpostorsCallback: (affectedImpostorsWithData: Array<PhysicsAffectedImpostorWithData>) => void;
 }
 
 /**
@@ -822,10 +855,10 @@ export enum PhysicsUpdraftMode {
 }
 
 /**
- * Interface for a physics force and contact point
+ * Interface for a physics hit data
  * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
  */
-export interface PhysicsForceAndContactPoint {
+export interface PhysicsHitData {
     /**
      * The force applied at the contact point
      */
@@ -834,6 +867,10 @@ export interface PhysicsForceAndContactPoint {
      * The contact point
      */
     contactPoint: Vector3;
+    /**
+     * The distance from the origin to the contact point
+     */
+    distanceFromOrigin: number;
 }
 
 /**
@@ -879,3 +916,19 @@ export interface PhysicsVortexEventData {
      */
     cylinder: Mesh;
 }
+
+/**
+ * Interface for an affected physics impostor
+ * @see https://doc.babylonjs.com/how_to/using_the_physics_engine#further-functionality-of-the-impostor-class
+ */
+export interface PhysicsAffectedImpostorWithData {
+    /**
+     * The impostor affected by the effect
+     */
+    impostor: PhysicsImpostor;
+
+    /**
+     * The data about the hit/horce from the explosion
+     */
+    hitData: PhysicsHitData;
+}

+ 1 - 1
src/Physics/physicsImpostor.ts

@@ -207,7 +207,7 @@ export class PhysicsImpostor {
     public static IDENTITY_QUATERNION = Quaternion.Identity();
 
     /** @hidden */
-    public _pluginData: any;
+    public _pluginData: any = {};
 
     private _physicsEngine: Nullable<IPhysicsEngine>;
     //The native cannon/oimo/energy physics body object.

+ 46 - 7
src/PostProcesses/RenderPipeline/Pipelines/standardRenderingPipeline.ts

@@ -146,10 +146,19 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
     public horizontalBlur: boolean = false;
 
     /**
-     * Sets the overall exposure used by the pipeline
+     * Gets the overall exposure used by the pipeline
      */
     @serialize()
-    public exposure: number = 1.0;
+    public get exposure(): number {
+        return this._fixedExposure;
+    }
+    /**
+     * Sets the overall exposure used by the pipeline
+     */
+    public set exposure(value: number) {
+        this._fixedExposure = value;
+        this._currentExposure = value;
+    }
 
     /**
      * Texture used typically to simulate "dirty" on camera lens
@@ -194,6 +203,26 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
      */
     @serialize()
     public hdrIncreaseRate: number = 0.5;
+    /**
+     * Gets wether or not the exposure of the overall pipeline should be automatically adjusted by the HDR post-process
+     */
+    @serialize()
+    public get hdrAutoExposure(): boolean {
+        return this._hdrAutoExposure;
+    }
+    /**
+     * Sets wether or not the exposure of the overall pipeline should be automatically adjusted by the HDR post-process
+     */
+    public set hdrAutoExposure(value: boolean) {
+        this._hdrAutoExposure = value;
+        if (this.hdrPostProcess) {
+            const defines = ["#define HDR"];
+            if (value) {
+                defines.push("#define AUTO_EXPOSURE");
+            }
+            this.hdrPostProcess.updateEffect(defines.join("\n"));
+        }
+    }
 
     /**
      * Lens color texture used by the lens flare effect. Mandatory if lens flare effect enabled
@@ -263,6 +292,9 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
     private _currentDepthOfFieldSource: Nullable<PostProcess> = null;
     private _basePostProcess: Nullable<PostProcess>;
 
+    private _fixedExposure: number = 1.0;
+    private _currentExposure: number = 1.0;
+    private _hdrAutoExposure: boolean = false;
     private _hdrCurrentLuminance: number = 1.0;
 
     private _floatTextureType: number;
@@ -675,7 +707,7 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
             effect.setTextureFromPostProcess("otherSampler", this._vlsEnabled ? this._currentDepthOfFieldSource : this.originalPostProcess);
             effect.setTexture("lensSampler", this.lensTexture);
 
-            effect.setFloat("exposure", this.exposure);
+            effect.setFloat("exposure", this._currentExposure);
 
             this._currentDepthOfFieldSource = this.textureAdderFinalPostProcess;
         };
@@ -824,7 +856,11 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
 
     // Create HDR post-process
     private _createHdrPostProcess(scene: Scene, ratio: number): void {
-        this.hdrPostProcess = new PostProcess("HDR", "standard", ["averageLuminance"], ["textureAdderSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, "#define HDR", Constants.TEXTURETYPE_UNSIGNED_INT);
+        const defines = ["#define HDR"];
+        if (this._hdrAutoExposure) {
+            defines.push("#define AUTO_EXPOSURE");
+        }
+        this.hdrPostProcess = new PostProcess("HDR", "standard", ["averageLuminance"], ["textureAdderSampler"], ratio, null, Texture.BILINEAR_SAMPLINGMODE, scene.getEngine(), false, defines.join("\n"), Constants.TEXTURETYPE_UNSIGNED_INT);
 
         var outputLiminance = 1;
         var time = 0;
@@ -851,9 +887,12 @@ export class StandardRenderingPipeline extends PostProcessRenderPipeline impleme
                 }
             }
 
-            outputLiminance = Scalar.Clamp(outputLiminance, this.hdrMinimumLuminance, 1e20);
-
-            effect.setFloat("averageLuminance", outputLiminance);
+            if (this.hdrAutoExposure) {
+                this._currentExposure = this._fixedExposure / outputLiminance;
+            } else {
+                outputLiminance = Scalar.Clamp(outputLiminance, this.hdrMinimumLuminance, 1e20);
+                effect.setFloat("averageLuminance", outputLiminance);
+            }
 
             lastTime = time;
 

+ 2 - 1
src/PostProcesses/index.ts

@@ -27,4 +27,5 @@ export * from "./sharpenPostProcess";
 export * from "./stereoscopicInterlacePostProcess";
 export * from "./tonemapPostProcess";
 export * from "./volumetricLightScatteringPostProcess";
-export * from "./vrDistortionCorrectionPostProcess";
+export * from "./vrDistortionCorrectionPostProcess";
+export * from "./vrMultiviewToSingleviewPostProcess";

+ 1 - 29
src/PostProcesses/vrDistortionCorrectionPostProcess.ts

@@ -6,7 +6,6 @@ import { Texture } from "../Materials/Textures/texture";
 import { PostProcess } from "./postProcess";
 
 import "../Shaders/vrDistortionCorrection.fragment";
-import "../Shaders/vrMultiviewToSingleview.fragment";
 
 /**
  * VRDistortionCorrectionPostProcess used for mobile VR
@@ -53,31 +52,4 @@ export class VRDistortionCorrectionPostProcess extends PostProcess {
             effect.setFloat4("HmdWarpParam", this._distortionFactors[0], this._distortionFactors[1], this._distortionFactors[2], this._distortionFactors[3]);
         });
     }
-}
-
-/**
- * VRMultiviewToSingleview used to convert multiview texture arrays to standard textures for scenarios such as webVR
- * This will not be used for webXR as it supports displaying texture arrays directly
- */
-export class VRMultiviewToSingleview extends PostProcess {
-    /**
-     * Initializes a VRMultiviewToSingleview
-     * @param name name of the post process
-     * @param camera camera to be applied to
-     * @param scaleFactor scaling factor to the size of the output texture
-     */
-    constructor(name: string, camera: Camera, scaleFactor: number) {
-        super(name, "vrMultiviewToSingleview", ["imageIndex"], ["multiviewSampler"], scaleFactor, camera, Texture.BILINEAR_SAMPLINGMODE);
-
-        this.onSizeChangedObservable.add(() => {
-        });
-        this.onApplyObservable.add((effect: Effect) => {
-            if (camera._scene.activeCamera && camera._scene.activeCamera.isLeftCamera) {
-                effect.setInt("imageIndex", 0);
-            }else {
-                effect.setInt("imageIndex", 1);
-            }
-            effect.setTexture("multiviewSampler", camera._multiviewTexture);
-        });
-    }
-}
+}

+ 34 - 0
src/PostProcesses/vrMultiviewToSingleviewPostProcess.ts

@@ -0,0 +1,34 @@
+import { Camera } from "../Cameras/camera";
+import { Effect } from "../Materials/effect";
+import { Texture } from "../Materials/Textures/texture";
+import { PostProcess } from "./postProcess";
+
+import "../Shaders/vrMultiviewToSingleview.fragment";
+import "../Engines/Extensions/engine.multiview";
+
+/**
+ * VRMultiviewToSingleview used to convert multiview texture arrays to standard textures for scenarios such as webVR
+ * This will not be used for webXR as it supports displaying texture arrays directly
+ */
+export class VRMultiviewToSingleviewPostProcess extends PostProcess {
+    /**
+     * Initializes a VRMultiviewToSingleview
+     * @param name name of the post process
+     * @param camera camera to be applied to
+     * @param scaleFactor scaling factor to the size of the output texture
+     */
+    constructor(name: string, camera: Camera, scaleFactor: number) {
+        super(name, "vrMultiviewToSingleview", ["imageIndex"], ["multiviewSampler"], scaleFactor, camera, Texture.BILINEAR_SAMPLINGMODE);
+
+        this.onSizeChangedObservable.add(() => {
+        });
+        this.onApplyObservable.add((effect: Effect) => {
+            if (camera._scene.activeCamera && camera._scene.activeCamera.isLeftCamera) {
+                effect.setInt("imageIndex", 0);
+            }else {
+                effect.setInt("imageIndex", 1);
+            }
+            effect.setTexture("multiviewSampler", camera._multiviewTexture);
+        });
+    }
+}

+ 2 - 0
src/Shaders/ShadersInclude/lightFragment.fx

@@ -56,6 +56,8 @@
             // Diffuse contribution
             #ifdef HEMILIGHT{X}
                 info.diffuse = computeHemisphericDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb, light{X}.vLightGround);
+            #elif defined(SS_TRANSLUCENCY)
+                info.diffuse = computeDiffuseAndTransmittedLighting(preInfo, light{X}.vLightDiffuse.rgb, transmittance);
             #else
                 info.diffuse = computeDiffuseLighting(preInfo, light{X}.vLightDiffuse.rgb);
             #endif

+ 18 - 0
src/Shaders/ShadersInclude/pbrBRDFFunctions.fx

@@ -267,3 +267,21 @@ float diffuseBRDF_Burley(float NdotL, float NdotV, float VdotH, float roughness)
 
     return fresnel / PI;
 }
+
+#ifdef SS_TRANSLUCENCY
+    // Pixar diffusion profile
+    // http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf
+    vec3 transmittanceBRDF_Burley(const vec3 tintColor, const vec3 diffusionDistance, float thickness) {
+        vec3 S = 1. / maxEps(diffusionDistance);
+        vec3 temp = exp((-0.333333333 * thickness) * S);
+        return tintColor.rgb * 0.25 * (temp * temp * temp + 3.0 * temp);
+    }
+
+    // Extends the dark area to prevent seams
+    // Keep it energy conserving by using McCauley solution: https://blog.selfshadow.com/2011/12/31/righting-wrap-part-1/
+    float computeWrappedDiffuseNdotL(float NdotL, float w) {
+        float t = 1.0 + w;
+        float invt2 = 1.0 / square(t);
+        return saturate((NdotL + w) * invt2);
+    }
+#endif

+ 7 - 1
src/Shaders/ShadersInclude/pbrDebug.fx

@@ -72,8 +72,10 @@
         gl_FragColor.rgb = sheenMapData.rgb;
     #elif DEBUGMODE == 30 && defined(ANISOTROPIC) && defined(ANISOTROPIC_TEXTURE)
         gl_FragColor.rgb = anisotropyMapData.rgb;
+    #elif DEBUGMODE == 31 && defined(SUBSURFACE) && defined(SS_THICKNESSANDMASK_TEXTURE)
+        gl_FragColor.rgb = thicknessMap.rgb;
 // Env
-    #elif DEBUGMODE == 40 && defined(REFRACTION)
+    #elif DEBUGMODE == 40 && defined(SS_REFRACTION)
         // Base color.
         gl_FragColor.rgb = environmentRefraction.rgb;
         #define DEBUGMODE_GAMMA
@@ -119,6 +121,10 @@
         gl_FragColor.rgb = vec3(clearCoatRoughness);
     #elif DEBUGMODE == 67 && defined(CLEARCOAT)
         gl_FragColor.rgb = vec3(clearCoatNdotV);
+    #elif DEBUGMODE == 68 && defined(SUBSURFACE) && defined(SS_TRANSLUCENCY)
+        gl_FragColor.rgb = transmittance;
+    #elif DEBUGMODE == 69 && defined(SUBSURFACE) && defined(SS_REFRACTION)
+        gl_FragColor.rgb = refractionTransmittance;
 // Misc
     #elif DEBUGMODE == 70 && defined(RADIANCEOCCLUSION)
         gl_FragColor.rgb = vec3(seo);

+ 16 - 0
src/Shaders/ShadersInclude/pbrDirectLightingFunctions.fx

@@ -46,6 +46,22 @@ vec3 computeProjectionTextureDiffuseLighting(sampler2D projectionLightSampler, m
     return toLinearSpace(textureColor);
 }
 
+#ifdef SS_TRANSLUCENCY
+    vec3 computeDiffuseAndTransmittedLighting(preLightingInfo info, vec3 lightColor, vec3 transmittance) {
+        float NdotL = absEps(info.NdotLUnclamped);
+
+        // Use wrap lighting to simulate SSS.
+        float wrapNdotL = computeWrappedDiffuseNdotL(NdotL, 0.02);
+
+        // Remap transmittance from tr to 1. if ndotl is negative.
+        float trAdapt = step(0., info.NdotLUnclamped);
+        vec3 transmittanceNdotL = mix(transmittance * wrapNdotL, vec3(wrapNdotL), trAdapt);
+
+        float diffuseTerm = diffuseBRDF_Burley(NdotL, info.NdotV, info.VdotH, info.roughness);
+        return diffuseTerm * transmittanceNdotL * info.attenuation * lightColor;
+    }
+#endif
+
 #ifdef SPECULARTERM
     vec3 computeSpecularLighting(preLightingInfo info, vec3 N, vec3 reflectance0, vec3 reflectance90, float geometricRoughnessFactor, vec3 lightColor) {
         float NdotH = saturateEps(dot(N, info.H));

+ 9 - 2
src/Shaders/ShadersInclude/pbrDirectLightingSetupFunctions.fx

@@ -13,6 +13,7 @@ struct preLightingInfo
     vec3 L;
     vec3 H;
     float NdotV;
+    float NdotLUnclamped;
     float NdotL;
     float VdotH;
     float roughness;
@@ -31,9 +32,11 @@ preLightingInfo computePointAndSpotPreLightingInfo(vec4 lightData, vec3 V, vec3
     // Geometry Data.
     result.L = normalize(result.lightOffset);
     result.H = normalize(V + result.L);
-    result.NdotL = saturateEps(dot(N, result.L));
     result.VdotH = saturate(dot(V, result.H));
 
+    result.NdotLUnclamped = dot(N, result.L);
+    result.NdotL = saturateEps(result.NdotLUnclamped);
+
     return result;
 }
 
@@ -46,9 +49,11 @@ preLightingInfo computeDirectionalPreLightingInfo(vec4 lightData, vec3 V, vec3 N
     // Geometry Data.
     result.L = normalize(-lightData.xyz);
     result.H = normalize(V + result.L);
-    result.NdotL = saturateEps(dot(N, result.L));
     result.VdotH = saturate(dot(V, result.H));
 
+    result.NdotLUnclamped = dot(N, result.L);
+    result.NdotL = saturateEps(result.NdotLUnclamped);
+
     return result;
 }
 
@@ -56,8 +61,10 @@ preLightingInfo computeHemisphericPreLightingInfo(vec4 lightData, vec3 V, vec3 N
     preLightingInfo result;
 
     // Geometry Data.
+    // Half Lambert for Hemispherix lighting.
     result.NdotL = dot(N, lightData.xyz) * 0.5 + 0.5;
     result.NdotL = saturateEps(result.NdotL);
+    result.NdotLUnclamped = result.NdotL;
 
     #ifdef SPECULARTERM
         result.L = normalize(lightData.xyz);

+ 20 - 8
src/Shaders/ShadersInclude/pbrFragmentDeclaration.fx

@@ -44,17 +44,10 @@ uniform vec2 vMicroSurfaceSamplerInfos;
 #endif
 
 // Refraction Reflection
-#if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(REFRACTION)
+#if defined(REFLECTIONMAP_SPHERICAL) || defined(REFLECTIONMAP_PROJECTION) || defined(SS_REFRACTION)
 uniform mat4 view;
 #endif
 
-// Refraction
-#ifdef REFRACTION
-    uniform vec4 vRefractionInfos;
-    uniform mat4 refractionMatrix;
-    uniform vec3 vRefractionMicrosurfaceInfos;
-#endif
-
 // Reflection
 #ifdef REFLECTION
     uniform vec2 vReflectionInfos;
@@ -112,4 +105,23 @@ uniform mat4 view;
         uniform vec2 vSheenInfos;
         uniform mat4 sheenMatrix;
     #endif
+#endif
+
+// SubSurface
+#ifdef SUBSURFACE
+    #ifdef SS_REFRACTION
+        uniform vec3 vRefractionMicrosurfaceInfos;
+        uniform vec4 vRefractionInfos;
+        uniform mat4 refractionMatrix;
+    #endif
+
+    #ifdef SS_THICKNESSANDMASK_TEXTURE
+        uniform vec2 vThicknessInfos;
+        uniform mat4 thicknessMatrix;;
+    #endif
+
+    uniform vec2 vThicknessParam;
+    uniform vec3 vDiffusionDistance;
+    uniform vec4 vTintColor;
+    uniform vec3 vSubSurfaceIntensity;
 #endif

+ 40 - 27
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -136,33 +136,6 @@
     #endif
 #endif
 
-// Refraction
-#ifdef REFRACTION
-    #ifdef REFRACTIONMAP_3D
-        #define sampleRefraction(s, c) textureCube(s, c)
-        
-        uniform samplerCube refractionSampler;
-
-        #ifdef LODBASEDMICROSFURACE
-            #define sampleRefractionLod(s, c, l) textureCubeLodEXT(s, c, l)
-        #else
-            uniform samplerCube refractionSamplerLow;
-            uniform samplerCube refractionSamplerHigh;
-        #endif
-    #else
-        #define sampleRefraction(s, c) texture2D(s, c)
-        
-        uniform sampler2D refractionSampler;
-
-        #ifdef LODBASEDMICROSFURACE
-            #define sampleRefractionLod(s, c, l) texture2DLodEXT(s, c, l)
-        #else
-            uniform samplerCube refractionSamplerLow;
-            uniform samplerCube refractionSamplerHigh;
-        #endif
-    #endif
-#endif
-
 // Reflection
 #ifdef REFLECTION
     #ifdef REFLECTIONMAP_3D
@@ -200,4 +173,44 @@
 
 #ifdef ENVIRONMENTBRDF
     uniform sampler2D environmentBrdfSampler;
+#endif
+
+// SUBSURFACE
+#ifdef SUBSURFACE
+    #ifdef SS_REFRACTION
+        #ifdef SS_REFRACTIONMAP_3D
+            #define sampleRefraction(s, c) textureCube(s, c)
+            
+            uniform samplerCube refractionSampler;
+
+            #ifdef LODBASEDMICROSFURACE
+                #define sampleRefractionLod(s, c, l) textureCubeLodEXT(s, c, l)
+            #else
+                uniform samplerCube refractionSamplerLow;
+                uniform samplerCube refractionSamplerHigh;
+            #endif
+        #else
+            #define sampleRefraction(s, c) texture2D(s, c)
+            
+            uniform sampler2D refractionSampler;
+
+            #ifdef LODBASEDMICROSFURACE
+                #define sampleRefractionLod(s, c, l) texture2DLodEXT(s, c, l)
+            #else
+                uniform samplerCube refractionSamplerLow;
+                uniform samplerCube refractionSamplerHigh;
+            #endif
+        #endif
+    #endif
+
+    #ifdef SS_THICKNESSANDMASK_TEXTURE
+        #if SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 1
+            #define vThicknessUV vMainUV1
+        #elif SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 2
+            #define vThicknessUV vMainUV2
+        #else
+            varying vec2 vThicknessUV;
+        #endif
+        uniform sampler2D thicknessSampler;
+    #endif
 #endif

+ 7 - 3
src/Shaders/ShadersInclude/pbrHelperFunctions.fx

@@ -60,16 +60,20 @@ vec2 getAARoughnessFactors(vec3 normalVector) {
     }
 #endif
 
-#ifdef CLEARCOAT
+#if defined(CLEARCOAT) || defined(SS_REFRACTION)
     // From beer lambert law I1/I0 = e −α′lc
     // c is considered included in alpha
     // https://blog.selfshadow.com/publications/s2017-shading-course/drobot/s2017_pbs_multilayered.pdf page 47
+    vec3 cocaLambert(vec3 alpha, float distance) {
+        return exp(-alpha * distance);
+    }
+
     // where L on a thin constant size layer can be (d * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))
     vec3 cocaLambert(float NdotVRefract, float NdotLRefract, vec3 alpha, float thickness) {
-        return exp(alpha * -(thickness * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))));
+        return cocaLambert(alpha, (thickness * ((NdotLRefract + NdotVRefract) / (NdotLRefract * NdotVRefract))));
     }
 
-    // From beerLambert Solves what alpha should be for a given resutlt at a known distance.
+    // From beerLambert Solves what alpha should be for a given result at a known distance.
     vec3 computeColorAtDistanceInMedia(vec3 color, float distance) {
         return -log(color) / distance;
     }

+ 2 - 2
src/Shaders/ShadersInclude/pbrIBLFunctions.fx

@@ -1,4 +1,4 @@
-#if defined(REFLECTION) || defined(REFRACTION)
+#if defined(REFLECTION) || defined(SS_REFRACTION)
     float getLodFromAlphaG(float cubeMapDimensionPixels, float microsurfaceAverageSlope) {
         float microsurfaceAverageSlopeTexels = microsurfaceAverageSlope * cubeMapDimensionPixels;
         float lod = log2(microsurfaceAverageSlopeTexels);
@@ -29,7 +29,7 @@
 // LEGACY
 // ___________________________________________________________________________________
 
-#if defined(LODINREFLECTIONALPHA) || defined(LODINREFRACTIONALPHA)
+#if defined(LODINREFLECTIONALPHA) || defined(SS_LODINREFRACTIONALPHA)
     // To enable 8 bit textures to be used we need to pack and unpack the LOD
     //inverse alpha is used to work around low-alpha bugs in Edge and Firefox
     #define UNPACK_LOD(x) (1.0 - x) * 255.0

+ 10 - 7
src/Shaders/ShadersInclude/pbrUboDeclaration.fx

@@ -9,7 +9,6 @@ uniform Material
     uniform vec2 vLightmapInfos;
     uniform vec3 vReflectivityInfos;
     uniform vec2 vMicroSurfaceSamplerInfos;
-    uniform vec4 vRefractionInfos;
     uniform vec2 vReflectionInfos;
     uniform vec3 vReflectionPosition;
     uniform vec3 vReflectionSize;
@@ -23,18 +22,12 @@ uniform Material
     uniform mat4 microSurfaceSamplerMatrix;
     uniform mat4 bumpMatrix;
     uniform vec2 vTangentSpaceParams;
-    uniform mat4 refractionMatrix;
     uniform mat4 reflectionMatrix;
-
     uniform vec3 vReflectionColor;
     uniform vec4 vAlbedoColor;
     uniform vec4 vLightingIntensity;
-
-    uniform vec3 vRefractionMicrosurfaceInfos;
     uniform vec3 vReflectionMicrosurfaceInfos;
-
     uniform float pointSize;
-
     uniform vec4 vReflectivityColor;
     uniform vec3 vEmissiveColor;
 
@@ -59,6 +52,16 @@ uniform Material
     uniform vec4 vSheenColor;
     uniform vec2 vSheenInfos;
     uniform mat4 sheenMatrix;
+
+    uniform vec3 vRefractionMicrosurfaceInfos;
+    uniform vec4 vRefractionInfos;
+    uniform mat4 refractionMatrix;
+    uniform vec2 vThicknessInfos;
+    uniform mat4 thicknessMatrix;
+    uniform vec2 vThicknessParam;
+    uniform vec3 vDiffusionDistance;
+    uniform vec4 vTintColor;
+    uniform vec3 vSubSurfaceIntensity;
 };
 
 uniform Scene {

+ 14 - 8
src/Shaders/ShadersInclude/pbrVertexDeclaration.fx

@@ -45,18 +45,10 @@ uniform mat4 bumpMatrix;
 uniform float pointSize;
 #endif
 
-// Refraction
-#ifdef REFRACTION
-    uniform vec4 vRefractionInfos;
-    uniform mat4 refractionMatrix;
-    uniform vec3 vRefractionMicrosurfaceInfos;
-#endif
-
 // Reflection
 #ifdef REFLECTION
     uniform vec2 vReflectionInfos;
     uniform mat4 reflectionMatrix;
-    uniform vec3 vReflectionMicrosurfaceInfos;
 #endif
 
 // Clear Coat
@@ -92,3 +84,17 @@ uniform float pointSize;
         uniform mat4 sheenMatrix;
     #endif
 #endif
+
+// Sub Surface
+#ifdef SUBSURFACE
+    #ifdef SS_REFRACTION
+        uniform vec4 vRefractionInfos;
+        uniform mat4 refractionMatrix;
+    #endif
+
+    #ifdef SS_THICKNESSANDMASK_TEXTURE
+        uniform vec2 vThicknessInfos;
+        uniform mat4 thicknessMatrix;;
+    #endif
+#endif
+

+ 97 - 37
src/Shaders/pbr.fragment.fx

@@ -124,7 +124,7 @@ void main(void) {
     alpha *= vColor.a;
 #endif
 
-#if !defined(LINKREFRACTIONTOTRANSPARENCY) && !defined(ALPHAFRESNEL)
+#if !defined(SS_LINKREFRACTIONTOTRANSPARENCY) && !defined(ALPHAFRESNEL)
     #ifdef ALPHATEST
         if (alpha < ALPHATESTVALUE)
             discard;
@@ -310,7 +310,7 @@ void main(void) {
     #endif
 
     // _____________________________ Refraction Info _______________________________________
-    #ifdef REFRACTION
+    #ifdef SS_REFRACTION
         vec4 environmentRefraction = vec4(0., 0., 0., 0.);
 
         #ifdef ANISOTROPIC
@@ -319,12 +319,12 @@ void main(void) {
             vec3 refractionVector = refract(-viewDirectionW, normalW, vRefractionInfos.y);
         #endif
 
-        #ifdef REFRACTIONMAP_OPPOSITEZ
+        #ifdef SS_REFRACTIONMAP_OPPOSITEZ
             refractionVector.z *= -1.0;
         #endif
 
         // _____________________________ 2D vs 3D Maps ________________________________
-        #ifdef REFRACTIONMAP_3D
+        #ifdef SS_REFRACTIONMAP_3D
             refractionVector.y = refractionVector.y * vRefractionInfos.w;
             vec3 refractionCoords = refractionVector;
             refractionCoords = vec3(refractionMatrix * vec4(refractionCoords, 0));
@@ -334,7 +334,7 @@ void main(void) {
             refractionCoords.y = 1.0 - refractionCoords.y;
         #endif
 
-        #ifdef LODINREFRACTIONALPHA
+        #ifdef SS_LODINREFRACTIONALPHA
             float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG, NdotVUnclamped);
         #else
             float refractionLOD = getLodFromAlphaG(vRefractionMicrosurfaceInfos.x, alphaG);
@@ -344,7 +344,7 @@ void main(void) {
             // Apply environment convolution scale/offset filter tuning parameters to the mipmap LOD selection
             refractionLOD = refractionLOD * vRefractionMicrosurfaceInfos.y + vRefractionMicrosurfaceInfos.z;
 
-            #ifdef LODINREFRACTIONALPHA
+            #ifdef SS_LODINREFRACTIONALPHA
                 // Automatic LOD adjustment to ensure that the smoothness-based environment LOD selection
                 // is constrained to appropriate LOD levels in order to prevent aliasing.
                 // The environment map is first sampled without custom LOD selection to determine
@@ -382,11 +382,11 @@ void main(void) {
             }
         #endif
 
-        #ifdef RGBDREFRACTION
+        #ifdef SS_RGBDREFRACTION
             environmentRefraction.rgb = fromRGBD(environmentRefraction);
         #endif
 
-        #ifdef GAMMAREFRACTION
+        #ifdef SS_GAMMAREFRACTION
             environmentRefraction.rgb = toLinearSpace(environmentRefraction.rgb);
         #endif
 
@@ -743,7 +743,7 @@ void main(void) {
         #endif
     #endif
 
-    // _____________________________ IBL BRDF + Energy Cons _________________________________
+    // _____________________________ IBL BRDF + Energy Cons ________________________________
     #if defined(ENVIRONMENTBRDF)
         // BRDF Lookup
         vec3 environmentBrdf = getBRDFLookup(NdotV, roughness, environmentBrdfSampler);
@@ -753,6 +753,49 @@ void main(void) {
         #endif
     #endif
 
+    // ___________________________________ SubSurface ______________________________________
+    #ifdef SUBSURFACE
+        #ifdef SS_REFRACTION
+            float refractionIntensity = vSubSurfaceIntensity.x;
+            #ifdef SS_LINKREFRACTIONTOTRANSPARENCY
+                refractionIntensity *= (1.0 - alpha);
+                // Put alpha back to 1;
+                alpha = 1.0;
+            #endif
+        #endif
+        #ifdef SS_TRANSLUCENCY
+            float translucencyIntensity = vSubSurfaceIntensity.y;
+        #endif
+        #ifdef SS_SCATTERING
+            float scatteringIntensity = vSubSurfaceIntensity.z;
+        #endif
+
+        #ifdef SS_THICKNESSANDMASK_TEXTURE
+            vec4 thicknessMap = texture2D(thicknessSampler, vThicknessUV + uvOffset);
+            float thickness = thicknessMap.r * vThicknessParam.y + vThicknessParam.x;
+
+            #ifdef SS_MASK_FROM_THICKNESS_TEXTURE
+                #ifdef SS_REFRACTION
+                    refractionIntensity *= thicknessMap.g;
+                #endif
+                #ifdef SS_TRANSLUCENCY
+                    translucencyIntensity *= thicknessMap.b;
+                #endif
+                #ifdef SS_SCATTERING
+                    scatteringIntensity *= thicknessMap.a;
+                #endif
+            #endif
+        #else
+            float thickness = vThicknessParam.y;
+        #endif
+
+        #ifdef SS_TRANSLUCENCY
+            thickness = maxEps(thickness);
+            vec3 transmittance = transmittanceBRDF_Burley(vTintColor.rgb, vDiffusionDistance, thickness);
+            transmittance *= translucencyIntensity;
+        #endif
+    #endif
+
     // ____________________________________________________________________________________
     // _____________________________ Direct Lighting Info __________________________________
     vec3 diffuseBase = vec3(0., 0., 0.);
@@ -885,42 +928,56 @@ void main(void) {
         specularEnvironmentReflectance *= (conservationFactor * conservationFactor);
     #endif
 
-    // _____________________________ Refractance+Tint ________________________________
-    #ifdef REFRACTION
-        vec3 refractance = vec3(0.0, 0.0, 0.0);
-        vec3 transmission = vec3(1.0, 1.0, 1.0);
-        #ifdef LINKREFRACTIONTOTRANSPARENCY
-            // Transmission based on alpha.
-            transmission *= (1.0 - alpha);
+    // _____________________________ Transmittance + Tint ________________________________
+    #ifdef SS_REFRACTION
+        vec3 refractionTransmittance = vec3(refractionIntensity);
+        #ifdef SS_THICKNESSANDMASK_TEXTURE
+            vec3 volumeAlbedo = computeColorAtDistanceInMedia(vTintColor.rgb, vTintColor.w);
 
-            // Tint the material with albedo.
-            // TODO. PBR Tinting.
-            vec3 mixedAlbedo = surfaceAlbedo;
-            float maxChannel = max(max(mixedAlbedo.r, mixedAlbedo.g), mixedAlbedo.b);
-            vec3 tint = saturate(maxChannel * mixedAlbedo);
+            // // Simulate Flat Surface
+            // thickness /=  dot(refractionVector, -normalW);
 
-            // Decrease Albedo Contribution
-            surfaceAlbedo *= alpha;
+            // // Simulate Curved Surface
+            // float NdotRefract = dot(normalW, refractionVector);
+            // thickness *= -NdotRefract;
 
-            // Decrease irradiance Contribution
-            environmentIrradiance *= alpha;
+            refractionTransmittance *= cocaLambert(volumeAlbedo, thickness);
+        #elif defined(SS_LINKREFRACTIONTOTRANSPARENCY)
+            // Tint the material with albedo.
+            float maxChannel = max(max(surfaceAlbedo.r, surfaceAlbedo.g), surfaceAlbedo.b);
+            vec3 volumeAlbedo = saturate(maxChannel * surfaceAlbedo);
 
             // Tint reflectance
-            environmentRefraction.rgb *= tint;
-
-            // Put alpha back to 1;
-            alpha = 1.0;
+            environmentRefraction.rgb *= volumeAlbedo;
+        #else
+            // Nothing to change for refraction.
         #endif
 
+        // Decrease Albedo Contribution
+        surfaceAlbedo *= (1. - refractionIntensity);
+
+        // Decrease irradiance Contribution
+        environmentIrradiance *= (1. - refractionIntensity);
+
         // Add Multiple internal bounces.
         vec3 bounceSpecularEnvironmentReflectance = (2.0 * specularEnvironmentReflectance) / (1.0 + specularEnvironmentReflectance);
-        specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, alpha);
+        specularEnvironmentReflectance = mix(bounceSpecularEnvironmentReflectance, specularEnvironmentReflectance, refractionIntensity);
 
         // In theory T = 1 - R.
-        transmission *= 1.0 - specularEnvironmentReflectance;
+        refractionTransmittance *= 1.0 - specularEnvironmentReflectance;
+    #endif
 
-        // Should baked in diffuse.
-        refractance = transmission;
+    // _______________________________  IBL Translucency ________________________________
+    #if defined(REFLECTION) && defined(USESPHERICALFROMREFLECTIONMAP) && defined(SS_TRANSLUCENCY)
+        #if defined(USESPHERICALINVERTEX)
+            vec3 irradianceVector = vec3(reflectionMatrix * vec4(normalW, 0)).xyz;
+            #ifdef REFLECTIONMAP_OPPOSITEZ
+                irradianceVector.z *= -1.0;
+            #endif
+        #endif
+
+        vec3 refractionIrradiance = environmentIrradianceJones(-irradianceVector);
+        refractionIrradiance *= transmittance;
     #endif
 
     // ______________________________________________________________________________
@@ -933,6 +990,9 @@ void main(void) {
     // _____________________________ Irradiance ______________________________________
     #ifdef REFLECTION
         vec3 finalIrradiance = environmentIrradiance;
+        #if defined(USESPHERICALFROMREFLECTIONMAP) && defined(SS_TRANSLUCENCY)
+            finalIrradiance += refractionIrradiance;
+        #endif
         finalIrradiance *= surfaceAlbedo.rgb;
     #endif
 
@@ -961,9 +1021,9 @@ void main(void) {
     #endif
 
     // _____________________________ Refraction ______________________________________
-    #ifdef REFRACTION
+    #ifdef SS_REFRACTION
         vec3 finalRefraction = environmentRefraction.rgb;
-        finalRefraction *= refractance;
+        finalRefraction *= refractionTransmittance;
     #endif
 
     // _____________________________ Clear Coat _______________________________________
@@ -986,7 +1046,7 @@ void main(void) {
             vec3 finalClearCoatRadianceScaled = finalClearCoatRadiance * vLightingIntensity.z;
         #endif
 
-        #ifdef REFRACTION
+        #ifdef SS_REFRACTION
             finalRefraction *= (conservationFactor * conservationFactor);
             #ifdef CLEARCOAT_TINT
                 finalRefraction *= absorption;
@@ -1100,7 +1160,7 @@ void main(void) {
             finalSheenRadianceScaled +
         #endif
     #endif
-    #ifdef REFRACTION
+    #ifdef SS_REFRACTION
         finalRefraction			* vLightingIntensity.z +
     #endif
 #endif

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

@@ -92,6 +92,12 @@ varying vec2 vBumpUV;
     #endif
 #endif
 
+#ifdef SUBSURFACE
+    #if defined(SS_THICKNESSANDMASK_TEXTURE) && SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 0 
+        varying vec2 vThicknessUV;
+    #endif
+#endif
+
 // Output
 varying vec3 vPositionW;
 #if DEBUGMODE > 0
@@ -362,6 +368,19 @@ void main(void) {
     #endif
 #endif
 
+#ifdef SUBSURFACE
+    #if defined(SS_THICKNESSANDMASK_TEXTURE) && SS_THICKNESSANDMASK_TEXTUREDIRECTUV == 0 
+        if (vThicknessInfos.x == 0.)
+        {
+            vThicknessUV = vec2(thicknessMatrix * vec4(uv, 1.0, 0.0));
+        }
+        else
+        {
+            vThicknessUV = vec2(thicknessMatrix * vec4(uv2, 1.0, 0.0));
+        }
+    #endif
+#endif
+
     // TBN
 #include<bumpVertex>
 

+ 0 - 0
src/Shaders/standard.fragment.fx


Некоторые файлы не были показаны из-за большого количества измененных файлов