sebastien 6 年之前
父节点
当前提交
bda908207e
共有 79 个文件被更改,包括 14284 次插入13532 次删除
  1. 5397 5365
      Playground/babylon.d.txt
  2. 7811 7779
      dist/preview release/babylon.d.ts
  3. 1 1
      dist/preview release/babylon.js
  4. 64 26
      dist/preview release/babylon.max.js
  5. 64 26
      dist/preview release/babylon.no-module.max.js
  6. 1 1
      dist/preview release/babylon.worker.js
  7. 64 26
      dist/preview release/es6.js
  8. 1 1
      dist/preview release/glTF2Interface/package.json
  9. 1 1
      dist/preview release/gui/babylon.gui.js
  10. 1 1
      dist/preview release/gui/babylon.gui.min.js
  11. 1 1
      dist/preview release/gui/babylon.gui.min.js.map
  12. 2 2
      dist/preview release/gui/package.json
  13. 12 12
      dist/preview release/inspector/babylon.inspector.bundle.js
  14. 1 1
      dist/preview release/inspector/babylon.inspector.bundle.js.map
  15. 7 9
      dist/preview release/inspector/babylon.inspector.d.ts
  16. 16 20
      dist/preview release/inspector/babylon.inspector.module.d.ts
  17. 5 5
      dist/preview release/inspector/package.json
  18. 3 3
      dist/preview release/loaders/package.json
  19. 2 2
      dist/preview release/materialsLibrary/package.json
  20. 2 2
      dist/preview release/postProcessesLibrary/package.json
  21. 2 2
      dist/preview release/proceduralTexturesLibrary/package.json
  22. 3 3
      dist/preview release/serializers/package.json
  23. 0 11
      dist/preview release/viewer/babylon.viewer.d.ts
  24. 2 2
      dist/preview release/viewer/babylon.viewer.js
  25. 4 4
      dist/preview release/viewer/babylon.viewer.max.js
  26. 1 11
      dist/preview release/viewer/babylon.viewer.module.d.ts
  27. 3 0
      dist/preview release/what's new.md
  28. 1 1
      gui/src/2D/controls/checkbox.ts
  29. 1 1
      gui/src/2D/controls/container.ts
  30. 58 4
      gui/src/2D/controls/control.ts
  31. 4 0
      inspector/src/components/actionTabs/actionTabs.scss
  32. 3 2
      inspector/src/components/actionTabs/actionTabsComponent.tsx
  33. 13 4
      inspector/src/components/actionTabs/lines/floatLineComponent.tsx
  34. 1 1
      inspector/src/components/actionTabs/lines/optionsLineComponent.tsx
  35. 18 7
      inspector/src/components/actionTabs/lines/textInputLineComponent.tsx
  36. 4 0
      inspector/src/components/actionTabs/lines/textureLineComponent.tsx
  37. 12 12
      inspector/src/components/actionTabs/lines/textureLinkLineComponent.tsx
  38. 14 14
      inspector/src/components/actionTabs/tabs/debugTabComponent.tsx
  39. 65 1
      inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx
  40. 4 4
      inspector/src/components/actionTabs/tabs/propertyGrids/gridPropertyGridComponent.tsx
  41. 35 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/checkboxPropertyGridComponent.tsx
  42. 31 2
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent.tsx
  43. 33 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/ellipsePropertyGridComponent.tsx
  44. 42 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/imageBasedSliderPropertyGridComponent.tsx
  45. 52 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/imagePropertyGridComponent.tsx
  46. 55 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/linePropertyGridComponent.tsx
  47. 38 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/radioButtonPropertyGridComponent.tsx
  48. 34 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/rectanglePropertyGridComponent.tsx
  49. 44 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/sliderPropertyGridComponent.tsx
  50. 30 0
      inspector/src/components/actionTabs/tabs/propertyGrids/gui/textBlockPropertyGridComponent.tsx
  51. 7 9
      inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx
  52. 16 16
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/axesViewerComponent.tsx
  53. 14 14
      inspector/src/components/actionTabs/tabs/propertyGrids/meshes/meshPropertyGridComponent.tsx
  54. 4 2
      inspector/src/components/embedHost/embedHostComponent.tsx
  55. 2 1
      inspector/src/components/headerComponent.tsx
  56. 25 25
      inspector/src/components/sceneExplorer/entities/meshTreeItemComponent.tsx
  57. 10 10
      inspector/src/components/sceneExplorer/entities/sceneTreeItemComponent.tsx
  58. 10 9
      inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx
  59. 2 2
      inspector/src/components/sceneExplorer/treeItemComponent.tsx
  60. 4 4
      inspector/src/components/sceneExplorer/treeItemSelectableComponent.tsx
  61. 10 2
      inspector/src/inspector.ts
  62. 3 3
      inspector/src/tools.ts
  63. 1 1
      package.json
  64. 3 1
      readme.md
  65. 6 4
      sandbox/index.js
  66. 1 0
      src/Cameras/XR/webXRCamera.ts
  67. 1 0
      src/Cameras/camera.ts
  68. 17 9
      src/Cameras/targetCamera.ts
  69. 4 0
      src/Debug/debugLayer.ts
  70. 1 1
      src/Engines/engine.ts
  71. 1 1
      src/Helpers/sceneHelpers.ts
  72. 30 25
      src/Materials/Textures/baseTexture.ts
  73. 5 0
      src/Materials/material.ts
  74. 1 4
      src/Meshes/meshBuilder.ts
  75. 24 11
      src/Meshes/transformNode.ts
  76. 8 2
      src/node.ts
  77. 6 0
      src/scene.ts
  78. 5 5
      tests/validation/config.json
  79. 0 1
      what's new.md

文件差异内容过多而无法显示
+ 5397 - 5365
Playground/babylon.d.txt


文件差异内容过多而无法显示
+ 7811 - 7779
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/babylon.js


+ 64 - 26
dist/preview release/babylon.max.js

@@ -12953,7 +12953,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.8";
+                return "4.0.0-alpha.9";
             },
             enumerable: true,
             configurable: true
@@ -18827,6 +18827,10 @@ var BABYLON;
              */
             this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
+            /**
              * Gets or sets a boolean used to define if the node must be serialized
              */
             this.doNotSerialize = false;
@@ -18914,7 +18918,7 @@ var BABYLON;
                     if (index !== -1) {
                         this._parentNode._children.splice(index, 1);
                     }
-                    if (!parent) {
+                    if (!parent && !this._isDisposed) {
                         this.addToSceneRootNodes();
                     }
                 }
@@ -19386,6 +19390,7 @@ var BABYLON;
          */
         Node.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
+            this._isDisposed = true;
             if (!doNotRecurse) {
                 var nodes = this.getDescendants(true);
                 for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
@@ -19416,7 +19421,6 @@ var BABYLON;
                 behavior.detach();
             }
             this._behaviors = [];
-            this._isDisposed = true;
         };
         /**
          * Parse animation range data from a serialization object and store them into a given node
@@ -20514,17 +20518,7 @@ var BABYLON;
             var dv = TransformNode._lookAtVectorCache;
             var pos = space === BABYLON.Space.LOCAL ? this.position : this.getAbsolutePosition();
             targetPoint.subtractToRef(pos, dv);
-            var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
-            var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
-            var pitch = Math.atan2(dv.y, len);
-            if (this.rotationQuaternion) {
-                BABYLON.Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
-            }
-            else {
-                this.rotation.x = pitch + pitchCor;
-                this.rotation.y = yaw + yawCor;
-                this.rotation.z = rollCor;
-            }
+            this.setDirection(dv, yawCor, pitchCor, rollCor);
             // Correct for parent's rotation offset
             if (space === BABYLON.Space.WORLD && this.parent) {
                 if (this.rotationQuaternion) {
@@ -20579,6 +20573,31 @@ var BABYLON;
             return this;
         };
         /**
+         * Sets this transform node rotation to the given local axis.
+         * @param localAxis the axis in local space
+         * @param yawCor optional yaw (y-axis) correction in radians
+         * @param pitchCor optional pitch (x-axis) correction in radians
+         * @param rollCor optional roll (z-axis) correction in radians
+         * @returns this TransformNode
+         */
+        TransformNode.prototype.setDirection = function (localAxis, yawCor, pitchCor, rollCor) {
+            if (yawCor === void 0) { yawCor = 0; }
+            if (pitchCor === void 0) { pitchCor = 0; }
+            if (rollCor === void 0) { rollCor = 0; }
+            var yaw = -Math.atan2(localAxis.z, localAxis.x) - Math.PI / 2;
+            var len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
+            var pitch = Math.atan2(localAxis.y, len);
+            if (this.rotationQuaternion) {
+                BABYLON.Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
+            }
+            else {
+                this.rotation.x = pitch + pitchCor;
+                this.rotation.y = yaw + yawCor;
+                this.rotation.z = rollCor;
+            }
+            return this;
+        };
+        /**
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
@@ -24614,6 +24633,7 @@ var BABYLON;
                 this._rigCameras[i].minZ = this.minZ;
                 this._rigCameras[i].maxZ = this.maxZ;
                 this._rigCameras[i].fov = this.fov;
+                this._rigCameras[i].upVector.copyFrom(this.upVector);
             }
             // only update viewport when ANAGLYPH
             if (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH) {
@@ -25844,6 +25864,10 @@ var BABYLON;
              */
             _this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            _this.reservedDataStore = null;
+            /**
              * Use this array to add regular expressions used to disable offline support for specific urls
              */
             _this.disableOfflineSupportExceptionRules = new Array();
@@ -31658,6 +31682,10 @@ var BABYLON;
              * Gets or sets an object used to store user defined information.
              */
             this.metadata = null;
+            /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
             this._hasAlpha = false;
             /**
              * Defines if the alpha value should be determined via the rgb values.
@@ -31746,8 +31774,8 @@ var BABYLON;
             this._cachedSize = BABYLON.Size.Zero();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
             if (this._scene) {
-                this._scene.addTexture(this);
                 this.uniqueId = this._scene.getUniqueId();
+                this._scene.addTexture(this);
             }
             this._uid = null;
         }
@@ -36959,6 +36987,10 @@ var BABYLON;
              */
             this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
+            /**
              * Specifies if the ready state should be checked on each call
              */
             this.checkReadyOnEveryCall = false;
@@ -49055,6 +49087,8 @@ var BABYLON;
             /** @hidden */
             _this._currentTarget = BABYLON.Vector3.Zero();
             /** @hidden */
+            _this._initialFocalDistance = 1;
+            /** @hidden */
             _this._viewMatrix = BABYLON.Matrix.Zero();
             /** @hidden */
             _this._camMatrix = BABYLON.Matrix.Zero();
@@ -49174,6 +49208,7 @@ var BABYLON;
         /** @hidden */
         TargetCamera.prototype.setTarget = function (target) {
             this.upVector.normalize();
+            this._initialFocalDistance = target.subtract(this.position).length();
             if (this.position.z === target.z) {
                 this.position.z += BABYLON.Epsilon;
             }
@@ -49374,10 +49409,8 @@ var BABYLON;
                     //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
                     var leftSign = (this.cameraRigMode === BABYLON.Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? 1 : -1;
                     var rightSign = (this.cameraRigMode === BABYLON.Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 : 1;
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft.position);
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * rightSign, camRight.position);
-                    camLeft.setTarget(this.getTarget());
-                    camRight.setTarget(this.getTarget());
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft);
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * rightSign, camRight);
                     break;
                 case BABYLON.Camera.RIG_MODE_VR:
                     if (camLeft.rotationQuaternion) {
@@ -49394,13 +49427,17 @@ var BABYLON;
             }
             _super.prototype._updateRigCameras.call(this);
         };
-        TargetCamera.prototype._getRigCamPosition = function (halfSpace, result) {
+        TargetCamera.prototype._getRigCamPositionAndTarget = function (halfSpace, rigCamera) {
             var target = this.getTarget();
-            BABYLON.Matrix.TranslationToRef(-target.x, -target.y, -target.z, TargetCamera._TargetTransformMatrix);
+            target.subtractToRef(this.position, TargetCamera._TargetFocalPoint);
+            TargetCamera._TargetFocalPoint.normalize().scaleInPlace(this._initialFocalDistance);
+            var newFocalTarget = TargetCamera._TargetFocalPoint.addInPlace(this.position);
+            BABYLON.Matrix.TranslationToRef(-newFocalTarget.x, -newFocalTarget.y, -newFocalTarget.z, TargetCamera._TargetTransformMatrix);
             TargetCamera._TargetTransformMatrix.multiplyToRef(BABYLON.Matrix.RotationY(halfSpace), TargetCamera._RigCamTransformMatrix);
-            BABYLON.Matrix.TranslationToRef(target.x, target.y, target.z, TargetCamera._TargetTransformMatrix);
+            BABYLON.Matrix.TranslationToRef(newFocalTarget.x, newFocalTarget.y, newFocalTarget.z, TargetCamera._TargetTransformMatrix);
             TargetCamera._RigCamTransformMatrix.multiplyToRef(TargetCamera._TargetTransformMatrix, TargetCamera._RigCamTransformMatrix);
-            BABYLON.Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, result);
+            BABYLON.Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, rigCamera.position);
+            rigCamera.setTarget(newFocalTarget);
         };
         /**
          * Gets the current object class name.
@@ -49411,6 +49448,7 @@ var BABYLON;
         };
         TargetCamera._RigCamTransformMatrix = new BABYLON.Matrix();
         TargetCamera._TargetTransformMatrix = new BABYLON.Matrix();
+        TargetCamera._TargetFocalPoint = new BABYLON.Vector3();
         __decorate([
             BABYLON.serializeAsVector3()
         ], TargetCamera.prototype, "rotation", void 0);
@@ -70660,9 +70698,7 @@ var BABYLON;
             vertexData.applyToMesh(plane, options.updatable);
             if (options.sourcePlane) {
                 plane.translate(options.sourcePlane.normal, -options.sourcePlane.d);
-                var dot = BABYLON.Vector3.Dot(plane.position, options.sourcePlane.normal);
-                var flip = dot >= 0;
-                plane.lookAt(BABYLON.Vector3.Zero(), 0, flip ? Math.PI : 0, 0);
+                plane.setDirection(options.sourcePlane.normal);
             }
             return plane;
         };
@@ -109407,6 +109443,7 @@ var BABYLON;
                 newCamera.minZ = 0;
                 newCamera.parent = this;
                 newCamera.rotationQuaternion = new BABYLON.Quaternion();
+                newCamera.updateUpVectorFromRotation = true;
                 this.rigCameras.push(newCamera);
             }
             while (this.rigCameras.length > viewCount) {
@@ -121191,6 +121228,7 @@ var BABYLON;
             hdrSkybox.infiniteDistance = true;
             hdrSkybox.material = skyboxMaterial;
         }
+        hdrSkybox.isPickable = false;
         return hdrSkybox;
     };
     BABYLON.Scene.prototype.createDefaultEnvironment = function (options) {

+ 64 - 26
dist/preview release/babylon.no-module.max.js

@@ -12920,7 +12920,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.8";
+                return "4.0.0-alpha.9";
             },
             enumerable: true,
             configurable: true
@@ -18794,6 +18794,10 @@ var BABYLON;
              */
             this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
