Forráskód Böngészése

Merge remote-tracking branch 'refs/remotes/BabylonJS/master' into ParticleAnimation

# Conflicts:
#	dist/preview release/babylon.d.ts
#	dist/preview release/babylon.js
#	dist/preview release/babylon.module.d.ts
#	dist/preview release/babylon.worker.js
DESKTOP-QJU4N0L\mityh 8 éve
szülő
commit
99ed989fa1
53 módosított fájl, 35279 hozzáadás és 33804 törlés
  1. 2 1
      Tools/Gulp/config.json
  2. 21 0
      assets/meshes/controllers/microsoft/045E-065B/LICENSE
  3. BIN
      assets/meshes/controllers/microsoft/045E-065B/left.glb
  4. BIN
      assets/meshes/controllers/microsoft/045E-065B/right.glb
  5. 21 0
      assets/meshes/controllers/microsoft/default/LICENSE
  6. BIN
      assets/meshes/controllers/microsoft/default/left.glb
  7. BIN
      assets/meshes/controllers/microsoft/default/right.glb
  8. 5231 5196
      dist/preview release/babylon.d.ts
  9. 45 45
      dist/preview release/babylon.js
  10. 452 61
      dist/preview release/babylon.max.js
  11. 5231 5196
      dist/preview release/babylon.module.d.ts
  12. 47 47
      dist/preview release/babylon.worker.js
  13. 10473 10416
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts
  14. 47 46
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js
  15. 2387 1988
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js
  16. 10473 10416
      dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts
  17. 3 3
      dist/preview release/gui/babylon.gui.min.js
  18. 265 265
      dist/preview release/inspector/babylon.inspector.bundle.js
  19. 3 3
      dist/preview release/inspector/babylon.inspector.min.js
  20. 2 2
      dist/preview release/loaders/babylon.glTF1FileLoader.min.js
  21. 1 0
      dist/preview release/loaders/babylon.glTF2FileLoader.d.ts
  22. 9 5
      dist/preview release/loaders/babylon.glTF2FileLoader.js
  23. 1 1
      dist/preview release/loaders/babylon.glTF2FileLoader.min.js
  24. 1 0
      dist/preview release/loaders/babylon.glTFFileLoader.d.ts
  25. 9 5
      dist/preview release/loaders/babylon.glTFFileLoader.js
  26. 3 3
      dist/preview release/loaders/babylon.glTFFileLoader.min.js
  27. 1 1
      dist/preview release/loaders/babylon.objFileLoader.min.js
  28. 1 1
      dist/preview release/materialsLibrary/babylon.customMaterial.min.js
  29. 1 1
      dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js
  30. 1 1
      dist/preview release/materialsLibrary/babylon.waterMaterial.min.js
  31. 1 1
      dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js
  32. 1 1
      dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js
  33. 1 0
      dist/preview release/what's new.md
  34. 10 4
      loaders/src/glTF/2.0/babylon.glTFLoader.ts
  35. 1 1
      loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts
  36. 16 6
      sandbox/index.js
  37. 5 1
      src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts
  38. 8 0
      src/Cameras/Inputs/babylon.freeCameraMouseInput.ts
  39. 25 23
      src/Cameras/VR/babylon.webVRCamera.ts
  40. 4 1
      src/Cameras/babylon.arcRotateCamera.ts
  41. 19 21
      src/Cameras/babylon.camera.ts
  42. 0 2
      src/Gamepad/Controllers/babylon.genericController.ts
  43. 0 2
      src/Gamepad/Controllers/babylon.oculusTouchController.ts
  44. 9 8
      src/Gamepad/Controllers/babylon.poseEnabledController.ts
  45. 0 1
      src/Gamepad/Controllers/babylon.viveController.ts
  46. 19 5
      src/Gamepad/Controllers/babylon.webVRController.ts
  47. 362 0
      src/Gamepad/Controllers/babylon.windowsMotionController.ts
  48. 6 0
      src/Gamepad/babylon.gamepad.ts
  49. 31 11
      src/Gamepad/babylon.gamepadManager.ts
  50. 1 1
      src/Mesh/babylon.meshBuilder.ts
  51. 10 2
      src/Tools/babylon.filesInput.ts
  52. 13 2
      src/babylon.engine.ts
  53. 6 8
      src/babylon.scene.ts

+ 2 - 1
Tools/Gulp/config.json

@@ -544,7 +544,8 @@
                 "../../src/Gamepad/Controllers/babylon.webVRController.js",
                 "../../src/Gamepad/Controllers/babylon.oculusTouchController.js",
                 "../../src/Gamepad/Controllers/babylon.viveController.js",
-                "../../src/Gamepad/Controllers/babylon.genericController.js"
+                "../../src/Gamepad/Controllers/babylon.genericController.js",
+                "../../src/Gamepad/Controllers/babylon.windowsMotionController.js"
             ],
             "dependUpon" : [
                 "core"

+ 21 - 0
assets/meshes/controllers/microsoft/045E-065B/LICENSE

@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright © Microsoft 2017. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

BIN
assets/meshes/controllers/microsoft/045E-065B/left.glb


BIN
assets/meshes/controllers/microsoft/045E-065B/right.glb


+ 21 - 0
assets/meshes/controllers/microsoft/default/LICENSE

@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright © Microsoft 2017. All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.

BIN
assets/meshes/controllers/microsoft/default/left.glb


BIN
assets/meshes/controllers/microsoft/default/right.glb


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 5231 - 5196
dist/preview release/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 45 - 45
dist/preview release/babylon.js


+ 452 - 61
dist/preview release/babylon.max.js

@@ -7629,6 +7629,7 @@ var BABYLON;
              * Observable event triggered each time the canvas receives pointerout event
              */
             this.onCanvasPointerOutObservable = new BABYLON.Observable();
+            this._vrExclusivePointerMode = false;
             // Uniform buffers list
             this.disableUniformBuffers = false;
             this._uniformBuffers = new Array();
@@ -7859,9 +7860,13 @@ var BABYLON;
                 document.addEventListener("mozpointerlockchange", this._onPointerLockChange, false);
                 document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);
                 this._onVRDisplayPointerRestricted = function () {
+                    _this._vrExclusivePointerMode = true;
+                    console.log("enter");
                     canvas.requestPointerLock();
                 };
                 this._onVRDisplayPointerUnrestricted = function () {
+                    _this._vrExclusivePointerMode = false;
+                    console.log("exit");
                     document.exitPointerLock();
                 };
                 window.addEventListener('vrdisplaypointerrestricted', this._onVRDisplayPointerRestricted, false);
@@ -8214,6 +8219,13 @@ var BABYLON;
             enumerable: true,
             configurable: true
         });
