瀏覽代碼

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

QuentinRillet 7 年之前
父節點
當前提交
43b5be716a
共有 40 個文件被更改,包括 11824 次插入10959 次删除
  1. 2 36
      Playground/css/index.css
  2. 18 27
      Playground/js/index.js
  3. 1 1
      Playground/package.json
  4. 6 2
      Playground/scripts/shadows.js
  5. 4501 4461
      dist/preview release/babylon.d.ts
  6. 42 42
      dist/preview release/babylon.js
  7. 256 58
      dist/preview release/babylon.max.js
  8. 4501 4461
      dist/preview release/babylon.module.d.ts
  9. 43 43
      dist/preview release/babylon.worker.js
  10. 824 778
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  11. 42 42
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  12. 256 58
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  13. 824 778
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  14. 4 0
      dist/preview release/gui/babylon.gui.d.ts
  15. 59 8
      dist/preview release/gui/babylon.gui.js
  16. 2 2
      dist/preview release/gui/babylon.gui.min.js
  17. 4 0
      dist/preview release/gui/babylon.gui.module.d.ts
  18. 5 5
      dist/preview release/inspector/babylon.inspector.bundle.js
  19. 55 76
      dist/preview release/inspector/babylon.inspector.css
  20. 26 0
      dist/preview release/inspector/babylon.inspector.d.ts
  21. 13 4
      dist/preview release/inspector/babylon.inspector.js
  22. 4 4
      dist/preview release/inspector/babylon.inspector.min.js
  23. 3 1
      dist/preview release/what's new.md
  24. 8 0
      gui/src/controls/container.ts
  25. 7 1
      gui/src/controls/control.ts
  26. 44 8
      gui/src/controls/inputText.ts
  27. 26 2
      gui/src/controls/textBlock.ts
  28. 2 1
      sandbox/index.js
  29. 3 3
      src/Cameras/Inputs/babylon.arcRotateCameraMouseWheelInput.ts
  30. 21 8
      src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts
  31. 47 7
      src/Cameras/VR/babylon.vrExperienceHelper.ts
  32. 2 0
      src/Cameras/VR/babylon.webVRCamera.ts
  33. 17 4
      src/Cameras/babylon.arcRotateCamera.ts
  34. 36 0
      src/Lights/Shadows/babylon.shadowGenerator.ts
  35. 10 1
      src/Loading/babylon.sceneLoader.ts
  36. 54 30
      src/Math/babylon.math.ts
  37. 16 3
      src/Mesh/babylon.abstractMesh.ts
  38. 30 0
      src/Tools/babylon.observable.ts
  39. 2 2
      src/Tools/babylon.tools.ts
  40. 8 2
      src/babylon.scene.ts

+ 2 - 36
Playground/css/index.css

@@ -403,41 +403,6 @@ body {
     display: inline-block;
 }
 
-/* MONACO */
-
-.monaco-editor .container:before,
-.monaco-editor .row:before {
-    content: "";
-    display: inherit;
-}
-.monaco-editor .container:after,
-.monaco-editor .row:after {
-    clear: inherit;
-}
-.monaco-editor .container {
-    width: auto;
-    margin: inherit;
-    padding: inherit;
-}
-.monaco-editor .close {
-    float: none;
-    font-size: inherit;
-    font-weight: inherit;
-    line-height: inherit;
-    color: inherit;
-    text-shadow: inherit;
-    opacity: inherit;
-    filter: inherit;
-}
-.monaco-editor .row {
-    margin: inherit;
-}
-.monaco-editor .invisible {
-    visibility: visible;
-}
-.monaco-editor .view-lines {
-    font-family: 'Inconsolata' !important;
-}
 /* Save form & co */
 
 .save-message {
@@ -540,4 +505,5 @@ body {
     .navbar .select .toDisplayBig ul {
         column-count: 1;
     }
-}
+}
+

+ 18 - 27
Playground/js/index.js

@@ -23,6 +23,22 @@
         });
     };
 
+    var editorOptions = {
+        value: "",
+        language: "javascript",
+        lineNumbers: true,
+        tabSize: "auto",
+        insertSpaces: "auto",
+        roundedSelection: true,
+        automaticLayout: true,
+        scrollBeyondLastLine: false,
+        readOnly: false,
+        theme: "vs",
+        contextmenu: false,
+        folding: true,
+        showFoldingControls: "always",
+        renderIndentGuides: true
+    };
 
     var fontSize = 14;
 
@@ -685,19 +701,7 @@
 
             var oldCode = jsEditor.getValue();
             jsEditor.dispose();
-            jsEditor = monaco.editor.create(document.getElementById('jsEditor'), {
-                value: "",
-                language: "javascript",
-                lineNumbers: true,
-                tabSize: "auto",
-                insertSpaces: "auto",
-                roundedSelection: true,
-                scrollBeyondLastLine: false,
-                automaticLayout: true,
-                readOnly: false,
-                theme: vsTheme,
-                contextmenu: false
-            });
+            jsEditor = monaco.editor.create(document.getElementById('jsEditor'), editorOptions);
             jsEditor.setValue(oldCode);
             setFontSize(fontSize);
 
@@ -977,20 +981,7 @@
                 require(['vs/editor/editor.main'], function () {
                     monaco.languages.typescript.javascriptDefaults.addExtraLib(xhr.responseText, 'babylon.d.ts');
 
-                    jsEditor = monaco.editor.create(document.getElementById('jsEditor'), {
-                        value: "",
-                        language: "javascript",
-                        lineNumbers: true,
-                        tabSize: "auto",
-                        insertSpaces: "auto",
-                        roundedSelection: true,
-                        scrollBeyondLastLine: false,
-                        automaticLayout: true,
-                        readOnly: false,
-                        theme: "vs",
-                        contextmenu: false,
-                        folding: true
-                    });
+                    jsEditor = monaco.editor.create(document.getElementById('jsEditor'), editorOptions);
 
                     run();
                 });

+ 1 - 1
Playground/package.json

@@ -9,7 +9,7 @@
   "readme": "https://github.com/BabylonJS/Babylon.js/blob/master/readme.md",
   "license": "(Apache-2.0)",
   "devDependencies": {
-    "monaco-editor": "~0.7.0"
+    "monaco-editor": "~0.10.0"
   },
   "scripts": {
     "test": "browser-sync start --server --files **/* --no-inject-changes --startPath index.html"

+ 6 - 2
Playground/scripts/shadows.js

@@ -42,13 +42,17 @@
 	// Torus
 	var torus = BABYLON.Mesh.CreateTorus("torus", 4, 2, 30, scene, false);
 
+	// Box
+    var box = BABYLON.Mesh.CreateBox("box", 3);
+    box.parent = torus;	
+
 	// Shadows
 	var shadowGenerator = new BABYLON.ShadowGenerator(1024, light);
-	shadowGenerator.getShadowMap().renderList.push(torus);
+	shadowGenerator.addShadowCaster(torus);
 	shadowGenerator.useExponentialShadowMap = true;
 
 	var shadowGenerator2 = new BABYLON.ShadowGenerator(1024, light2);
-	shadowGenerator2.getShadowMap().renderList.push(torus);
+	shadowGenerator2.addShadowCaster(torus);
 	shadowGenerator2.usePoissonSampling = true;
 
 	ground.receiveShadows = true;

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


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


+ 256 - 58
dist/preview release/babylon.max.js

@@ -4184,46 +4184,64 @@ var BABYLON;
             Frustum.GetPlanesToRef(transform, frustumPlanes);
             return frustumPlanes;
         };
+        Frustum.GetNearPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] + transform.m[2];
+            frustumPlane.normal.y = transform.m[7] + transform.m[6];
+            frustumPlane.normal.z = transform.m[11] + transform.m[10];
+            frustumPlane.d = transform.m[15] + transform.m[14];
+            frustumPlane.normalize();
+        };
+        Frustum.GetFarPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] - transform.m[2];
+            frustumPlane.normal.y = transform.m[7] - transform.m[6];
+            frustumPlane.normal.z = transform.m[11] - transform.m[10];
+            frustumPlane.d = transform.m[15] - transform.m[14];
+            frustumPlane.normalize();
+        };
+        Frustum.GetLeftPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] + transform.m[0];
+            frustumPlane.normal.y = transform.m[7] + transform.m[4];
+            frustumPlane.normal.z = transform.m[11] + transform.m[8];
+            frustumPlane.d = transform.m[15] + transform.m[12];
+            frustumPlane.normalize();
+        };
+        Frustum.GetRightPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] - transform.m[0];
+            frustumPlane.normal.y = transform.m[7] - transform.m[4];
+            frustumPlane.normal.z = transform.m[11] - transform.m[8];
+            frustumPlane.d = transform.m[15] - transform.m[12];
+            frustumPlane.normalize();
+        };
+        Frustum.GetTopPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] - transform.m[1];
+            frustumPlane.normal.y = transform.m[7] - transform.m[5];
+            frustumPlane.normal.z = transform.m[11] - transform.m[9];
+            frustumPlane.d = transform.m[15] - transform.m[13];
+            frustumPlane.normalize();
+        };
+        Frustum.GetBottomPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] + transform.m[1];
+            frustumPlane.normal.y = transform.m[7] + transform.m[5];
+            frustumPlane.normal.z = transform.m[11] + transform.m[9];
+            frustumPlane.d = transform.m[15] + transform.m[13];
+            frustumPlane.normalize();
+        };
         /**
          * Sets the passed array "frustumPlanes" with the 6 Frustum planes computed by the passed transformation matrix.
          */
         Frustum.GetPlanesToRef = function (transform, frustumPlanes) {
             // Near
-            frustumPlanes[0].normal.x = transform.m[3] + transform.m[2];
-            frustumPlanes[0].normal.y = transform.m[7] + transform.m[6];
-            frustumPlanes[0].normal.z = transform.m[11] + transform.m[10];
-            frustumPlanes[0].d = transform.m[15] + transform.m[14];
-            frustumPlanes[0].normalize();
+            Frustum.GetNearPlaneToRef(transform, frustumPlanes[0]);
             // Far
-            frustumPlanes[1].normal.x = transform.m[3] - transform.m[2];
-            frustumPlanes[1].normal.y = transform.m[7] - transform.m[6];
-            frustumPlanes[1].normal.z = transform.m[11] - transform.m[10];
-            frustumPlanes[1].d = transform.m[15] - transform.m[14];
-            frustumPlanes[1].normalize();
+            Frustum.GetFarPlaneToRef(transform, frustumPlanes[1]);
             // Left
-            frustumPlanes[2].normal.x = transform.m[3] + transform.m[0];
-            frustumPlanes[2].normal.y = transform.m[7] + transform.m[4];
-            frustumPlanes[2].normal.z = transform.m[11] + transform.m[8];
-            frustumPlanes[2].d = transform.m[15] + transform.m[12];
-            frustumPlanes[2].normalize();
+            Frustum.GetLeftPlaneToRef(transform, frustumPlanes[2]);
             // Right
-            frustumPlanes[3].normal.x = transform.m[3] - transform.m[0];
-            frustumPlanes[3].normal.y = transform.m[7] - transform.m[4];
-            frustumPlanes[3].normal.z = transform.m[11] - transform.m[8];
-            frustumPlanes[3].d = transform.m[15] - transform.m[12];
-            frustumPlanes[3].normalize();
+            Frustum.GetRightPlaneToRef(transform, frustumPlanes[3]);
             // Top
-            frustumPlanes[4].normal.x = transform.m[3] - transform.m[1];
-            frustumPlanes[4].normal.y = transform.m[7] - transform.m[5];
-            frustumPlanes[4].normal.z = transform.m[11] - transform.m[9];
-            frustumPlanes[4].d = transform.m[15] - transform.m[13];
-            frustumPlanes[4].normalize();
+            Frustum.GetTopPlaneToRef(transform, frustumPlanes[4]);
             // Bottom
-            frustumPlanes[5].normal.x = transform.m[3] + transform.m[1];
-            frustumPlanes[5].normal.y = transform.m[7] + transform.m[5];
-            frustumPlanes[5].normal.z = transform.m[11] + transform.m[9];
-            frustumPlanes[5].d = transform.m[15] + transform.m[13];
-            frustumPlanes[5].normalize();
+            Frustum.GetBottomPlaneToRef(transform, frustumPlanes[5]);
         };
         return Frustum;
     }());