+            /**
              * Gets or sets a boolean used to define if the node must be serialized
              */
             this.doNotSerialize = false;
@@ -18881,7 +18885,7 @@ var BABYLON;
                     if (index !== -1) {
                         this._parentNode._children.splice(index, 1);
                     }
-                    if (!parent) {
+                    if (!parent && !this._isDisposed) {
                         this.addToSceneRootNodes();
                     }
                 }
@@ -19353,6 +19357,7 @@ var BABYLON;
          */
         Node.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
+            this._isDisposed = true;
             if (!doNotRecurse) {
                 var nodes = this.getDescendants(true);
                 for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
@@ -19383,7 +19388,6 @@ var BABYLON;
                 behavior.detach();
             }
             this._behaviors = [];
-            this._isDisposed = true;
         };
         /**
          * Parse animation range data from a serialization object and store them into a given node
@@ -20481,17 +20485,7 @@ var BABYLON;
             var dv = TransformNode._lookAtVectorCache;
             var pos = space === BABYLON.Space.LOCAL ? this.position : this.getAbsolutePosition();
             targetPoint.subtractToRef(pos, dv);
-            var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
-            var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
-            var pitch = Math.atan2(dv.y, len);
-            if (this.rotationQuaternion) {
-                BABYLON.Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
-            }
-            else {
-                this.rotation.x = pitch + pitchCor;
-                this.rotation.y = yaw + yawCor;
-                this.rotation.z = rollCor;
-            }
+            this.setDirection(dv, yawCor, pitchCor, rollCor);
             // Correct for parent's rotation offset
             if (space === BABYLON.Space.WORLD && this.parent) {
                 if (this.rotationQuaternion) {
@@ -20546,6 +20540,31 @@ var BABYLON;
             return this;
         };
         /**
+         * Sets this transform node rotation to the given local axis.
+         * @param localAxis the axis in local space
+         * @param yawCor optional yaw (y-axis) correction in radians
+         * @param pitchCor optional pitch (x-axis) correction in radians
+         * @param rollCor optional roll (z-axis) correction in radians
+         * @returns this TransformNode
+         */
+        TransformNode.prototype.setDirection = function (localAxis, yawCor, pitchCor, rollCor) {
+            if (yawCor === void 0) { yawCor = 0; }
+            if (pitchCor === void 0) { pitchCor = 0; }
+            if (rollCor === void 0) { rollCor = 0; }
+            var yaw = -Math.atan2(localAxis.z, localAxis.x) - Math.PI / 2;
+            var len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
+            var pitch = Math.atan2(localAxis.y, len);
+            if (this.rotationQuaternion) {
+                BABYLON.Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
+            }
+            else {
+                this.rotation.x = pitch + pitchCor;
+                this.rotation.y = yaw + yawCor;
+                this.rotation.z = rollCor;
+            }
+            return this;
+        };
+        /**
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
@@ -24581,6 +24600,7 @@ var BABYLON;
                 this._rigCameras[i].minZ = this.minZ;
                 this._rigCameras[i].maxZ = this.maxZ;
                 this._rigCameras[i].fov = this.fov;
+                this._rigCameras[i].upVector.copyFrom(this.upVector);
             }
             // only update viewport when ANAGLYPH
             if (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH) {
@@ -25811,6 +25831,10 @@ var BABYLON;
              */
             _this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            _this.reservedDataStore = null;
+            /**
              * Use this array to add regular expressions used to disable offline support for specific urls
              */
             _this.disableOfflineSupportExceptionRules = new Array();
@@ -31625,6 +31649,10 @@ var BABYLON;
              * Gets or sets an object used to store user defined information.
              */
             this.metadata = null;
+            /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
             this._hasAlpha = false;
             /**
              * Defines if the alpha value should be determined via the rgb values.
@@ -31713,8 +31741,8 @@ var BABYLON;
             this._cachedSize = BABYLON.Size.Zero();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
             if (this._scene) {
-                this._scene.addTexture(this);
                 this.uniqueId = this._scene.getUniqueId();
+                this._scene.addTexture(this);
             }
             this._uid = null;
         }
@@ -36926,6 +36954,10 @@ var BABYLON;
              */
             this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
+            /**
              * Specifies if the ready state should be checked on each call
              */
             this.checkReadyOnEveryCall = false;
@@ -49022,6 +49054,8 @@ var BABYLON;
             /** @hidden */
             _this._currentTarget = BABYLON.Vector3.Zero();
             /** @hidden */
+            _this._initialFocalDistance = 1;
+            /** @hidden */
             _this._viewMatrix = BABYLON.Matrix.Zero();
             /** @hidden */
             _this._camMatrix = BABYLON.Matrix.Zero();
@@ -49141,6 +49175,7 @@ var BABYLON;
         /** @hidden */
         TargetCamera.prototype.setTarget = function (target) {
             this.upVector.normalize();
+            this._initialFocalDistance = target.subtract(this.position).length();
             if (this.position.z === target.z) {
                 this.position.z += BABYLON.Epsilon;
             }
@@ -49341,10 +49376,8 @@ var BABYLON;
                     //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
                     var leftSign = (this.cameraRigMode === BABYLON.Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? 1 : -1;
                     var rightSign = (this.cameraRigMode === BABYLON.Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 : 1;
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft.position);
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * rightSign, camRight.position);
-                    camLeft.setTarget(this.getTarget());
-                    camRight.setTarget(this.getTarget());
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft);
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * rightSign, camRight);
                     break;
                 case BABYLON.Camera.RIG_MODE_VR:
                     if (camLeft.rotationQuaternion) {
@@ -49361,13 +49394,17 @@ var BABYLON;
             }
             _super.prototype._updateRigCameras.call(this);
         };
-        TargetCamera.prototype._getRigCamPosition = function (halfSpace, result) {
+        TargetCamera.prototype._getRigCamPositionAndTarget = function (halfSpace, rigCamera) {
             var target = this.getTarget();
-            BABYLON.Matrix.TranslationToRef(-target.x, -target.y, -target.z, TargetCamera._TargetTransformMatrix);
+            target.subtractToRef(this.position, TargetCamera._TargetFocalPoint);
+            TargetCamera._TargetFocalPoint.normalize().scaleInPlace(this._initialFocalDistance);
+            var newFocalTarget = TargetCamera._TargetFocalPoint.addInPlace(this.position);
+            BABYLON.Matrix.TranslationToRef(-newFocalTarget.x, -newFocalTarget.y, -newFocalTarget.z, TargetCamera._TargetTransformMatrix);
             TargetCamera._TargetTransformMatrix.multiplyToRef(BABYLON.Matrix.RotationY(halfSpace), TargetCamera._RigCamTransformMatrix);
-            BABYLON.Matrix.TranslationToRef(target.x, target.y, target.z, TargetCamera._TargetTransformMatrix);
+            BABYLON.Matrix.TranslationToRef(newFocalTarget.x, newFocalTarget.y, newFocalTarget.z, TargetCamera._TargetTransformMatrix);
             TargetCamera._RigCamTransformMatrix.multiplyToRef(TargetCamera._TargetTransformMatrix, TargetCamera._RigCamTransformMatrix);
-            BABYLON.Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, result);
+            BABYLON.Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, rigCamera.position);
+            rigCamera.setTarget(newFocalTarget);
         };
         /**
          * Gets the current object class name.
@@ -49378,6 +49415,7 @@ var BABYLON;
         };
         TargetCamera._RigCamTransformMatrix = new BABYLON.Matrix();
         TargetCamera._TargetTransformMatrix = new BABYLON.Matrix();
+        TargetCamera._TargetFocalPoint = new BABYLON.Vector3();
         __decorate([
             BABYLON.serializeAsVector3()
         ], TargetCamera.prototype, "rotation", void 0);
@@ -70627,9 +70665,7 @@ var BABYLON;
             vertexData.applyToMesh(plane, options.updatable);
             if (options.sourcePlane) {
                 plane.translate(options.sourcePlane.normal, -options.sourcePlane.d);
-                var dot = BABYLON.Vector3.Dot(plane.position, options.sourcePlane.normal);
-                var flip = dot >= 0;
-                plane.lookAt(BABYLON.Vector3.Zero(), 0, flip ? Math.PI : 0, 0);
+                plane.setDirection(options.sourcePlane.normal);
             }
             return plane;
         };
@@ -109374,6 +109410,7 @@ var BABYLON;
                 newCamera.minZ = 0;
                 newCamera.parent = this;
                 newCamera.rotationQuaternion = new BABYLON.Quaternion();
+                newCamera.updateUpVectorFromRotation = true;
                 this.rigCameras.push(newCamera);
             }
             while (this.rigCameras.length > viewCount) {
@@ -121158,6 +121195,7 @@ var BABYLON;
             hdrSkybox.infiniteDistance = true;
             hdrSkybox.material = skyboxMaterial;
         }
+        hdrSkybox.isPickable = false;
         return hdrSkybox;
     };
     BABYLON.Scene.prototype.createDefaultEnvironment = function (options) {

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/babylon.worker.js


+ 64 - 26
dist/preview release/es6.js

@@ -12920,7 +12920,7 @@ var BABYLON;
              * Returns the current version of the framework
              */
             get: function () {
-                return "4.0.0-alpha.8";
+                return "4.0.0-alpha.9";
             },
             enumerable: true,
             configurable: true
@@ -18794,6 +18794,10 @@ var BABYLON;
              */
             this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
+            /**
              * Gets or sets a boolean used to define if the node must be serialized
              */
             this.doNotSerialize = false;
@@ -18881,7 +18885,7 @@ var BABYLON;
                     if (index !== -1) {
                         this._parentNode._children.splice(index, 1);
                     }
-                    if (!parent) {
+                    if (!parent && !this._isDisposed) {
                         this.addToSceneRootNodes();
                     }
                 }
@@ -19353,6 +19357,7 @@ var BABYLON;
          */
         Node.prototype.dispose = function (doNotRecurse, disposeMaterialAndTextures) {
             if (disposeMaterialAndTextures === void 0) { disposeMaterialAndTextures = false; }
+            this._isDisposed = true;
             if (!doNotRecurse) {
                 var nodes = this.getDescendants(true);
                 for (var _i = 0, nodes_1 = nodes; _i < nodes_1.length; _i++) {
@@ -19383,7 +19388,6 @@ var BABYLON;
                 behavior.detach();
             }
             this._behaviors = [];
-            this._isDisposed = true;
         };
         /**
          * Parse animation range data from a serialization object and store them into a given node
@@ -20481,17 +20485,7 @@ var BABYLON;
             var dv = TransformNode._lookAtVectorCache;
             var pos = space === BABYLON.Space.LOCAL ? this.position : this.getAbsolutePosition();
             targetPoint.subtractToRef(pos, dv);
-            var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
-            var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
-            var pitch = Math.atan2(dv.y, len);
-            if (this.rotationQuaternion) {
-                BABYLON.Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
-            }
-            else {
-                this.rotation.x = pitch + pitchCor;
-                this.rotation.y = yaw + yawCor;
-                this.rotation.z = rollCor;
-            }
+            this.setDirection(dv, yawCor, pitchCor, rollCor);
             // Correct for parent's rotation offset
             if (space === BABYLON.Space.WORLD && this.parent) {
                 if (this.rotationQuaternion) {
@@ -20546,6 +20540,31 @@ var BABYLON;
             return this;
         };
         /**
+         * Sets this transform node rotation to the given local axis.
+         * @param localAxis the axis in local space
+         * @param yawCor optional yaw (y-axis) correction in radians
+         * @param pitchCor optional pitch (x-axis) correction in radians
+         * @param rollCor optional roll (z-axis) correction in radians
+         * @returns this TransformNode
+         */
+        TransformNode.prototype.setDirection = function (localAxis, yawCor, pitchCor, rollCor) {
+            if (yawCor === void 0) { yawCor = 0; }
+            if (pitchCor === void 0) { pitchCor = 0; }
+            if (rollCor === void 0) { rollCor = 0; }
+            var yaw = -Math.atan2(localAxis.z, localAxis.x) - Math.PI / 2;
+            var len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
+            var pitch = Math.atan2(localAxis.y, len);
+            if (this.rotationQuaternion) {
+                BABYLON.Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
+            }
+            else {
+                this.rotation.x = pitch + pitchCor;
+                this.rotation.y = yaw + yawCor;
+                this.rotation.z = rollCor;
+            }
+            return this;
+        };
+        /**
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)
@@ -24581,6 +24600,7 @@ var BABYLON;
                 this._rigCameras[i].minZ = this.minZ;
                 this._rigCameras[i].maxZ = this.maxZ;
                 this._rigCameras[i].fov = this.fov;
+                this._rigCameras[i].upVector.copyFrom(this.upVector);
             }
             // only update viewport when ANAGLYPH
             if (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_ANAGLYPH) {
@@ -25811,6 +25831,10 @@ var BABYLON;
              */
             _this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            _this.reservedDataStore = null;
+            /**
              * Use this array to add regular expressions used to disable offline support for specific urls
              */
             _this.disableOfflineSupportExceptionRules = new Array();
@@ -31625,6 +31649,10 @@ var BABYLON;
              * Gets or sets an object used to store user defined information.
              */
             this.metadata = null;
+            /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
             this._hasAlpha = false;
             /**
              * Defines if the alpha value should be determined via the rgb values.
@@ -31713,8 +31741,8 @@ var BABYLON;
             this._cachedSize = BABYLON.Size.Zero();
             this._scene = scene || BABYLON.Engine.LastCreatedScene;
             if (this._scene) {
-                this._scene.addTexture(this);
                 this.uniqueId = this._scene.getUniqueId();
+                this._scene.addTexture(this);
             }
             this._uid = null;
         }
@@ -36926,6 +36954,10 @@ var BABYLON;
              */
             this.metadata = null;
             /**
+             * For internal use only. Please do not use.
+             */
+            this.reservedDataStore = null;
+            /**
              * Specifies if the ready state should be checked on each call
              */
             this.checkReadyOnEveryCall = false;
@@ -49022,6 +49054,8 @@ var BABYLON;
             /** @hidden */
             _this._currentTarget = BABYLON.Vector3.Zero();
             /** @hidden */
+            _this._initialFocalDistance = 1;
+            /** @hidden */
             _this._viewMatrix = BABYLON.Matrix.Zero();
             /** @hidden */
             _this._camMatrix = BABYLON.Matrix.Zero();
@@ -49141,6 +49175,7 @@ var BABYLON;
         /** @hidden */
         TargetCamera.prototype.setTarget = function (target) {
             this.upVector.normalize();
+            this._initialFocalDistance = target.subtract(this.position).length();
             if (this.position.z === target.z) {
                 this.position.z += BABYLON.Epsilon;
             }
@@ -49341,10 +49376,8 @@ var BABYLON;
                     //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
                     var leftSign = (this.cameraRigMode === BABYLON.Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? 1 : -1;
                     var rightSign = (this.cameraRigMode === BABYLON.Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 : 1;
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft.position);
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * rightSign, camRight.position);
-                    camLeft.setTarget(this.getTarget());
-                    camRight.setTarget(this.getTarget());
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft);
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * rightSign, camRight);
                     break;
                 case BABYLON.Camera.RIG_MODE_VR:
                     if (camLeft.rotationQuaternion) {
@@ -49361,13 +49394,17 @@ var BABYLON;
             }
             _super.prototype._updateRigCameras.call(this);
         };