+        Object.defineProperty(Engine.prototype, "isInVRExclusivePointerMode", {
+            get: function () {
+                return this._vrExclusivePointerMode;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Object.defineProperty(Engine.prototype, "supportsUniformBuffers", {
             get: function () {
                 return this.webGLVersion > 1 && !this.disableUniformBuffers;
@@ -11001,8 +11013,10 @@ var BABYLON;
             }
             var anisotropicFilterExtension = this._caps.textureAnisotropicFilterExtension;
             var value = texture.anisotropicFilteringLevel;
-            if (internalTexture.samplingMode === BABYLON.Texture.NEAREST_SAMPLINGMODE) {
-                value = 1;
+            if (internalTexture.samplingMode !== BABYLON.Texture.LINEAR_LINEAR_MIPNEAREST
+                && internalTexture.samplingMode !== BABYLON.Texture.LINEAR_LINEAR_MIPLINEAR
+                && internalTexture.samplingMode !== BABYLON.Texture.LINEAR_LINEAR) {
+                value = 1; // Forcing the anisotropic to 1 because else webgl will force filters to linear
             }
             if (anisotropicFilterExtension && internalTexture._cachedAnisotropicFilteringLevel !== value) {
                 this._gl.texParameterf(key, anisotropicFilterExtension.TEXTURE_MAX_ANISOTROPY_EXT, Math.min(value, this._caps.maxAnisotropy));
@@ -15200,22 +15214,6 @@ var BABYLON;
             var engine = this.getEngine();
             this._cache.position.copyFrom(this.position);
             this._cache.upVector.copyFrom(this.upVector);
-            this._cache.mode = this.mode;
-            this._cache.minZ = this.minZ;
-            this._cache.maxZ = this.maxZ;
-            this._cache.fov = this.fov;
-            this._cache.fovMode = this.fovMode;
-            this._cache.aspectRatio = engine.getAspectRatio(this);
-            this._cache.orthoLeft = this.orthoLeft;
-            this._cache.orthoRight = this.orthoRight;
-            this._cache.orthoBottom = this.orthoBottom;
-            this._cache.orthoTop = this.orthoTop;
-            this._cache.renderWidth = engine.getRenderWidth();
-            this._cache.renderHeight = engine.getRenderHeight();
-        };
-        Camera.prototype._updateFromScene = function () {
-            this.updateCache();
-            this.update();
         };
         // Synchronized
         Camera.prototype._isSynchronized = function () {
@@ -15257,10 +15255,10 @@ var BABYLON;
         Camera.prototype.detachControl = function (element) {
         };
         Camera.prototype.update = function () {
+            this._checkInputs();
             if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
                 this._updateRigCameras();
             }
-            this._checkInputs();
         };
         Camera.prototype._checkInputs = function () {
             this.onAfterCheckInputsObservable.notifyObservers(this);
@@ -15359,6 +15357,7 @@ var BABYLON;
             if (!force && this._isSynchronizedViewMatrix()) {
                 return this._computedViewMatrix;
             }
+            this.updateCache();
             this._computedViewMatrix = this._getViewMatrix();
             this._currentRenderId = this.getScene().getRenderId();
             this._refreshFrustumPlanes = true;
@@ -15396,10 +15395,18 @@ var BABYLON;
             if (this._doNotComputeProjectionMatrix || (!force && this._isSynchronizedProjectionMatrix())) {
                 return this._projectionMatrix;
             }
+            // Cache
+            this._cache.mode = this.mode;
+            this._cache.minZ = this.minZ;
+            this._cache.maxZ = this.maxZ;
+            // Matrix
             this._refreshFrustumPlanes = true;
             var engine = this.getEngine();
             var scene = this.getScene();
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
+                this._cache.fov = this.fov;
+                this._cache.fovMode = this.fovMode;
+                this._cache.aspectRatio = engine.getAspectRatio(this);
                 if (this.minZ <= 0) {
                     this.minZ = 0.1;
                 }
@@ -15419,6 +15426,12 @@ var BABYLON;
                 else {
                     BABYLON.Matrix.OrthoOffCenterLHToRef(this.orthoLeft || -halfWidth, this.orthoRight || halfWidth, this.orthoBottom || -halfHeight, this.orthoTop || halfHeight, this.minZ, this.maxZ, this._projectionMatrix);
                 }
+                this._cache.orthoLeft = this.orthoLeft;
+                this._cache.orthoRight = this.orthoRight;
+                this._cache.orthoBottom = this.orthoBottom;
+                this._cache.orthoTop = this.orthoTop;
+                this._cache.renderWidth = engine.getRenderWidth();
+                this._cache.renderHeight = engine.getRenderHeight();
             }
             this.onProjectionMatrixChangedObservable.notifyObservers(this);
             return this._projectionMatrix;
@@ -18804,6 +18817,7 @@ var BABYLON;
             // Camera
             this.resetCachedMaterial();
             this._renderId++;
+            this.activeCamera.update();
             this.updateTransformMatrix();
             if (camera._alternateCamera) {
                 this.updateAlternateTransformMatrix(camera._alternateCamera);
@@ -18938,8 +18952,6 @@ var BABYLON;
             this._renderDuration.endMonitoring(false);
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
-            // Update camera
-            this.activeCamera._updateFromScene();
             // Reset some special arrays
             this._renderTargets.reset();
             this._alternateRendering = false;
@@ -18951,14 +18963,14 @@ var BABYLON;
                 this._renderForCamera(camera);
                 return;
             }
+            // Update camera
+            this.activeCamera.update();
             // rig cameras
             for (var index = 0; index < camera._rigCameras.length; index++) {
                 this._renderForCamera(camera._rigCameras[index]);
             }
             this.activeCamera = camera;
             this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
-            // Update camera
-            this.activeCamera._updateFromScene();
         };
         Scene.prototype._checkIntersections = function () {
             for (var index = 0; index < this._meshesForIntersections.length; index++) {
@@ -34373,6 +34385,9 @@ var BABYLON;
             if (!this._pointerInput) {
                 this._pointerInput = function (p, s) {
                     var evt = p.event;
+                    if (engine.isInVRExclusivePointerMode) {
+                        return;
+                    }
                     if (!_this.touchEnabled && evt.pointerType === "touch") {
                         return;
                     }
@@ -34434,6 +34449,9 @@ var BABYLON;
                 if (!engine.isPointerLock) {
                     return;
                 }
+                if (engine.isInVRExclusivePointerMode) {
+                    return;
+                }
                 var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
                 var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
                 if (_this.camera.getScene().useRightHandedSystem) {
@@ -35379,6 +35397,9 @@ var BABYLON;
             var previousMultiTouchPanPosition = { x: 0, y: 0, isPaning: false };
             this._pointerInput = function (p, s) {
                 var evt = p.event;
+                if (engine.isInVRExclusivePointerMode) {
+                    return;
+                }
                 if (p.type !== BABYLON.PointerEventTypes.POINTERMOVE && _this.buttons.indexOf(evt.button) === -1) {
                     return;
                 }
@@ -44513,7 +44534,7 @@ var BABYLON;
             var height = options.height || 10.0;
             var subdivisions = options.subdivisions || 1 | 0;
             var minHeight = options.minHeight || 0.0;
-            var maxHeight = options.maxHeight || 10.0;
+            var maxHeight = options.maxHeight || 1.0;
             var filter = options.colorFilter || new BABYLON.Color3(0.3, 0.59, 0.11);
             var updatable = options.updatable;
             var onReady = options.onReady;
@@ -49462,6 +49483,7 @@ var BABYLON;
 (function (BABYLON) {
     var FilesInput = (function () {
         function FilesInput(engine, scene, sceneLoadedCallback, progressCallback, additionalRenderLoopLogicCallback, textureLoadingCallback, startingProcessingFilesCallback, onReloadCallback) {
+            this.onProcessFileCallback = function () { return true; };
             this._engine = engine;
             this._currentScene = scene;
             this._sceneLoadedCallback = sceneLoadedCallback;
@@ -49540,9 +49562,14 @@ var BABYLON;
             });
         };
         FilesInput.prototype._processFiles = function (files) {
+            var skippedFiles = 0;
             for (var i = 0; i < files.length; i++) {
                 var name = files[i].correctName.toLowerCase();
                 var extension = name.split('.').pop();
+                if (!this.onProcessFileCallback(files[i], name, extension)) {
+                    skippedFiles++;
+                    continue;
+                }
                 if ((extension === "babylon" || extension === "stl" || extension === "obj" || extension === "gltf" || extension === "glb")
                     && name.indexOf(".binary.babylon") === -1 && name.indexOf(".incremental.babylon") === -1) {
                     this._sceneFileToLoad = files[i];
@@ -49554,7 +49581,7 @@ var BABYLON;
             if (this._onReloadCallback) {
                 this._onReloadCallback(this._sceneFileToLoad);
             }
-            else {
+            else if (skippedFiles < files.length) {
                 this.reload();
             }
         };
@@ -51510,12 +51537,20 @@ var BABYLON;
             this.onGamepadDisconnectedObservable = new BABYLON.Observable();
             this._onGamepadConnectedEvent = function (evt) {
                 var gamepad = evt.gamepad;
-                // Protection code for Chrome which has a very buggy gamepad implementation...
-                // And raises a connected event on disconnection for instance
                 if (gamepad.index in _this._babylonGamepads) {
-                    return;
+                    if (_this._babylonGamepads[gamepad.index].isConnected) {
+                        return;
+                    }
+                }
+                var newGamepad;
+                if (_this._babylonGamepads[gamepad.index]) {
+                    newGamepad = _this._babylonGamepads[gamepad.index];
+                    newGamepad.browserGamepad = gamepad;
+                    newGamepad._isConnected = true;
+                }
+                else {
+                    newGamepad = _this._addNewGamepad(gamepad);
                 }
-                var newGamepad = _this._addNewGamepad(gamepad);
                 _this.onGamepadConnectedObservable.notifyObservers(newGamepad);
                 _this._startMonitoringGamepads();
             };
@@ -51524,10 +51559,9 @@ var BABYLON;
                 // Remove the gamepad from the list of gamepads to monitor.
                 for (var i in _this._babylonGamepads) {
                     if (_this._babylonGamepads[i].index === gamepad.index) {
-                        var gamepadToRemove = _this._babylonGamepads[i];
-                        _this._babylonGamepads[i] = null;
-                        _this.onGamepadDisconnectedObservable.notifyObservers(gamepadToRemove);
-                        gamepadToRemove.dispose();
+                        var disconnectedGamepad = _this._babylonGamepads[i];
+                        disconnectedGamepad._isConnected = false;
+                        _this.onGamepadDisconnectedObservable.notifyObservers(disconnectedGamepad);
                         break;
                     }
                 }
@@ -51548,6 +51582,13 @@ var BABYLON;
                 }
             }
         }
+        Object.defineProperty(GamepadManager.prototype, "gamepads", {
+            get: function () {
+                return this._babylonGamepads;
+            },
+            enumerable: true,
+            configurable: true
+        });
         GamepadManager.prototype.getGamepadByType = function (type) {
             if (type === void 0) { type = BABYLON.Gamepad.XBOX; }
             for (var _i = 0, _a = this._babylonGamepads; _i < _a.length; _i++) {
@@ -51565,6 +51606,10 @@ var BABYLON;
                 this._onGamepadConnectedEvent = null;
                 this._onGamepadDisconnectedEvent = null;
             }
+            for (var _i = 0, _a = this._babylonGamepads; _i < _a.length; _i++) {
+                var gamepad = _a[_i];
+                gamepad.dispose();
+            }
             this._oneGamepadConnected = false;
             this._stopMonitoringGamepads();
             this._babylonGamepads = [];
@@ -51602,7 +51647,7 @@ var BABYLON;
             this._updateGamepadObjects();
             for (var i in this._babylonGamepads) {
                 var gamepad = this._babylonGamepads[i];
-                if (!gamepad) {
+                if (!gamepad || !gamepad.isConnected) {
                     continue;
                 }
                 gamepad.update();
@@ -51624,6 +51669,10 @@ var BABYLON;
                     else {
                         // Forced to copy again this object for Chrome for unknown reason
                         this._babylonGamepads[i].browserGamepad = gamepads[i];
+                        if (!this._babylonGamepads[i].isConnected) {
+                            this._babylonGamepads[i]._isConnected = true;
+                            this.onGamepadConnectedObservable.notifyObservers(this._babylonGamepads[i]);
+                        }
                     }
                 }
             }
@@ -51655,6 +51704,7 @@ var BABYLON;
             this.id = id;
             this.index = index;
             this.browserGamepad = browserGamepad;
+            this._isConnected = true;
             this.type = Gamepad.GAMEPAD;
             this._leftStickAxisX = leftStickX;
             this._leftStickAxisY = leftStickY;
@@ -51667,6 +51717,13 @@ var BABYLON;
                 this._rightStick = { x: this.browserGamepad.axes[this._rightStickAxisX], y: this.browserGamepad.axes[this._rightStickAxisY] };
             }
         }
+        Object.defineProperty(Gamepad.prototype, "isConnected", {
+            get: function () {
+                return this._isConnected;
+            },
+            enumerable: true,
+            configurable: true
+        });
         Gamepad.prototype.onleftstickchanged = function (callback) {
             this._onleftstickchanged = callback;
         };
@@ -52073,6 +52130,9 @@ var BABYLON;
             if (vrGamepad.id.indexOf('Oculus Touch') !== -1) {
                 return new BABYLON.OculusTouchController(vrGamepad);
             }
+            else if (vrGamepad.id.indexOf(BABYLON.WindowsMotionController.GAMEPAD_ID_PREFIX) === 0) {
+                return new BABYLON.WindowsMotionController(vrGamepad);
+            }
             else if (vrGamepad.id.toLowerCase().indexOf('openvr') !== -1) {
                 return new BABYLON.ViveController(vrGamepad);
             }
@@ -52085,9 +52145,8 @@ var BABYLON;
     BABYLON.PoseEnabledControllerHelper = PoseEnabledControllerHelper;
     var PoseEnabledController = (function (_super) {
         __extends(PoseEnabledController, _super);
-        function PoseEnabledController(vrGamepad) {
-            var _this = _super.call(this, vrGamepad.id, vrGamepad.index, vrGamepad) || this;
-            _this.vrGamepad = vrGamepad;
+        function PoseEnabledController(browserGamepad) {
+            var _this = _super.call(this, browserGamepad.id, browserGamepad.index, browserGamepad) || this;
             _this.deviceScaleFactor = 1;
             _this._leftHandSystemQuaternion = new BABYLON.Quaternion();
             _this.type = BABYLON.Gamepad.POSE_ENABLED;
@@ -52103,7 +52162,7 @@ var BABYLON;
         }
         PoseEnabledController.prototype.update = function () {
             _super.prototype.update.call(this);
-            var pose = this.vrGamepad.pose;
+            var pose = this.browserGamepad.pose;
             this.updateFromDevice(pose);
             if (this._mesh) {
                 this._mesh.position.copyFrom(this._calculatedPosition);
@@ -52195,6 +52254,7 @@ var BABYLON;
         __extends(WebVRController, _super);
         function WebVRController(vrGamepad) {
             var _this = _super.call(this, vrGamepad) || this;
+            // Observables
             _this.onTriggerStateChangedObservable = new BABYLON.Observable();
             _this.onMainButtonStateChangedObservable = new BABYLON.Observable();
             _this.onSecondaryButtonStateChangedObservable = new BABYLON.Observable();
@@ -52215,10 +52275,17 @@ var BABYLON;
         WebVRController.prototype.onButtonStateChange = function (callback) {
             this._onButtonStateChange = callback;
         };
+        Object.defineProperty(WebVRController.prototype, "defaultModel", {
+            get: function () {
+                return this._defaultModel;
+            },
+            enumerable: true,
+            configurable: true
+        });
         WebVRController.prototype.update = function () {
             _super.prototype.update.call(this);
             for (var index = 0; index < this._buttons.length; index++) {
-                this._setButtonValue(this.vrGamepad.buttons[index], this._buttons[index], index);
+                this._setButtonValue(this.browserGamepad.buttons[index], this._buttons[index], index);
             }
             ;
             if (this.leftStick.x !== this.pad.x || this.leftStick.y !== this.pad.y) {
@@ -52260,6 +52327,14 @@ var BABYLON;
             this._changes.changed = this._changes.pressChanged || this._changes.touchChanged || this._changes.valueChanged;
             return this._changes;
         };
+        WebVRController.prototype.dispose = function () {
+            _super.prototype.dispose.call(this);
+            this.onTriggerStateChangedObservable.clear();
+            this.onMainButtonStateChangedObservable.clear();
+            this.onSecondaryButtonStateChangedObservable.clear();
+            this.onPadStateChangedObservable.clear();
+            this.onPadValuesChangedObservable.clear();
+        };
         return WebVRController;
     }(BABYLON.PoseEnabledController));
     BABYLON.WebVRController = WebVRController;
@@ -52535,6 +52610,319 @@ var BABYLON;
 
 //# sourceMappingURL=babylon.genericController.js.map
 
+
+var BABYLON;
+(function (BABYLON) {
+    var LoadedMeshInfo = (function () {
+        function LoadedMeshInfo() {
+            this.buttonMeshes = {};
+            this.axisMeshes = {};
+        }
+        return LoadedMeshInfo;
+    }());
+    var WindowsMotionController = (function (_super) {
+        __extends(WindowsMotionController, _super);
+        function WindowsMotionController(vrGamepad) {
+            var _this = _super.call(this, vrGamepad) || this;
+            _this._mapping = {
+                // Semantic button names
+                buttons: ['thumbstick', 'trigger', 'grip', 'menu', 'trackpad'],
+                // A mapping of the button name to glTF model node name
+                // that should be transformed by button value.
+                buttonMeshNames: {
+                    'trigger': 'SELECT',
+                    'menu': 'MENU',
+                    'grip': 'GRASP',
+                    'thumbstick': 'THUMBSTICK_PRESS',
+                    'trackpad': 'TOUCHPAD_PRESS'
+                },
+                // This mapping is used to translate from the Motion Controller to Babylon semantics
+                buttonObservableNames: {
+                    'trigger': 'onTriggerStateChangedObservable',
+                    'menu': 'onSecondaryButtonStateChangedObservable',
+                    'grip': 'onMainButtonStateChangedObservable',
+                    'thumbstick': 'onPadStateChangedObservable',
+                    'trackpad': 'onTrackpadChangedObservable'
+                },
+                // A mapping of the axis name to glTF model node name
+                // that should be transformed by axis value.
+                // This array mirrors the browserGamepad.axes array, such that 
+                // the mesh corresponding to axis 0 is in this array index 0.
+                axisMeshNames: [
+                    'THUMBSTICK_X',
+                    'THUMBSTICK_Y',
+                    'TOUCHPAD_TOUCH_X',
+                    'TOUCHPAD_TOUCH_Y'
+                ]
+            };
+            _this.onTrackpadChangedObservable = new BABYLON.Observable();
+            _this.controllerType = BABYLON.PoseEnabledControllerType.WINDOWS;
+            _this._loadedMeshInfo = null;
+            return _this;
+        }
+        Object.defineProperty(WindowsMotionController.prototype, "onTriggerButtonStateChangedObservable", {
+            get: function () {
+                return this.onTriggerStateChangedObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(WindowsMotionController.prototype, "onMenuButtonStateChangedObservable", {
+            get: function () {
+                return this.onSecondaryButtonStateChangedObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(WindowsMotionController.prototype, "onGripButtonStateChangedObservable", {
+            get: function () {
+                return this.onMainButtonStateChangedObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(WindowsMotionController.prototype, "onThumbstickButtonStateChangedObservable", {
+            get: function () {
+                return this.onPadStateChangedObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        Object.defineProperty(WindowsMotionController.prototype, "onTouchpadButtonStateChangedObservable", {
+            get: function () {
+                return this.onTrackpadChangedObservable;
+            },
+            enumerable: true,
+            configurable: true
+        });
+        /**
+         * Called once per frame by the engine.
+         */
+        WindowsMotionController.prototype.update = function () {
+            _super.prototype.update.call(this);
+            // Only need to animate axes if there is a loaded mesh
+            if (this._loadedMeshInfo) {
+                if (this.browserGamepad.axes) {
+                    for (var axis = 0; axis < this._mapping.axisMeshNames.length; axis++) {
+                        this.lerpAxisTransform(axis, this.browserGamepad.axes[axis]);
+                    }
+                }
+            }
+        };
+        /**
+         * Called once for each button that changed state since the last frame
+         * @param buttonIdx Which button index changed
+         * @param state New state of the button
+         * @param changes Which properties on the state changed since last frame
+         */
+        WindowsMotionController.prototype.handleButtonChange = function (buttonIdx, state, changes) {
+            var buttonName = this._mapping.buttons[buttonIdx];
+            if (!buttonName) {
+                return;
+            }
+            // Only emit events for buttons that we know how to map from index to name
+            var observable = this[this._mapping.buttonObservableNames[buttonName]];
+            if (observable) {
+                observable.notifyObservers(state);
+            }
+            this.lerpButtonTransform(buttonName, state.value);
+        };
+        WindowsMotionController.prototype.lerpButtonTransform = function (buttonName, buttonValue) {
+            // If there is no loaded mesh, there is nothing to transform.
+            if (!this._loadedMeshInfo) {
+                return;
+            }
+            var meshInfo = this._loadedMeshInfo.buttonMeshes[buttonName];
+            BABYLON.Quaternion.SlerpToRef(meshInfo.unpressed.rotationQuaternion, meshInfo.pressed.rotationQuaternion, buttonValue, meshInfo.value.rotationQuaternion);
+            BABYLON.Vector3.LerpToRef(meshInfo.unpressed.position, meshInfo.pressed.position, buttonValue, meshInfo.value.position);
+        };
+        WindowsMotionController.prototype.lerpAxisTransform = function (axis, axisValue) {
+            var meshInfo = this._loadedMeshInfo.axisMeshes[axis];
+            if (!meshInfo) {
+                return;
+            }
+            // Convert from gamepad value range (-1 to +1) to lerp range (0 to 1)
+            var lerpValue = axisValue * 0.5 + 0.5;
+            BABYLON.Quaternion.SlerpToRef(meshInfo.min.rotationQuaternion, meshInfo.max.rotationQuaternion, lerpValue, meshInfo.value.rotationQuaternion);
+            BABYLON.Vector3.LerpToRef(meshInfo.min.position, meshInfo.max.position, lerpValue, meshInfo.value.position);
+        };
+        /**
+         * Implements abstract method on WebVRController class, loading controller meshes and calling this.attachToMesh if successful.
+         * @param scene scene in which to add meshes
+         * @param meshLoaded optional callback function that will be called if the mesh loads successfully.
+         */
+        WindowsMotionController.prototype.initControllerMesh = function (scene, meshLoaded, forceDefault) {
+            var _this = this;
+            if (forceDefault === void 0) { forceDefault = false; }
+            // Determine the device specific folder based on the ID suffix
+            var device = 'default';
+            if (this.id && !forceDefault) {
+                var match = this.id.match(WindowsMotionController.GAMEPAD_ID_PATTERN);
+                device = ((match && match[0]) || device);
+            }
+            // Hand
+            var filename;
+            if (this.hand === 'left') {
+                filename = WindowsMotionController.MODEL_LEFT_FILENAME;
+            }
+            else {
+                filename = WindowsMotionController.MODEL_RIGHT_FILENAME;
+            }
+            var path = WindowsMotionController.MODEL_BASE_URL + device + '/';
+            BABYLON.SceneLoader.ImportMesh("", path, filename, scene, function (meshes) {
+                // glTF files successfully loaded from the remote server, now process them to ensure they are in the right format.
+                _this._loadedMeshInfo = _this.processModel(scene, meshes);
+                if (!_this._loadedMeshInfo) {
+                    return;
+                }
+                _this._defaultModel = _this._loadedMeshInfo.rootNode;
+                _this.attachToMesh(_this._defaultModel);
+                if (meshLoaded) {
+                    meshLoaded(_this._defaultModel);
+                }
+            }, null, function (scene, message) {
+                BABYLON.Tools.Log(message);
+                BABYLON.Tools.Warn('Failed to retrieve controller model from the remote server: ' + path + filename);
+                _this.initControllerMesh(scene, meshLoaded, true);
+            });
+        };
+        /**
+         * Takes a list of meshes (as loaded from the glTF file) and finds the root node, as well as nodes that
+         * can be transformed by button presses and axes values, based on this._mapping.
+         *
+         * @param scene scene in which the meshes exist
+         * @param meshes list of meshes that make up the controller model to process
+         * @return structured view of the given meshes, with mapping of buttons and axes to meshes that can be transformed.
+         */
+        WindowsMotionController.prototype.processModel = function (scene, meshes) {
+            var loadedMeshInfo = null;
+            // Create a new mesh to contain the glTF hierarchy
+            var parentMesh = new BABYLON.Mesh(this.id + " " + this.hand, scene);
+            // Find the root node in the loaded glTF scene, and attach it as a child of 'parentMesh'
+            var childMesh = null;
+            for (var i = 0; i < meshes.length; i++) {
+                var mesh = meshes[i];
+                if (mesh.id === WindowsMotionController.MODEL_ROOT_NODE_NAME) {
+                    // There may be a parent mesh to perform the RH to LH matrix transform.
+                    // Exclude controller meshes from picking results
+                    mesh.isPickable = false;
+                    // Handle root node, attach to the new parentMesh
+                    if (mesh.parent && mesh.parent.name === WindowsMotionController.GLTF_ROOT_TRANSFORM_NAME)
+                        mesh = mesh.parent;
+                    childMesh = mesh;
+                    break;
+                }
+            }
+            if (childMesh) {
+                childMesh.setParent(parentMesh);
+                // Create our mesh info. Note that this method will always return non-null.
+                loadedMeshInfo = this.createMeshInfo(parentMesh);
+                // Apply rotation offsets
+                var rotOffset = WindowsMotionController.ROTATE_OFFSET;
+                childMesh.addRotation(rotOffset[0], rotOffset[1], rotOffset[2]);
+            }
+            else {
+                BABYLON.Tools.Warn('No node with name ' + WindowsMotionController.MODEL_ROOT_NODE_NAME + ' in model file.');
+            }
+            return loadedMeshInfo;
+        };
+        WindowsMotionController.prototype.createMeshInfo = function (rootNode) {
+            var loadedMeshInfo = new LoadedMeshInfo();
+            var i;
+            loadedMeshInfo.rootNode = rootNode;
+            // Reset the caches
+            loadedMeshInfo.buttonMeshes = {};
+            loadedMeshInfo.axisMeshes = {};
+            // Button Meshes
+            for (i = 0; i < this._mapping.buttons.length; i++) {
+                var buttonMeshName = this._mapping.buttonMeshNames[this._mapping.buttons[i]];
+                if (!buttonMeshName) {
+                    BABYLON.Tools.Log('Skipping unknown button at index: ' + i + ' with mapped name: ' + this._mapping.buttons[i]);
+                    continue;
+                }
+                var buttonMesh = getChildByName(rootNode, buttonMeshName);
+                if (!buttonMesh) {
+                    BABYLON.Tools.Warn('Missing button mesh with name: ' + buttonMeshName);
+                    continue;
+                }
+                var buttonMeshInfo = {
+                    index: i,
+                    value: getImmediateChildByName(buttonMesh, 'VALUE'),
+                    pressed: getImmediateChildByName(buttonMesh, 'PRESSED'),
+                    unpressed: getImmediateChildByName(buttonMesh, 'UNPRESSED')
+                };
+                if (buttonMeshInfo.value && buttonMeshInfo.pressed && buttonMeshInfo.unpressed) {
+                    loadedMeshInfo.buttonMeshes[this._mapping.buttons[i]] = buttonMeshInfo;
+                }
+                else {
+                    // If we didn't find the mesh, it simply means this button won't have transforms applied as mapped button value changes.
+                    BABYLON.Tools.Warn('Missing button submesh under mesh with name: ' + buttonMeshName +
+                        '(VALUE: ' + !!buttonMeshInfo.value +
+                        ', PRESSED: ' + !!buttonMeshInfo.pressed +
+                        ', UNPRESSED:' + !!buttonMeshInfo.unpressed +
+                        ')');
+                }
+            }
+            // Axis Meshes
+            for (i = 0; i < this._mapping.axisMeshNames.length; i++) {
+                var axisMeshName = this._mapping.axisMeshNames[i];
+                if (!axisMeshName) {
+                    BABYLON.Tools.Log('Skipping unknown axis at index: ' + i);
+                    continue;
+                }
+                var axisMesh = getChildByName(rootNode, axisMeshName);
+                if (!axisMesh) {
+                    BABYLON.Tools.Warn('Missing axis mesh with name: ' + axisMeshName);
+                    continue;
+                }
+                var axisMeshInfo = {
+                    index: i,
+                    value: getImmediateChildByName(axisMesh, 'VALUE'),
+                    min: getImmediateChildByName(axisMesh, 'MIN'),
+                    max: getImmediateChildByName(axisMesh, 'MAX')
+                };
+                if (axisMeshInfo.value && axisMeshInfo.min && axisMeshInfo.max) {
+                    loadedMeshInfo.axisMeshes[i] = axisMeshInfo;
+                }
+                else {
+                    // If we didn't find the mesh, it simply means thit axis won't have transforms applied as mapped axis values change.
+                    BABYLON.Tools.Warn('Missing axis submesh under mesh with name: ' + axisMeshName +
+                        '(VALUE: ' + !!axisMeshInfo.value +
+                        ', MIN: ' + !!axisMeshInfo.min +
+                        ', MAX:' + !!axisMeshInfo.max +
+                        ')');
+                }
+            }
+            return loadedMeshInfo;
+            // Look through all children recursively. This will return null if no mesh exists with the given name.
+            function getChildByName(node, name) {
+                return node.getChildMeshes(false, function (n) { return n.name === name; })[0];
+            }
+            // Look through only immediate children. This will return null if no mesh exists with the given name.
+            function getImmediateChildByName(node, name) {
+                return node.getChildMeshes(true, function (n) { return n.name == name; })[0];
+            }
+        };
+        WindowsMotionController.prototype.dispose = function () {
+            _super.prototype.dispose.call(this);
+            this.onTrackpadChangedObservable.clear();
+        };
+        WindowsMotionController.MODEL_BASE_URL = 'https://controllers.babylonjs.com/microsoft/';
+        WindowsMotionController.MODEL_LEFT_FILENAME = 'left.glb';
+        WindowsMotionController.MODEL_RIGHT_FILENAME = 'right.glb';
+        WindowsMotionController.MODEL_ROOT_NODE_NAME = 'RootNode';
+        WindowsMotionController.GLTF_ROOT_TRANSFORM_NAME = 'root';
+        WindowsMotionController.GAMEPAD_ID_PREFIX = 'Spatial Controller (Spatial Interaction Source) ';
+        WindowsMotionController.GAMEPAD_ID_PATTERN = /([0-9a-zA-Z]+-[0-9a-zA-Z]+)$/;
+        // Art assets is backward facing
+        WindowsMotionController.ROTATE_OFFSET = [Math.PI, 0, 0]; // x, y, z.
+        return WindowsMotionController;
+    }(BABYLON.WebVRController));
+    BABYLON.WindowsMotionController = WindowsMotionController;
+})(BABYLON || (BABYLON = {}));
+
+//# sourceMappingURL=babylon.windowsMotionController.js.map
+
 /// <reference path="babylon.targetCamera.ts" />
 
 
@@ -66704,36 +67092,39 @@ var BABYLON;
             this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add(function (gamepad) {
                 if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
                     var webVrController = gamepad;
-                    var index = _this.controllers.indexOf(webVrController);
-                    if (index === -1) {
-                        // we are good
-                        return;
+                    if (webVrController.defaultModel) {
+                        webVrController.defaultModel.setEnabled(false);
                     }
-                    _this.controllers.splice(index, 1);
                 }
             });
             this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add(function (gamepad) {
                 if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
                     var webVrController = gamepad;
                     if (_this.webVROptions.controllerMeshes) {
-                        webVrController.initControllerMesh(_this.getScene(), function (loadedMesh) {
-                            if (_this.webVROptions.defaultLightingOnControllers) {
-                                if (!_this._lightOnControllers) {
-                                    _this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), _this.getScene());
-                                }
-                                var activateLightOnSubMeshes_1 = function (mesh, light) {
-                                    var children = mesh.getChildren();
-                                    if (children.length !== 0) {
-                                        children.forEach(function (mesh) {
-                                            light.includedOnlyMeshes.push(mesh);
-                                            activateLightOnSubMeshes_1(mesh, light);
-                                        });
+                        if (webVrController.defaultModel) {
+                            webVrController.defaultModel.setEnabled(true);
+                        }
+                        else {
+                            // Load the meshes
+                            webVrController.initControllerMesh(_this.getScene(), function (loadedMesh) {
+                                if (_this.webVROptions.defaultLightingOnControllers) {
+                                    if (!_this._lightOnControllers) {
+                                        _this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), _this.getScene());
                                     }
-                                };
-                                _this._lightOnControllers.includedOnlyMeshes.push(loadedMesh);
-                                activateLightOnSubMeshes_1(loadedMesh, _this._lightOnControllers);
-                            }
-                        });
+                                    var activateLightOnSubMeshes_1 = function (mesh, light) {
+                                        var children = mesh.getChildren();
+                                        if (children.length !== 0) {
+                                            children.forEach(function (mesh) {
+                                                light.includedOnlyMeshes.push(mesh);
+                                                activateLightOnSubMeshes_1(mesh, light);
+                                            });
+                                        }
+                                    };
+                                    _this._lightOnControllers.includedOnlyMeshes.push(loadedMesh);
+                                    activateLightOnSubMeshes_1(loadedMesh, _this._lightOnControllers);
+                                }
+                            });
+                        }
                     }
                     webVrController.attachToPoseControlledCamera(_this);
                     // since this is async - sanity check. Is the controller already stored?

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 5231 - 5196
dist/preview release/babylon.module.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 47 - 47
dist/preview release/babylon.worker.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 10473 - 10416
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 47 - 46
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2387 - 1988
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.max.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 10473 - 10416
dist/preview release/customConfigurations/minimalGLTFViewer/babylon.module.d.ts


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 3
dist/preview release/gui/babylon.gui.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 265 - 265
dist/preview release/inspector/babylon.inspector.bundle.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 3
dist/preview release/inspector/babylon.inspector.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 2 - 2
dist/preview release/loaders/babylon.glTF1FileLoader.min.js


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

@@ -301,6 +301,7 @@ declare module BABYLON.GLTF2 {
         private _disposed;
         private _blockPendingTracking;
         private _nonBlockingData;
+        private _rootMesh;
         private _renderReadyObservable;
         private _renderPendingCount;
         private _loaderPendingCount;

+ 9 - 5
dist/preview release/loaders/babylon.glTF2FileLoader.js

@@ -462,21 +462,25 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._addRightHandToLeftHandRootTransform = function () {
-                var rootMesh = new BABYLON.Mesh("root", this._babylonScene);
-                rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
-                rootMesh.rotation.y = Math.PI;
+                this._rootMesh = new BABYLON.Mesh("root", this._babylonScene);
+                this._rootMesh.isVisible = false;
+                this._rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
+                this._rootMesh.rotation.y = Math.PI;
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     for (var i = 0; i < nodes.length; i++) {
                         var mesh = nodes[i].babylonMesh;
                         if (mesh && !mesh.parent) {
-                            mesh.parent = rootMesh;
+                            mesh.parent = this._rootMesh;
                         }
                     }
                 }
             };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = [];
+                if (this._rootMesh) {
+                    meshes.push(this._rootMesh);
+                }
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     nodes.forEach(function (node) {
@@ -1420,7 +1424,7 @@ var BABYLON;
             GLTFUtils.GetTextureSamplingMode = function (magFilter, minFilter) {
                 // Set defaults if undefined
                 magFilter = magFilter === undefined ? GLTF2.ETextureMagFilter.LINEAR : magFilter;
-                minFilter = minFilter === undefined ? GLTF2.ETextureMinFilter.LINEAR_MIPMAP_NEAREST : minFilter;
+                minFilter = minFilter === undefined ? GLTF2.ETextureMinFilter.LINEAR_MIPMAP_LINEAR : minFilter;
                 if (magFilter === GLTF2.ETextureMagFilter.LINEAR) {
                     switch (minFilter) {
                         case GLTF2.ETextureMinFilter.NEAREST: return BABYLON.Texture.LINEAR_NEAREST;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/loaders/babylon.glTF2FileLoader.min.js


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

@@ -797,6 +797,7 @@ declare module BABYLON.GLTF2 {
         private _disposed;
         private _blockPendingTracking;
         private _nonBlockingData;
+        private _rootMesh;
         private _renderReadyObservable;
         private _renderPendingCount;
         private _loaderPendingCount;

+ 9 - 5
dist/preview release/loaders/babylon.glTFFileLoader.js

@@ -2620,21 +2620,25 @@ var BABYLON;
                 }
             };
             GLTFLoader.prototype._addRightHandToLeftHandRootTransform = function () {
-                var rootMesh = new BABYLON.Mesh("root", this._babylonScene);
-                rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
-                rootMesh.rotation.y = Math.PI;
+                this._rootMesh = new BABYLON.Mesh("root", this._babylonScene);
+                this._rootMesh.isVisible = false;
+                this._rootMesh.scaling = new BABYLON.Vector3(1, 1, -1);
+                this._rootMesh.rotation.y = Math.PI;
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     for (var i = 0; i < nodes.length; i++) {
                         var mesh = nodes[i].babylonMesh;
                         if (mesh && !mesh.parent) {
-                            mesh.parent = rootMesh;
+                            mesh.parent = this._rootMesh;
                         }
                     }
                 }
             };
             GLTFLoader.prototype._getMeshes = function () {
                 var meshes = [];
+                if (this._rootMesh) {
+                    meshes.push(this._rootMesh);
+                }
                 var nodes = this._gltf.nodes;
                 if (nodes) {
                     nodes.forEach(function (node) {
@@ -3578,7 +3582,7 @@ var BABYLON;
             GLTFUtils.GetTextureSamplingMode = function (magFilter, minFilter) {
                 // Set defaults if undefined
                 magFilter = magFilter === undefined ? GLTF2.ETextureMagFilter.LINEAR : magFilter;
-                minFilter = minFilter === undefined ? GLTF2.ETextureMinFilter.LINEAR_MIPMAP_NEAREST : minFilter;
+                minFilter = minFilter === undefined ? GLTF2.ETextureMinFilter.LINEAR_MIPMAP_LINEAR : minFilter;
                 if (magFilter === GLTF2.ETextureMagFilter.LINEAR) {
                     switch (minFilter) {
                         case GLTF2.ETextureMinFilter.NEAREST: return BABYLON.Texture.LINEAR_NEAREST;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 3 - 3
dist/preview release/loaders/babylon.glTFFileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/loaders/babylon.objFileLoader.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.customMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.shadowOnlyMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/materialsLibrary/babylon.waterMaterial.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.asciiArtPostProcess.min.js


A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 1 - 1
dist/preview release/postProcessesLibrary/babylon.digitalRainPostProcess.min.js


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

@@ -13,6 +13,7 @@
 - New InputText for Babylon.GUI. [Doc here](http://doc.babylonjs.com/overviews/gui#inputtext) ([deltakosh](https://github.com/deltakosh))
 - New VirtualKeyboard for Babylon.GUI. [Doc here](http://doc.babylonjs.com/overviews/gui#virtualkeyboard) ([deltakosh](https://github.com/deltakosh) / [adam](https://github.com/abow))
 - Added support for depth pre-pass rendering. [Doc here](http://doc.babylonjs.com/tutorials/transparency_and_how_meshes_are_rendered#depth-pre-pass-meshes) ([deltakosh](https://github.com/deltakosh))
+- Added support for Windows Motion Controllers ([Lewis Weaver](https://github.com/leweaver))
 
 ## Updates
 - 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))

+ 10 - 4
loaders/src/glTF/2.0/babylon.glTFLoader.ts

@@ -14,6 +14,7 @@ module BABYLON.GLTF2 {
         private _disposed: boolean = false;
         private _blockPendingTracking: boolean = false;
         private _nonBlockingData: Array<any>;
+        private _rootMesh: Mesh;
 
         // Observable with boolean indicating success or error.
         private _renderReadyObservable = new Observable<GLTFLoader>();
@@ -181,16 +182,17 @@ module BABYLON.GLTF2 {
         }
 
         private _addRightHandToLeftHandRootTransform(): void {
-            var rootMesh = new Mesh("root", this._babylonScene);
-            rootMesh.scaling = new Vector3(1, 1, -1);
-            rootMesh.rotation.y = Math.PI;
+            this._rootMesh = new Mesh("root", this._babylonScene);
+            this._rootMesh.isVisible = false;
+            this._rootMesh.scaling = new Vector3(1, 1, -1);
+            this._rootMesh.rotation.y = Math.PI;
 
             var nodes = this._gltf.nodes;
             if (nodes) {
                 for (var i = 0; i < nodes.length; i++) {
                     var mesh = nodes[i].babylonMesh;
                     if (mesh && !mesh.parent) {
-                        mesh.parent = rootMesh;
+                        mesh.parent = this._rootMesh;
                     }
                 }
             }
@@ -199,6 +201,10 @@ module BABYLON.GLTF2 {
         private _getMeshes(): Mesh[] {
             var meshes = [];
 
+            if (this._rootMesh) {
+                meshes.push(this._rootMesh);
+            }
+
             var nodes = this._gltf.nodes;
             if (nodes) {
                 nodes.forEach(node => {

+ 1 - 1
loaders/src/glTF/2.0/babylon.glTFLoaderUtils.ts

@@ -52,7 +52,7 @@ module BABYLON.GLTF2 {
         public static GetTextureSamplingMode(magFilter: ETextureMagFilter, minFilter: ETextureMinFilter): number {
             // Set defaults if undefined
             magFilter = magFilter === undefined ? ETextureMagFilter.LINEAR : magFilter;
-            minFilter = minFilter === undefined ? ETextureMinFilter.LINEAR_MIPMAP_NEAREST : minFilter;
+            minFilter = minFilter === undefined ? ETextureMinFilter.LINEAR_MIPMAP_LINEAR : minFilter;
 
             if (magFilter === ETextureMagFilter.LINEAR) {
                 switch (minFilter) {

+ 16 - 6
sandbox/index.js

@@ -14,6 +14,7 @@
     var filesInput;
     var currentHelpCounter;
     var currentScene;
+    var currentSkybox;
     var enableDebugLayer = false;
     var currentPluginName;
 
@@ -68,20 +69,19 @@
             currentScene.createDefaultCameraOrLight(true);
             // Enable camera's behaviors
             currentScene.activeCamera.useBouncingBehavior = true;
-            currentScene.activeCamera.useAutoRotationBehavior  = true;
             currentScene.activeCamera.useFramingBehavior = true;
 
             var framingBehavior = currentScene.activeCamera.getBehaviorByName("Framing");
             framingBehavior.framingTime = 0;
 
             var bouncingBehavior = currentScene.activeCamera.getBehaviorByName("Bouncing");
-            bouncingBehavior.autoTransitionRange = true;                
+            bouncingBehavior.autoTransitionRange = true;        
 
             if (currentScene.meshes.length) {
                 // Let's zoom on the first object with geometry
                 for (var index = 0; index < currentScene.meshes.length; index++) {
                     var mesh = currentScene.meshes[index];
-    
+
                     if (mesh.getTotalVertices()) {
                         currentScene.activeCamera.setTarget(mesh);
                         break;
@@ -95,9 +95,7 @@
         // Environment
         if (currentPluginName === "gltf") {
             var hdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("Assets/environment.dds", currentScene);
-            hdrTexture.gammaSpace = false;
-                        
-            currentScene.createDefaultSkybox(hdrTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3);
+            currentSkybox = currentScene.createDefaultSkybox(hdrTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3);
         }
 
         // In case of error during loading, meshes will be empty and clearColor is set to red
@@ -125,6 +123,18 @@
     };
 
     filesInput = new BABYLON.FilesInput(engine, null, sceneLoaded);
+    filesInput.onProcessFileCallback = (function (file, name, extension) {
+        if (extension === "dds") {
+            BABYLON.FilesInput.FilesToLoad[name] = file;
+            var newHdrTexture = BABYLON.CubeTexture.CreateFromPrefilteredData("file:" + file.correctName, currentScene);
+            if (currentSkybox) {
+                currentSkybox.dispose();
+            }
+            currentSkybox = currentScene.createDefaultSkybox(newHdrTexture, true, (currentScene.activeCamera.maxZ - currentScene.activeCamera.minZ) / 2, 0.3);
+            return false;
+        }
+        return true;
+    }).bind(this);
     filesInput.monitorElementForDragNDrop(canvas);
 
     window.addEventListener("keydown", function (evt) {

+ 5 - 1
src/Cameras/Inputs/babylon.arcRotateCameraPointersInput.ts

@@ -45,6 +45,10 @@ module BABYLON {
             this._pointerInput = (p, s) => {
                 var evt = <PointerEvent>p.event;
 
+                if (engine.isInVRExclusivePointerMode) {
+                    return;
+                }
+
                 if (p.type !== PointerEventTypes.POINTERMOVE && this.buttons.indexOf(evt.button) === -1) {
                     return;
                 }
@@ -135,7 +139,7 @@ module BABYLON {
                             return;
                         }
 
-                        if (Math.abs(pinchDistance - previousPinchDistance) > this.camera.pinchToPanMaxDistance) {
+                        if (pinchDistance > this.camera.panMaxFingersDistance || Math.abs(pinchDistance - previousPinchDistance) > this.camera.pinchToPanMaxDistance) {
                             this.camera
                                 .inertialRadiusOffset += (pinchSquaredDistance - previousPinchSquaredDistance) /
                                 (this.pinchPrecision *

+ 8 - 0
src/Cameras/Inputs/babylon.freeCameraMouseInput.ts

@@ -24,6 +24,10 @@ module BABYLON {
                 this._pointerInput = (p, s) => {
                     var evt = <PointerEvent>p.event;
 
+                    if (engine.isInVRExclusivePointerMode) {
+                        return;
+                    }
+
                     if (!this.touchEnabled && evt.pointerType === "touch") {
                         return;
                     }
@@ -95,6 +99,10 @@ module BABYLON {
                     return;
                 }
 
+                if (engine.isInVRExclusivePointerMode) {
+                    return;
+                }
+
                 var offsetX = evt.movementX || evt.mozMovementX || evt.webkitMovementX || evt.msMovementX || 0;
                 var offsetY = evt.movementY || evt.mozMovementY || evt.webkitMovementY || evt.msMovementY || 0;
 

+ 25 - 23
src/Cameras/VR/babylon.webVRCamera.ts

@@ -386,39 +386,41 @@ module BABYLON {
             this._onGamepadDisconnectedObserver = manager.onGamepadDisconnectedObservable.add((gamepad) => {
                 if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
                     let webVrController: WebVRController = <WebVRController>gamepad;
-                    let index = this.controllers.indexOf(webVrController);
-
-                    if (index === -1) {
-                        // we are good
-                        return;
+                    
+                    if (webVrController.defaultModel) {
+                        webVrController.defaultModel.setEnabled(false);
                     }
-
-                    this.controllers.splice(index, 1);
                 }
             });
 
             this._onGamepadConnectedObserver = manager.onGamepadConnectedObservable.add((gamepad) => {
                 if (gamepad.type === BABYLON.Gamepad.POSE_ENABLED) {
                     let webVrController: WebVRController = <WebVRController>gamepad;
+                    
                     if (this.webVROptions.controllerMeshes) {
-                        webVrController.initControllerMesh(this.getScene(), (loadedMesh) => {
-                            if (this.webVROptions.defaultLightingOnControllers) {
-                                if (!this._lightOnControllers) {
-                                    this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), this.getScene());
-                                }
-                                let activateLightOnSubMeshes = function(mesh: AbstractMesh, light: HemisphericLight) {
-                                    let children = mesh.getChildren();
-                                    if (children.length !== 0) {
-                                        children.forEach((mesh) => {
-                                            light.includedOnlyMeshes.push(<AbstractMesh>mesh);
-                                            activateLightOnSubMeshes(<AbstractMesh>mesh, light);
-                                        });
+                        if (webVrController.defaultModel) {
+                            webVrController.defaultModel.setEnabled(true);
+                        } else {
+                            // Load the meshes
+                            webVrController.initControllerMesh(this.getScene(), (loadedMesh) => {
+                                if (this.webVROptions.defaultLightingOnControllers) {
+                                    if (!this._lightOnControllers) {
+                                        this._lightOnControllers = new BABYLON.HemisphericLight("vrControllersLight", new BABYLON.Vector3(0, 1, 0), this.getScene());
                                     }
+                                    let activateLightOnSubMeshes = function(mesh: AbstractMesh, light: HemisphericLight) {
+                                        let children = mesh.getChildren();
+                                        if (children.length !== 0) {
+                                            children.forEach((mesh) => {
+                                                light.includedOnlyMeshes.push(<AbstractMesh>mesh);
+                                                activateLightOnSubMeshes(<AbstractMesh>mesh, light);
+                                            });
+                                        }
+                                    }
+                                    this._lightOnControllers.includedOnlyMeshes.push(loadedMesh);
+                                    activateLightOnSubMeshes(loadedMesh, this._lightOnControllers);
                                 }
-                                this._lightOnControllers.includedOnlyMeshes.push(loadedMesh);
-                                activateLightOnSubMeshes(loadedMesh, this._lightOnControllers);
-                            }
-                        });
+                            });
+                        }
                     }
                     webVrController.attachToPoseControlledCamera(this);
 

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

@@ -57,7 +57,10 @@ module BABYLON {
         public inertialPanningY: number = 0;
 
         @serialize()
-        public pinchToPanMaxDistance: number = 2;
+        public pinchToPanMaxDistance: number = 3;
+
+        @serialize()
+        public panMaxFingersDistance: number = 100;
 
         @serialize()
         public panningDistanceLimit: number = null;

+ 19 - 21
src/Cameras/babylon.camera.ts

@@ -261,26 +261,6 @@
 
             this._cache.position.copyFrom(this.position);
             this._cache.upVector.copyFrom(this.upVector);
-
-            this._cache.mode = this.mode;
-            this._cache.minZ = this.minZ;
-            this._cache.maxZ = this.maxZ;
-
-            this._cache.fov = this.fov;
-            this._cache.fovMode = this.fovMode;
-            this._cache.aspectRatio = engine.getAspectRatio(this);
-
-            this._cache.orthoLeft = this.orthoLeft;
-            this._cache.orthoRight = this.orthoRight;
-            this._cache.orthoBottom = this.orthoBottom;
-            this._cache.orthoTop = this.orthoTop;
-            this._cache.renderWidth = engine.getRenderWidth();
-            this._cache.renderHeight = engine.getRenderHeight();
-        }
-
-        public _updateFromScene(): void {
-            this.updateCache();
-            this.update();
         }
 
         // Synchronized
@@ -333,10 +313,10 @@
         }
 
         public update(): void {
+            this._checkInputs();
             if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
                 this._updateRigCameras();
             }
-            this._checkInputs();
         }
 
         public _checkInputs(): void {
@@ -441,6 +421,7 @@
                 return this._computedViewMatrix;
             }
 
+            this.updateCache();
             this._computedViewMatrix = this._getViewMatrix();
             this._currentRenderId = this.getScene().getRenderId();
             
@@ -489,11 +470,21 @@
                 return this._projectionMatrix;
             }
 
+            // Cache
+            this._cache.mode = this.mode;
+            this._cache.minZ = this.minZ;
+            this._cache.maxZ = this.maxZ;
+        
+            // Matrix
             this._refreshFrustumPlanes = true;
 
             var engine = this.getEngine();
             var scene = this.getScene();
             if (this.mode === Camera.PERSPECTIVE_CAMERA) {
+                this._cache.fov = this.fov;
+                this._cache.fovMode = this.fovMode;
+                this._cache.aspectRatio = engine.getAspectRatio(this);
+                
                 if (this.minZ <= 0) {
                     this.minZ = 0.1;
                 }
@@ -533,6 +524,13 @@
                         this.maxZ,
                         this._projectionMatrix);
                 }
+
+                this._cache.orthoLeft = this.orthoLeft;
+                this._cache.orthoRight = this.orthoRight;
+                this._cache.orthoBottom = this.orthoBottom;
+                this._cache.orthoTop = this.orthoTop;
+                this._cache.renderWidth = engine.getRenderWidth();
+                this._cache.renderHeight = engine.getRenderHeight();                    
             }
 
             this.onProjectionMatrixChangedObservable.notifyObservers(this);

+ 0 - 2
src/Gamepad/Controllers/babylon.genericController.ts

@@ -1,8 +1,6 @@
 module BABYLON {
     
     export class GenericController extends WebVRController {
-        private _defaultModel: BABYLON.AbstractMesh;
-
         constructor(vrGamepad) {
             super(vrGamepad);
         }

+ 0 - 2
src/Gamepad/Controllers/babylon.oculusTouchController.ts

@@ -1,8 +1,6 @@
 module BABYLON {
 
     export class OculusTouchController extends WebVRController {
-        private _defaultModel: BABYLON.AbstractMesh;
-
         public onSecondaryTriggerStateChangedObservable = new Observable<ExtendedGamepadButton>();
 
         public onThumbRestChangedObservable = new Observable<ExtendedGamepadButton>();

+ 9 - 8
src/Gamepad/Controllers/babylon.poseEnabledController.ts

@@ -26,9 +26,9 @@ module BABYLON {
                 return new OculusTouchController(vrGamepad);
             }
             // Windows Mixed Reality controllers 
-            // else if (vrGamepad.id.indexOf('Spatial Control') === 0) {
-            //     //return new WindowsMixedRealityController(vrGamepad);
-            // }
+            else if (vrGamepad.id.indexOf(WindowsMotionController.GAMEPAD_ID_PREFIX) === 0) {
+                return new WindowsMotionController(vrGamepad);
+            }
             // HTC Vive
             else if (vrGamepad.id.toLowerCase().indexOf('openvr') !== -1) {
                 return new ViveController(vrGamepad);
@@ -58,9 +58,9 @@ module BABYLON {
         private _poseControlledCamera: TargetCamera;
 
         private _leftHandSystemQuaternion: Quaternion = new Quaternion();
-
-        constructor(public vrGamepad) {
-            super(vrGamepad.id, vrGamepad.index, vrGamepad);
+        
+        constructor(browserGamepad) {
+            super(browserGamepad.id, browserGamepad.index, browserGamepad);
             this.type = Gamepad.POSE_ENABLED;
             this.controllerType = PoseEnabledControllerType.GENERIC;
             this.position = Vector3.Zero();
@@ -75,9 +75,9 @@ module BABYLON {
 
         public update() {
             super.update();
-            var pose: GamepadPose = this.vrGamepad.pose;
+            var pose: GamepadPose = this.browserGamepad.pose;
             this.updateFromDevice(pose);
-
+            
             if (this._mesh) {
                 this._mesh.position.copyFrom(this._calculatedPosition);
                 this._mesh.rotationQuaternion.copyFrom(this._calculatedRotation);
@@ -95,6 +95,7 @@ module BABYLON {
 
                     this.devicePosition.scaleToRef(this.deviceScaleFactor, this._calculatedPosition);
                     this._calculatedPosition.addInPlace(this.position);
+
                 }
                 if (poseData.orientation) {
                     this.deviceRotationQuaternion.copyFromFloats(this.rawPose.orientation[0], this.rawPose.orientation[1], -this.rawPose.orientation[2], -this.rawPose.orientation[3]);

+ 0 - 1
src/Gamepad/Controllers/babylon.viveController.ts

@@ -1,7 +1,6 @@
 module BABYLON {
 
     export class ViveController extends WebVRController {
-        private _defaultModel: BABYLON.AbstractMesh;
 
         constructor(vrGamepad) {
             super(vrGamepad);

+ 19 - 5
src/Gamepad/Controllers/babylon.webVRController.ts

@@ -2,12 +2,12 @@ module BABYLON {
 
     export abstract class WebVRController extends PoseEnabledController {
 
-        public onTriggerStateChangedObservable = new Observable<ExtendedGamepadButton>();
+        protected _defaultModel: AbstractMesh;
 
+        // Observables
+        public onTriggerStateChangedObservable = new Observable<ExtendedGamepadButton>();
         public onMainButtonStateChangedObservable = new Observable<ExtendedGamepadButton>();
-
         public onSecondaryButtonStateChangedObservable = new Observable<ExtendedGamepadButton>();
-
         public onPadStateChangedObservable = new Observable<ExtendedGamepadButton>();
         public onPadValuesChangedObservable = new Observable<StickValues>();
 
@@ -23,6 +23,10 @@ module BABYLON {
 
         public hand: string; // 'left' or 'right', see https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum
 
+        public get defaultModel(): AbstractMesh {
+            return this._defaultModel;
+        }
+
         constructor(vrGamepad) {
             super(vrGamepad);
             this._buttons = new Array<ExtendedGamepadButton>(vrGamepad.buttons.length);
@@ -32,7 +36,7 @@ module BABYLON {
         public update() {
             super.update();
             for (var index = 0; index < this._buttons.length; index++) {
-                this._setButtonValue(this.vrGamepad.buttons[index], this._buttons[index], index);
+                this._setButtonValue(this.browserGamepad.buttons[index], this._buttons[index], index);
             };
             if (this.leftStick.x !== this.pad.x || this.leftStick.y !== this.pad.y) {
                 this.pad.x = this.leftStick.x;
@@ -88,6 +92,16 @@ module BABYLON {
             this._changes.changed = this._changes.pressChanged || this._changes.touchChanged || this._changes.valueChanged;
             return this._changes;
         }
+
+        public dispose(): void {
+            super.dispose();
+
+            this.onTriggerStateChangedObservable.clear();
+            this.onMainButtonStateChangedObservable.clear();
+            this.onSecondaryButtonStateChangedObservable.clear();
+            this.onPadStateChangedObservable.clear();
+            this.onPadValuesChangedObservable.clear();
+        }
     }
         
-}
+}

+ 362 - 0
src/Gamepad/Controllers/babylon.windowsMotionController.ts

@@ -0,0 +1,362 @@
+module BABYLON {
+
+    class LoadedMeshInfo {
+        public rootNode: AbstractMesh;
+        public pointingPoseNode: AbstractMesh;
+        public holdingPoseNode: AbstractMesh;
+        public buttonMeshes: { [id: string] : IButtonMeshInfo; } = {};
+        public axisMeshes: { [id: number] : IAxisMeshInfo; } = {};
+    }
+
+    interface IMeshInfo {
+        index: number;
+        value: AbstractMesh;
+    }
+
+    interface IButtonMeshInfo extends IMeshInfo {
+        pressed: AbstractMesh;
+        unpressed: AbstractMesh;
+    }
+
+    interface IAxisMeshInfo extends IMeshInfo {
+        min: AbstractMesh;
+        max: AbstractMesh;
+    }
+
+    export class WindowsMotionController extends WebVRController {
+        private static readonly MODEL_BASE_URL:string = 'https://controllers.babylonjs.com/microsoft/';
+        private static readonly MODEL_LEFT_FILENAME:string = 'left.glb';
+        private static readonly MODEL_RIGHT_FILENAME:string = 'right.glb';
+        private static readonly MODEL_ROOT_NODE_NAME:string = 'RootNode';
+        private static readonly GLTF_ROOT_TRANSFORM_NAME:string = 'root';
+
+        public static readonly GAMEPAD_ID_PREFIX:string = 'Spatial Controller (Spatial Interaction Source) ';
+        private static readonly GAMEPAD_ID_PATTERN = /([0-9a-zA-Z]+-[0-9a-zA-Z]+)$/;
+
+        // Art assets is backward facing
+        private static readonly ROTATE_OFFSET:number[] = [Math.PI, 0, 0]; // x, y, z.
+
+        private _loadedMeshInfo: LoadedMeshInfo;
+        private readonly _mapping = {
+            // Semantic button names
+            buttons: ['thumbstick', 'trigger', 'grip', 'menu', 'trackpad'],
+            
+            // A mapping of the button name to glTF model node name
+            // that should be transformed by button value.
+            buttonMeshNames: {
+                'trigger': 'SELECT',
+                'menu': 'MENU',
+                'grip': 'GRASP',
+                'thumbstick': 'THUMBSTICK_PRESS',
+                'trackpad': 'TOUCHPAD_PRESS'
+            },
+            // This mapping is used to translate from the Motion Controller to Babylon semantics
+            buttonObservableNames: {
+                'trigger': 'onTriggerStateChangedObservable',
+                'menu': 'onSecondaryButtonStateChangedObservable',
+                'grip': 'onMainButtonStateChangedObservable',
+                'thumbstick': 'onPadStateChangedObservable',
+                'trackpad': 'onTrackpadChangedObservable'
+            },
+            // A mapping of the axis name to glTF model node name
+            // that should be transformed by axis value.
+            // This array mirrors the browserGamepad.axes array, such that 
+            // the mesh corresponding to axis 0 is in this array index 0.
+            axisMeshNames: [
+                'THUMBSTICK_X',
+                'THUMBSTICK_Y',
+                'TOUCHPAD_TOUCH_X',
+                'TOUCHPAD_TOUCH_Y'
+            ]
+        };
+
+        public onTrackpadChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.WINDOWS;
+            this._loadedMeshInfo = null;
+        }
+        
+        public get onTriggerButtonStateChangedObservable(): Observable<ExtendedGamepadButton> {
+            return this.onTriggerStateChangedObservable;
+        }
+
+        public get onMenuButtonStateChangedObservable(): Observable<ExtendedGamepadButton> {
+            return this.onSecondaryButtonStateChangedObservable;
+        }
+
+        public get onGripButtonStateChangedObservable(): Observable<ExtendedGamepadButton> {
+            return this.onMainButtonStateChangedObservable;
+        }
+
+        public get onThumbstickButtonStateChangedObservable(): Observable<ExtendedGamepadButton> {
+            return this.onPadStateChangedObservable;
+        }    
+
+        public get onTouchpadButtonStateChangedObservable(): Observable<ExtendedGamepadButton> {
+            return this.onTrackpadChangedObservable;
+        }
+        
+        /**
+         * Called once per frame by the engine.
+         */
+        public update() {
+            super.update();
+            
+            // Only need to animate axes if there is a loaded mesh
+            if (this._loadedMeshInfo) {
+                if (this.browserGamepad.axes) {
+                    for (let axis = 0; axis < this._mapping.axisMeshNames.length; axis++) {
+                        this.lerpAxisTransform(axis, this.browserGamepad.axes[axis]);
+                    }
+                }
+            }
+        }
+        
+        /**
+         * Called once for each button that changed state since the last frame
+         * @param buttonIdx Which button index changed
+         * @param state New state of the button
+         * @param changes Which properties on the state changed since last frame
+         */
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            let buttonName = this._mapping.buttons[buttonIdx];
+            if (!buttonName) {
+                return; 
+            }
+
+            // Only emit events for buttons that we know how to map from index to name
+            let observable = this[this._mapping.buttonObservableNames[buttonName]];
+            if (observable) {
+                observable.notifyObservers(state);
+            }
+
+            this.lerpButtonTransform(buttonName, state.value);
+        }
+        
+        protected lerpButtonTransform(buttonName: string, buttonValue: number) {
+            
+            // If there is no loaded mesh, there is nothing to transform.
+            if (!this._loadedMeshInfo) {
+                return;
+            }
+
+            var meshInfo = this._loadedMeshInfo.buttonMeshes[buttonName];
+            BABYLON.Quaternion.SlerpToRef(
+                meshInfo.unpressed.rotationQuaternion, 
+                meshInfo.pressed.rotationQuaternion, 
+                buttonValue,
+                meshInfo.value.rotationQuaternion);
+            BABYLON.Vector3.LerpToRef(
+                meshInfo.unpressed.position, 
+                meshInfo.pressed.position,
+                buttonValue,
+                meshInfo.value.position);
+        }
+        
+        protected lerpAxisTransform(axis:number, axisValue: number) {
+            let meshInfo = this._loadedMeshInfo.axisMeshes[axis];
+            if (!meshInfo) {
+                return;
+            }
+
+            // Convert from gamepad value range (-1 to +1) to lerp range (0 to 1)
+            let lerpValue = axisValue * 0.5 + 0.5;
+            BABYLON.Quaternion.SlerpToRef(
+                meshInfo.min.rotationQuaternion, 
+                meshInfo.max.rotationQuaternion, 
+                lerpValue,
+                meshInfo.value.rotationQuaternion);
+            BABYLON.Vector3.LerpToRef(
+                meshInfo.min.position, 
+                meshInfo.max.position,
+                lerpValue,
+                meshInfo.value.position);
+        }
+        
+        /**
+         * Implements abstract method on WebVRController class, loading controller meshes and calling this.attachToMesh if successful.
+         * @param scene scene in which to add meshes
+         * @param meshLoaded optional callback function that will be called if the mesh loads successfully.
+         */
+        public initControllerMesh(scene: Scene, meshLoaded?: (mesh: AbstractMesh) => void, forceDefault = false) {
+            // Determine the device specific folder based on the ID suffix
+            let device = 'default';
+            if (this.id && !forceDefault) {
+                let match = this.id.match(WindowsMotionController.GAMEPAD_ID_PATTERN);
+                device = ((match && match[0]) || device);
+            }
+
+            // Hand
+            let filename: string;
+            if (this.hand === 'left') {
+                filename = WindowsMotionController.MODEL_LEFT_FILENAME;
+            }
+            else { // Right is the default if no hand is specified
+                filename = WindowsMotionController.MODEL_RIGHT_FILENAME;
+            }
+
+            let path = WindowsMotionController.MODEL_BASE_URL + device + '/';
+
+            SceneLoader.ImportMesh("", path, filename, scene, (meshes: AbstractMesh[]) => {
+                // glTF files successfully loaded from the remote server, now process them to ensure they are in the right format.
+                this._loadedMeshInfo = this.processModel(scene, meshes);
+
+                if (!this._loadedMeshInfo) {
+                    return;
+                }
+
+                this._defaultModel = this._loadedMeshInfo.rootNode;
+                this.attachToMesh(this._defaultModel);
+
+                if (meshLoaded) {
+                    meshLoaded(this._defaultModel);
+                }
+            }, null, (scene: Scene, message: string) => {
+                Tools.Log(message);
+                Tools.Warn('Failed to retrieve controller model from the remote server: ' + path + filename);
+                this.initControllerMesh(scene, meshLoaded, true);
+            });
+        }
+
+        /**
+         * Takes a list of meshes (as loaded from the glTF file) and finds the root node, as well as nodes that 
+         * can be transformed by button presses and axes values, based on this._mapping.
+         * 
+         * @param scene scene in which the meshes exist
+         * @param meshes list of meshes that make up the controller model to process
+         * @return structured view of the given meshes, with mapping of buttons and axes to meshes that can be transformed.
+         */
+        private processModel(scene: Scene, meshes: AbstractMesh[]) : LoadedMeshInfo {
+            let loadedMeshInfo = null;
+
+            // Create a new mesh to contain the glTF hierarchy
+            let parentMesh = new BABYLON.Mesh(this.id + " " + this.hand, scene);
+
+            // Find the root node in the loaded glTF scene, and attach it as a child of 'parentMesh'
+            let childMesh : AbstractMesh = null;
+            for (let i = 0; i < meshes.length; i++) {
+                let mesh = meshes[i];
+                if (mesh.id === WindowsMotionController.MODEL_ROOT_NODE_NAME) {
+                    // There may be a parent mesh to perform the RH to LH matrix transform.
+                    // Exclude controller meshes from picking results
+                    mesh.isPickable = false;
+
+                    // Handle root node, attach to the new parentMesh
+                    if (mesh.parent && mesh.parent.name === WindowsMotionController.GLTF_ROOT_TRANSFORM_NAME)
+                        mesh = <AbstractMesh>mesh.parent;
+                    
+                    childMesh = mesh;
+                    break;
+                }
+            }
+
+            if (childMesh) {
+                childMesh.setParent(parentMesh);
+
+                // Create our mesh info. Note that this method will always return non-null.
+                loadedMeshInfo = this.createMeshInfo(parentMesh);
+
+                // Apply rotation offsets
+                var rotOffset = WindowsMotionController.ROTATE_OFFSET;
+                childMesh.addRotation(rotOffset[0], rotOffset[1], rotOffset[2]);
+            } else {
+                Tools.Warn('No node with name ' + WindowsMotionController.MODEL_ROOT_NODE_NAME +' in model file.');
+            }
+
+            return loadedMeshInfo;
+        }
+        
+        private createMeshInfo(rootNode: AbstractMesh) : LoadedMeshInfo {
+            let loadedMeshInfo = new LoadedMeshInfo();
+            var i;
+            loadedMeshInfo.rootNode = rootNode;
+
+            // Reset the caches
+            loadedMeshInfo.buttonMeshes = {};
+            loadedMeshInfo.axisMeshes = {};
+
+            // Button Meshes
+            for (i = 0; i < this._mapping.buttons.length; i++) {
+                var buttonMeshName = this._mapping.buttonMeshNames[this._mapping.buttons[i]];
+                if (!buttonMeshName) {
+                    Tools.Log('Skipping unknown button at index: ' + i + ' with mapped name: ' + this._mapping.buttons[i]);
+                    continue;
+                }
+
+                var buttonMesh = getChildByName(rootNode, buttonMeshName);
+                if (!buttonMesh) {
+                    Tools.Warn('Missing button mesh with name: ' + buttonMeshName);
+                    continue;
+                }
+
+                var buttonMeshInfo = {
+                    index: i,
+                    value: getImmediateChildByName(buttonMesh, 'VALUE'),
+                    pressed: getImmediateChildByName(buttonMesh, 'PRESSED'),
+                    unpressed: getImmediateChildByName(buttonMesh, 'UNPRESSED')
+                };
+                if (buttonMeshInfo.value && buttonMeshInfo.pressed && buttonMeshInfo.unpressed) {
+                    loadedMeshInfo.buttonMeshes[this._mapping.buttons[i]] = buttonMeshInfo;
+                } else {
+                    // If we didn't find the mesh, it simply means this button won't have transforms applied as mapped button value changes.
+                    Tools.Warn('Missing button submesh under mesh with name: ' + buttonMeshName +
+                        '(VALUE: ' + !!buttonMeshInfo.value +
+                        ', PRESSED: ' + !!buttonMeshInfo.pressed +
+                        ', UNPRESSED:' + !!buttonMeshInfo.unpressed +
+                        ')');
+                }
+            }
+
+            // Axis Meshes
+            for (i = 0; i < this._mapping.axisMeshNames.length; i++) {
+                var axisMeshName = this._mapping.axisMeshNames[i];
+                if (!axisMeshName) {
+                    Tools.Log('Skipping unknown axis at index: ' + i);
+                    continue;
+                }
+
+                var axisMesh = getChildByName(rootNode, axisMeshName);
+                if (!axisMesh) {
+                    Tools.Warn('Missing axis mesh with name: ' + axisMeshName);
+                    continue;
+                }
+
+                var axisMeshInfo = {
+                    index: i,
+                    value: getImmediateChildByName(axisMesh, 'VALUE'),
+                    min: getImmediateChildByName(axisMesh, 'MIN'),
+                    max: getImmediateChildByName(axisMesh, 'MAX')
+                };
+                if (axisMeshInfo.value && axisMeshInfo.min && axisMeshInfo.max) {
+                    loadedMeshInfo.axisMeshes[i] = axisMeshInfo;
+                } else {
+                    // If we didn't find the mesh, it simply means thit axis won't have transforms applied as mapped axis values change.
+                    Tools.Warn('Missing axis submesh under mesh with name: ' + axisMeshName +
+                        '(VALUE: ' + !!axisMeshInfo.value +
+                        ', MIN: ' + !!axisMeshInfo.min +
+                        ', MAX:' + !!axisMeshInfo.max +
+                        ')');
+                }
+            }
+
+            return loadedMeshInfo;
+            
+            // Look through all children recursively. This will return null if no mesh exists with the given name.
+            function getChildByName(node, name) {
+                return node.getChildMeshes(false, n => n.name === name)[0];
+            }
+            // Look through only immediate children. This will return null if no mesh exists with the given name.
+            function getImmediateChildByName (node, name) : AbstractMesh {
+                return node.getChildMeshes(true, n => n.name == name)[0];
+            }
+        }
+
+        public dispose(): void {
+            super.dispose();
+
+            this.onTrackpadChangedObservable.clear();
+        }
+    }
+}

+ 6 - 0
src/Gamepad/babylon.gamepad.ts

@@ -18,6 +18,8 @@
         private _leftStick: StickValues;
         private _rightStick: StickValues;
 
+        public _isConnected = true;
+
         private _leftStickAxisX: number;
         private _leftStickAxisY: number;
         private _rightStickAxisX: number;
@@ -31,6 +33,10 @@
         public static XBOX = 2;
         public static POSE_ENABLED = 3;
 
+        public get isConnected(): boolean {
+            return this._isConnected;
+        }
+
         constructor(public id: string, public index: number, public browserGamepad, leftStickX: number = 0, leftStickY: number = 1, rightStickX: number = 2, rightStickY: number = 3) {
             this.type = Gamepad.GAMEPAD;
             this._leftStickAxisX = leftStickX;

+ 31 - 11
src/Gamepad/babylon.gamepadManager.ts

@@ -18,13 +18,21 @@
             this._onGamepadConnectedEvent = (evt) => {
                 let gamepad = evt.gamepad;
 
-                // Protection code for Chrome which has a very buggy gamepad implementation...
-                // And raises a connected event on disconnection for instance
                 if (gamepad.index in this._babylonGamepads) {
-                    return;
+                    if (this._babylonGamepads[gamepad.index].isConnected) {
+                        return;
+                    }
                 }
 
-                var newGamepad = this._addNewGamepad(gamepad);
+                let newGamepad: Gamepad;
+
+                if (this._babylonGamepads[gamepad.index]) {
+                    newGamepad = this._babylonGamepads[gamepad.index];
+                    newGamepad.browserGamepad = gamepad;
+                    newGamepad._isConnected = true;
+                } else {
+                    newGamepad = this._addNewGamepad(gamepad);
+                }
                 this.onGamepadConnectedObservable.notifyObservers(newGamepad);
                 this._startMonitoringGamepads();                
             };
@@ -35,12 +43,10 @@
                 // Remove the gamepad from the list of gamepads to monitor.
                 for (var i in this._babylonGamepads) {
                     if (this._babylonGamepads[i].index === gamepad.index) {
-                        let gamepadToRemove = this._babylonGamepads[i];
-                        this._babylonGamepads[i] = null;
+                        let disconnectedGamepad = this._babylonGamepads[i];
+                        disconnectedGamepad._isConnected = false;
                         
-                        this.onGamepadDisconnectedObservable.notifyObservers(gamepadToRemove);
-
-                        gamepadToRemove.dispose();
+                        this.onGamepadDisconnectedObservable.notifyObservers(disconnectedGamepad);
                         break;
                     }
                 }            
@@ -63,7 +69,11 @@
             }
         }
 
-        public getGamepadByType(type: number = Gamepad.XBOX) {
+        public get gamepads(): Gamepad[] {
+            return this._babylonGamepads;
+        }
+
+        public getGamepadByType(type: number = Gamepad.XBOX): Gamepad {
             for (var gamepad of this._babylonGamepads) {
                 if (gamepad && gamepad.type === type) {
                     return gamepad;
@@ -80,6 +90,11 @@
                 this._onGamepadConnectedEvent = null;
                 this._onGamepadDisconnectedEvent = null;
             }
+
+            for (let gamepad of this._babylonGamepads) {
+                gamepad.dispose();
+            }
+
             this._oneGamepadConnected = false;
             this._stopMonitoringGamepads();
             this._babylonGamepads = [];
@@ -123,7 +138,7 @@
 
             for (var i in this._babylonGamepads) {
                 let gamepad = this._babylonGamepads[i];
-                if (!gamepad) {
+                if (!gamepad || !gamepad.isConnected) {
                     continue;
                 }
                 gamepad.update();
@@ -147,6 +162,11 @@
                     else {
                         // Forced to copy again this object for Chrome for unknown reason
                         this._babylonGamepads[i].browserGamepad = gamepads[i];
+
+                        if (!this._babylonGamepads[i].isConnected) {
+                            this._babylonGamepads[i]._isConnected = true;                            
+                            this.onGamepadConnectedObservable.notifyObservers(this._babylonGamepads[i]);
+                        }
                     }
                 }
             }

+ 1 - 1
src/Mesh/babylon.meshBuilder.ts

@@ -728,7 +728,7 @@
             var height = options.height || 10.0;
             var subdivisions = options.subdivisions || 1|0;
             var minHeight = options.minHeight || 0.0;
-            var maxHeight = options.maxHeight || 10.0;
+            var maxHeight = options.maxHeight || 1.0;
             var filter = options.colorFilter || new Color3(0.3, 0.59, 0.11);
             var updatable = options.updatable;
             var onReady = options.onReady;

+ 10 - 2
src/Tools/babylon.filesInput.ts

@@ -2,6 +2,8 @@
     export class FilesInput {
         public static FilesToLoad: File[] = new Array<File>();
 
+        public onProcessFileCallback: (file: File, name: string, extension: string) => true = () => { return true; };
+
         private _engine: Engine;
         private _currentScene: Scene;
         private _sceneLoadedCallback: (sceneFile: File, scene: Scene) => void;
@@ -114,10 +116,16 @@
         }
 
         private _processFiles(files: Array<any>): void {
+            var skippedFiles = 0;
             for (var i = 0; i < files.length; i++) {
                 var name = files[i].correctName.toLowerCase();
                 var extension = name.split('.').pop();
-                
+
+                if (!this.onProcessFileCallback(files[i], name, extension)) {
+                    skippedFiles++;
+                    continue;
+                }
+
                 if ((extension === "babylon" || extension === "stl" || extension === "obj" || extension === "gltf" || extension === "glb") 
                     && name.indexOf(".binary.babylon") === -1 && name.indexOf(".incremental.babylon") === -1) {
                     this._sceneFileToLoad = files[i];
@@ -130,7 +138,7 @@
             if (this._onReloadCallback) {
                 this._onReloadCallback(this._sceneFileToLoad);
             }
-            else {
+            else if (skippedFiles < files.length) {
                 this.reload();
             }
         }

+ 13 - 2
src/babylon.engine.ts

@@ -564,6 +564,11 @@
         private _vrDisplayEnabled;
         private _oldSize: BABYLON.Size;
         private _oldHardwareScaleFactor: number;
+        private _vrExclusivePointerMode = false;
+
+        public get isInVRExclusivePointerMode(): boolean {
+            return this._vrExclusivePointerMode;
+        }
 
         // Uniform buffers list
         public disableUniformBuffers = false;
@@ -941,10 +946,14 @@
                 document.addEventListener("webkitpointerlockchange", this._onPointerLockChange, false);
 
                 this._onVRDisplayPointerRestricted = () => {
+                    this._vrExclusivePointerMode = true;
+                    console.log("enter");
                     canvas.requestPointerLock();
                 }
 
                 this._onVRDisplayPointerUnrestricted = () => {
+                    this._vrExclusivePointerMode = false;
+                    console.log("exit");
                     document.exitPointerLock();
                 }
 
@@ -4235,8 +4244,10 @@
             var value = texture.anisotropicFilteringLevel;
 
 
-            if (internalTexture.samplingMode === Texture.NEAREST_SAMPLINGMODE) {
-                value = 1;
+            if (internalTexture.samplingMode !== Texture.LINEAR_LINEAR_MIPNEAREST 
+                && internalTexture.samplingMode !== Texture.LINEAR_LINEAR_MIPLINEAR
+                && internalTexture.samplingMode !== Texture.LINEAR_LINEAR) {
+                value = 1; // Forcing the anisotropic to 1 because else webgl will force filters to linear
             }
 
             if (anisotropicFilterExtension && internalTexture._cachedAnisotropicFilteringLevel !== value) {

+ 6 - 8
src/babylon.scene.ts

@@ -2829,13 +2829,14 @@
                 throw new Error("Active camera not set");
 
             Tools.StartPerformanceCounter("Rendering camera " + this.activeCamera.name);
-
+           
             // Viewport
             engine.setViewport(this.activeCamera.viewport);
 
             // Camera
             this.resetCachedMaterial();
             this._renderId++;
+            this.activeCamera.update();
             this.updateTransformMatrix();
 
             if (camera._alternateCamera) {
@@ -3003,10 +3004,7 @@
 
             // Finalize frame
             this.postProcessManager._finalizeFrame(camera.isIntermediate);
-
-            // Update camera
-            this.activeCamera._updateFromScene();
-
+           
             // Reset some special arrays
             this._renderTargets.reset();
 
@@ -3023,6 +3021,9 @@
                 return;
             }
 
+            // Update camera
+            this.activeCamera.update();
+            
             // rig cameras
             for (var index = 0; index < camera._rigCameras.length; index++) {
                 this._renderForCamera(camera._rigCameras[index]);
@@ -3030,9 +3031,6 @@
 
             this.activeCamera = camera;
             this.setTransformMatrix(this.activeCamera.getViewMatrix(), this.activeCamera.getProjectionMatrix());
-
-            // Update camera
-            this.activeCamera._updateFromScene();
         }
 
         private _checkIntersections(): void {