@@ -5342,6 +5360,34 @@ var BABYLON;
     }());
     BABYLON.Observer = Observer;
     /**
+     * Represent a list of observers registered to multiple Observables object.
+     */
+    var MultiObserver = (function () {
+        function MultiObserver() {
+        }
+        MultiObserver.prototype.dispose = function () {
+            for (var index = 0; index < this._observers.length; index++) {
+                this._observables[index].remove(this._observers[index]);
+            }
+            this._observers = null;
+            this._observables = null;
+        };
+        MultiObserver.Watch = function (observables, callback, mask, scope) {
+            if (mask === void 0) { mask = -1; }
+            if (scope === void 0) { scope = null; }
+            var result = new MultiObserver();
+            result._observers = new Array();
+            result._observables = observables;
+            for (var _i = 0, observables_1 = observables; _i < observables_1.length; _i++) {
+                var observable = observables_1[_i];
+                result._observers.push(observable.add(callback, mask, false, scope));
+            }
+            return result;
+        };
+        return MultiObserver;
+    }());
+    BABYLON.MultiObserver = MultiObserver;
+    /**
      * The Observable class is a simple implementation of the Observable pattern.
      * There's one slight particularity though: a given Observable can notify its observer using a particular mask value, only the Observers registered with this mask value will be notified.
      * This enable a more fine grained execution without having to rely on multiple different Observable objects.
@@ -13290,7 +13336,7 @@ var BABYLON;
          */
         AbstractMesh.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
             if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