-        TargetCamera.prototype._getRigCamPosition = function (halfSpace, result) {
+        TargetCamera.prototype._getRigCamPositionAndTarget = function (halfSpace, rigCamera) {
             var target = this.getTarget();
-            BABYLON.Matrix.TranslationToRef(-target.x, -target.y, -target.z, TargetCamera._TargetTransformMatrix);
+            target.subtractToRef(this.position, TargetCamera._TargetFocalPoint);
+            TargetCamera._TargetFocalPoint.normalize().scaleInPlace(this._initialFocalDistance);
+            var newFocalTarget = TargetCamera._TargetFocalPoint.addInPlace(this.position);
+            BABYLON.Matrix.TranslationToRef(-newFocalTarget.x, -newFocalTarget.y, -newFocalTarget.z, TargetCamera._TargetTransformMatrix);
             TargetCamera._TargetTransformMatrix.multiplyToRef(BABYLON.Matrix.RotationY(halfSpace), TargetCamera._RigCamTransformMatrix);
-            BABYLON.Matrix.TranslationToRef(target.x, target.y, target.z, TargetCamera._TargetTransformMatrix);
+            BABYLON.Matrix.TranslationToRef(newFocalTarget.x, newFocalTarget.y, newFocalTarget.z, TargetCamera._TargetTransformMatrix);
             TargetCamera._RigCamTransformMatrix.multiplyToRef(TargetCamera._TargetTransformMatrix, TargetCamera._RigCamTransformMatrix);
-            BABYLON.Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, result);
+            BABYLON.Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, rigCamera.position);
+            rigCamera.setTarget(newFocalTarget);
         };
         /**
          * Gets the current object class name.
@@ -49378,6 +49415,7 @@ var BABYLON;
         };
         TargetCamera._RigCamTransformMatrix = new BABYLON.Matrix();
         TargetCamera._TargetTransformMatrix = new BABYLON.Matrix();
+        TargetCamera._TargetFocalPoint = new BABYLON.Vector3();
         __decorate([
             BABYLON.serializeAsVector3()
         ], TargetCamera.prototype, "rotation", void 0);
@@ -70627,9 +70665,7 @@ var BABYLON;
             vertexData.applyToMesh(plane, options.updatable);
             if (options.sourcePlane) {
                 plane.translate(options.sourcePlane.normal, -options.sourcePlane.d);
-                var dot = BABYLON.Vector3.Dot(plane.position, options.sourcePlane.normal);
-                var flip = dot >= 0;
-                plane.lookAt(BABYLON.Vector3.Zero(), 0, flip ? Math.PI : 0, 0);
+                plane.setDirection(options.sourcePlane.normal);
             }
             return plane;
         };
@@ -109374,6 +109410,7 @@ var BABYLON;
                 newCamera.minZ = 0;
                 newCamera.parent = this;
                 newCamera.rotationQuaternion = new BABYLON.Quaternion();
+                newCamera.updateUpVectorFromRotation = true;
                 this.rigCameras.push(newCamera);
             }
             while (this.rigCameras.length > viewCount) {
@@ -121158,6 +121195,7 @@ var BABYLON;
             hdrSkybox.infiniteDistance = true;
             hdrSkybox.material = skyboxMaterial;
         }
+        hdrSkybox.isPickable = false;
         return hdrSkybox;
     };
     BABYLON.Scene.prototype.createDefaultEnvironment = function (options) {

+ 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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/gui/babylon.gui.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/gui/babylon.gui.min.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/gui/babylon.gui.min.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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.8"
+        "babylonjs": "4.0.0-alpha.9"
     },
     "engines": {
         "node": "*"

文件差异内容过多而无法显示
+ 12 - 12
dist/preview release/inspector/babylon.inspector.bundle.js


文件差异内容过多而无法显示
+ 1 - 1
dist/preview release/inspector/babylon.inspector.bundle.js.map


+ 7 - 9
dist/preview release/inspector/babylon.inspector.d.ts

@@ -3,15 +3,13 @@
 //   ../../../../Tools/Gulp/babylonjs
 declare module INSPECTOR {
 }
-declare module INSPECTOR {
-    export class Inspector {
-        static OnSelectionChangeObservable: BABYLON.Observable<string>;
-        static OnPropertyChangedObservable: BABYLON.Observable<PropertyChangedEvent>;
-        static readonly IsVisible: boolean;
-        static EarlyAttachToLoader(): void;
-        static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
-        static Hide(): void;
-    }
+export declare class Inspector {
+    static OnSelectionChangeObservable: BABYLON.Observable<string>;
+    static OnPropertyChangedObservable: BABYLON.Observable<PropertyChangedEvent>;
+    static readonly IsVisible: boolean;
+    static EarlyAttachToLoader(): void;
+    static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
+    static Hide(): void;
 }
 declare module INSPECTOR {
     export class PropertyChangedEvent {

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

@@ -6,17 +6,15 @@ declare module 'babylonjs-inspector' {
     export * from "babylonjs-inspector/inspector";
 }
 
-declare module 'babylonjs-inspector/inspector' {
-    import { Scene, Observable, IInspectorOptions } from "babylonjs";
-    import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
-    export class Inspector {
-        static OnSelectionChangeObservable: Observable<string>;
-        static OnPropertyChangedObservable: Observable<PropertyChangedEvent>;
-        static readonly IsVisible: boolean;
-        static EarlyAttachToLoader(): void;
-        static Show(scene: Scene, userOptions: Partial<IInspectorOptions>): void;
-        static Hide(): void;
-    }
+import { Scene, Observable, IInspectorOptions } from "babylonjs";
+import { PropertyChangedEvent } from "babylonjs-inspector/components/propertyChangedEvent";
+export declare class Inspector {
+    static OnSelectionChangeObservable: Observable<string>;
+    static OnPropertyChangedObservable: Observable<PropertyChangedEvent>;
+    static readonly IsVisible: boolean;
+    static EarlyAttachToLoader(): void;
+    static Show(scene: Scene, userOptions: Partial<IInspectorOptions>): void;
+    static Hide(): void;
 }
 
 declare module 'babylonjs-inspector/components/propertyChangedEvent' {
@@ -34,15 +32,13 @@ declare module 'babylonjs-inspector/components/propertyChangedEvent' {
 //   ../../../../Tools/Gulp/babylonjs
 declare module INSPECTOR {
 }
-declare module INSPECTOR {
-    export class Inspector {
-        static OnSelectionChangeObservable: BABYLON.Observable<string>;
-        static OnPropertyChangedObservable: BABYLON.Observable<PropertyChangedEvent>;
-        static readonly IsVisible: boolean;
-        static EarlyAttachToLoader(): void;
-        static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
-        static Hide(): void;
-    }
+export declare class Inspector {
+    static OnSelectionChangeObservable: BABYLON.Observable<string>;
+    static OnPropertyChangedObservable: BABYLON.Observable<PropertyChangedEvent>;
+    static readonly IsVisible: boolean;
+    static EarlyAttachToLoader(): void;
+    static Show(scene: BABYLON.Scene, userOptions: Partial<BABYLON.IInspectorOptions>): void;
+    static Hide(): void;
 }
 declare module INSPECTOR {
     export class PropertyChangedEvent {

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

@@ -4,7 +4,7 @@
     },
     "name": "babylonjs-inspector",
     "description": "The Babylon.js inspector.",
-    "version": "4.0.0-alpha.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -28,10 +28,10 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.8",
-        "babylonjs-gui": "4.0.0-alpha.8",
-        "babylonjs-loaders": "4.0.0-alpha.8",
-        "babylonjs-serializers": "4.0.0-alpha.8"
+        "babylonjs": "4.0.0-alpha.9",
+        "babylonjs-gui": "4.0.0-alpha.9",
+        "babylonjs-loaders": "4.0.0-alpha.9",
+        "babylonjs-serializers": "4.0.0-alpha.9"
     },
     "engines": {
         "node": "*"

+ 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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs-gltf2interface": "4.0.0-alpha.8",
-        "babylonjs": "4.0.0-alpha.8"
+        "babylonjs-gltf2interface": "4.0.0-alpha.9",
+        "babylonjs": "4.0.0-alpha.9"
     },
     "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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.8"
+        "babylonjs": "4.0.0-alpha.9"
     },
     "engines": {
         "node": "*"

+ 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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.8"
+        "babylonjs": "4.0.0-alpha.9"
     },
     "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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,7 +27,7 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.8"
+        "babylonjs": "4.0.0-alpha.9"
     },
     "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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"
@@ -27,8 +27,8 @@
     ],
     "license": "Apache-2.0",
     "dependencies": {
-        "babylonjs": "4.0.0-alpha.8",
-        "babylonjs-gltf2interface": "4.0.0-alpha.8"
+        "babylonjs": "4.0.0-alpha.9",
+        "babylonjs-gltf2interface": "4.0.0-alpha.9"
     },
     "engines": {
         "node": "*"

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

@@ -928,17 +928,6 @@ declare module BabylonViewer {
     export function registerCustomOptimizer(name: string, optimizer: (sceneManager: SceneManager) => boolean): void;
 }
 declare module BabylonViewer {
-    /**
-        * Will attach an init function the the DOMContentLoaded event.
-        * The init function will be removed automatically after the event was triggered.
-        */
-    export function initListeners(): void;
-    /**
-        * Select all HTML tags on the page that match the selector and initialize a viewer
-        *
-        * @param selector the selector to initialize the viewer on (default is 'babylon')
-        */
-    export function InitTags(selector?: string): void;
 }
 declare module BabylonViewer {
 }

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/viewer/babylon.viewer.js


文件差异内容过多而无法显示
+ 4 - 4
dist/preview release/viewer/babylon.viewer.max.js


+ 1 - 11
dist/preview release/viewer/babylon.viewer.module.d.ts

@@ -997,17 +997,7 @@ declare module 'babylonjs-viewer/optimizer/custom' {
 }
 
 declare module 'babylonjs-viewer/initializer' {
-    /**
-        * Will attach an init function the the DOMContentLoaded event.
-        * The init function will be removed automatically after the event was triggered.
-        */
-    export function initListeners(): void;
-    /**
-        * Select all HTML tags on the page that match the selector and initialize a viewer
-        *
-        * @param selector the selector to initialize the viewer on (default is 'babylon')
-        */
-    export function InitTags(selector?: string): void;
+    
 }
 
 declare module 'babylonjs-viewer/configuration' {

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

@@ -69,6 +69,7 @@
 - GizmoManager clearGizmoOnEmptyPointerEvent options and onAttachedToMeshObservable event ([TrevorDev](https://github.com/TrevorDev))
 - Added support for overriding the mesh used for the world matrix for a mesh with a skeleton ([bghgary](https://github.com/bghgary))
 - Added support for linking a bone to a transform node ([bghgary](https://github.com/bghgary))
+- Factored out `setDirection` function from `lookAt` for transform node ([bghgary](https://github.com/bghgary))
 
 ### glTF Loader
 
@@ -87,6 +88,7 @@
 ### Materials Library
 
 ## Bug fixes
+- Fixed anaglyph mode for Free and Universal cameras ([Deltakosh](https://github.com/deltakosh))
 - Fixed FileLoader's loading of a skybox, & added a parsed value for whether to create with PBR or STDMaterial ([Palmer-JC](https://github.com/Palmer-JC))
 - Removed bones from rootNodes where they should never have been ([Deltakosh](https://github.com/deltakosh))
 - Refocusing on input gui with pointer events ([TrevorDev](https://github.com/TrevorDev))
@@ -112,6 +114,7 @@
 - Fixed effect layer compatibility with multi materials ([Sebavan](https://github.com/Sebavan))
 - Added a `DeepImmutable<T>` type to specifiy that a referenced object should be considered recursively immutable, meaning that all its properties are `readonly` and that if a property is a reference to an object, this object is also recursively immutable. ([barroij](https://github.com/barroij))
 - Fixed `VideoTexture` poster property when autoplay is turned off.
+- Fixed position and rotation of plane mesh created by MeshBuilder.CreatePlane when specifying a source plane ([sable](https://github.com/thscott), [bghgary](https://github.com/bghgary))
 
 ### Viewer
 

+ 1 - 1
gui/src/2D/controls/checkbox.ts

@@ -88,7 +88,7 @@ export class Checkbox extends Control {
     }
 
     protected _getTypeName(): string {
-        return "CheckBox";
+        return "Checkbox";
     }
 
     /** @hidden */

+ 1 - 1
gui/src/2D/controls/container.ts

@@ -13,7 +13,7 @@ export class Container extends Control {
     /** @hidden */
     protected _measureForChildren = Measure.Empty();
     /** @hidden */
-    protected _background: string;
+    protected _background = "";
     /** @hidden */
     protected _adaptWidthToChildren = false;
     /** @hidden */

+ 58 - 4
gui/src/2D/controls/control.ts

@@ -107,14 +107,65 @@ export class Control {
     /** Gets or sets a boolean indicating if the children are clipped to the current control bounds */
     public clipChildren = true;
 
+    private _shadowOffsetX = 0;
     /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
-    public shadowOffsetX = 0;
+    public get shadowOffsetX() {
+        return this._shadowOffsetX;
+    }
+
+    public set shadowOffsetX(value: number) {
+        if (this._shadowOffsetX === value) {
+            return;
+        }
+
+        this._shadowOffsetX = value;
+        this._markAsDirty();
+    }
+
+    private _shadowOffsetY = 0;
     /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
-    public shadowOffsetY = 0;
+    public get shadowOffsetY() {
+        return this._shadowOffsetY;
+    }
+
+    public set shadowOffsetY(value: number) {
+        if (this._shadowOffsetY === value) {
+            return;
+        }
+
+        this._shadowOffsetY = value;
+        this._markAsDirty();
+    }
+
+    private _shadowBlur = 0;
     /** Gets or sets a value indicating the amount of blur to use to render the shadow */
-    public shadowBlur = 0;
+    public get shadowBlur() {
+        return this._shadowBlur;
+    }
+
+    public set shadowBlur(value: number) {
+        if (this._shadowBlur === value) {
+            return;
+        }
+
+        this._shadowBlur = value;
+        this._markAsDirty();
+    }
+
+    private _shadowColor = 'black';
     /** Gets or sets a value indicating the color of the shadow (black by default ie. "#000") */
-    public shadowColor = '#000';
+    public get shadowColor() {
+        return this._shadowColor;
+    }
+
+    public set shadowColor(value: string) {
+        if (this._shadowColor === value) {
+            return;
+        }
+
+        this._shadowColor = value;
+        this._markAsDirty();
+    }
 
     /** Gets or sets the cursor to use when the control is hovered */
     public hoverCursor = "";
@@ -396,6 +447,9 @@ export class Control {
 
     /** Gets or set font family */
     public get fontFamily(): string {
+        if (!this._fontSet) {
+            return "";
+        }
         return this._fontFamily;
     }
 

+ 4 - 0
inspector/src/components/actionTabs/actionTabs.scss

@@ -729,6 +729,10 @@
                         display: flex;
                         align-items: center;   
                         margin-right: 5px;
+
+                        select {
+                            width: 115px;
+                        }
                     }                    
                 }                   
 

+ 3 - 2
inspector/src/components/actionTabs/actionTabsComponent.tsx

@@ -17,6 +17,7 @@ interface IActionTabsComponentProps {
     noCommands?: boolean,
     noHeader?: boolean,
     noExpand?: boolean,
+    noClose?: boolean,
     popupMode?: boolean,
     onPopup?: () => void,
     onClose?: () => void,
@@ -101,7 +102,7 @@ export class ActionTabsComponent extends React.Component<IActionTabsComponentPro
                 <div id="actionTabs">
                     {
                         !this.props.noHeader &&
-                        <HeaderComponent title="INSPECTOR" handleBack={true} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
+                        <HeaderComponent title="INSPECTOR" handleBack={true} noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
                     }
                     {this.renderContent()}
                 </div>
@@ -124,7 +125,7 @@ export class ActionTabsComponent extends React.Component<IActionTabsComponentPro
             <Resizable id="actionTabs" minWidth={300} maxWidth={600} size={{ height: "100%" }} minHeight="100%" enable={{ top: false, right: false, bottom: false, left: true, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }}>
                 {
                     !this.props.noHeader &&
-                    <HeaderComponent title="INSPECTOR" handleBack={true} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
+                    <HeaderComponent title="INSPECTOR" handleBack={true} noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
                 }
                 {this.renderContent()}
             </Resizable>

+ 13 - 4
inspector/src/components/actionTabs/lines/floatLineComponent.tsx

@@ -9,6 +9,7 @@ interface IFloatLineComponentProps {
     propertyName: string;
     lockObject?: LockObject;
     onChange?: (newValue: number) => void;
+    isInteger?: boolean;
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>;
     additionalClass?: string;
 }
@@ -21,7 +22,7 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
         super(props);
 
         let currentValue = this.props.target[this.props.propertyName];
-        this.state = { value: currentValue ? currentValue.toFixed(3) : "0" };
+        this.state = { value: currentValue ? (this.props.isInteger ? currentValue.toFixed(0) : currentValue.toFixed(3)) : "0" };
         this._store = currentValue;
     }
 
@@ -36,8 +37,10 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
         }
 
         const newValue = nextProps.target[nextProps.propertyName];
-        if (newValue && newValue !== nextState.value) {
-            nextState.value = newValue.toFixed(3);
+        const newValueString = newValue ? this.props.isInteger ? newValue.toFixed(0) : newValue.toFixed(3) : "0";
+
+        if (newValueString !== nextState.value) {
+            nextState.value = newValueString;
             return true;
         }
         return false;
@@ -65,7 +68,13 @@ export class FloatLineComponent extends React.Component<IFloatLineComponentProps
             return;
         }
 
-        let valueAsNumber = parseFloat(valueString);
+        let valueAsNumber: number;
+
+        if (this.props.isInteger) {
+            valueAsNumber = parseInt(valueString);
+        } else {
+            valueAsNumber = parseFloat(valueString);
+        }
 
         this._localChange = true;
         this.setState({ value: valueString });

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

@@ -33,7 +33,7 @@ export class OptionsLineComponent extends React.Component<IOptionsLineComponentP
         }
 
         const newValue = nextProps.target[nextProps.propertyName];
-        if (newValue !== nextState.value) {
+        if (newValue != null && newValue !== nextState.value) {
             nextState.value = newValue;
             return true;
         }

+ 18 - 7
inspector/src/components/actionTabs/lines/textInputLineComponent.tsx

@@ -5,9 +5,11 @@ import { LockObject } from "../tabs/propertyGrids/lockObject";
 
 interface ITextInputLineComponentProps {
     label: string,
-    target: any,
     lockObject: LockObject,
-    propertyName: string,
+    target?: any,
+    propertyName?: string,
+    value?: string,
+    onChange?: (value: string) => void,
     onPropertyChangedObservable?: Observable<PropertyChangedEvent>
 }
 
@@ -17,7 +19,7 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
     constructor(props: ITextInputLineComponentProps) {
         super(props);
 
-        this.state = { value: this.props.target[this.props.propertyName] || "" }
+        this.state = { value: this.props.value || this.props.target[this.props.propertyName!] || "" }
     }
 
     componentWillUnmount() {
@@ -30,7 +32,7 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
             return true;
         }
 
-        const newValue = nextProps.target[nextProps.propertyName];
+        const newValue = nextProps.value || nextProps.target[nextProps.propertyName!];
         if (newValue !== nextState.value) {
             nextState.value = newValue || "";
             return true;
@@ -39,12 +41,18 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
     }
 
     raiseOnPropertyChanged(newValue: string, previousValue: string) {
+        if (this.props.onChange) {
+            this.props.onChange(newValue);
+            return;
+        }
+
         if (!this.props.onPropertyChangedObservable) {
             return;
         }
+
         this.props.onPropertyChangedObservable.notifyObservers({
             object: this.props.target,
-            property: this.props.propertyName,
+            property: this.props.propertyName!,
             value: newValue,
             initialValue: previousValue
         });
@@ -53,11 +61,14 @@ export class TextInputLineComponent extends React.Component<ITextInputLineCompon
     updateValue(value: string) {
 
         this._localChange = true;
-        const store = this.props.target[this.props.propertyName];
+        const store = this.props.value || this.props.target[this.props.propertyName!];
         this.setState({ value: value });
 
         this.raiseOnPropertyChanged(value, store);
-        this.props.target[this.props.propertyName] = value;
+
+        if (this.props.propertyName) {
+            this.props.target[this.props.propertyName] = value;
+        }
     }
 
     render() {

+ 4 - 0
inspector/src/components/actionTabs/lines/textureLineComponent.tsx

@@ -20,6 +20,10 @@ export class TextureLineComponent extends React.Component<ITextureLineComponentP
         };
     }
 
+    shouldComponentUpdate(nextProps: ITextureLineComponentProps): boolean {
+        return (nextProps.texture !== this.props.texture);
+    }
+
     componentDidMount() {
         this.updatePreview();
     }

+ 12 - 12
inspector/src/components/actionTabs/lines/textureLinkLineComponent.tsx

@@ -21,7 +21,7 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
         const material = this.props.material;
         const texture = this.props.texture;
 
-        this.state = { isDebugSelected: material && material.metadata && material.metadata.debugTexture === texture };
+        this.state = { isDebugSelected: material && material.reservedDataStore && material.reservedDataStore.debugTexture === texture };
     }
 
     componentWillMount() {
@@ -50,8 +50,8 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
         }
         const scene = material.getScene();
 
-        if (material.metadata && material.metadata.debugTexture === texture) {
-            const debugMaterial = material.metadata.debugMaterial;
+        if (material.reservedDataStore && material.reservedDataStore.debugTexture === texture) {
+            const debugMaterial = material.reservedDataStore.debugMaterial;
 
             for (var mesh of scene.meshes) {
                 if (mesh.material === debugMaterial) {
@@ -59,8 +59,8 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
                 }
             }
             debugMaterial.dispose();
-            material.metadata.debugTexture = null;
-            material.metadata.debugMaterial = null;
+            material.reservedDataStore.debugTexture = null;
+            material.reservedDataStore.debugMaterial = null;
 
             this.setState({ isDebugSelected: false });
             return;
@@ -68,8 +68,8 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
 
         let checkMaterial = material;
         let needToDisposeCheckMaterial = false;
-        if (material.metadata && material.metadata.debugTexture) {
-            checkMaterial = material.metadata.debugMaterial;
+        if (material.reservedDataStore && material.reservedDataStore.debugTexture) {
+            checkMaterial = material.reservedDataStore.debugMaterial;
             needToDisposeCheckMaterial = true;
         }
 
@@ -78,7 +78,7 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
         debugMaterial.sideOrientation = material.sideOrientation;
         debugMaterial.emissiveTexture = texture!;
         debugMaterial.forceDepthWrite = true;
-        debugMaterial.metadata = { hidden: true };
+        debugMaterial.reservedDataStore = { hidden: true };
 
         for (var mesh of scene.meshes) {
             if (mesh.material === checkMaterial) {
@@ -86,12 +86,12 @@ export class TextureLinkLineComponent extends React.Component<ITextureLinkLineCo
             }
         }
 
-        if (!material.metadata) {
-            material.metadata = {};
+        if (!material.reservedDataStore) {
+            material.reservedDataStore = {};
         }
 
-        material.metadata.debugTexture = texture;
-        material.metadata.debugMaterial = debugMaterial;
+        material.reservedDataStore.debugTexture = texture;
+        material.reservedDataStore.debugMaterial = debugMaterial;
 
         if (this.props.onDebugSelectionChangeObservable) {
             this.props.onDebugSelectionChangeObservable.notifyObservers(texture!);

+ 14 - 14
inspector/src/components/actionTabs/tabs/debugTabComponent.tsx

@@ -21,18 +21,18 @@ export class DebugTabComponent extends PaneComponent {
             return;
         }
 
-        if (!scene.metadata) {
-            scene.metadata = {};
+        if (!scene.reservedDataStore) {
+            scene.reservedDataStore = {};
         }
 
         for (var mesh of scene.meshes) {
-            if (mesh.skeleton && mesh.metadata && mesh.metadata.skeletonViewer) {
-                this._skeletonViewers.push(mesh.metadata.skeletonViewer);
+            if (mesh.skeleton && mesh.reservedDataStore && mesh.reservedDataStore.skeletonViewer) {
+                this._skeletonViewers.push(mesh.reservedDataStore.skeletonViewer);
             }
         }
 
         this._skeletonViewersEnabled = (this._skeletonViewers.length > 0);
-        this._physicsViewersEnabled = scene.metadata.physicsViewer != null;
+        this._physicsViewersEnabled = scene.reservedDataStore.physicsViewer != null;
     }
 
     componentWillUnmount() {
@@ -58,15 +58,15 @@ export class DebugTabComponent extends PaneComponent {
                     var viewer = new SkeletonViewer(mesh.skeleton, mesh, scene, true, 0);
                     viewer.isEnabled = true;
                     this._skeletonViewers.push(viewer);
-                    if (!mesh.metadata) {
-                        mesh.metadata = {};
+                    if (!mesh.reservedDataStore) {
+                        mesh.reservedDataStore = {};
                     }
-                    mesh.metadata.skeletonViewer = viewer;
+                    mesh.reservedDataStore.skeletonViewer = viewer;
                 }
             }
         } else {
             for (var index = 0; index < this._skeletonViewers.length; index++) {
-                this._skeletonViewers[index].mesh.metadata.skeletonViewer = null;
+                this._skeletonViewers[index].mesh.reservedDataStore.skeletonViewer = null;
                 this._skeletonViewers[index].dispose();
             }
             this._skeletonViewers = [];
@@ -80,21 +80,21 @@ export class DebugTabComponent extends PaneComponent {
 
         if (this._physicsViewersEnabled) {
             const physicsViewer = new PhysicsViewer(scene);
-            scene.metadata.physicsViewer = physicsViewer;
+            scene.reservedDataStore.physicsViewer = physicsViewer;
 
             for (var mesh of scene.meshes) {
                 if (mesh.physicsImpostor) {
                     let debugMesh = physicsViewer.showImpostor(mesh.physicsImpostor);
 
                     if (debugMesh) {
-                        debugMesh.metadata = { hidden: true };
-                        debugMesh.material!.metadata = { hidden: true };
+                        debugMesh.reservedDataStore = { hidden: true };
+                        debugMesh.material!.reservedDataStore = { hidden: true };
                     }
                 }
             }
         } else {
-            scene.metadata.physicsViewer.dispose();
-            scene.metadata.physicsViewer = null;
+            scene.reservedDataStore.physicsViewer.dispose();
+            scene.reservedDataStore.physicsViewer = null;
         }
     }
 

+ 65 - 1
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -19,10 +19,18 @@ import { TextBlockPropertyGridComponent } from "./propertyGrids/gui/textBlockPro
 import { TextBlock } from "babylonjs-gui/2D/controls/textBlock";
 import { InputText } from "babylonjs-gui/2D/controls/inputText";
 import { InputTextPropertyGridComponent } from "./propertyGrids/gui/inputTextPropertyGridComponent";
-import { ColorPicker } from "babylonjs-gui";
+import { ColorPicker, Image, Slider, ImageBasedSlider, Rectangle, Ellipse, Checkbox, RadioButton, Line } from "babylonjs-gui";
 import { ColorPickerPropertyGridComponent } from "./propertyGrids/gui/colorPickerPropertyGridComponent";
 import { AnimationGroupGridComponent } from "./propertyGrids/animationGroupPropertyGridComponent";
 import { LockObject } from "./propertyGrids/lockObject";
+import { ImagePropertyGridComponent } from "./propertyGrids/gui/imagePropertyGridComponent";
+import { SliderPropertyGridComponent } from "./propertyGrids/gui/sliderPropertyGridComponent";
+import { ImageBasedSliderPropertyGridComponent } from "./propertyGrids/gui/imageBasedSliderPropertyGridComponent";
+import { RectanglePropertyGridComponent } from "./propertyGrids/gui/rectanglePropertyGridComponent";
+import { EllipsePropertyGridComponent } from "./propertyGrids/gui/ellipsePropertyGridComponent";
+import { CheckboxPropertyGridComponent } from "./propertyGrids/gui/checkboxPropertyGridComponent";
+import { RadioButtonPropertyGridComponent } from "./propertyGrids/gui/radioButtonPropertyGridComponent";
+import { LinePropertyGridComponent } from "./propertyGrids/gui/linePropertyGridComponent";
 
 export class PropertyGridTabComponent extends PaneComponent {
     private _timerIntervalId: number;
@@ -178,6 +186,62 @@ export class PropertyGridTabComponent extends PaneComponent {
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
+            if (className === "Image") {
+                const image = entity as Image;
+                return (<ImagePropertyGridComponent image={image}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Slider") {
+                const slider = entity as Slider;
+                return (<SliderPropertyGridComponent slider={slider}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "ImageBasedSlider") {
+                const imageBasedSlider = entity as ImageBasedSlider;
+                return (<ImageBasedSliderPropertyGridComponent imageBasedSlider={imageBasedSlider}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Rectangle") {
+                const rectangle = entity as Rectangle;
+                return (<RectanglePropertyGridComponent rectangle={rectangle}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Ellipse") {
+                const ellipse = entity as Ellipse;
+                return (<EllipsePropertyGridComponent ellipse={ellipse}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Checkbox") {
+                const checkbox = entity as Checkbox;
+                return (<CheckboxPropertyGridComponent checkbox={checkbox}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "RadioButton") {
+                const radioButton = entity as RadioButton;
+                return (<RadioButtonPropertyGridComponent radioButton={radioButton}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
+            if (className === "Line") {
+                const line = entity as Line;
+                return (<LinePropertyGridComponent line={line}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }
+
             if (entity._host) {
                 const control = entity as Control;
                 return (<ControlPropertyGridComponent control={control}

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

@@ -18,7 +18,7 @@ export class GridPropertyGridComponent extends React.Component<IGridPropertyGrid
         const scene = UtilityLayerRenderer.DefaultKeepDepthUtilityLayer.utilityLayerScene;
 
         for (var mesh of scene.meshes) {
-            if (mesh.metadata && mesh.metadata.isInspectorGrid) {
+            if (mesh.reservedDataStore && mesh.reservedDataStore.isInspectorGrid) {
                 this._gridMesh = mesh;
                 this.setState({ isEnabled: true });
                 return;
@@ -43,12 +43,12 @@ export class GridPropertyGridComponent extends React.Component<IGridPropertyGrid
             var depth = (extend.max.z - extend.min.z) * 5.0;
 
             this._gridMesh = Mesh.CreateGround("grid", 1.0, 1.0, 1, scene);
-            if (!this._gridMesh.metadata) {
-                this._gridMesh.metadata = {};
+            if (!this._gridMesh.reservedDataStore) {
+                this._gridMesh.reservedDataStore = {};
             }
             this._gridMesh.scaling.x = Math.max(width, depth);
             this._gridMesh.scaling.z = this._gridMesh.scaling.x;
-            this._gridMesh.metadata.isInspectorGrid = true;
+            this._gridMesh.reservedDataStore.isInspectorGrid = true;
             this._gridMesh.isPickable = false;
 
             var groundMaterial = new (BABYLON as any).GridMaterial("GridMaterial", scene);

+ 35 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/checkboxPropertyGridComponent.tsx

@@ -0,0 +1,35 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Checkbox } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+
+interface ICheckboxPropertyGridComponentProps {
+    checkbox: Checkbox,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class CheckboxPropertyGridComponent extends React.Component<ICheckboxPropertyGridComponentProps> {
+    constructor(props: ICheckboxPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const checkbox = this.props.checkbox;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={checkbox} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="CHECKBOX">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Check size ratio" target={checkbox} propertyName="checkSizeRatio" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Checked" target={checkbox} propertyName="isChecked" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 31 - 2
inspector/src/components/actionTabs/tabs/propertyGrids/gui/commonControlPropertyGridComponent.tsx

@@ -8,6 +8,7 @@ import { SliderLineComponent } from "../../../lines/sliderLineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
 import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
 import { LockObject } from "../lockObject";
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
 
 interface ICommonControlPropertyGridComponentProps {
     control: Control;
@@ -23,13 +24,35 @@ export class CommonControlPropertyGridComponent extends React.Component<ICommonC
     render() {
         const control = this.props.control;
 
+        var horizontalOptions = [
+            { label: "Left", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT },
+            { label: "Right", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT },
+            { label: "Center", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER },
+        ];
+
+        var verticalOptions = [
+            { label: "Top", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP },
+            { label: "Bottom", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM },
+            { label: "Center", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER },
+        ];
+
         return (
             <div>
                 <LineContainerComponent title="GENERAL">
                     <TextLineComponent label="Class" value={control.getClassName()} />
                     <SliderLineComponent label="Alpha" target={control} propertyName="alpha" minimum={0} maximum={1} step={0.01} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent lockObject={this.props.lockObject} label="Color" target={control} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                    <TextInputLineComponent lockObject={this.props.lockObject} label="Background" target={control} propertyName="background" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    {
+                        control.color &&
+                        <TextInputLineComponent lockObject={this.props.lockObject} label="Color" target={control} propertyName="color" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                    {
+                        (control as any).background !== undefined &&
+                        <TextInputLineComponent lockObject={this.props.lockObject} label="Background" target={control} propertyName="background" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    }
+                </LineContainerComponent>
+                <LineContainerComponent title="ALIGNMENT">
+                    <OptionsLineComponent label="Horizontal" options={horizontalOptions} target={control} propertyName="horizontalAlignment" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Vertical" options={verticalOptions} target={control} propertyName="verticalAlignment" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
                 <LineContainerComponent title="POSITION">
                     <TextInputLineComponent lockObject={this.props.lockObject} label="Left" target={control} propertyName="left" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
@@ -54,6 +77,12 @@ export class CommonControlPropertyGridComponent extends React.Component<ICommonC
                     <TextInputLineComponent lockObject={this.props.lockObject} label="Weight" target={control} propertyName="fontWeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     <TextInputLineComponent lockObject={this.props.lockObject} label="Style" target={control} propertyName="fontStyle" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
+                <LineContainerComponent title="SHADOWS" closed={true}>
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Color" target={control} propertyName="shadowColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Offset X" target={control} propertyName="shadowOffsetX" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Offset Y" target={control} propertyName="shadowOffsetY" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Blur" target={control} propertyName="shadowBlur" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
             </div>
         );
     }

+ 33 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/ellipsePropertyGridComponent.tsx

@@ -0,0 +1,33 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Ellipse } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+
+interface IEllipsePropertyGridComponentProps {
+    ellipse: Ellipse,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class EllipsePropertyGridComponent extends React.Component<IEllipsePropertyGridComponentProps> {
+    constructor(props: IEllipsePropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const ellipse = this.props.ellipse;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={ellipse} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="ELLIPSE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={ellipse} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 42 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/imageBasedSliderPropertyGridComponent.tsx

@@ -0,0 +1,42 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { LockObject } from "../lockObject";
+import { ImageBasedSlider } from "babylonjs-gui";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface IImageBasedSliderPropertyGridComponentProps {
+    imageBasedSlider: ImageBasedSlider,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class ImageBasedSliderPropertyGridComponent extends React.Component<IImageBasedSliderPropertyGridComponentProps> {
+    constructor(props: IImageBasedSliderPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const imageBasedSlider = this.props.imageBasedSlider;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={imageBasedSlider} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="IMAGE BASED SLIDER">
+                    <CheckBoxLineComponent label="Display thumb" target={imageBasedSlider} propertyName="displayThumb" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Vertical" target={imageBasedSlider} propertyName="isVertical" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Thumb clamped" target={imageBasedSlider} propertyName="isThumbClamped" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar offset" target={imageBasedSlider} propertyName="barOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Thumb width" target={imageBasedSlider} propertyName="thumbWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Minimum" target={imageBasedSlider} propertyName="minimum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Maximum" target={imageBasedSlider} propertyName="maximum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Value" target={imageBasedSlider} propertyName="value" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 52 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/imagePropertyGridComponent.tsx

@@ -0,0 +1,52 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { LockObject } from "../lockObject";
+import { Image } from "babylonjs-gui";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+
+interface IImagePropertyGridComponentProps {
+    image: Image,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class ImagePropertyGridComponent extends React.Component<IImagePropertyGridComponentProps> {
+    constructor(props: IImagePropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const image = this.props.image;
+
+        var stretchOptions = [
+            { label: "None", value: BABYLON.GUI.Image.STRETCH_NONE },
+            { label: "Fill", value: BABYLON.GUI.Image.STRETCH_FILL },
+            { label: "Uniform", value: BABYLON.GUI.Image.STRETCH_UNIFORM },
+            { label: "Extend", value: BABYLON.GUI.Image.STRETCH_EXTEND }
+        ];
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={image} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="IMAGE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source left" target={image} propertyName="sourceLeft" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source top" target={image} propertyName="sourceTop" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source width" target={image} propertyName="sourceWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Source height" target={image} propertyName="sourceHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Autoscale" target={image} propertyName="autoScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Stretch" options={stretchOptions} target={image} propertyName="stretch" onPropertyChangedObservable={this.props.onPropertyChangedObservable} onSelect={value => this.setState({ mode: value })} />
+                </LineContainerComponent>
+                <LineContainerComponent title="ANIMATION SHEET">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Cell Id" isInteger={true} target={image} propertyName="cellId" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Cell width" target={image} propertyName="cellWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Cell height" target={image} propertyName="cellHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 55 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/linePropertyGridComponent.tsx

@@ -0,0 +1,55 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Line } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface ILinePropertyGridComponentProps {
+    line: Line,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class LinePropertyGridComponent extends React.Component<ILinePropertyGridComponentProps> {
+    constructor(props: ILinePropertyGridComponentProps) {
+        super(props);
+    }
+
+    onDashChange(value: string) {
+        const line = this.props.line;
+        const split = value.split(",");
+        line.dash = [];
+
+        split.forEach(v => {
+            const int = parseInt(v);
+
+            if (isNaN(int)) {
+                return;
+            }
+
+            line.dash.push(int);
+        });
+    }
+
+    render() {
+        const line = this.props.line;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={line} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="LINE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Line width" target={line} propertyName="lineWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="X1" target={line} propertyName="x1" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Y1" target={line} propertyName="y1" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="X2" target={line} propertyName="x2" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Y2" target={line} propertyName="y2" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Dash pattern" target={line} value={line.dash.join(",")} onChange={newValue => this.onDashChange(newValue)} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 38 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/radioButtonPropertyGridComponent.tsx

@@ -0,0 +1,38 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { RadioButton } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+
+interface IRadioButtonPropertyGridComponentProps {
+    radioButton: RadioButton,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class RadioButtonPropertyGridComponent extends React.Component<IRadioButtonPropertyGridComponentProps> {
+    constructor(props: IRadioButtonPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const radioButton = this.props.radioButton;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={radioButton} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="RADIO BUTTON">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={radioButton} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Check size ratio" target={radioButton} propertyName="checkSizeRatio" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Group" target={radioButton} propertyName="group" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Checked" target={radioButton} propertyName="isChecked" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 34 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/rectanglePropertyGridComponent.tsx

@@ -0,0 +1,34 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { Rectangle } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+
+interface IRectanglePropertyGridComponentProps {
+    rectangle: Rectangle,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class RectanglePropertyGridComponent extends React.Component<IRectanglePropertyGridComponentProps> {
+    constructor(props: IRectanglePropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const rectangle = this.props.rectangle;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={rectangle} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="RECTANGLE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={rectangle} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Corner radius" target={rectangle} propertyName="cornerRadius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 44 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/sliderPropertyGridComponent.tsx

@@ -0,0 +1,44 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { LockObject } from "../lockObject";
+import { Slider } from "babylonjs-gui";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface ISliderPropertyGridComponentProps {
+    slider: Slider,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class SliderPropertyGridComponent extends React.Component<ISliderPropertyGridComponentProps> {
+    constructor(props: ISliderPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const slider = this.props.slider;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={slider} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="SLIDER">
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Border color" target={slider} propertyName="borderColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Display thumb" target={slider} propertyName="displayThumb" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Thumb circle" target={slider} propertyName="isThumbCircle" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Vertical" target={slider} propertyName="isVertical" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Thumb clamped" target={slider} propertyName="isThumbClamped" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar offset" target={slider} propertyName="barOffset" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Thumb width" target={slider} propertyName="thumbWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Minimum" target={slider} propertyName="minimum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Maximum" target={slider} propertyName="maximum" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Value" target={slider} propertyName="value" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}

+ 30 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/textBlockPropertyGridComponent.tsx

@@ -6,6 +6,9 @@ import { TextBlock } from "babylonjs-gui";
 import { LineContainerComponent } from "../../../lineContainerComponent";
 import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
 import { LockObject } from "../lockObject";
+import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
+import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
 
 interface ITextBlockPropertyGridComponentProps {
     textBlock: TextBlock;
@@ -21,11 +24,38 @@ export class TextBlockPropertyGridComponent extends React.Component<ITextBlockPr
     render() {
         const textBlock = this.props.textBlock;
 
+        var horizontalOptions = [
+            { label: "Left", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_LEFT },
+            { label: "Right", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_RIGHT },
+            { label: "Center", value: BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER },
+        ];
+
+        var verticalOptions = [
+            { label: "Top", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_TOP },
+            { label: "Bottom", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_BOTTOM },
+            { label: "Center", value: BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER },
+        ];
+
+        var wrappingOptions = [
+            { label: "Clip", value: BABYLON.GUI.TextWrapping.Clip },
+            { label: "Ellipsis", value: BABYLON.GUI.TextWrapping.Ellipsis },
+            { label: "Word wrap", value: BABYLON.GUI.TextWrapping.WordWrap },
+        ];
+
         return (
             <div className="pane">
                 <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={textBlock} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 <LineContainerComponent title="TEXTBLOCK">
                     <TextInputLineComponent lockObject={this.props.lockObject} label="Text" target={textBlock} propertyName="text" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Horizontal text alignment" options={horizontalOptions} target={textBlock} propertyName="textHorizontalAlignment" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Vertical text alignment" options={verticalOptions} target={textBlock} propertyName="textVerticalAlignment" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <CheckBoxLineComponent label="Resize to fit" target={textBlock} propertyName="resizeToFit" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <OptionsLineComponent label="Wrapping" options={wrappingOptions} target={textBlock} propertyName="textWrapping" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Line spacing" target={textBlock} propertyName="lineSpacing" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent title="OUTLINE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Outline width" target={textBlock} propertyName="outlineWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Outline color" target={textBlock} propertyName="outlineColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                 </LineContainerComponent>
             </div>
         );

+ 7 - 9
inspector/src/components/actionTabs/tabs/propertyGrids/materials/texturePropertyGridComponent.tsx

@@ -7,7 +7,6 @@ import { TextLineComponent } from "../../../lines/textLineComponent";
 import { CheckBoxLineComponent } from "../../../lines/checkBoxLineComponent";
 import { TextureLineComponent } from "../../../lines/textureLineComponent";
 import { FloatLineComponent } from "../../../lines/floatLineComponent";
-import { AdvancedDynamicTexture } from "babylonjs-gui";
 import { OptionsLineComponent } from "../../../lines/optionsLineComponent";
 import { FileButtonLineComponent } from "../../../lines/fileButtonLineComponent";
 import { LockObject } from "../lockObject";
@@ -47,7 +46,6 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
 
     render() {
         const texture = this.props.texture;
-        const adtTexture = texture instanceof AdvancedDynamicTexture ? texture as AdvancedDynamicTexture : null;
 
         var samplingMode = [
             { label: "Nearest", value: Texture.NEAREST_NEAREST },
@@ -76,14 +74,14 @@ export class TexturePropertyGridComponent extends React.Component<ITextureProper
                     }
                 </LineContainerComponent>
                 {
-                    adtTexture &&
+                    (texture as any).rootContainer &&
                     <LineContainerComponent title="ADVANCED TEXTURE PROPERTIES">
-                        <SliderLineComponent label="Render scale" minimum={0.1} maximum={5} step={0.1} target={adtTexture} propertyName="renderScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <CheckBoxLineComponent label="Premultiply alpha" target={adtTexture} propertyName="premulAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal width" target={adtTexture} propertyName="idealWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal height" target={adtTexture} propertyName="idealHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <CheckBoxLineComponent label="Use smallest ideal" target={adtTexture} propertyName="useSmallestIdeal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
-                        <CheckBoxLineComponent label="Render at ideal size" target={adtTexture} propertyName="renderAtIdealSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <SliderLineComponent label="Render scale" minimum={0.1} maximum={5} step={0.1} target={texture} propertyName="renderScale" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <CheckBoxLineComponent label="Premultiply alpha" target={texture} propertyName="premulAlpha" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal width" target={texture} propertyName="idealWidth" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <FloatLineComponent lockObject={this.props.lockObject} label="Ideal height" target={texture} propertyName="idealHeight" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <CheckBoxLineComponent label="Use smallest ideal" target={texture} propertyName="useSmallestIdeal" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                        <CheckBoxLineComponent label="Render at ideal size" target={texture} propertyName="renderAtIdealSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
                     </LineContainerComponent>
                 }
                 <LineContainerComponent title="TRANSFORM">

+ 16 - 16
inspector/src/components/actionTabs/tabs/propertyGrids/meshes/axesViewerComponent.tsx

@@ -11,23 +11,23 @@ export class AxesViewerComponent extends React.Component<IAxisViewerComponentPro
         super(props);
         const node = this.props.node;
 
-        if (!node.metadata) {
-            node.metadata = {};
+        if (!node.reservedDataStore) {
+            node.reservedDataStore = {};
         }
 
-        this.state = { displayAxis: (node.metadata && node.metadata.axisViewer) ? true : false };
+        this.state = { displayAxis: (node.reservedDataStore && node.reservedDataStore.axisViewer) ? true : false };
     }
 
     displayAxes() {
         const node = this.props.node;
         const scene = node.getScene();
 
-        if (node.metadata.axisViewer) {
-            node.metadata.axisViewer.dispose();
-            node.metadata.axisViewer = null;
+        if (node.reservedDataStore.axisViewer) {
+            node.reservedDataStore.axisViewer.dispose();
+            node.reservedDataStore.axisViewer = null;
 
-            scene.onBeforeRenderObservable.remove(node.metadata.onBeforeRenderObserver);
-            node.metadata.onBeforeRenderObserver = null;
+            scene.onBeforeRenderObservable.remove(node.reservedDataStore.onBeforeRenderObserver);
+            node.reservedDataStore.onBeforeRenderObserver = null;
 
             this.setState({ displayAxis: false });
 
@@ -35,16 +35,16 @@ export class AxesViewerComponent extends React.Component<IAxisViewerComponentPro
         }
 
         const viewer = new AxesViewer(scene);
-        node.metadata.axisViewer = viewer;
-        const x = new Vector3(1, 0, 0);
-        const y = new Vector3(0, 1, 0);
-        const z = new Vector3(0, 0, 1);
+        node.reservedDataStore.axisViewer = viewer;
+        const x = new BABYLON.Vector3(1, 0, 0);
+        const y = new BABYLON.Vector3(0, 1, 0);
+        const z = new BABYLON.Vector3(0, 0, 1);
 
-        viewer.xAxisMesh!.metadata = { hidden: true };
-        viewer.yAxisMesh!.metadata = { hidden: true };
-        viewer.zAxisMesh!.metadata = { hidden: true };
+        viewer.xAxisMesh!.reservedDataStore = { hidden: true };
+        viewer.yAxisMesh!.reservedDataStore = { hidden: true };
+        viewer.zAxisMesh!.reservedDataStore = { hidden: true };
 
-        node.metadata.onBeforeRenderObserver = scene.onBeforeRenderObservable.add(() => {
+        node.reservedDataStore.onBeforeRenderObserver = scene.onBeforeRenderObservable.add(() => {
             let matrix = node.getWorldMatrix();
             let extend = Tmp.Vector3[0];
             const worldExtend = scene.getWorldExtends();

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

@@ -23,16 +23,16 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         super(props);
         const mesh = this.props.mesh;
 
-        this.state = { displayNormals: false, renderNormalVectors: mesh.metadata && mesh.metadata.normalLines };
+        this.state = { displayNormals: false, renderNormalVectors: mesh.reservedDataStore && mesh.reservedDataStore.normalLines };
     }
 
     renderNormalVectors() {
         const mesh = this.props.mesh;
         const scene = mesh.getScene();
 
-        if (mesh.metadata && mesh.metadata.normalLines) {
-            mesh.metadata.normalLines.dispose();
-            mesh.metadata.normalLines = null;
+        if (mesh.reservedDataStore && mesh.reservedDataStore.normalLines) {
+            mesh.reservedDataStore.normalLines.dispose();
+            mesh.reservedDataStore.normalLines = null;
 
             this.setState({ renderNormalVectors: false });
             return;
@@ -55,11 +55,11 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         normalLines.color = color;
         normalLines.parent = mesh;
 
-        if (!mesh.metadata) {
-            mesh.metadata = {};
+        if (!mesh.reservedDataStore) {
+            mesh.reservedDataStore = {};
         }
 
-        mesh.metadata.normalLines = normalLines;
+        mesh.reservedDataStore.normalLines = normalLines;
 
         this.setState({ renderNormalVectors: true });
     }
@@ -74,8 +74,8 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         if (mesh.material.getClassName() === "NormalMaterial") {
             mesh.material.dispose();
 
-            mesh.material = mesh.metadata.originalMaterial;
-            mesh.metadata.originalMaterial = null;
+            mesh.material = mesh.reservedDataStore.originalMaterial;
+            mesh.reservedDataStore.originalMaterial = null;
             this.setState({ displayNormals: false });
         } else {
 
@@ -87,15 +87,15 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
                 return;
             }
 
-            if (!mesh.metadata) {
-                mesh.metadata = {};
+            if (!mesh.reservedDataStore) {
+                mesh.reservedDataStore = {};
             }
 
-            mesh.metadata.originalMaterial = mesh.material;
+            mesh.reservedDataStore.originalMaterial = mesh.material;
             const normalMaterial = new (BABYLON as any).NormalMaterial("normalMaterial", scene);
             normalMaterial.disableLighting = true;
             normalMaterial.sideOrientation = mesh.material.sideOrientation;
-            normalMaterial.metadata = { hidden: true };
+            normalMaterial.reservedDataStore = { hidden: true };
             mesh.material = normalMaterial;
             this.setState({ displayNormals: true });
         }
@@ -139,7 +139,7 @@ export class MeshPropertyGridComponent extends React.Component<IMeshPropertyGrid
         const scene = mesh.getScene();
 
         const displayNormals = mesh.material != null && mesh.material.getClassName() === "NormalMaterial";
-        const renderNormalVectors = (mesh.metadata && mesh.metadata.normalLines) ? true : false;
+        const renderNormalVectors = (mesh.reservedDataStore && mesh.reservedDataStore.normalLines) ? true : false;
 
         return (
             <div className="pane">

+ 4 - 2
inspector/src/components/embedHost/embedHostComponent.tsx

@@ -14,6 +14,8 @@ interface IEmbedHostComponentProps {
     scene: Scene,
     globalState: GlobalState,
     popupMode: boolean,
+    noClose?: boolean,
+    noExpand?: boolean,
     onClose: () => void,
     onPopup: () => void
 }
@@ -80,7 +82,7 @@ export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps
         if (this.props.popupMode) {
             return (
                 <div id="embed">
-                    <HeaderComponent title="INSPECTOR" handleBack={true} onClose={() => this.props.onClose()} onPopup={() => this.props.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
+                    <HeaderComponent title="INSPECTOR" noClose={this.props.noClose} noExpand={this.props.noExpand} handleBack={true} onClose={() => this.props.onClose()} onPopup={() => this.props.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
                     {this.renderContent()}
                 </div>
             );
@@ -100,7 +102,7 @@ export class EmbedHostComponent extends React.Component<IEmbedHostComponentProps
 
         return (
             <Resizable id="embed" minWidth={300} maxWidth={600} size={{ height: "100%" }} minHeight="100%" enable={{ top: false, right: false, bottom: false, left: true, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }}>
-                <HeaderComponent title="INSPECTOR" handleBack={true} onClose={() => this.props.onClose()} onPopup={() => this.props.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
+                <HeaderComponent title="INSPECTOR" noClose={this.props.noClose} noExpand={this.props.noExpand} handleBack={true} onClose={() => this.props.onClose()} onPopup={() => this.props.onPopup()} onSelectionChangedObservable={this.props.globalState.onSelectionChangedObservable} />
                 {this.renderContent()}
             </Resizable>
         );

+ 2 - 1
inspector/src/components/headerComponent.tsx

@@ -7,6 +7,7 @@ export interface IHeaderComponentProps {
     title: string,
     handleBack?: boolean,
     noExpand?: boolean,
+    noClose?: boolean,
     noCommands?: boolean,
     onPopup: () => void,
     onClose: () => void,
@@ -89,7 +90,7 @@ export class HeaderComponent extends React.Component<IHeaderComponentProps, { is
                         </div>
                     }
                     {
-                        !this.props.noCommands &&
+                        !this.props.noCommands && !this.props.noClose &&
                         <div className="close" onClick={() => this.props.onClose()}>
                             <FontAwesomeIcon icon={faTimes} />
                         </div>

+ 25 - 25
inspector/src/components/sceneExplorer/entities/meshTreeItemComponent.tsx

@@ -19,7 +19,7 @@ export class MeshTreeItemComponent extends React.Component<IMeshTreeItemComponen
 
         const mesh = this.props.mesh;
 
-        this.state = { isGizmoEnabled: mesh.metadata && mesh.metadata.gizmo, isVisible: this.props.mesh.isVisible };
+        this.state = { isGizmoEnabled: mesh.reservedDataStore && mesh.reservedDataStore.gizmo, isVisible: this.props.mesh.isVisible };
     }
 
     showGizmos(): void {
@@ -27,60 +27,60 @@ export class MeshTreeItemComponent extends React.Component<IMeshTreeItemComponen
 
         if (!this.state.isGizmoEnabled) {
 
-            if (!mesh.metadata) {
-                mesh.metadata = {};
+            if (!mesh.reservedDataStore) {
+                mesh.reservedDataStore = {};
             }
-            mesh.metadata.previousParent = mesh.parent;
+            mesh.reservedDataStore.previousParent = mesh.parent;
 
-            if (mesh.metadata.previousParent) {
-                if (!mesh.metadata.previousParent.metadata) {
-                    mesh.metadata.previousParent.metadata = {};
+            if (mesh.reservedDataStore.previousParent) {
+                if (!mesh.reservedDataStore.previousParent.reservedDataStore) {
+                    mesh.reservedDataStore.previousParent.reservedDataStore = {};
                 }
 
-                if (!mesh.metadata.previousParent.metadata.detachedChildren) {
-                    mesh.metadata.previousParent.metadata.detachedChildren = [];
+                if (!mesh.reservedDataStore.previousParent.reservedDataStore.detachedChildren) {
+                    mesh.reservedDataStore.previousParent.reservedDataStore.detachedChildren = [];
                 }
 
-                mesh.metadata.previousParent.metadata.detachedChildren.push(mesh);
+                mesh.reservedDataStore.previousParent.reservedDataStore.detachedChildren.push(mesh);
             }
 
             // Connect to gizmo
             const dummy = BoundingBoxGizmo.MakeNotPickableAndWrapInBoundingBox(mesh as Mesh);
-            dummy.metadata = { hidden: true };
-            const gizmo = new BoundingBoxGizmo(Color3.FromHexString("#0984e3"));
+            dummy.reservedDataStore = { hidden: true };
+            const gizmo = new BoundingBoxGizmo(BABYLON.Color3.FromHexString("#0984e3"));
             gizmo.attachedMesh = dummy;
 
             gizmo.updateBoundingBox();
 
             gizmo.fixedDragMeshScreenSize = true;
-            mesh.metadata.gizmo = gizmo;
+            mesh.reservedDataStore.gizmo = gizmo;
 
             var pointerDragBehavior = new PointerDragBehavior();
             pointerDragBehavior.useObjectOrienationForDragging = false;
 
             dummy.addBehavior(pointerDragBehavior);
 
-            mesh.metadata.pointerDragBehavior = pointerDragBehavior;
-            mesh.metadata.dummy = dummy;
+            mesh.reservedDataStore.pointerDragBehavior = pointerDragBehavior;
+            mesh.reservedDataStore.dummy = dummy;
 
             this.setState({ isGizmoEnabled: true });
             return;
         }
 
-        const previousParent = mesh.metadata.previousParent;
-        mesh.removeBehavior(mesh.metadata.pointerDragBehavior);
-        mesh.metadata.gizmo.dispose();
-        mesh.metadata.gizmo = null;
+        const previousParent = mesh.reservedDataStore.previousParent;
+        mesh.removeBehavior(mesh.reservedDataStore.pointerDragBehavior);
+        mesh.reservedDataStore.gizmo.dispose();
+        mesh.reservedDataStore.gizmo = null;
         mesh.setParent(previousParent);
-        mesh.metadata.dummy.dispose();
-        mesh.metadata.dummy = null;
+        mesh.reservedDataStore.dummy.dispose();
+        mesh.reservedDataStore.dummy = null;
 
-        if (previousParent && previousParent.metadata) {
-            previousParent.metadata.detachedChildren = null;
+        if (previousParent && previousParent.reservedDataStore) {
+            previousParent.reservedDataStore.detachedChildren = null;
         }
 
-        mesh.metadata.previousParent = null;
-        mesh.metadata.pointerDragBehavior = null;
+        mesh.reservedDataStore.previousParent = null;
+        mesh.reservedDataStore.pointerDragBehavior = null;
 
         this.setState({ isGizmoEnabled: false });
     }

+ 10 - 10
inspector/src/components/sceneExplorer/entities/sceneTreeItemComponent.tsx

@@ -22,8 +22,8 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
 
         const scene = this.props.scene;
         let gizmoMode = 0;
-        if (scene.metadata && scene.metadata.gizmoManager) {
-            const manager: GizmoManager = scene.metadata.gizmoManager;
+        if (scene.reservedDataStore && scene.reservedDataStore.gizmoManager) {
+            const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
             if (manager.positionGizmoEnabled) {
                 gizmoMode = 1;
             } else if (manager.rotationGizmoEnabled) {
@@ -57,8 +57,8 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
         const scene = this.props.scene;
         this._onSelectionChangeObserver = this.props.onSelectionChangedObservable.add((entity) => {
             this._selectedEntity = entity;
-            if (scene.metadata && scene.metadata.gizmoManager) {
-                const manager: GizmoManager = scene.metadata.gizmoManager;
+            if (scene.reservedDataStore && scene.reservedDataStore.gizmoManager) {
+                const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
 
                 const className = entity.getClassName();
 
@@ -115,15 +115,15 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
     setGizmoMode(mode: number) {
         const scene = this.props.scene;
 
-        if (!scene.metadata) {
-            scene.metadata = {};
+        if (!scene.reservedDataStore) {
+            scene.reservedDataStore = {};
         }
 
-        if (!scene.metadata.gizmoManager) {
-            scene.metadata.gizmoManager = new GizmoManager(scene);
+        if (!scene.reservedDataStore.gizmoManager) {
+            scene.reservedDataStore.gizmoManager = new GizmoManager(scene);
         }
 
-        const manager: GizmoManager = scene.metadata.gizmoManager;
+        const manager: GizmoManager = scene.reservedDataStore.gizmoManager;
 
         manager.positionGizmoEnabled = false;
         manager.rotationGizmoEnabled = false;
@@ -132,7 +132,7 @@ export class SceneTreeItemComponent extends React.Component<ISceneTreeItemCompon
         if (this.state.gizmoMode === mode) {
             mode = 0;
             manager.dispose();
-            scene.metadata.gizmoManager = null;
+            scene.reservedDataStore.gizmoManager = null;
         } else {
             switch (mode) {
                 case 1:

+ 10 - 9
inspector/src/components/sceneExplorer/sceneExplorerComponent.tsx

@@ -33,6 +33,7 @@ interface ISceneExplorerComponentProps {
     noCommands?: boolean;
     noHeader?: boolean;
     noExpand?: boolean;
+    noClose?: boolean;
     extensibilityGroups?: IExplorerExtensibilityGroup[];
     globalState: GlobalState;
     popupMode?: boolean;
@@ -126,7 +127,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 data.previousOne = item;
             }
 
-            if (item.getChildren && item.metadata && item.metadata.isExpanded) {
+            if (item.getChildren && item.reservedDataStore && item.reservedDataStore.isExpanded) {
                 if (this.findSiblings(item, item.getChildren(), target, goNext, data)) {
                     return true;
                 }
@@ -152,16 +153,16 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             goNext = true;
             search = true;
         } else if (keyEvent.keyCode === 13 || keyEvent.keyCode === 39) { // enter or right
-            var metadata = this.state.selectedEntity.metadata;
-            if (metadata && metadata.setExpandedState) {
-                metadata.setExpandedState(true);
+            var reservedDataStore = this.state.selectedEntity.reservedDataStore;
+            if (reservedDataStore && reservedDataStore.setExpandedState) {
+                reservedDataStore.setExpandedState(true);
             }
             keyEvent.preventDefault();
             return;
         } else if (keyEvent.keyCode === 37) { // left
-            var metadata = this.state.selectedEntity.metadata;
-            if (metadata && metadata.setExpandedState) {
-                metadata.setExpandedState(false);
+            var reservedDataStore = this.state.selectedEntity.reservedDataStore;
+            if (reservedDataStore && reservedDataStore.setExpandedState) {
+                reservedDataStore.setExpandedState(false);
             }
             keyEvent.preventDefault();
             return;
@@ -234,7 +235,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
                 <div id="sceneExplorer">
                     {
                         !this.props.noHeader &&
-                        <HeaderComponent title="SCENE EXPLORER" noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} />
+                        <HeaderComponent title="SCENE EXPLORER" noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} />
                     }
                     {this.renderContent()}
                 </div>
@@ -273,7 +274,7 @@ export class SceneExplorerComponent extends React.Component<ISceneExplorerCompon
             <Resizable tabIndex={-1} id="sceneExplorer" ref="sceneExplorer" size={{ height: "100%" }} minWidth={300} maxWidth={600} minHeight="100%" enable={{ top: false, right: true, bottom: false, left: false, topRight: false, bottomRight: false, bottomLeft: false, topLeft: false }} onKeyDown={(keyEvent) => this.processKeys(keyEvent)}>
                 {
                     !this.props.noHeader &&
-                    <HeaderComponent title="SCENE EXPLORER" noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} />
+                    <HeaderComponent title="SCENE EXPLORER" noClose={this.props.noClose} noExpand={this.props.noExpand} noCommands={this.props.noCommands} onClose={() => this.onClose()} onPopup={() => this.onPopup()} />
                 }
                 {this.renderContent()}
             </Resizable>

+ 2 - 2
inspector/src/components/sceneExplorer/treeItemComponent.tsx

@@ -30,7 +30,7 @@ class TreeItemExpandableHeaderComponent extends React.Component<ITreeItemExpanda
                 <div className="text">
                     <div className="arrow icon" onClick={() => this.props.onClick()}>
                         {chevron}
-                    </div> 
+                    </div>
                     <div className="text-value">
                         {this.props.label}
                     </div>
@@ -113,7 +113,7 @@ export class TreeItemComponent extends React.Component<ITreeItemComponentProps,
     }
 
     expandAll(expand: boolean) {
-        this.setState({isExpanded: expand, mustExpand: expand});
+        this.setState({ isExpanded: expand, mustExpand: expand });
     }
 
     render() {

+ 4 - 4
inspector/src/components/sceneExplorer/treeItemSelectableComponent.tsx

@@ -108,14 +108,14 @@ export class TreeItemSelectableComponent extends React.Component<ITreeItemSelect
         const children = Tools.SortAndFilter(entity, entity.getChildren ? entity.getChildren() : entity.children);
         const hasChildren = children.length > 0;
 
-        if (!entity.metadata) {
-            entity.metadata = {};
+        if (!entity.reservedDataStore) {
+            entity.reservedDataStore = {};
         }
 
-        entity.metadata.setExpandedState = (value: boolean) => {
+        entity.reservedDataStore.setExpandedState = (value: boolean) => {
             this.setState({ isExpanded: value });
         }
-        entity.metadata.isExpanded = this.state.isExpanded;
+        entity.reservedDataStore.isExpanded = this.state.isExpanded;
 
         if (this.props.filter) {
             const lowerCaseFilter = this.props.filter.toLowerCase();

+ 10 - 2
inspector/src/inspector.ts

@@ -69,6 +69,7 @@ export class Inspector {
                 embedMode: options.embedMode,
                 handleResize: options.handleResize,
                 enablePopup: options.enablePopup,
+                enableClose: options.enablePopup,
                 explorerExtensibility: options.explorerExtensibility
             };
         }
@@ -98,6 +99,7 @@ export class Inspector {
             const sceneExplorerElement = React.createElement(SceneExplorerComponent, {
                 scene, globalState: this._GlobalState,
                 extensibilityGroups: options.explorerExtensibility,
+                noClose: !options.enableClose,
                 noExpand: !options.enablePopup, popupMode: options.popup, onPopup: () => {
                     ReactDOM.unmountComponentAtNode(this._SceneExplorerHost!);
 
@@ -152,7 +154,9 @@ export class Inspector {
         if (this._ActionTabsHost) {
             this._OpenedPane++;
             const actionTabsElement = React.createElement(ActionTabsComponent, {
-                globalState: this._GlobalState, scene: scene, noExpand: !options.enablePopup, popupMode: options.popup, onPopup: () => {
+                globalState: this._GlobalState, scene: scene,
+                noClose: !options.enableClose,
+                noExpand: !options.enablePopup, popupMode: options.popup, onPopup: () => {
                     ReactDOM.unmountComponentAtNode(this._ActionTabsHost!);
 
                     this._RemoveElementFromDOM(this._ActionTabsHost);
@@ -205,7 +209,10 @@ export class Inspector {
         if (this._EmbedHost) {
             this._OpenedPane++;
             const embedHostElement = React.createElement(EmbedHostComponent, {
-                globalState: this._GlobalState, scene: scene, popupMode: options.popup, onPopup: () => {
+                globalState: this._GlobalState, scene: scene,
+                    noExpand: !options.enablePopup,
+                    noClose: !options.enableClose,
+                    popupMode: options.popup, onPopup: () => {
                     ReactDOM.unmountComponentAtNode(this._EmbedHost!);
 
                     if (options.popup) {
@@ -300,6 +307,7 @@ export class Inspector {
             showExplorer: true,
             showInspector: true,
             embedMode: false,
+            enableClose: true,
             handleResize: true,
             enablePopup: true,
             ...userOptions

+ 3 - 3
inspector/src/tools.ts

@@ -21,10 +21,10 @@ export class Tools {
             return [];
         }
 
-        const finalArray = items.filter((i) => !i.metadata || !i.metadata.hidden);
+        const finalArray = items.filter((i) => !i.reservedDataStore || !i.reservedDataStore.hidden);
 
-        if (parent && parent.metadata && parent.metadata.detachedChildren) {
-            finalArray.push(...parent.metadata.detachedChildren);
+        if (parent && parent.reservedDataStore && parent.reservedDataStore.detachedChildren) {
+            finalArray.push(...parent.reservedDataStore.detachedChildren);
         }
 
         return finalArray.sort((a: any, b: any) => {

+ 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.8",
+    "version": "4.0.0-alpha.9",
     "repository": {
         "type": "git",
         "url": "https://github.com/BabylonJS/Babylon.js.git"

+ 3 - 1
readme.md

@@ -2,9 +2,11 @@
 
 Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.
 
+[![npm version](https://badge.fury.io/js/babylonjs.svg)](https://badge.fury.io/js/babylonjs)
 [![Build Status](https://travis-ci.com/BabylonJS/Babylon.js.svg?branch=master)](https://travis-ci.com/BabylonJS/Babylon.js)
-[![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/babylonJS/babylon.js.svg)](https://isitmaintained.com/project/babylonJS/babylon.js "Average time to resolve an issue")
+[![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/BabylonJS/Babylon.js.svg)](http://isitmaintained.com/project/BabylonJS/Babylon.js "Average time to resolve an issue")
 [![Percentage of issues still open](https://isitmaintained.com/badge/open/babylonJS/babylon.js.svg)](https://isitmaintained.com/project/babylonJS/babylon.js "Percentage of issues still open")
+[![Build Size](https://img.badgesize.io/BabylonJS/Babylon.js/master/dist/preview%20release/babylon.js.svg?compression=gzip)](https://img.badgesize.io/BabylonJS/Babylon.js/master/dist/preview%20release/babylon.js.svg?compression=gzip)
 
 **Any questions?** Here is our official [forum](http://www.html5gamedevs.com/forum/16-babylonjs/) on www.html5gamedevs.com.
 

+ 6 - 4
sandbox/index.js

@@ -315,17 +315,19 @@ if (BABYLON.Engine.isSupported()) {
         if (event.keyCode === 32 && event.target.nodeName !== "INPUT") {
             if (footer.style.display === "none") {
                 footer.style.display = "block";
-                canvas.style.height = "calc(100% - 56px)";
+                canvasZone.style.height = "calc(100% - 56px)";
+                if (debugLayerEnabled) {
+                    currentScene.debugLayer.show();
+                }
                 engine.resize();
             }
             else {
                 footer.style.display = "none";
-                canvas.style.height = "100%";
+                canvasZone.style.height = "100%";
                 errorZone.style.display = "none";
                 engine.resize();
-                if (debugLayerEnabled) {
+                if (currentScene.debugLayer.isVisible()) {
                     currentScene.debugLayer.hide();
-                    debugLayerEnabled = false;
                 }
             }
         }

+ 1 - 0
src/Cameras/XR/webXRCamera.ts

@@ -34,6 +34,7 @@ import { WebXRSessionManager } from "./webXRSessionManager";
                 newCamera.minZ = 0;
                 newCamera.parent = this;
                 newCamera.rotationQuaternion = new Quaternion();
+                newCamera.updateUpVectorFromRotation = true;
                 this.rigCameras.push(newCamera);
             }
             while (this.rigCameras.length > viewCount) {

+ 1 - 0
src/Cameras/camera.ts

@@ -1104,6 +1104,7 @@ declare type TargetCamera = import("./targetCamera").TargetCamera;
                 this._rigCameras[i].minZ = this.minZ;
                 this._rigCameras[i].maxZ = this.maxZ;
                 this._rigCameras[i].fov = this.fov;
+                this._rigCameras[i].upVector.copyFrom(this.upVector);
             }
 
             // only update viewport when ANAGLYPH

+ 17 - 9
src/Cameras/targetCamera.ts

@@ -11,6 +11,7 @@ import { Quaternion, Matrix, Vector3, Vector2, Epsilon, Tmp, Axis } from "Maths/
     export class TargetCamera extends Camera {
         private static _RigCamTransformMatrix = new Matrix();
         private static _TargetTransformMatrix = new Matrix();
+        private static _TargetFocalPoint = new Vector3();
 
         /**
          * Define the current direction the camera is moving to
@@ -58,6 +59,8 @@ import { Quaternion, Matrix, Vector3, Vector2, Epsilon, Tmp, Axis } from "Maths/
         /** @hidden */
         public _currentTarget = Vector3.Zero();
         /** @hidden */
+        public _initialFocalDistance = 1;
+        /** @hidden */
         public _viewMatrix = Matrix.Zero();
         /** @hidden */
         public _camMatrix = Matrix.Zero();
@@ -217,6 +220,8 @@ import { Quaternion, Matrix, Vector3, Vector2, Epsilon, Tmp, Axis } from "Maths/
         public setTarget(target: Vector3): void {
             this.upVector.normalize();
 
+            this._initialFocalDistance = target.subtract(this.position).length();
+
             if (this.position.z === target.z) {
                 this.position.z += Epsilon;
             }
@@ -450,11 +455,8 @@ import { Quaternion, Matrix, Vector3, Vector2, Epsilon, Tmp, Axis } from "Maths/
                     //provisionnaly using _cameraRigParams.stereoHalfAngle instead of calculations based on _cameraRigParams.interaxialDistance:
                     var leftSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? 1 : -1;
                     var rightSign = (this.cameraRigMode === Camera.RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED) ? -1 : 1;
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft.position);
-                    this._getRigCamPosition(this._cameraRigParams.stereoHalfAngle * rightSign, camRight.position);
-
-                    camLeft.setTarget(this.getTarget());
-                    camRight.setTarget(this.getTarget());
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * leftSign, camLeft);
+                    this._getRigCamPositionAndTarget(this._cameraRigParams.stereoHalfAngle * rightSign, camRight);
                     break;
 
                 case Camera.RIG_MODE_VR:
@@ -473,15 +475,21 @@ import { Quaternion, Matrix, Vector3, Vector2, Epsilon, Tmp, Axis } from "Maths/
             super._updateRigCameras();
         }
 
-        private _getRigCamPosition(halfSpace: number, result: Vector3) {
+        private _getRigCamPositionAndTarget(halfSpace: number, rigCamera: TargetCamera) {
             var target = this.getTarget();
-            Matrix.TranslationToRef(-target.x, -target.y, -target.z, TargetCamera._TargetTransformMatrix);
+            target.subtractToRef(this.position, TargetCamera._TargetFocalPoint);
+
+            TargetCamera._TargetFocalPoint.normalize().scaleInPlace(this._initialFocalDistance);
+            var newFocalTarget = TargetCamera._TargetFocalPoint.addInPlace(this.position);
+
+            Matrix.TranslationToRef(-newFocalTarget.x, -newFocalTarget.y, -newFocalTarget.z, TargetCamera._TargetTransformMatrix);
             TargetCamera._TargetTransformMatrix.multiplyToRef(Matrix.RotationY(halfSpace), TargetCamera._RigCamTransformMatrix);
-            Matrix.TranslationToRef(target.x, target.y, target.z, TargetCamera._TargetTransformMatrix);
+            Matrix.TranslationToRef(newFocalTarget.x, newFocalTarget.y, newFocalTarget.z, TargetCamera._TargetTransformMatrix);
 
             TargetCamera._RigCamTransformMatrix.multiplyToRef(TargetCamera._TargetTransformMatrix, TargetCamera._RigCamTransformMatrix);
 
-            Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, result);
+            Vector3.TransformCoordinatesToRef(this.position, TargetCamera._RigCamTransformMatrix, rigCamera.position);
+            rigCamera.setTarget(newFocalTarget);
         }
 
         /**

+ 4 - 0
src/Debug/debugLayer.ts

@@ -67,6 +67,10 @@ import { Scene } from "scene";
          */
         enablePopup?: boolean;
         /**
+         * Allow the panes to be closed by users (default: true)
+         */
+        enableClose?: boolean;
+        /**
          * Optional list of extensibility entries
          */
         explorerExtensibility?: IExplorerExtensibilityGroup[];

+ 1 - 1
src/Engines/engine.ts

@@ -516,7 +516,7 @@ declare type RenderTargetTexture = import("Materials/Textures/renderTargetTextur
          * Returns the current version of the framework
          */
         public static get Version(): string {
-            return "4.0.0-alpha.8";
+            return "4.0.0-alpha.9";
         }
 
         /**

+ 1 - 1
src/Helpers/sceneHelpers.ts

@@ -190,7 +190,7 @@ declare module "scene" {
             hdrSkybox.infiniteDistance = true;
             hdrSkybox.material = skyboxMaterial;
         }
-
+        hdrSkybox.isPickable = false;
         return hdrSkybox;
     };
 

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

@@ -44,6 +44,11 @@ declare type Animation = import("Animations/animation").Animation;
         @serialize()
         public metadata: any = null;
 
+        /**
+         * For internal use only. Please do not use.
+         */
+        public reservedDataStore: any = null;
+
         @serialize("hasAlpha")
         private _hasAlpha = false;
         /**
@@ -311,8 +316,8 @@ declare type Animation = import("Animations/animation").Animation;
         constructor(scene: Nullable<Scene>) {
             this._scene = scene || Engine.LastCreatedScene;
             if (this._scene) {
-                this._scene.addTexture(this);
                 this.uniqueId = this._scene.getUniqueId();
+                this._scene.addTexture(this);
             }
             this._uid = null;
         }
@@ -414,30 +419,30 @@ declare type Animation = import("Animations/animation").Animation;
             return new Size(this._texture.baseWidth, this._texture.baseHeight);
         }
 
-  /**
-         * Update the sampling mode of the texture.
-         * Default is Trilinear mode.
-         *
-         * | Value | Type               | Description |
-         * | ----- | ------------------ | ----------- |
-         * | 1     | NEAREST_SAMPLINGMODE or NEAREST_NEAREST_MIPLINEAR  | Nearest is: mag = nearest, min = nearest, mip = linear |
-         * | 2     | BILINEAR_SAMPLINGMODE or LINEAR_LINEAR_MIPNEAREST | Bilinear is: mag = linear, min = linear, mip = nearest |
-         * | 3     | TRILINEAR_SAMPLINGMODE or LINEAR_LINEAR_MIPLINEAR | Trilinear is: mag = linear, min = linear, mip = linear |
-         * | 4     | NEAREST_NEAREST_MIPNEAREST |             |
-         * | 5    | NEAREST_LINEAR_MIPNEAREST |             |
-         * | 6    | NEAREST_LINEAR_MIPLINEAR |             |
-         * | 7    | NEAREST_LINEAR |             |
-         * | 8    | NEAREST_NEAREST |             |
-         * | 9   | LINEAR_NEAREST_MIPNEAREST |             |
-         * | 10   | LINEAR_NEAREST_MIPLINEAR |             |
-         * | 11   | LINEAR_LINEAR |             |
-         * | 12   | LINEAR_NEAREST |             |
-         *
-         *    > _mag_: magnification filter (close to the viewer)
-         *    > _min_: minification filter (far from the viewer)
-         *    > _mip_: filter used between mip map levels
-         *@param samplingMode Define the new sampling mode of the texture
-         */
+        /**
+               * Update the sampling mode of the texture.
+               * Default is Trilinear mode.
+               *
+               * | Value | Type               | Description |
+               * | ----- | ------------------ | ----------- |
+               * | 1     | NEAREST_SAMPLINGMODE or NEAREST_NEAREST_MIPLINEAR  | Nearest is: mag = nearest, min = nearest, mip = linear |
+               * | 2     | BILINEAR_SAMPLINGMODE or LINEAR_LINEAR_MIPNEAREST | Bilinear is: mag = linear, min = linear, mip = nearest |
+               * | 3     | TRILINEAR_SAMPLINGMODE or LINEAR_LINEAR_MIPLINEAR | Trilinear is: mag = linear, min = linear, mip = linear |
+               * | 4     | NEAREST_NEAREST_MIPNEAREST |             |
+               * | 5    | NEAREST_LINEAR_MIPNEAREST |             |
+               * | 6    | NEAREST_LINEAR_MIPLINEAR |             |
+               * | 7    | NEAREST_LINEAR |             |
+               * | 8    | NEAREST_NEAREST |             |
+               * | 9   | LINEAR_NEAREST_MIPNEAREST |             |
+               * | 10   | LINEAR_NEAREST_MIPLINEAR |             |
+               * | 11   | LINEAR_LINEAR |             |
+               * | 12   | LINEAR_NEAREST |             |
+               *
+               *    > _mag_: magnification filter (close to the viewer)
+               *    > _min_: minification filter (far from the viewer)
+               *    > _mip_: filter used between mip map levels
+               *@param samplingMode Define the new sampling mode of the texture
+               */
         public updateSamplingMode(samplingMode: number): void {
             if (!this._texture) {
                 return;

+ 5 - 0
src/Materials/material.ts

@@ -178,6 +178,11 @@ declare var BABYLON: any;
         public metadata: any = null;
 
         /**
+         * For internal use only. Please do not use.
+         */
+        public reservedDataStore: any = null;
+
+        /**
          * Specifies if the ready state should be checked on each call
          */
         @serialize()

+ 1 - 4
src/Meshes/meshBuilder.ts

@@ -975,10 +975,7 @@ Mesh.CreateDecal = (name: string, sourceMesh: AbstractMesh, position: Vector3, n
 
             if (options.sourcePlane) {
                 plane.translate(options.sourcePlane.normal, -options.sourcePlane.d);
-
-                const dot = Vector3.Dot(plane.position, options.sourcePlane.normal);
-                const flip = dot >= 0;
-                plane.lookAt(Vector3.Zero(), 0, flip ? Math.PI : 0, 0);
+                plane.setDirection(options.sourcePlane.normal);
             }
 
             return plane;

+ 24 - 11
src/Meshes/transformNode.ts

@@ -465,17 +465,7 @@ import { Bone } from "Bones/bone";
             var dv = TransformNode._lookAtVectorCache;
             var pos = space === Space.LOCAL ? this.position : this.getAbsolutePosition();
             targetPoint.subtractToRef(pos, dv);
-            var yaw = -Math.atan2(dv.z, dv.x) - Math.PI / 2;
-            var len = Math.sqrt(dv.x * dv.x + dv.z * dv.z);
-            var pitch = Math.atan2(dv.y, len);
-            if (this.rotationQuaternion) {
-                Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
-            }
-            else {
-                this.rotation.x = pitch + pitchCor;
-                this.rotation.y = yaw + yawCor;
-                this.rotation.z = rollCor;
-            }
+            this.setDirection(dv, yawCor, pitchCor, rollCor);
 
             // Correct for parent's rotation offset
             if (space === Space.WORLD && this.parent) {
@@ -538,6 +528,29 @@ import { Bone } from "Bones/bone";
         }
 
         /**
+         * Sets this transform node rotation to the given local axis.
+         * @param localAxis the axis in local space
+         * @param yawCor optional yaw (y-axis) correction in radians
+         * @param pitchCor optional pitch (x-axis) correction in radians
+         * @param rollCor optional roll (z-axis) correction in radians
+         * @returns this TransformNode
+         */
+        public setDirection(localAxis: Vector3, yawCor: number = 0, pitchCor: number = 0, rollCor: number = 0): TransformNode {
+            var yaw = -Math.atan2(localAxis.z, localAxis.x) - Math.PI / 2;
+            var len = Math.sqrt(localAxis.x * localAxis.x + localAxis.z * localAxis.z);
+            var pitch = Math.atan2(localAxis.y, len);
+            if (this.rotationQuaternion) {
+                Quaternion.RotationYawPitchRollToRef(yaw + yawCor, pitch + pitchCor, rollCor, this.rotationQuaternion);
+            }
+            else {
+                this.rotation.x = pitch + pitchCor;
+                this.rotation.y = yaw + yawCor;
+                this.rotation.z = rollCor;
+            }
+            return this;
+        }
+
+        /**
          * Sets a new pivot point to the current node
          * @param point defines the new pivot point to use
          * @param space defines if the point is in world or local space (local by default)

+ 8 - 2
src/node.ts

@@ -87,6 +87,11 @@ declare type AbstractMesh = import("Meshes/abstractMesh").AbstractMesh;
         public metadata: any = null;
 
         /**
+         * For internal use only. Please do not use.
+         */
+        public reservedDataStore: any = null;
+
+        /**
          * Gets or sets a boolean used to define if the node must be serialized
          */
         public doNotSerialize = false;
@@ -156,7 +161,7 @@ declare type AbstractMesh = import("Meshes/abstractMesh").AbstractMesh;
                     this._parentNode._children.splice(index, 1);
                 }
 
-                if (!parent) {
+                if (!parent && !this._isDisposed) {
                     this.addToSceneRootNodes();
                 }
             }
@@ -695,6 +700,8 @@ declare type AbstractMesh = import("Meshes/abstractMesh").AbstractMesh;
          * @param disposeMaterialAndTextures Set to true to also dispose referenced materials and textures (false by default)
          */
         public dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
+            this._isDisposed = true;
+
             if (!doNotRecurse) {
                 const nodes = this.getDescendants(true);
                 for (const node of nodes) {
@@ -718,7 +725,6 @@ declare type AbstractMesh = import("Meshes/abstractMesh").AbstractMesh;
             }
 
             this._behaviors = [];
-            this._isDisposed = true;
         }
 
         /**

+ 6 - 0
src/scene.ts

@@ -311,6 +311,12 @@ import { Logger } from "Misc/logger";
          * Gets or sets user defined metadata
          */
         public metadata: any = null;
+
+        /**
+         * For internal use only. Please do not use.
+         */
+        public reservedDataStore: any = null;
+
         /**
          * Gets the name of the plugin used to load this scene (null by default)
          */

+ 5 - 5
tests/validation/config.json

@@ -2,6 +2,11 @@
   "root": "https://rawgit.com/BabylonJS/Website/master",
   "tests": [
     {
+      "title": "Camera rig",
+      "playgroundId": "#ATL1CS#9",
+      "referenceImage": "cameraRig.png"
+    },
+    {
       "title": "XR camera container rotation",
       "playgroundId": "#JV98QW#5",
       "referenceImage": "xrCameraContainerRotation.png",
@@ -37,11 +42,6 @@
       "referenceImage": "simulatePointer.png"
     },
     {
-      "title": "Camera rig",
-      "playgroundId": "#ATL1CS#9",
-      "referenceImage": "cameraRig.png"
-    },
-    {
       "title": "Color Grading",
       "playgroundId": "#8EDB5N#2",
       "referenceImage": "colorGrading.png"

+ 0 - 1
what's new.md

@@ -204,7 +204,6 @@
 - Spring Joint could not be removed ([TrevorDev](https://github.com/TrevorDev))
 - Sometimes duplicate controller models are loaded in VR ([TrevorDev](https://github.com/TrevorDev))
 - Particle emit rate and start size over time do not reset on every particle system start ([TrevorDev](https://github.com/TrevorDev))
-- Fix position and rotation of plane mesh created by MeshBuilder.CreatePlane when specifying a source plane ([sable](https://github.com/thscott))
 
 ### Core Engine