-            this._pivotMatrix = matrix;
+            this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             if (this._postMultiplyPivotMatrix) {
@@ -13649,13 +13695,25 @@ var BABYLON;
         /**
          * True if the mesh intersects another mesh or a SolidParticle object.
          * Unless the parameter `precise` is set to `true` the intersection is computed according to Axis Aligned Bounding Boxes (AABB), else according to OBB (Oriented BBoxes)
+         * includeDescendants can be set to true to test if the mesh defined in parameters intersects with the current mesh or any child meshes
          * Returns a boolean.
          */
-        AbstractMesh.prototype.intersectsMesh = function (mesh, precise) {
+        AbstractMesh.prototype.intersectsMesh = function (mesh, precise, includeDescendants) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
                 return false;
             }
-            return this._boundingInfo.intersects(mesh._boundingInfo, precise);
+            if (this._boundingInfo.intersects(mesh._boundingInfo, precise)) {
+                return true;
+            }
+            if (includeDescendants) {
+                for (var _i = 0, _a = this.getChildMeshes(); _i < _a.length; _i++) {
+                    var child = _a[_i];
+                    if (child.intersectsMesh(mesh, precise, true)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
         };
         /**
          * Returns true if the passed point (Vector3) is inside the mesh bounding box.
@@ -18190,6 +18248,11 @@ var BABYLON;
             else {
                 BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
             }
+            if (this.activeCamera._alternateCamera) {
+                var otherCamera = this.activeCamera._alternateCamera;
+                otherCamera.getViewMatrix().multiplyToRef(otherCamera.getProjectionMatrix(), BABYLON.Tmp.Matrix[0]);
+                BABYLON.Frustum.GetRightPlaneToRef(BABYLON.Tmp.Matrix[0], this._frustumPlanes[3]); // Replace right plane by second camera right plane
+            }
             if (this._sceneUbo.useUbo) {
                 this._sceneUbo.updateMatrix("viewProjection", this._transformMatrix);
                 this._sceneUbo.updateMatrix("view", this._viewMatrix);
@@ -20033,8 +20096,9 @@ var BABYLON;
             }
             return hdrSkybox;
         };
-        Scene.prototype.createDefaultVRExperience = function () {
-            this.VRHelper = new BABYLON.VRExperienceHelper(this, null);
+        Scene.prototype.createDefaultVRExperience = function (webVROptions) {
+            if (webVROptions === void 0) { webVROptions = {}; }
+            this.VRHelper = new BABYLON.VRExperienceHelper(this, webVROptions);
         };
         // Tags
         Scene.prototype._getByTags = function (list, tagsQuery, forEach) {
@@ -35576,6 +35640,11 @@ var BABYLON;
     var ArcRotateCameraMouseWheelInput = (function () {
         function ArcRotateCameraMouseWheelInput() {
             this.wheelPrecision = 3.0;
+            /**
+             * wheelDeltaPercentage will be used instead of wheelPrecision if different from 0.
+             * It defines the percentage of current camera.radius to use as delta when wheel is used.
+             */
+            this.wheelDeltaPercentage = 0;
         }
         ArcRotateCameraMouseWheelInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
@@ -35586,7 +35655,7 @@ var BABYLON;
                 var event = p.event;
                 var delta = 0;
                 if (event.wheelDelta) {
-                    delta = event.wheelDelta / (_this.wheelPrecision * 40);
+                    delta = _this.wheelDeltaPercentage ? (event.wheelDelta * 0.01) * _this.camera.radius * _this.wheelDeltaPercentage : event.wheelDelta / (_this.wheelPrecision * 40);
                 }
                 else if (event.detail) {
                     delta = -event.detail / _this.wheelPrecision;
@@ -35617,6 +35686,9 @@ var BABYLON;
         __decorate([
             BABYLON.serialize()
         ], ArcRotateCameraMouseWheelInput.prototype, "wheelPrecision", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraMouseWheelInput.prototype, "wheelDeltaPercentage", void 0);
         return ArcRotateCameraMouseWheelInput;
     }());
     BABYLON.ArcRotateCameraMouseWheelInput = ArcRotateCameraMouseWheelInput;
@@ -35635,6 +35707,11 @@ var BABYLON;
             this.angularSensibilityX = 1000.0;
             this.angularSensibilityY = 1000.0;
             this.pinchPrecision = 12.0;
+            /**
+             * pinchDeltaPercentage will be used instead of pinchPrecision if different from 0.
+             * It defines the percentage of current camera.radius to use as delta when pinch zoom is used.
+             */
+            this.pinchDeltaPercentage = 0;
             this.panningSensibility = 1000.0;
             this.multiTouchPanning = true;
             this.multiTouchPanAndZoom = true;
@@ -35764,11 +35841,15 @@ var BABYLON;
                             return;
                         }
                         if (_this.multiTouchPanAndZoom) {
-                            _this.camera
-                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
-                                (_this.pinchPrecision *
-                                    ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
-                                    direction);
+                            if (_this.pinchDeltaPercentage) {
+                                _this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * _this.camera.radius * _this.pinchDeltaPercentage;
+                            }
+                            else {
+                                _this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                                    (_this.pinchPrecision *
+                                        ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                        direction);
+                            }
                             if (_this.panningSensibility !== 0) {
                                 var pointersCenterX = (pointA.x + pointB.x) / 2;
                                 var pointersCenterY = (pointA.y + pointB.y) / 2;
@@ -35783,11 +35864,15 @@ var BABYLON;
                         else {
                             twoFingerActivityCount++;
                             if (previousMultiTouchPanPosition.isPinching || (twoFingerActivityCount < 20 && Math.abs(pinchDistance - initialDistance) > _this.camera.pinchToPanMaxDistance)) {
-                                _this.camera
-                                    .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
-                                    (_this.pinchPrecision *
-                                        ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
-                                        direction);
+                                if (_this.pinchDeltaPercentage) {
+                                    _this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * _this.camera.radius * _this.pinchDeltaPercentage;
+                                }
+                                else {
+                                    _this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                                        (_this.pinchPrecision *
+                                            ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                            direction);
+                                }
                                 previousMultiTouchPanPosition.isPaning = false;
                                 previousMultiTouchPanPosition.isPinching = true;
                             }
@@ -35909,6 +35994,9 @@ var BABYLON;
         ], ArcRotateCameraPointersInput.prototype, "pinchPrecision", void 0);
         __decorate([
             BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "pinchDeltaPercentage", void 0);
+        __decorate([
+            BABYLON.serialize()
         ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
         __decorate([
             BABYLON.serialize()
@@ -36069,6 +36157,21 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(ArcRotateCamera.prototype, "pinchDeltaPercentage", {
+            get: function () {
+                var pointers = this.inputs.attached["pointers"];
+                if (pointers)
+                    return pointers.pinchDeltaPercentage;
+            },
+            set: function (value) {
+                var pointers = this.inputs.attached["pointers"];
+                if (pointers) {
+                    pointers.pinchDeltaPercentage = value;
+                }
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(ArcRotateCamera.prototype, "panningSensibility", {
             get: function () {
                 var pointers = this.inputs.attached["pointers"];
@@ -36154,6 +36257,20 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(ArcRotateCamera.prototype, "wheelDeltaPercentage", {
+            get: function () {
+                var mousewheel = this.inputs.attached["mousewheel"];
+                if (mousewheel)
+                    return mousewheel.wheelDeltaPercentage;
+            },
+            set: function (value) {
+                var mousewheel = this.inputs.attached["mousewheel"];
+                if (mousewheel)
+                    mousewheel.wheelDeltaPercentage = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(ArcRotateCamera.prototype, "bouncingBehavior", {
             get: function () {
                 return this._bouncingBehavior;
@@ -48431,6 +48548,39 @@ var BABYLON;
             return this._shadowMap;
         };
         /**
+         * Helper function to add a mesh and its descendants to the list of shadow casters
+         * @param mesh Mesh to add
+         * @param includeDescendants boolean indicating if the descendants should be added. Default to true
+         */
+        ShadowGenerator.prototype.addShadowCaster = function (mesh, includeDescendants) {
+            if (includeDescendants === void 0) { includeDescendants = true; }
+            this._shadowMap.renderList.push(mesh);
+            if (includeDescendants) {
+                (_a = this._shadowMap.renderList).push.apply(_a, mesh.getChildMeshes());
+            }
+            return this;
+            var _a;
+        };
+        /**
+         * Helper function to remove a mesh and its descendants from the list of shadow casters
+         * @param mesh Mesh to remove
+         * @param includeDescendants boolean indicating if the descendants should be removed. Default to true
+         */
+        ShadowGenerator.prototype.removeShadowCaster = function (mesh, includeDescendants) {
+            if (includeDescendants === void 0) { includeDescendants = true; }
+            var index = this._shadowMap.renderList.indexOf(mesh);
+            if (index !== -1) {
+                this._shadowMap.renderList.splice(index, 1);
+            }
+            if (includeDescendants) {
+                for (var _i = 0, _a = mesh.getChildren(); _i < _a.length; _i++) {
+                    var child = _a[_i];
+                    this.removeShadowCaster(child);
+                }
+            }
+            return this;
+        };
+        /**
          * Returns the associated light object.
          */
         ShadowGenerator.prototype.getLight = function () {
@@ -49176,7 +49326,17 @@ var BABYLON;
                 }
             }
             else {
-                BABYLON.Tools.ReadFile(sceneFilename, dataCallback, onProgress, useArrayBuffer);
+                var fileOrString = sceneFilename;
+                if (fileOrString.name) {
+                    BABYLON.Tools.ReadFile(fileOrString, dataCallback, onProgress, useArrayBuffer);
+                }
+                else if (BABYLON.FilesInput.FilesToLoad[sceneFilename]) {
+                    BABYLON.Tools.ReadFile(BABYLON.FilesInput.FilesToLoad[sceneFilename], dataCallback, onProgress, useArrayBuffer);
+                }
+                else {
+                    onError("Unable to find file named " + sceneFilename);
+                    return;
+                }
             }
         };
         // Public functions
@@ -67378,6 +67538,7 @@ var BABYLON;
             _this.deviceScaleFactor = 1;
             _this.controllers = [];
             _this.onControllersAttachedObservable = new BABYLON.Observable();
+            _this.onControllerMeshLoadedObservable = new BABYLON.Observable();
             _this.rigParenting = true; // should the rig cameras be used as parent instead of this camera.
             _this.minZ = 0.1;
             //legacy support - the compensation boolean was removed.
@@ -67622,14 +67783,15 @@ var BABYLON;
             });
             this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add(function (gamepad) {
                 if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
-                    var webVrController = gamepad;
+                    var webVrController_1 = gamepad;
                     if (_this.webVROptions.controllerMeshes) {
-                        if (webVrController.defaultModel) {
-                            webVrController.defaultModel.setEnabled(true);
+                        if (webVrController_1.defaultModel) {
+                            webVrController_1.defaultModel.setEnabled(true);
                         }
                         else {
                             // Load the meshes
-                            webVrController.initControllerMesh(_this.getScene(), function (loadedMesh) {
+                            webVrController_1.initControllerMesh(_this.getScene(), function (loadedMesh) {
+                                _this.onControllerMeshLoadedObservable.notifyObservers(webVrController_1);
                                 if (_this.webVROptions.defaultLightingOnControllers) {
                                     if (!_this._lightOnControllers) {
                                         _this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), _this.getScene());
@@ -67649,11 +67811,11 @@ var BABYLON;
                             });
                         }
                     }
-                    webVrController.attachToPoseControlledCamera(_this);
+                    webVrController_1.attachToPoseControlledCamera(_this);
                     // since this is async - sanity check. Is the controller already stored?
-                    if (_this.controllers.indexOf(webVrController) === -1) {
+                    if (_this.controllers.indexOf(webVrController_1) === -1) {
                         //add to the controllers array
-                        _this.controllers.push(webVrController);
+                        _this.controllers.push(webVrController_1);
                         //did we find enough controllers? Great! let the developer know.
                         if (_this.controllers.length >= 2) {
                             // Forced to add some control code for Vive as it doesn't always fill properly the "hand" property
@@ -67972,10 +68134,14 @@ var BABYLON;
             window.addEventListener("resize", function () {
                 _this._btnVR.style.top = _this._canvas.offsetTop + _this._canvas.offsetHeight - 70 + "px";
                 _this._btnVR.style.left = _this._canvas.offsetLeft + _this._canvas.offsetWidth - 100 + "px";
-                if (_this._fullscreenVRpresenting) {
+                if (_this._fullscreenVRpresenting && _this._webVRready) {
                     _this.exitVR();
                 }
             });
+            document.addEventListener("fullscreenchange", function () { _this._onFullscreenChange(); }, false);
+            document.addEventListener("mozfullscreenchange", function () { _this._onFullscreenChange(); }, false);
+            document.addEventListener("webkitfullscreenchange", function () { _this._onFullscreenChange(); }, false);
+            document.addEventListener("msfullscreenchange", function () { _this._onFullscreenChange(); }, false);
             document.body.appendChild(this._btnVR);
             // Exiting VR mode using 'ESC' key on desktop
             this._onKeyDown = function (event) {
@@ -67988,6 +68154,9 @@ var BABYLON;
             this._scene.onPrePointerObservable.add(function (pointerInfo, eventState) {
                 if (_this.isInVRMode()) {
                     _this.exitVR();
+                    if (_this._fullscreenVRpresenting) {
+                        _this._scene.getEngine().switchFullscreen(true);
+                    }
                 }
             }, BABYLON.PointerEventTypes.POINTERDOUBLETAP, false);
             // Listen for WebVR display changes
@@ -68007,9 +68176,34 @@ var BABYLON;
             window.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
             // Create the cameras
             this._vrDeviceOrientationCamera = new BABYLON.VRDeviceOrientationFreeCamera("VRDeviceOrientationVRHelper", this._position, this._scene);
-            this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene);
+            this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene, webVROptions);
+            this._webVRCamera.onControllerMeshLoadedObservable.add(function (webVRController) { return _this._onDefaultMeshLoaded(webVRController); });
             this.updateButtonVisibility();
         }
+        VRExperienceHelper.prototype._onDefaultMeshLoaded = function (webVRController) {
+            if (this.onControllerMeshLoaded) {
+                this.onControllerMeshLoaded(webVRController);
+            }
+        };
+        VRExperienceHelper.prototype._onFullscreenChange = function () {
+            if (document.fullscreen !== undefined) {
+                this._fullscreenVRpresenting = document.fullscreen;
+            }
+            else if (document.mozFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.mozFullScreen;
+            }
+            else if (document.webkitIsFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.webkitIsFullScreen;
+            }
+            else if (document.msIsFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.msIsFullScreen;
+            }
+            if (!this._fullscreenVRpresenting) {
+                this.exitVR();
+                this._btnVR.style.top = this._canvas.offsetTop + this._canvas.offsetHeight - 70 + "px";
+                this._btnVR.style.left = this._canvas.offsetLeft + this._canvas.offsetWidth - 100 + "px";
+            }
+        };
         VRExperienceHelper.prototype.isInVRMode = function () {
             return this._webVRpresenting || this._fullscreenVRpresenting;
         };
@@ -68055,6 +68249,9 @@ var BABYLON;
          * Otherwise, will use the fullscreen API.
          */
         VRExperienceHelper.prototype.enterVR = function () {
+            if (this.onEnteringVR) {
+                this.onEnteringVR();
+            }
             if (this._webVRrequesting)
                 return;
             // If WebVR is supported and a headset is connected
@@ -68068,7 +68265,6 @@ var BABYLON;
                 this._vrDeviceOrientationCamera.position = this._position;
                 this._scene.activeCamera = this._vrDeviceOrientationCamera;
                 this._scene.getEngine().switchFullscreen(true);
-                this._fullscreenVRpresenting = true;
                 this.updateButtonVisibility();
             }
             this._scene.activeCamera.attachControl(this._canvas);
@@ -68077,6 +68273,9 @@ var BABYLON;
          * Attempt to exit VR, or fullscreen.
          */
         VRExperienceHelper.prototype.exitVR = function () {
+            if (this.onExitingVR) {
+                this.onExitingVR();
+            }
             if (this._webVRpresenting) {
                 this._scene.getEngine().disableVR();
             }
@@ -68086,7 +68285,6 @@ var BABYLON;
             this._deviceOrientationCamera.position = this._position;
             this._scene.activeCamera = this._deviceOrientationCamera;
             this._scene.activeCamera.attachControl(this._canvas);
-            this._fullscreenVRpresenting = false;
             this.updateButtonVisibility();
         };
         Object.defineProperty(VRExperienceHelper.prototype, "position", {

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


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


File diff suppressed because it is too large
+ 824 - 778
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


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


+ 256 - 58
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js

@@ -4184,46 +4184,64 @@ var BABYLON;
             Frustum.GetPlanesToRef(transform, frustumPlanes);
             return frustumPlanes;
         };
+        Frustum.GetNearPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] + transform.m[2];
+            frustumPlane.normal.y = transform.m[7] + transform.m[6];
+            frustumPlane.normal.z = transform.m[11] + transform.m[10];
+            frustumPlane.d = transform.m[15] + transform.m[14];
+            frustumPlane.normalize();
+        };
+        Frustum.GetFarPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] - transform.m[2];
+            frustumPlane.normal.y = transform.m[7] - transform.m[6];
+            frustumPlane.normal.z = transform.m[11] - transform.m[10];
+            frustumPlane.d = transform.m[15] - transform.m[14];
+            frustumPlane.normalize();
+        };
+        Frustum.GetLeftPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] + transform.m[0];
+            frustumPlane.normal.y = transform.m[7] + transform.m[4];
+            frustumPlane.normal.z = transform.m[11] + transform.m[8];
+            frustumPlane.d = transform.m[15] + transform.m[12];
+            frustumPlane.normalize();
+        };
+        Frustum.GetRightPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] - transform.m[0];
+            frustumPlane.normal.y = transform.m[7] - transform.m[4];
+            frustumPlane.normal.z = transform.m[11] - transform.m[8];
+            frustumPlane.d = transform.m[15] - transform.m[12];
+            frustumPlane.normalize();
+        };
+        Frustum.GetTopPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] - transform.m[1];
+            frustumPlane.normal.y = transform.m[7] - transform.m[5];
+            frustumPlane.normal.z = transform.m[11] - transform.m[9];
+            frustumPlane.d = transform.m[15] - transform.m[13];
+            frustumPlane.normalize();
+        };
+        Frustum.GetBottomPlaneToRef = function (transform, frustumPlane) {
+            frustumPlane.normal.x = transform.m[3] + transform.m[1];
+            frustumPlane.normal.y = transform.m[7] + transform.m[5];
+            frustumPlane.normal.z = transform.m[11] + transform.m[9];
+            frustumPlane.d = transform.m[15] + transform.m[13];
+            frustumPlane.normalize();
+        };
         /**
          * Sets the passed array "frustumPlanes" with the 6 Frustum planes computed by the passed transformation matrix.
          */
         Frustum.GetPlanesToRef = function (transform, frustumPlanes) {
             // Near
-            frustumPlanes[0].normal.x = transform.m[3] + transform.m[2];
-            frustumPlanes[0].normal.y = transform.m[7] + transform.m[6];
-            frustumPlanes[0].normal.z = transform.m[11] + transform.m[10];
-            frustumPlanes[0].d = transform.m[15] + transform.m[14];
-            frustumPlanes[0].normalize();
+            Frustum.GetNearPlaneToRef(transform, frustumPlanes[0]);
             // Far
-            frustumPlanes[1].normal.x = transform.m[3] - transform.m[2];
-            frustumPlanes[1].normal.y = transform.m[7] - transform.m[6];
-            frustumPlanes[1].normal.z = transform.m[11] - transform.m[10];
-            frustumPlanes[1].d = transform.m[15] - transform.m[14];
-            frustumPlanes[1].normalize();
+            Frustum.GetFarPlaneToRef(transform, frustumPlanes[1]);
             // Left
-            frustumPlanes[2].normal.x = transform.m[3] + transform.m[0];
-            frustumPlanes[2].normal.y = transform.m[7] + transform.m[4];
-            frustumPlanes[2].normal.z = transform.m[11] + transform.m[8];
-            frustumPlanes[2].d = transform.m[15] + transform.m[12];
-            frustumPlanes[2].normalize();
+            Frustum.GetLeftPlaneToRef(transform, frustumPlanes[2]);
             // Right
-            frustumPlanes[3].normal.x = transform.m[3] - transform.m[0];
-            frustumPlanes[3].normal.y = transform.m[7] - transform.m[4];
-            frustumPlanes[3].normal.z = transform.m[11] - transform.m[8];
-            frustumPlanes[3].d = transform.m[15] - transform.m[12];
-            frustumPlanes[3].normalize();
+            Frustum.GetRightPlaneToRef(transform, frustumPlanes[3]);
             // Top
-            frustumPlanes[4].normal.x = transform.m[3] - transform.m[1];
-            frustumPlanes[4].normal.y = transform.m[7] - transform.m[5];
-            frustumPlanes[4].normal.z = transform.m[11] - transform.m[9];
-            frustumPlanes[4].d = transform.m[15] - transform.m[13];
-            frustumPlanes[4].normalize();
+            Frustum.GetTopPlaneToRef(transform, frustumPlanes[4]);
             // Bottom
-            frustumPlanes[5].normal.x = transform.m[3] + transform.m[1];
-            frustumPlanes[5].normal.y = transform.m[7] + transform.m[5];
-            frustumPlanes[5].normal.z = transform.m[11] + transform.m[9];
-            frustumPlanes[5].d = transform.m[15] + transform.m[13];
-            frustumPlanes[5].normalize();
+            Frustum.GetBottomPlaneToRef(transform, frustumPlanes[5]);
         };
         return Frustum;
     }());
@@ -5342,6 +5360,34 @@ var BABYLON;
     }());
     BABYLON.Observer = Observer;
     /**
+     * Represent a list of observers registered to multiple Observables object.
+     */
+    var MultiObserver = (function () {
+        function MultiObserver() {
+        }
+        MultiObserver.prototype.dispose = function () {
+            for (var index = 0; index < this._observers.length; index++) {
+                this._observables[index].remove(this._observers[index]);
+            }
+            this._observers = null;
+            this._observables = null;
+        };
+        MultiObserver.Watch = function (observables, callback, mask, scope) {
+            if (mask === void 0) { mask = -1; }
+            if (scope === void 0) { scope = null; }
+            var result = new MultiObserver();
+            result._observers = new Array();
+            result._observables = observables;
+            for (var _i = 0, observables_1 = observables; _i < observables_1.length; _i++) {
+                var observable = observables_1[_i];
+                result._observers.push(observable.add(callback, mask, false, scope));
+            }
+            return result;
+        };
+        return MultiObserver;
+    }());
+    BABYLON.MultiObserver = MultiObserver;
+    /**
      * The Observable class is a simple implementation of the Observable pattern.
      * There's one slight particularity though: a given Observable can notify its observer using a particular mask value, only the Observers registered with this mask value will be notified.
      * This enable a more fine grained execution without having to rely on multiple different Observable objects.
@@ -13290,7 +13336,7 @@ var BABYLON;
          */
         AbstractMesh.prototype.setPivotMatrix = function (matrix, postMultiplyPivotMatrix) {
             if (postMultiplyPivotMatrix === void 0) { postMultiplyPivotMatrix = false; }
-            this._pivotMatrix = matrix;
+            this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
             if (this._postMultiplyPivotMatrix) {
@@ -13649,13 +13695,25 @@ var BABYLON;
         /**
          * True if the mesh intersects another mesh or a SolidParticle object.
          * Unless the parameter `precise` is set to `true` the intersection is computed according to Axis Aligned Bounding Boxes (AABB), else according to OBB (Oriented BBoxes)
+         * includeDescendants can be set to true to test if the mesh defined in parameters intersects with the current mesh or any child meshes
          * Returns a boolean.
          */
-        AbstractMesh.prototype.intersectsMesh = function (mesh, precise) {
+        AbstractMesh.prototype.intersectsMesh = function (mesh, precise, includeDescendants) {
             if (!this._boundingInfo || !mesh._boundingInfo) {
                 return false;
             }
-            return this._boundingInfo.intersects(mesh._boundingInfo, precise);
+            if (this._boundingInfo.intersects(mesh._boundingInfo, precise)) {
+                return true;
+            }
+            if (includeDescendants) {
+                for (var _i = 0, _a = this.getChildMeshes(); _i < _a.length; _i++) {
+                    var child = _a[_i];
+                    if (child.intersectsMesh(mesh, precise, true)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
         };
         /**
          * Returns true if the passed point (Vector3) is inside the mesh bounding box.
@@ -18190,6 +18248,11 @@ var BABYLON;
             else {
                 BABYLON.Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
             }
+            if (this.activeCamera._alternateCamera) {
+                var otherCamera = this.activeCamera._alternateCamera;
+                otherCamera.getViewMatrix().multiplyToRef(otherCamera.getProjectionMatrix(), BABYLON.Tmp.Matrix[0]);
+                BABYLON.Frustum.GetRightPlaneToRef(BABYLON.Tmp.Matrix[0], this._frustumPlanes[3]); // Replace right plane by second camera right plane
+            }
             if (this._sceneUbo.useUbo) {
                 this._sceneUbo.updateMatrix("viewProjection", this._transformMatrix);
                 this._sceneUbo.updateMatrix("view", this._viewMatrix);
@@ -20033,8 +20096,9 @@ var BABYLON;
             }
             return hdrSkybox;
         };
-        Scene.prototype.createDefaultVRExperience = function () {
-            this.VRHelper = new BABYLON.VRExperienceHelper(this, null);
+        Scene.prototype.createDefaultVRExperience = function (webVROptions) {
+            if (webVROptions === void 0) { webVROptions = {}; }
+            this.VRHelper = new BABYLON.VRExperienceHelper(this, webVROptions);
         };
         // Tags
         Scene.prototype._getByTags = function (list, tagsQuery, forEach) {
@@ -35576,6 +35640,11 @@ var BABYLON;
     var ArcRotateCameraMouseWheelInput = (function () {
         function ArcRotateCameraMouseWheelInput() {
             this.wheelPrecision = 3.0;
+            /**
+             * wheelDeltaPercentage will be used instead of wheelPrecision if different from 0.
+             * It defines the percentage of current camera.radius to use as delta when wheel is used.
+             */
+            this.wheelDeltaPercentage = 0;
         }
         ArcRotateCameraMouseWheelInput.prototype.attachControl = function (element, noPreventDefault) {
             var _this = this;
@@ -35586,7 +35655,7 @@ var BABYLON;
                 var event = p.event;
                 var delta = 0;
                 if (event.wheelDelta) {
-                    delta = event.wheelDelta / (_this.wheelPrecision * 40);
+                    delta = _this.wheelDeltaPercentage ? (event.wheelDelta * 0.01) * _this.camera.radius * _this.wheelDeltaPercentage : event.wheelDelta / (_this.wheelPrecision * 40);
                 }
                 else if (event.detail) {
                     delta = -event.detail / _this.wheelPrecision;
@@ -35617,6 +35686,9 @@ var BABYLON;
         __decorate([
             BABYLON.serialize()
         ], ArcRotateCameraMouseWheelInput.prototype, "wheelPrecision", void 0);
+        __decorate([
+            BABYLON.serialize()
+        ], ArcRotateCameraMouseWheelInput.prototype, "wheelDeltaPercentage", void 0);
         return ArcRotateCameraMouseWheelInput;
     }());
     BABYLON.ArcRotateCameraMouseWheelInput = ArcRotateCameraMouseWheelInput;
@@ -35635,6 +35707,11 @@ var BABYLON;
             this.angularSensibilityX = 1000.0;
             this.angularSensibilityY = 1000.0;
             this.pinchPrecision = 12.0;
+            /**
+             * pinchDeltaPercentage will be used instead of pinchPrecision if different from 0.
+             * It defines the percentage of current camera.radius to use as delta when pinch zoom is used.
+             */
+            this.pinchDeltaPercentage = 0;
             this.panningSensibility = 1000.0;
             this.multiTouchPanning = true;
             this.multiTouchPanAndZoom = true;
@@ -35764,11 +35841,15 @@ var BABYLON;
                             return;
                         }
                         if (_this.multiTouchPanAndZoom) {
-                            _this.camera
-                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
-                                (_this.pinchPrecision *
-                                    ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
-                                    direction);
+                            if (_this.pinchDeltaPercentage) {
+                                _this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * _this.camera.radius * _this.pinchDeltaPercentage;
+                            }
+                            else {
+                                _this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                                    (_this.pinchPrecision *
+                                        ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                        direction);
+                            }
                             if (_this.panningSensibility !== 0) {
                                 var pointersCenterX = (pointA.x + pointB.x) / 2;
                                 var pointersCenterY = (pointA.y + pointB.y) / 2;
@@ -35783,11 +35864,15 @@ var BABYLON;
                         else {
                             twoFingerActivityCount++;
                             if (previousMultiTouchPanPosition.isPinching || (twoFingerActivityCount < 20 && Math.abs(pinchDistance - initialDistance) > _this.camera.pinchToPanMaxDistance)) {
-                                _this.camera
-                                    .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
-                                    (_this.pinchPrecision *
-                                        ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
-                                        direction);
+                                if (_this.pinchDeltaPercentage) {
+                                    _this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * _this.camera.radius * _this.pinchDeltaPercentage;
+                                }
+                                else {
+                                    _this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                                        (_this.pinchPrecision *
+                                            ((_this.angularSensibilityX + _this.angularSensibilityY) / 2) *
+                                            direction);
+                                }
                                 previousMultiTouchPanPosition.isPaning = false;
                                 previousMultiTouchPanPosition.isPinching = true;
                             }
@@ -35909,6 +35994,9 @@ var BABYLON;
         ], ArcRotateCameraPointersInput.prototype, "pinchPrecision", void 0);
         __decorate([
             BABYLON.serialize()
+        ], ArcRotateCameraPointersInput.prototype, "pinchDeltaPercentage", void 0);
+        __decorate([
+            BABYLON.serialize()
         ], ArcRotateCameraPointersInput.prototype, "panningSensibility", void 0);
         __decorate([
             BABYLON.serialize()
@@ -36069,6 +36157,21 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(ArcRotateCamera.prototype, "pinchDeltaPercentage", {
+            get: function () {
+                var pointers = this.inputs.attached["pointers"];
+                if (pointers)
+                    return pointers.pinchDeltaPercentage;
+            },
+            set: function (value) {
+                var pointers = this.inputs.attached["pointers"];
+                if (pointers) {
+                    pointers.pinchDeltaPercentage = value;
+                }
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(ArcRotateCamera.prototype, "panningSensibility", {
             get: function () {
                 var pointers = this.inputs.attached["pointers"];
@@ -36154,6 +36257,20 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(ArcRotateCamera.prototype, "wheelDeltaPercentage", {
+            get: function () {
+                var mousewheel = this.inputs.attached["mousewheel"];
+                if (mousewheel)
+                    return mousewheel.wheelDeltaPercentage;
+            },
+            set: function (value) {
+                var mousewheel = this.inputs.attached["mousewheel"];
+                if (mousewheel)
+                    mousewheel.wheelDeltaPercentage = value;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(ArcRotateCamera.prototype, "bouncingBehavior", {
             get: function () {
                 return this._bouncingBehavior;
@@ -48431,6 +48548,39 @@ var BABYLON;
             return this._shadowMap;
         };
         /**
+         * Helper function to add a mesh and its descendants to the list of shadow casters
+         * @param mesh Mesh to add
+         * @param includeDescendants boolean indicating if the descendants should be added. Default to true
+         */
+        ShadowGenerator.prototype.addShadowCaster = function (mesh, includeDescendants) {
+            if (includeDescendants === void 0) { includeDescendants = true; }
+            this._shadowMap.renderList.push(mesh);
+            if (includeDescendants) {
+                (_a = this._shadowMap.renderList).push.apply(_a, mesh.getChildMeshes());
+            }
+            return this;
+            var _a;
+        };
+        /**
+         * Helper function to remove a mesh and its descendants from the list of shadow casters
+         * @param mesh Mesh to remove
+         * @param includeDescendants boolean indicating if the descendants should be removed. Default to true
+         */
+        ShadowGenerator.prototype.removeShadowCaster = function (mesh, includeDescendants) {
+            if (includeDescendants === void 0) { includeDescendants = true; }
+            var index = this._shadowMap.renderList.indexOf(mesh);
+            if (index !== -1) {
+                this._shadowMap.renderList.splice(index, 1);
+            }
+            if (includeDescendants) {
+                for (var _i = 0, _a = mesh.getChildren(); _i < _a.length; _i++) {
+                    var child = _a[_i];
+                    this.removeShadowCaster(child);
+                }
+            }
+            return this;
+        };
+        /**
          * Returns the associated light object.
          */
         ShadowGenerator.prototype.getLight = function () {
@@ -49176,7 +49326,17 @@ var BABYLON;
                 }
             }
             else {
-                BABYLON.Tools.ReadFile(sceneFilename, dataCallback, onProgress, useArrayBuffer);
+                var fileOrString = sceneFilename;
+                if (fileOrString.name) {
+                    BABYLON.Tools.ReadFile(fileOrString, dataCallback, onProgress, useArrayBuffer);
+                }
+                else if (BABYLON.FilesInput.FilesToLoad[sceneFilename]) {
+                    BABYLON.Tools.ReadFile(BABYLON.FilesInput.FilesToLoad[sceneFilename], dataCallback, onProgress, useArrayBuffer);
+                }
+                else {
+                    onError("Unable to find file named " + sceneFilename);
+                    return;
+                }
             }
         };
         // Public functions
@@ -67378,6 +67538,7 @@ var BABYLON;
             _this.deviceScaleFactor = 1;
             _this.controllers = [];
             _this.onControllersAttachedObservable = new BABYLON.Observable();
+            _this.onControllerMeshLoadedObservable = new BABYLON.Observable();
             _this.rigParenting = true; // should the rig cameras be used as parent instead of this camera.
             _this.minZ = 0.1;
             //legacy support - the compensation boolean was removed.
@@ -67622,14 +67783,15 @@ var BABYLON;
             });
             this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add(function (gamepad) {
                 if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
-                    var webVrController = gamepad;
+                    var webVrController_1 = gamepad;
                     if (_this.webVROptions.controllerMeshes) {
-                        if (webVrController.defaultModel) {
-                            webVrController.defaultModel.setEnabled(true);
+                        if (webVrController_1.defaultModel) {
+                            webVrController_1.defaultModel.setEnabled(true);
                         }
                         else {
                             // Load the meshes
-                            webVrController.initControllerMesh(_this.getScene(), function (loadedMesh) {
+                            webVrController_1.initControllerMesh(_this.getScene(), function (loadedMesh) {
+                                _this.onControllerMeshLoadedObservable.notifyObservers(webVrController_1);
                                 if (_this.webVROptions.defaultLightingOnControllers) {
                                     if (!_this._lightOnControllers) {
                                         _this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), _this.getScene());
@@ -67649,11 +67811,11 @@ var BABYLON;
                             });
                         }
                     }
-                    webVrController.attachToPoseControlledCamera(_this);
+                    webVrController_1.attachToPoseControlledCamera(_this);
                     // since this is async - sanity check. Is the controller already stored?
-                    if (_this.controllers.indexOf(webVrController) === -1) {
+                    if (_this.controllers.indexOf(webVrController_1) === -1) {
                         //add to the controllers array
-                        _this.controllers.push(webVrController);
+                        _this.controllers.push(webVrController_1);
                         //did we find enough controllers? Great! let the developer know.
                         if (_this.controllers.length >= 2) {
                             // Forced to add some control code for Vive as it doesn't always fill properly the "hand" property
@@ -67972,10 +68134,14 @@ var BABYLON;
             window.addEventListener("resize", function () {
                 _this._btnVR.style.top = _this._canvas.offsetTop + _this._canvas.offsetHeight - 70 + "px";
                 _this._btnVR.style.left = _this._canvas.offsetLeft + _this._canvas.offsetWidth - 100 + "px";
-                if (_this._fullscreenVRpresenting) {
+                if (_this._fullscreenVRpresenting && _this._webVRready) {
                     _this.exitVR();
                 }
             });
+            document.addEventListener("fullscreenchange", function () { _this._onFullscreenChange(); }, false);
+            document.addEventListener("mozfullscreenchange", function () { _this._onFullscreenChange(); }, false);
+            document.addEventListener("webkitfullscreenchange", function () { _this._onFullscreenChange(); }, false);
+            document.addEventListener("msfullscreenchange", function () { _this._onFullscreenChange(); }, false);
             document.body.appendChild(this._btnVR);
             // Exiting VR mode using 'ESC' key on desktop
             this._onKeyDown = function (event) {
@@ -67988,6 +68154,9 @@ var BABYLON;
             this._scene.onPrePointerObservable.add(function (pointerInfo, eventState) {
                 if (_this.isInVRMode()) {
                     _this.exitVR();
+                    if (_this._fullscreenVRpresenting) {
+                        _this._scene.getEngine().switchFullscreen(true);
+                    }
                 }
             }, BABYLON.PointerEventTypes.POINTERDOUBLETAP, false);
             // Listen for WebVR display changes
@@ -68007,9 +68176,34 @@ var BABYLON;
             window.addEventListener('vrdisplaypresentchange', this._onVrDisplayPresentChange);
             // Create the cameras
             this._vrDeviceOrientationCamera = new BABYLON.VRDeviceOrientationFreeCamera("VRDeviceOrientationVRHelper", this._position, this._scene);
-            this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene);
+            this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene, webVROptions);
+            this._webVRCamera.onControllerMeshLoadedObservable.add(function (webVRController) { return _this._onDefaultMeshLoaded(webVRController); });
             this.updateButtonVisibility();
         }
+        VRExperienceHelper.prototype._onDefaultMeshLoaded = function (webVRController) {
+            if (this.onControllerMeshLoaded) {
+                this.onControllerMeshLoaded(webVRController);
+            }
+        };
+        VRExperienceHelper.prototype._onFullscreenChange = function () {
+            if (document.fullscreen !== undefined) {
+                this._fullscreenVRpresenting = document.fullscreen;
+            }
+            else if (document.mozFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.mozFullScreen;
+            }
+            else if (document.webkitIsFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.webkitIsFullScreen;
+            }
+            else if (document.msIsFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.msIsFullScreen;
+            }
+            if (!this._fullscreenVRpresenting) {
+                this.exitVR();
+                this._btnVR.style.top = this._canvas.offsetTop + this._canvas.offsetHeight - 70 + "px";
+                this._btnVR.style.left = this._canvas.offsetLeft + this._canvas.offsetWidth - 100 + "px";
+            }
+        };
         VRExperienceHelper.prototype.isInVRMode = function () {
             return this._webVRpresenting || this._fullscreenVRpresenting;
         };
@@ -68055,6 +68249,9 @@ var BABYLON;
          * Otherwise, will use the fullscreen API.
          */
         VRExperienceHelper.prototype.enterVR = function () {
+            if (this.onEnteringVR) {
+                this.onEnteringVR();
+            }
             if (this._webVRrequesting)
                 return;
             // If WebVR is supported and a headset is connected
@@ -68068,7 +68265,6 @@ var BABYLON;
                 this._vrDeviceOrientationCamera.position = this._position;
                 this._scene.activeCamera = this._vrDeviceOrientationCamera;
                 this._scene.getEngine().switchFullscreen(true);
-                this._fullscreenVRpresenting = true;
                 this.updateButtonVisibility();
             }
             this._scene.activeCamera.attachControl(this._canvas);
@@ -68077,6 +68273,9 @@ var BABYLON;
          * Attempt to exit VR, or fullscreen.
          */
         VRExperienceHelper.prototype.exitVR = function () {
+            if (this.onExitingVR) {
+                this.onExitingVR();
+            }
             if (this._webVRpresenting) {
                 this._scene.getEngine().disableVR();
             }
@@ -68086,7 +68285,6 @@ var BABYLON;
             this._deviceOrientationCamera.position = this._position;
             this._scene.activeCamera = this._deviceOrientationCamera;
             this._scene.activeCamera.attachControl(this._canvas);
-            this._fullscreenVRpresenting = false;
             this.updateButtonVisibility();
         };
         Object.defineProperty(VRExperienceHelper.prototype, "position", {

File diff suppressed because it is too large
+ 824 - 778
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


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

@@ -502,6 +502,8 @@ declare module BABYLON.GUI {
         private _textVerticalAlignment;
         private _lines;
         private _totalHeight;
+        private _resizeToFit;
+        resizeToFit: boolean;
         textWrapping: boolean;
         text: string;
         textHorizontalAlignment: number;
@@ -635,6 +637,8 @@ declare module BABYLON.GUI {
         private _blinkIsEven;
         private _cursorOffset;
         private _scrollLeft;
+        private _textWidth;
+        private _clickedCoordinate;
         promptMessage: string;
         onTextChangedObservable: Observable<InputText>;
         onFocusObservable: Observable<InputText>;

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

@@ -2933,9 +2933,24 @@ var BABYLON;
                 _this._textWrapping = false;
                 _this._textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
                 _this._textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_CENTER;
+                _this._resizeToFit = false;
                 _this.text = text;
                 return _this;
             }
+            Object.defineProperty(TextBlock.prototype, "resizeToFit", {
+                get: function () {
+                    return this._resizeToFit;
+                },
+                set: function (value) {
+                    this._resizeToFit = value;
+                    if (this._resizeToFit) {
+                        this._width.ignoreAdaptiveScaling = true;
+                        this._height.ignoreAdaptiveScaling = true;
+                    }
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(TextBlock.prototype, "textWrapping", {
                 get: function () {
                     return this._textWrapping;
@@ -3023,7 +3038,7 @@ var BABYLON;
             TextBlock.prototype._additionalProcessing = function (parentMeasure, context) {
                 this._lines = [];
                 var _lines = this.text.split("\n");
-                if (this._textWrapping) {
+                if (this._textWrapping && !this._resizeToFit) {
                     for (var _i = 0, _lines_1 = _lines; _i < _lines_1.length; _i++) {
                         var _line = _lines_1[_i];
                         this._lines.push(this._parseLineWithTextWrapping(_line, context));
@@ -3080,10 +3095,17 @@ var BABYLON;
                         break;
                 }
                 rootY += this._currentMeasure.top;
+                var maxLineWidth = 0;
                 for (var _i = 0, _a = this._lines; _i < _a.length; _i++) {
                     var line = _a[_i];
                     this._drawText(line.text, line.width, rootY, context);
                     rootY += this._fontOffset.height;
+                    if (line.width > maxLineWidth)
+                        maxLineWidth = line.width;
+                }
+                if (this._resizeToFit) {
+                    this.width = this.paddingLeftInPixels + this.paddingRightInPixels + maxLineWidth + 'px';
+                    this.height = this.paddingTopInPixels + this.paddingBottomInPixels + this._fontOffset.height * this._lines.length + 'px';
                 }
             };
             return TextBlock;
@@ -3813,8 +3835,8 @@ var BABYLON;
                 _this.name = name;
                 _this._text = "";
                 _this._placeholderText = "";
-                _this._background = "black";
-                _this._focusedBackground = "black";
+                _this._background = "#222222";
+                _this._focusedBackground = "#000000";
                 _this._placeholderColor = "gray";
                 _this._thickness = 1;
                 _this._margin = new GUI.ValueAndUnit(10, GUI.ValueAndUnit.UNITMODE_PIXEL);
@@ -4104,10 +4126,10 @@ var BABYLON;
                             context.fillStyle = this._placeholderColor;
                         }
                     }
-                    var textWidth = context.measureText(text).width;
+                    this._textWidth = context.measureText(text).width;
                     var marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
                     if (this._autoStretchWidth) {
-                        this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), textWidth + marginWidth) + "px";
+                        this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), this._textWidth + marginWidth) + "px";
                     }
                     var rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
                     var availableWidth = this._width.getValueInPixel(this._host, parentMeasure.width) - marginWidth;
@@ -4115,8 +4137,8 @@ var BABYLON;
                     context.beginPath();
                     context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
                     context.clip();
-                    if (this._isFocused && textWidth > availableWidth) {
-                        var textLeft = clipTextLeft - textWidth + availableWidth;
+                    if (this._isFocused && this._textWidth > availableWidth) {
+                        var textLeft = clipTextLeft - this._textWidth + availableWidth;
                         if (!this._scrollLeft) {
                             this._scrollLeft = textLeft;
                         }
@@ -4127,10 +4149,32 @@ var BABYLON;
                     context.fillText(text, this._scrollLeft, this._currentMeasure.top + rootY);
                     // Cursor
                     if (this._isFocused) {
+                        // Need to move cursor
+                        if (this._clickedCoordinate) {
+                            var rightPosition = this._scrollLeft + this._textWidth;
+                            var absoluteCursorPosition = rightPosition - this._clickedCoordinate;
+                            var currentSize = 0;
+                            this._cursorOffset = 0;
+                            var previousDist = 0;
+                            do {
+                                if (this._cursorOffset) {
+                                    previousDist = Math.abs(absoluteCursorPosition - currentSize);
+                                }
+                                this._cursorOffset++;
+                                currentSize = context.measureText(text.substr(text.length - this._cursorOffset, this._cursorOffset)).width;
+                            } while (currentSize < absoluteCursorPosition);
+                            // Find closest move
+                            if (Math.abs(absoluteCursorPosition - currentSize) > previousDist) {
+                                this._cursorOffset--;
+                            }
+                            this._blinkIsEven = false;
+                            this._clickedCoordinate = null;
+                        }
+                        // Render cursor
                         if (!this._blinkIsEven) {
                             var cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
                             var cursorOffsetWidth = context.measureText(cursorOffsetText).width;
-                            var cursorLeft = this._scrollLeft + textWidth - cursorOffsetWidth;
+                            var cursorLeft = this._scrollLeft + this._textWidth - cursorOffsetWidth;
                             if (cursorLeft < clipTextLeft) {
                                 this._scrollLeft += (clipTextLeft - cursorLeft);
                                 cursorLeft = clipTextLeft;
@@ -4165,6 +4209,13 @@ var BABYLON;
                 if (!_super.prototype._onPointerDown.call(this, coordinates, buttonIndex)) {
                     return false;
                 }
+                this._clickedCoordinate = coordinates.x;
+                if (this._host.focusedControl === this) {
+                    // Move cursor
+                    clearTimeout(this._blinkTimeout);
+                    this._markAsDirty();
+                    return true;
+                }
                 this._host.focusedControl = this;
                 return true;
             };

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


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

@@ -507,6 +507,8 @@ declare module BABYLON.GUI {
         private _textVerticalAlignment;
         private _lines;
         private _totalHeight;
+        private _resizeToFit;
+        resizeToFit: boolean;
         textWrapping: boolean;
         text: string;
         textHorizontalAlignment: number;
@@ -640,6 +642,8 @@ declare module BABYLON.GUI {
         private _blinkIsEven;
         private _cursorOffset;
         private _scrollLeft;
+        private _textWidth;
+        private _clickedCoordinate;
         promptMessage: string;
         onTextChangedObservable: Observable<InputText>;
         onFocusObservable: Observable<InputText>;

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


+ 55 - 76
dist/preview release/inspector/babylon.inspector.css

@@ -17,7 +17,10 @@
  * - a refresh tool - refresh the whole panel
  * - a popup tool - Open the inspector in a new panel
  * ...
- */ }
+ */
+  /*
+* 1. Set to 0 height and width, and remove border for a slider without a thumb
+*/ }
   .insp-wrapper .gutter {
     background-color: #2c2c2c; }
     .insp-wrapper .gutter.gutter-vertical:not(.blocked) {
@@ -430,79 +433,55 @@
       color: #b3b3b3;
       padding: 3px 0 3px 10px;
       margin: 6px 0 6px 0; }
-  .insp-wrapper [type='range'] {
+  .insp-wrapper input[type="range"] {
+    margin: auto;
     -webkit-appearance: none;
-    margin: 7px 0;
-    width: 50%; }
-    .insp-wrapper [type='range']:focus {
-      outline: 0; }
-      .insp-wrapper [type='range']:focus::-webkit-slider-runnable-track {
-        background: #54b3f3; }
-      .insp-wrapper [type='range']:focus::-ms-fill-lower {
-        background: #3ca9f1; }
-      .insp-wrapper [type='range']:focus::-ms-fill-upper {
-        background: #54b3f3; }
-    .insp-wrapper [type='range']::-webkit-slider-runnable-track {
-      cursor: pointer;
-      height: 5px;
-      transition: all .2s ease;
-      width: 50%;
-      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
-      background: #3ca9f1;
-      border: 1px solid #5db0d7;
-      border-radius: 5px; }
-    .insp-wrapper [type='range']::-webkit-slider-thumb {
-      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
-      background: #5db0d7;
-      border: 1px solid #5db0d7;
-      border-radius: 0px;
-      cursor: pointer;
-      height: 14px;
-      width: 7px;
-      -webkit-appearance: none;
-      margin-top: -5.5px; }
-    .insp-wrapper [type='range']::-moz-range-track {
-      cursor: pointer;
-      height: 5px;
-      transition: all .2s ease;
-      width: 50%;
-      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
-      background: #3ca9f1;
-      border: 1px solid #5db0d7;
-      border-radius: 5px; }
-    .insp-wrapper [type='range']::-moz-range-thumb {
-      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
-      background: #5db0d7;
-      border: 1px solid #5db0d7;
-      border-radius: 0px;
-      cursor: pointer;
-      height: 14px;
-      width: 7px; }
-    .insp-wrapper [type='range']::-ms-track {
-      cursor: pointer;
-      height: 5px;
-      transition: all .2s ease;
-      width: 50%;
-      background: transparent;
-      border-color: transparent;
-      border-width: 7px 0;
-      color: transparent; }
-    .insp-wrapper [type='range']::-ms-fill-lower {
-      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
-      background: #249fef;
-      border: 1px solid #5db0d7;
-      border-radius: 10px; }
-    .insp-wrapper [type='range']::-ms-fill-upper {
-      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
-      background: #3ca9f1;
-      border: 1px solid #5db0d7;
-      border-radius: 10px; }
-    .insp-wrapper [type='range']::-ms-thumb {
-      box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.2), 0 0 1px rgba(13, 13, 13, 0.2);
-      background: #5db0d7;
-      border: 1px solid #5db0d7;
-      border-radius: 0px;
-      cursor: pointer;
-      height: 14px;
-      width: 7px;
-      margin-top: 0; }
+    position: relative;
+    overflow: hidden;
+    height: 15px;
+    width: 50%;
+    cursor: pointer;
+    border-radius: 0;
+    /* iOS */ }
+  .insp-wrapper ::-webkit-slider-runnable-track {
+    background: #ddd; }
+  .insp-wrapper ::-webkit-slider-thumb {
+    -webkit-appearance: none;
+    width: 20px;
+    /* 1 */
+    height: 15px;
+    /* 1 */
+    background: #fff;
+    box-shadow: -100vw 0 0 100vw dodgerblue;
+    border: 0px solid #999;
+    /* 1 */ }
+  .insp-wrapper ::-moz-range-track {
+    height: 15px;
+    background: #ddd; }
+  .insp-wrapper ::-moz-range-thumb {
+    background: #fff;
+    height: 15px;
+    width: 20px;
+    border: 0px solid #999;
+    border-radius: 0 !important;
+    box-shadow: -100vw 0 0 100vw dodgerblue;
+    box-sizing: border-box; }
+  .insp-wrapper ::-ms-fill-lower {
+    background: dodgerblue; }
+  .insp-wrapper ::-ms-thumb {
+    background: #fff;
+    border: 0px solid #999;
+    height: 15px;
+    width: 20px;
+    box-sizing: border-box; }
+  .insp-wrapper ::-ms-ticks-after {
+    display: none; }
+  .insp-wrapper ::-ms-ticks-before {
+    display: none; }
+  .insp-wrapper ::-ms-track {
+    background: #ddd;
+    color: transparent;
+    height: 15px;
+    border: none; }
+  .insp-wrapper ::-ms-tooltip {
+    display: none; }

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

@@ -174,11 +174,28 @@ declare module INSPECTOR {
                     max: number;
                     step: number;
                 };
+                beta: {
+                    min: number;
+                    max: number;
+                    step: number;
+                };
+                fov: {
+                    min: number;
+                    max: number;
+                    step: number;
+                };
             };
         };
         'FreeCamera': {
             type: typeof BABYLON.FreeCamera;
             properties: string[];
+            slider: {
+                fov: {
+                    min: number;
+                    max: number;
+                    step: number;
+                };
+            };
         };
         'Scene': {
             type: typeof BABYLON.Scene;
@@ -188,6 +205,13 @@ declare module INSPECTOR {
             type: typeof BABYLON.Mesh;
             properties: string[];
             format: (m: BABYLON.Mesh) => string;
+            slider: {
+                visibility: {
+                    min: number;
+                    max: number;
+                    step: number;
+                };
+            };
         };
         'StandardMaterial': {
             type: typeof BABYLON.StandardMaterial;
@@ -606,6 +630,8 @@ declare module INSPECTOR {
         private _onMouseDownHandler;
         private _onMouseDragHandler;
         private _onMouseUpHandler;
+        private _sliderfill;
+        private _slidertrack;
         private _textValue;
         /** Save previous Y mouse position */
         private _prevY;

+ 13 - 4
dist/preview release/inspector/babylon.inspector.js

@@ -466,7 +466,9 @@ var INSPECTOR;
                 'checkCollisions'
             ],
             slider: {
-                alpha: { min: 0, max: 1, step: 0.01 }
+                alpha: { min: 0, max: 2 * Math.PI, step: 0.01 },
+                beta: { min: -Math.PI, max: Math.PI, step: 0.01 },
+                fov: { min: 0, max: 180, step: 1 }
             }
         },
         'FreeCamera': {
@@ -497,7 +499,10 @@ var INSPECTOR;
                 'orthoTop',
                 'orthoLeft',
                 'orthoRight'
-            ]
+            ],
+            slider: {
+                fov: { min: 0, max: 180, step: 1 }
+            }
         },
         'Scene': {
             type: BABYLON.Scene,
@@ -559,7 +564,10 @@ var INSPECTOR;
                 'checkCollisions',
                 'hasLODLevels'
             ],
-            format: function (m) { return m.name; }
+            format: function (m) { return m.name; },
+            slider: {
+                visibility: { min: 0, max: 1, step: 0.1 }
+            }
         },
         'StandardMaterial': {
             type: BABYLON.StandardMaterial,
@@ -2094,13 +2102,14 @@ var INSPECTOR;
                     INSPECTOR.Scheduler.getInstance().pause = false;
                 });
                 this._textValue = INSPECTOR.Helpers.CreateDiv('value-text', this._valueDiv);
-                this._textValue.innerText = this.value;
+                this._textValue.innerText = INSPECTOR.Helpers.Trunc(this.value).toString();
                 this._textValue.style.paddingLeft = '10px';
                 this._textValue.style.display = 'inline-block';
             }
         };
         PropertyLine.prototype._rangeHandler = function () {
             INSPECTOR.Scheduler.getInstance().pause = true;
+            //this._input.style.backgroundSize = ((parseFloat(this._input.value) - parseFloat(this._input.min)) * 100 / ( parseFloat(this._input.max) - parseFloat(this._input.min))) + '% 100%'
             this._textValue.innerText = this._input.value;
             this.validateInput(this._input.value, false);
         };

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


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

@@ -18,7 +18,9 @@
 - Added support for Particle animation in ParticleSystem. [Doc here](http://doc.babylonjs.com/tutorials/particles#particle-animation) ([Ibraheem Osama](https://github.com/IbraheemOsama))
 
 ## Updates
-- Several inspector improvments ([temechon](https://github.com/temechon))
+- Added `MultiObserver`. [Doc here](http://doc.babylonjs.com/overviews/observables#multiobserver) ([deltakosh](https://github.com/deltakosh))
+- Added `shadowGenerator.addShadowCaster` and `shadowGenerator.removeShadowCaster` helper functions ([deltakosh](https://github.com/deltakosh))
+- Several inspector improvements ([temechon](https://github.com/temechon))
 - New observables for actions: `onBeforeExecuteObservable` for all actions and `onInterpolationDoneObservable` for `InterpolateValueAction` ([deltakosh](https://github.com/deltakosh))
 - New observables for gamepads: `onButtonDownObservable`, `onButtonUpObservable`, `onPadDownObservable`, `onPadUpObservable` ([deltakosh](https://github.com/deltakosh))
 - New `camera.storeState()` and `camera.restoreState()` functions to store / restore cameras position / rotation / fov. (Doc here)[http://doc.babylonjs.com/tutorials/cameras#state] ([deltakosh](https://github.com/deltakosh))

+ 8 - 0
gui/src/controls/container.ts

@@ -143,10 +143,18 @@ module BABYLON.GUI {
                 for (var child of this._children) {
                     if (child.isVisible && !child.notRenderable) {
                         child._draw(this._measureForChildren, context);
+
+                        if (child.onAfterDrawObservable.hasObservers()) {
+                            child.onAfterDrawObservable.notifyObservers(child);
+                        }
                     }
                 }
             }
             context.restore();
+
+            if (this.onAfterDrawObservable.hasObservers()) {
+                this.onAfterDrawObservable.notifyObservers(this);
+            }
         }
 
         public _processPicking(x: number, y: number, type: number, buttonIndex: number): boolean {

+ 7 - 1
gui/src/controls/control.ts

@@ -93,7 +93,13 @@ module BABYLON.GUI {
         * An event triggered when the control is marked as dirty
         * @type {BABYLON.Observable}
         */
-        public onDirtyObservable = new Observable<Control>();           
+        public onDirtyObservable = new Observable<Control>();         
+        
+         /**
+        * An event triggered after the control is drawn
+        * @type {BABYLON.Observable}
+        */
+        public onAfterDrawObservable = new Observable<Control>();    
 
         public get alpha(): number {
             return this._alpha;

+ 44 - 8
gui/src/controls/inputText.ts

@@ -4,8 +4,8 @@ module BABYLON.GUI {
     export class InputText extends Control implements IFocusableControl {
         private _text = "";
         private _placeholderText = "";
-        private _background = "black";   
-        private _focusedBackground = "black";   
+        private _background = "#222222";   
+        private _focusedBackground = "#000000";   
         private _placeholderColor = "gray";   
         private _thickness = 1;
         private _margin = new ValueAndUnit(10, ValueAndUnit.UNITMODE_PIXEL);
@@ -16,7 +16,9 @@ module BABYLON.GUI {
         private _blinkIsEven = false;
         private _cursorOffset = 0;        
         private _scrollLeft: number;
-
+        private _textWidth: number;
+        private _clickedCoordinate: number;
+        
         public promptMessage = "Please enter text:";
 
         public onTextChangedObservable = new Observable<InputText>();
@@ -305,10 +307,10 @@ module BABYLON.GUI {
                     }
                 }
 
-                let textWidth = context.measureText(text).width;   
+                this._textWidth = context.measureText(text).width;   
                 let marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
                 if (this._autoStretchWidth) {
-                    this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), textWidth + marginWidth) + "px";
+                    this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), this._textWidth + marginWidth) + "px";
                 }
 
                 let rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
@@ -318,8 +320,8 @@ module BABYLON.GUI {
                 context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
                 context.clip();
 
-                if (this._isFocused && textWidth > availableWidth) {      
-                    let textLeft = clipTextLeft - textWidth + availableWidth;
+                if (this._isFocused && this._textWidth > availableWidth) {      
+                    let textLeft = clipTextLeft - this._textWidth + availableWidth;
                     if (!this._scrollLeft) {
                         this._scrollLeft = textLeft;
                     }
@@ -331,10 +333,37 @@ module BABYLON.GUI {
 
                 // Cursor
                 if (this._isFocused) {         
+
+                    // Need to move cursor
+                    if (this._clickedCoordinate) {
+                        var rightPosition = this._scrollLeft + this._textWidth;
+                        var absoluteCursorPosition = rightPosition - this._clickedCoordinate;
+                        var currentSize = 0;
+                        this._cursorOffset = 0;
+                        var previousDist = 0;
+                        do {
+                            if (this._cursorOffset) {
+                                previousDist = Math.abs(absoluteCursorPosition - currentSize);
+                            }
+                            this._cursorOffset++;
+                            currentSize = context.measureText(text.substr(text.length - this._cursorOffset, this._cursorOffset)).width;
+
+                        } while(currentSize < absoluteCursorPosition);
+
+                        // Find closest move
+                        if (Math.abs(absoluteCursorPosition - currentSize) > previousDist) {
+                            this._cursorOffset--;
+                        }
+
+                        this._blinkIsEven = false;
+                        this._clickedCoordinate = null;
+                    }
+
+                    // Render cursor
                     if (!this._blinkIsEven) {
                         let cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
                         let cursorOffsetWidth = context.measureText(cursorOffsetText).width;   
-                        let cursorLeft = this._scrollLeft  + textWidth - cursorOffsetWidth;
+                        let cursorLeft = this._scrollLeft  + this._textWidth - cursorOffsetWidth;
     
                         if (cursorLeft < clipTextLeft) {
                             this._scrollLeft += (clipTextLeft - cursorLeft);
@@ -376,6 +405,13 @@ module BABYLON.GUI {
                 return false;
             }
 
+            this._clickedCoordinate = coordinates.x;
+            if (this._host.focusedControl === this) {
+                // Move cursor
+                clearTimeout(this._blinkTimeout);
+                this._markAsDirty();
+                return true;
+            }
             this._host.focusedControl = this;
 
             return true;

+ 26 - 2
gui/src/controls/textBlock.ts

@@ -10,6 +10,20 @@ module BABYLON.GUI {
 
         private _lines: any[];
         private _totalHeight: number;
+        private _resizeToFit: boolean = false;
+
+        get resizeToFit(): boolean {
+            return this._resizeToFit;
+        }
+
+        set resizeToFit(value: boolean) {
+            this._resizeToFit = value;
+
+            if (this._resizeToFit) {
+                this._width.ignoreAdaptiveScaling = true;
+                this._height.ignoreAdaptiveScaling = true;
+            }
+        }
 
         public get textWrapping(): boolean {
             return this._textWrapping;
@@ -86,6 +100,7 @@ module BABYLON.GUI {
                     break;
             }
 
+
             context.fillText(text, this._currentMeasure.left + x, y);
         }
 
@@ -105,7 +120,7 @@ module BABYLON.GUI {
             this._lines = [];
             var _lines = this.text.split("\n");
 
-            if (this._textWrapping) {
+            if (this._textWrapping && !this._resizeToFit) {
                 for(var _line of _lines) {
                     this._lines.push(this._parseLineWithTextWrapping(_line, context));
                 }
@@ -165,10 +180,19 @@ module BABYLON.GUI {
 
             rootY += this._currentMeasure.top;
 
+            var maxLineWidth: number = 0;
+
             for (var line of this._lines) {
                 this._drawText(line.text, line.width, rootY, context);
                 rootY += this._fontOffset.height;
+
+                if (line.width > maxLineWidth) maxLineWidth = line.width;
+            }
+
+            if (this._resizeToFit) {
+                this.width = this.paddingLeftInPixels + this.paddingRightInPixels + maxLineWidth + 'px';
+                this.height = this.paddingTopInPixels + this.paddingBottomInPixels + this._fontOffset.height * this._lines.length + 'px';
             }
         }
     }
-}
+}

+ 2 - 1
sandbox/index.js

@@ -94,7 +94,8 @@ if (BABYLON.Engine.isSupported()) {
             currentScene.activeCamera.pinchPrecision = 200 / currentScene.activeCamera.radius;
             currentScene.activeCamera.upperRadiusLimit = 5 * currentScene.activeCamera.radius;
 
-            currentScene.activeCamera.wheelPrecisionPercentage = 0.01;
+            currentScene.activeCamera.wheelDeltaPercentage = 0.01;
+            currentScene.activeCamera.pinchDeltaPercentage = 0.01;
         }
 
         currentScene.activeCamera.attachControl(canvas); 

+ 3 - 3
src/Cameras/Inputs/babylon.arcRotateCameraMouseWheelInput.ts

@@ -9,11 +9,11 @@ module BABYLON {
         public wheelPrecision = 3.0;
 
         /**
-         * wheelPrecisionPercentage will be used instead of whellPrecision if different from 0. 
+         * wheelDeltaPercentage will be used instead of wheelPrecision if different from 0. 
          * It defines the percentage of current camera.radius to use as delta when wheel is used.
          */
         @serialize()
-        public wheelPrecisionPercentage = 0;
+        public wheelDeltaPercentage = 0;
 
         public attachControl(element: HTMLElement, noPreventDefault?: boolean) {
             this._wheel = (p, s) => {
@@ -23,7 +23,7 @@ module BABYLON {
                 var delta = 0;
 
                 if (event.wheelDelta) {
-                    delta = this.wheelPrecisionPercentage ? (event.wheelDelta * 0.01) * this.camera.radius * this.wheelPrecisionPercentage : event.wheelDelta / (this.wheelPrecision * 40);
+                    delta = this.wheelDeltaPercentage ? (event.wheelDelta * 0.01) * this.camera.radius * this.wheelDeltaPercentage : event.wheelDelta / (this.wheelPrecision * 40);
                 } else if (event.detail) {
                     delta = -event.detail / this.wheelPrecision;
                 }

+ 21 - 8
src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts

@@ -16,6 +16,13 @@ module BABYLON {
         @serialize()
         public pinchPrecision = 12.0;
 
+        /**
+         * pinchDeltaPercentage will be used instead of pinchPrecision if different from 0. 
+         * It defines the percentage of current camera.radius to use as delta when pinch zoom is used.
+         */        
+        @serialize()
+        public pinchDeltaPercentage = 0;
+
         @serialize()
         public panningSensibility: number = 1000.0;
 
@@ -171,12 +178,15 @@ module BABYLON {
                         }
 
                         if (this.multiTouchPanAndZoom) {
-                            this.camera
-                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                            if (this.pinchDeltaPercentage) {
+                                this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001)* this.camera.radius * this.pinchDeltaPercentage; 
+                            } else {
+                                this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
                                 (this.pinchPrecision *
                                     ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
                                     direction);
-                            
+                                }
+
                             if (this.panningSensibility !== 0) {
                                 var pointersCenterX = (pointA.x + pointB.x) / 2;
                                 var pointersCenterY = (pointA.y + pointB.y) / 2;
@@ -194,11 +204,14 @@ module BABYLON {
                             twoFingerActivityCount++;
 
                             if (previousMultiTouchPanPosition.isPinching || (twoFingerActivityCount < 20 && Math.abs(pinchDistance - initialDistance) > this.camera.pinchToPanMaxDistance)) {                   
-                                this.camera
-                                .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
-                                (this.pinchPrecision *
-                                    ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
-                                    direction);
+                                if (this.pinchDeltaPercentage) {
+                                    this.camera.inertialRadiusOffset += ((pinchSquaredDistance - previousPinchSquaredDistance) * 0.001) * this.camera.radius * this.pinchDeltaPercentage;
+                                } else {
+                                    this.camera.inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
+                                    (this.pinchPrecision *
+                                        ((this.angularSensibilityX + this.angularSensibilityY) / 2) *
+                                        direction);
+                                }
                                 previousMultiTouchPanPosition.isPaning = false;
                                 previousMultiTouchPanPosition.isPinching = true;
                             }

+ 47 - 7
src/Cameras/VR/babylon.vrExperienceHelper.ts

@@ -25,7 +25,11 @@ module BABYLON {
         private _onVrDisplayPresentChange: any;
         private _onVRDisplayChanged: (eventArgs:IDisplayChangedEventArgs) => void;
         private _onVRRequestPresentStart: () => void;
-        private _onVRRequestPresentComplete: (success: boolean) => void
+        private _onVRRequestPresentComplete: (success: boolean) => void;
+        
+        public onEnteringVR: () => void;
+        public onExitingVR: () => void;
+        public onControllerMeshLoaded: (WebVRController) => void;
                 
         constructor(scene: Scene, private webVROptions: WebVROptions = {}) {
             this._scene = scene;
@@ -71,11 +75,16 @@ module BABYLON {
                 this._btnVR.style.top = this._canvas.offsetTop + this._canvas.offsetHeight - 70 + "px";
                 this._btnVR.style.left = this._canvas.offsetLeft + this._canvas.offsetWidth - 100 + "px";
 
-                if (this._fullscreenVRpresenting) {
+                if (this._fullscreenVRpresenting && this._webVRready) {
                     this.exitVR();
                 }
             });
-            
+
+            document.addEventListener("fullscreenchange", () => { this._onFullscreenChange() }, false);
+            document.addEventListener("mozfullscreenchange", () => { this._onFullscreenChange() }, false);
+            document.addEventListener("webkitfullscreenchange", () => { this._onFullscreenChange() }, false);
+            document.addEventListener("msfullscreenchange", () => { this._onFullscreenChange() }, false);
+
             document.body.appendChild(this._btnVR);
 
             // Exiting VR mode using 'ESC' key on desktop
@@ -90,6 +99,9 @@ module BABYLON {
             this._scene.onPrePointerObservable.add( (pointerInfo, eventState) => {
                 if (this.isInVRMode()) {
                     this.exitVR();
+                    if (this._fullscreenVRpresenting) {
+                        this._scene.getEngine().switchFullscreen(true);
+                    }
                 }
             }, BABYLON.PointerEventTypes.POINTERDOUBLETAP, false);
             
@@ -112,11 +124,35 @@ module BABYLON {
 
             // Create the cameras
             this._vrDeviceOrientationCamera = new BABYLON.VRDeviceOrientationFreeCamera("VRDeviceOrientationVRHelper", this._position, this._scene);            
-            this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene);
+            this._webVRCamera = new BABYLON.WebVRFreeCamera("WebVRHelper", this._position, this._scene, webVROptions);
+            this._webVRCamera.onControllerMeshLoadedObservable.add((webVRController) => this._onDefaultMeshLoaded(webVRController));
             
             this.updateButtonVisibility();
         }
 
+        private _onDefaultMeshLoaded(webVRController: WebVRController) {
+            if (this.onControllerMeshLoaded) {
+                this.onControllerMeshLoaded(webVRController);
+            }
+        }
+
+        private _onFullscreenChange() {
+            if (document.fullscreen !== undefined) {
+                this._fullscreenVRpresenting = document.fullscreen;
+            } else if (document.mozFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.mozFullScreen;
+            } else if (document.webkitIsFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.webkitIsFullScreen;
+            } else if (document.msIsFullScreen !== undefined) {
+                this._fullscreenVRpresenting = document.msIsFullScreen;
+            }
+            if (!this._fullscreenVRpresenting) {
+                this.exitVR();
+                this._btnVR.style.top = this._canvas.offsetTop + this._canvas.offsetHeight - 70 + "px";
+                this._btnVR.style.left = this._canvas.offsetLeft + this._canvas.offsetWidth - 100 + "px";
+            }
+        }
+
         private isInVRMode() {
             return this._webVRpresenting || this._fullscreenVRpresenting;
         }
@@ -165,6 +201,9 @@ module BABYLON {
          * Otherwise, will use the fullscreen API.
          */
         public enterVR() {
+            if (this.onEnteringVR) {
+                this.onEnteringVR();
+            }
             if (this._webVRrequesting)
                 return;
 
@@ -179,7 +218,6 @@ module BABYLON {
                 this._vrDeviceOrientationCamera.position = this._position;
                 this._scene.activeCamera = this._vrDeviceOrientationCamera;
                 this._scene.getEngine().switchFullscreen(true);
-                this._fullscreenVRpresenting = true;
                 this.updateButtonVisibility();
             }
             
@@ -190,6 +228,9 @@ module BABYLON {
          * Attempt to exit VR, or fullscreen.
          */
         public exitVR() {
+            if (this.onExitingVR) {
+                this.onExitingVR();
+            }
             if (this._webVRpresenting) {
                 this._scene.getEngine().disableVR();
             }
@@ -199,8 +240,7 @@ module BABYLON {
             this._deviceOrientationCamera.position = this._position;
             this._scene.activeCamera = this._deviceOrientationCamera;
             this._scene.activeCamera.attachControl(this._canvas);
-            
-            this._fullscreenVRpresenting = false;
+
             this.updateButtonVisibility();
         }
 

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

@@ -61,6 +61,7 @@ module BABYLON {
 
         public controllers: Array<WebVRController> = [];
         public onControllersAttachedObservable = new Observable<Array<WebVRController>>();
+        public onControllerMeshLoadedObservable = new Observable<WebVRController>();
 
         public rigParenting: boolean = true; // should the rig cameras be used as parent instead of this camera.
 
@@ -359,6 +360,7 @@ module BABYLON {
                         } else {
                             // Load the meshes
                             webVrController.initControllerMesh(this.getScene(), (loadedMesh) => {
+                                this.onControllerMeshLoadedObservable.notifyObservers(webVrController);
                                 if (this.webVROptions.defaultLightingOnControllers) {
                                     if (!this._lightOnControllers) {
                                         this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), this.getScene());

+ 17 - 4
src/Cameras/babylon.arcRotateCamera.ts

@@ -108,6 +108,19 @@ module BABYLON {
             }
         }
 
+        public get pinchDeltaPercentage(): number {
+            var pointers = <ArcRotateCameraPointersInput>this.inputs.attached["pointers"];
+            if (pointers)
+                return pointers.pinchDeltaPercentage;
+        }
+
+        public set pinchDeltaPercentage(value: number) {
+            var pointers = <ArcRotateCameraPointersInput>this.inputs.attached["pointers"];
+            if (pointers) {
+                pointers.pinchDeltaPercentage = value;
+            }
+        }        
+
         public get panningSensibility(): number {
             var pointers = <ArcRotateCameraPointersInput>this.inputs.attached["pointers"];
             if (pointers)
@@ -181,16 +194,16 @@ module BABYLON {
                 mousewheel.wheelPrecision = value;
         }
 
-        public get wheelPrecisionPercentage(): number {
+        public get wheelDeltaPercentage(): number {
             var mousewheel = <ArcRotateCameraMouseWheelInput>this.inputs.attached["mousewheel"];
             if (mousewheel)
-                return mousewheel.wheelPrecisionPercentage;
+                return mousewheel.wheelDeltaPercentage;
         }
 
-        public set wheelPrecisionPercentage(value: number) {
+        public set wheelDeltaPercentage(value: number) {
             var mousewheel = <ArcRotateCameraMouseWheelInput>this.inputs.attached["mousewheel"];
             if (mousewheel)
-                mousewheel.wheelPrecisionPercentage = value;
+                mousewheel.wheelDeltaPercentage = value;
         }        
 
         //-- end properties for backward compatibility for inputs

+ 36 - 0
src/Lights/Shadows/babylon.shadowGenerator.ts

@@ -270,6 +270,42 @@
         }
 
         /**
+         * Helper function to add a mesh and its descendants to the list of shadow casters
+         * @param mesh Mesh to add
+         * @param includeDescendants boolean indicating if the descendants should be added. Default to true
+         */
+        public addShadowCaster(mesh: AbstractMesh, includeDescendants = true): ShadowGenerator {
+            this._shadowMap.renderList.push(mesh);
+
+            if (includeDescendants) {
+                this._shadowMap.renderList.push(...mesh.getChildMeshes());
+            }
+
+            return this;
+        }
+
+        /**
+         * Helper function to remove a mesh and its descendants from the list of shadow casters
+         * @param mesh Mesh to remove
+         * @param includeDescendants boolean indicating if the descendants should be removed. Default to true
+         */
+        public removeShadowCaster(mesh: AbstractMesh, includeDescendants = true): ShadowGenerator {
+            var index = this._shadowMap.renderList.indexOf(mesh);
+
+            if (index !== -1) {
+                this._shadowMap.renderList.splice(index, 1);
+            }
+
+            if (includeDescendants) {
+                for (var child of mesh.getChildren()) {
+                    this.removeShadowCaster(<any>child);
+                }
+            }
+
+            return this;
+        }
+
+        /**
 		 * Controls the extent to which the shadows fade out at the edge of the frustum
          * Used only by directionals and spots
 		 */

+ 10 - 1
src/Loading/babylon.sceneLoader.ts

@@ -185,7 +185,16 @@
             }
             // Loading file from disk via input file or drag'n'drop
             else {
-                Tools.ReadFile(sceneFilename, dataCallback, onProgress, useArrayBuffer);
+                var fileOrString = <any> sceneFilename;
+
+                if (fileOrString.name) { // File
+                    Tools.ReadFile(fileOrString, dataCallback, onProgress, useArrayBuffer);
+                } else if (FilesInput.FilesToLoad[sceneFilename]) {
+                    Tools.ReadFile(FilesInput.FilesToLoad[sceneFilename], dataCallback, onProgress, useArrayBuffer);
+                } else {
+                    onError("Unable to find file named " + sceneFilename);
+                    return;
+                }
             }
         }
 

+ 54 - 30
src/Math/babylon.math.ts

@@ -4415,51 +4415,75 @@
             return frustumPlanes;
         }
 
+        public static GetNearPlaneToRef(transform: Matrix, frustumPlane: Plane): void {
+            frustumPlane.normal.x = transform.m[3] + transform.m[2];
+            frustumPlane.normal.y = transform.m[7] + transform.m[6];
+            frustumPlane.normal.z = transform.m[11] + transform.m[10];
+            frustumPlane.d = transform.m[15] + transform.m[14];
+            frustumPlane.normalize();
+        }
+
+        public static GetFarPlaneToRef(transform: Matrix, frustumPlane: Plane): void {
+            frustumPlane.normal.x = transform.m[3] - transform.m[2];
+            frustumPlane.normal.y = transform.m[7] - transform.m[6];
+            frustumPlane.normal.z = transform.m[11] - transform.m[10];
+            frustumPlane.d = transform.m[15] - transform.m[14];
+            frustumPlane.normalize();
+        }
+
+        public static GetLeftPlaneToRef(transform: Matrix, frustumPlane: Plane): void {
+            frustumPlane.normal.x = transform.m[3] + transform.m[0];
+            frustumPlane.normal.y = transform.m[7] + transform.m[4];
+            frustumPlane.normal.z = transform.m[11] + transform.m[8];
+            frustumPlane.d = transform.m[15] + transform.m[12];
+            frustumPlane.normalize();
+        }       
+        
+        public static GetRightPlaneToRef(transform: Matrix, frustumPlane: Plane): void {
+            frustumPlane.normal.x = transform.m[3] - transform.m[0];
+            frustumPlane.normal.y = transform.m[7] - transform.m[4];
+            frustumPlane.normal.z = transform.m[11] - transform.m[8];
+            frustumPlane.d = transform.m[15] - transform.m[12];
+            frustumPlane.normalize();
+        }     
+        
+        public static GetTopPlaneToRef(transform: Matrix, frustumPlane: Plane): void {
+            frustumPlane.normal.x = transform.m[3] - transform.m[1];
+            frustumPlane.normal.y = transform.m[7] - transform.m[5];
+            frustumPlane.normal.z = transform.m[11] - transform.m[9];
+            frustumPlane.d = transform.m[15] - transform.m[13];
+            frustumPlane.normalize();
+        }      
+        
+        public static GetBottomPlaneToRef(transform: Matrix, frustumPlane: Plane): void {
+            frustumPlane.normal.x = transform.m[3] + transform.m[1];
+            frustumPlane.normal.y = transform.m[7] + transform.m[5];
+            frustumPlane.normal.z = transform.m[11] + transform.m[9];
+            frustumPlane.d = transform.m[15] + transform.m[13];
+            frustumPlane.normalize();
+        }           
+
         /**
          * Sets the passed array "frustumPlanes" with the 6 Frustum planes computed by the passed transformation matrix.  
          */
         public static GetPlanesToRef(transform: Matrix, frustumPlanes: Plane[]): void {
             // Near
-            frustumPlanes[0].normal.x = transform.m[3] + transform.m[2];
-            frustumPlanes[0].normal.y = transform.m[7] + transform.m[6];
-            frustumPlanes[0].normal.z = transform.m[11] + transform.m[10];
-            frustumPlanes[0].d = transform.m[15] + transform.m[14];
-            frustumPlanes[0].normalize();
+            Frustum.GetNearPlaneToRef(transform, frustumPlanes[0]);
 
             // Far
-            frustumPlanes[1].normal.x = transform.m[3] - transform.m[2];
-            frustumPlanes[1].normal.y = transform.m[7] - transform.m[6];
-            frustumPlanes[1].normal.z = transform.m[11] - transform.m[10];
-            frustumPlanes[1].d = transform.m[15] - transform.m[14];
-            frustumPlanes[1].normalize();
+            Frustum.GetFarPlaneToRef(transform, frustumPlanes[1]);
 
             // Left
-            frustumPlanes[2].normal.x = transform.m[3] + transform.m[0];
-            frustumPlanes[2].normal.y = transform.m[7] + transform.m[4];
-            frustumPlanes[2].normal.z = transform.m[11] + transform.m[8];
-            frustumPlanes[2].d = transform.m[15] + transform.m[12];
-            frustumPlanes[2].normalize();
+            Frustum.GetLeftPlaneToRef(transform, frustumPlanes[2]);
 
             // Right
-            frustumPlanes[3].normal.x = transform.m[3] - transform.m[0];
-            frustumPlanes[3].normal.y = transform.m[7] - transform.m[4];
-            frustumPlanes[3].normal.z = transform.m[11] - transform.m[8];
-            frustumPlanes[3].d = transform.m[15] - transform.m[12];
-            frustumPlanes[3].normalize();
+            Frustum.GetRightPlaneToRef(transform, frustumPlanes[3]);
 
             // Top
-            frustumPlanes[4].normal.x = transform.m[3] - transform.m[1];
-            frustumPlanes[4].normal.y = transform.m[7] - transform.m[5];
-            frustumPlanes[4].normal.z = transform.m[11] - transform.m[9];
-            frustumPlanes[4].d = transform.m[15] - transform.m[13];
-            frustumPlanes[4].normalize();
+            Frustum.GetTopPlaneToRef(transform, frustumPlanes[4]);
 
             // Bottom
-            frustumPlanes[5].normal.x = transform.m[3] + transform.m[1];
-            frustumPlanes[5].normal.y = transform.m[7] + transform.m[5];
-            frustumPlanes[5].normal.z = transform.m[11] + transform.m[9];
-            frustumPlanes[5].d = transform.m[15] + transform.m[13];
-            frustumPlanes[5].normalize();
+            Frustum.GetBottomPlaneToRef(transform, frustumPlanes[5]);
         }
     }
 

+ 16 - 3
src/Mesh/babylon.abstractMesh.ts

@@ -1090,7 +1090,7 @@
          * Returns the AbstractMesh.
          */
         public setPivotMatrix(matrix: Matrix, postMultiplyPivotMatrix = false): AbstractMesh {
-            this._pivotMatrix = matrix;
+            this._pivotMatrix = matrix.clone();
             this._cache.pivotMatrixUpdated = true;
             this._postMultiplyPivotMatrix = postMultiplyPivotMatrix;
 
@@ -1505,14 +1505,27 @@
         /** 
          * True if the mesh intersects another mesh or a SolidParticle object.  
          * Unless the parameter `precise` is set to `true` the intersection is computed according to Axis Aligned Bounding Boxes (AABB), else according to OBB (Oriented BBoxes)
+         * includeDescendants can be set to true to test if the mesh defined in parameters intersects with the current mesh or any child meshes
          * Returns a boolean.  
          */
-        public intersectsMesh(mesh: AbstractMesh | SolidParticle, precise?: boolean): boolean {
+        public intersectsMesh(mesh: AbstractMesh | SolidParticle, precise?: boolean, includeDescendants?: boolean): boolean {
             if (!this._boundingInfo || !mesh._boundingInfo) {
                 return false;
             }
 
-            return this._boundingInfo.intersects(mesh._boundingInfo, precise);
+            if (this._boundingInfo.intersects(mesh._boundingInfo, precise)) {
+                return true;
+            }
+
+            if (includeDescendants) {
+                for (var child of this.getChildMeshes()) {
+                    if (child.intersectsMesh(mesh, precise, true)) {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
         }
 
         /**

+ 30 - 0
src/Tools/babylon.observable.ts

@@ -38,6 +38,36 @@
     }
 
     /**
+     * Represent a list of observers registered to multiple Observables object.
+     */
+    export class MultiObserver<T> {
+        private _observers: Observer<T>[];
+        private _observables: Observable<T>[];
+        
+        public dispose(): void {
+            for (var index = 0; index < this._observers.length; index++) {
+                this._observables[index].remove(this._observers[index]);
+            }
+
+            this._observers = null;
+            this._observables = null;
+        }
+
+        public static Watch<T>(observables: Observable<T>[], callback: (eventData: T, eventState: EventState) => void, mask: number = -1, scope: any = null): MultiObserver<T> {
+            let result = new MultiObserver<T>();
+
+            result._observers = new Array<Observer<T>>();
+            result._observables = observables;            
+
+            for (var observable of observables) {
+                result._observers.push(observable.add(callback, mask, false, scope));
+            }
+
+            return result;
+        }
+    }
+
+    /**
      * The Observable class is a simple implementation of the Observable pattern.
      * There's one slight particularity though: a given Observable can notify its observer using a particular mask value, only the Observers registered with this mask value will be notified.
      * This enable a more fine grained execution without having to rely on multiple different Observable objects.

+ 2 - 2
src/Tools/babylon.tools.ts

@@ -563,7 +563,7 @@
             head.appendChild(script);
         }
 
-        public static ReadFileAsDataURL(fileToLoad, callback, progressCallback): void {
+        public static ReadFileAsDataURL(fileToLoad: Blob, callback, progressCallback): void {
             var reader = new FileReader();
             reader.onload = e => {
                 //target doesn't have result from ts 1.3
@@ -573,7 +573,7 @@
             reader.readAsDataURL(fileToLoad);
         }
 
-        public static ReadFile(fileToLoad, callback, progressCallBack, useArrayBuffer?: boolean): void {
+        public static ReadFile(fileToLoad: File, callback, progressCallBack, useArrayBuffer?: boolean): void {
             var reader = new FileReader();
             reader.onerror = e => {
                 Tools.Log("Error while reading file: " + fileToLoad.name);

+ 8 - 2
src/babylon.scene.ts

@@ -1916,6 +1916,12 @@
                 Frustum.GetPlanesToRef(this._transformMatrix, this._frustumPlanes);
             }
 
+            if (this.activeCamera._alternateCamera) {
+                let otherCamera = this.activeCamera._alternateCamera;
+                otherCamera.getViewMatrix().multiplyToRef(otherCamera.getProjectionMatrix(), Tmp.Matrix[0]);
+                Frustum.GetRightPlaneToRef(Tmp.Matrix[0], this._frustumPlanes[3]); // Replace right plane by second camera right plane
+            }
+
             if (this._sceneUbo.useUbo) {
                 this._sceneUbo.updateMatrix("viewProjection", this._transformMatrix);
                 this._sceneUbo.updateMatrix("view", this._viewMatrix);
@@ -4121,8 +4127,8 @@
             return hdrSkybox;
         }
 
-        public createDefaultVRExperience() {
-            this.VRHelper = new BABYLON.VRExperienceHelper(this, null);
+        public createDefaultVRExperience(webVROptions: WebVROptions = {}) {
+            this.VRHelper = new BABYLON.VRExperienceHelper(this, webVROptions);
         }
 
         // Tags