Sfoglia il codice sorgente

Merge pull request #1841 from RaananW/vr-controller

WebVR Controllers
David Catuhe 8 anni fa
parent
commit
3038409951

File diff suppressed because it is too large
+ 620 - 568
Tools/Gulp/config.json


+ 1 - 1
src/Cameras/Inputs/babylon.arcrotatecamera.input.gamepad.ts

@@ -3,7 +3,7 @@ module BABYLON {
         camera: ArcRotateCamera;
         camera: ArcRotateCamera;
 
 
         public gamepad: Gamepad;
         public gamepad: Gamepad;
-        private _gamepads: Gamepads;
+        private _gamepads: Gamepads<Gamepad>;
 
 
         @serialize()
         @serialize()
         public gamepadRotationSensibility = 80;
         public gamepadRotationSensibility = 80;

+ 1 - 1
src/Cameras/Inputs/babylon.freecamera.input.gamepad.ts

@@ -3,7 +3,7 @@ module BABYLON {
         camera: FreeCamera;
         camera: FreeCamera;
 
 
         public gamepad: Gamepad;
         public gamepad: Gamepad;
-        private _gamepads: Gamepads;
+        private _gamepads: Gamepads<Gamepad>;
 
 
         @serialize()
         @serialize()
         public gamepadAngularSensibility = 200;
         public gamepadAngularSensibility = 200;

+ 147 - 37
src/Cameras/VR/babylon.webVRCamera.ts

@@ -4,15 +4,41 @@ declare var VRFrameData;
 
 
 module BABYLON {
 module BABYLON {
 
 
+    /**
+     * This is a copy of VRPose.
+     * IMPORTANT!! The data is right-hand data.
+     * @export
+     * @interface DevicePose
+     */
+    export interface DevicePose {
+        readonly position?: Float32Array;
+        readonly linearVelocity?: Float32Array;
+        readonly linearAcceleration?: Float32Array;
+
+        readonly orientation?: Float32Array;
+        readonly angularVelocity?: Float32Array;
+        readonly angularAcceleration?: Float32Array;
+    }
+
+    export interface PoseControlled {
+        position: Vector3;
+        rotationQuaternion: Quaternion;
+        devicePosition?: Vector3;
+        deviceRotationQuaternion: Quaternion;
+        rawPose: DevicePose;
+        deviceScaleFactor: number;
+        updateFromDevice(poseData: DevicePose);
+    }
+
     export interface WebVROptions {
     export interface WebVROptions {
         trackPosition?: boolean; //update the camera's position
         trackPosition?: boolean; //update the camera's position
         positionScale?: number;
         positionScale?: number;
         displayName?: string; //if there are more than one VRDisplays.
         displayName?: string; //if there are more than one VRDisplays.
     }
     }
 
 
-    export class WebVRFreeCamera extends FreeCamera {
+    export class WebVRFreeCamera extends FreeCamera implements PoseControlled {
         public _vrDevice = null;
         public _vrDevice = null;
-        private _cacheState = null;
+        public rawPose: DevicePose = null;
         private _vrEnabled = false;
         private _vrEnabled = false;
         private _attached: boolean = false;
         private _attached: boolean = false;
 
 
@@ -23,7 +49,13 @@ module BABYLON {
 
 
         private _quaternionCache: Quaternion;
         private _quaternionCache: Quaternion;
 
 
-        private _positionOffset: Vector3;
+        private _positionOffset: Vector3 = Vector3.Zero();
+
+        public devicePosition = Vector3.Zero();
+        public deviceRotationQuaternion = new Quaternion();
+        public deviceScaleFactor: number = 1;
+
+        public controllers: Array<WebVRController> = [];
 
 
         constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = false, private webVROptions: WebVROptions = {}) {
         constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = false, private webVROptions: WebVROptions = {}) {
             super(name, position, scene);
             super(name, position, scene);
@@ -31,6 +63,10 @@ module BABYLON {
             //using the position provided as the current position offset
             //using the position provided as the current position offset
             this._positionOffset = position;
             this._positionOffset = position;
 
 
+            if (this.webVROptions && this.webVROptions.positionScale) {
+                this.deviceScaleFactor = this.webVROptions.positionScale;
+            }
+
             //enable VR
             //enable VR
             this.getEngine().initWebVR();
             this.getEngine().initWebVR();
 
 
@@ -62,7 +98,7 @@ module BABYLON {
                         }
                         }
 
 
                         //reset the rig parameters.
                         //reset the rig parameters.
-                        this.setCameraRigMode(Camera.RIG_MODE_WEBVR, { vrDisplay: this._vrDevice, frameData: this._frameData });
+                        this.setCameraRigMode(Camera.RIG_MODE_WEBVR, { parentCamera: this, vrDisplay: this._vrDevice, frameData: this._frameData });
 
 
                         if (this._attached) {
                         if (this._attached) {
                             this.getEngine().enableVR(this._vrDevice)
                             this.getEngine().enableVR(this._vrDevice)
@@ -74,29 +110,48 @@ module BABYLON {
             }
             }
 
 
             this.rotationQuaternion = new Quaternion();
             this.rotationQuaternion = new Quaternion();
-            this._quaternionCache = new Quaternion();
+            this.deviceRotationQuaternion = new Quaternion();
         }
         }
 
 
         public _checkInputs(): void {
         public _checkInputs(): void {
             if (this._vrEnabled && this._vrDevice.getFrameData(this._frameData)) {
             if (this._vrEnabled && this._vrDevice.getFrameData(this._frameData)) {
-                var currentPost = this._frameData.pose;
-                //make sure we have data
-                if (currentPost && currentPost.orientation) {
-                    this._cacheState = currentPost;
-                    this.rotationQuaternion.copyFromFloats(this._cacheState.orientation[0], this._cacheState.orientation[1], -this._cacheState.orientation[2], -this._cacheState.orientation[3]);
-                    if (this.webVROptions.trackPosition && this._cacheState.position) {
-                        this.position.copyFromFloats(this._cacheState.position[0], this._cacheState.position[1], -this._cacheState.position[2]);
-                        //scale the position accordingly
-                        this.webVROptions.positionScale && this.position.scaleInPlace(this.webVROptions.positionScale);
-                        //add the position offset
-                        this.position.addInPlace(this._positionOffset);
-                    }
-                }
+                var currentPose = this._frameData.pose;
+                this.updateFromDevice(currentPose);
             }
             }
 
 
             super._checkInputs();
             super._checkInputs();
         }
         }
 
 
+        updateFromDevice(poseData: DevicePose) {
+            if (poseData && poseData.orientation) {
+                this.rawPose = poseData;
+                this.deviceRotationQuaternion.copyFromFloats(this.rawPose.orientation[0], this.rawPose.orientation[1], -this.rawPose.orientation[2], -this.rawPose.orientation[3]);
+
+                if (this.getScene().useRightHandedSystem) {
+                    this.deviceRotationQuaternion.z *= -1;
+                    this.deviceRotationQuaternion.w *= -1;
+                }
+                if (this.webVROptions.trackPosition && this.rawPose.position) {
+                    this.devicePosition.copyFromFloats(this.rawPose.position[0], this.rawPose.position[1], -this.rawPose.position[2]);
+                    if (this.getScene().useRightHandedSystem) {
+                        this.devicePosition.z *= -1;
+                    }
+                }
+            }
+        }
+
+
+        /**
+         * WebVR's attach control will start broadcasting frames to the device.
+         * Note that in certain browsers (chrome for example) this function must be called
+         * within a user-interaction callback. Example:
+         * <pre> scene.onPointerDown = function() { camera.attachControl(canvas); }</pre>
+         * 
+         * @param {HTMLElement} element 
+         * @param {boolean} [noPreventDefault] 
+         * 
+         * @memberOf WebVRFreeCamera
+         */
         public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
         public attachControl(element: HTMLElement, noPreventDefault?: boolean): void {
             super.attachControl(element, noPreventDefault);
             super.attachControl(element, noPreventDefault);
             this._attached = true;
             this._attached = true;
@@ -106,6 +161,9 @@ module BABYLON {
             if (this._vrEnabled) {
             if (this._vrEnabled) {
                 this.getEngine().enableVR(this._vrDevice)
                 this.getEngine().enableVR(this._vrDevice)
             }
             }
+
+            // try to attach the controllers, if found.
+            this.initControllers();
         }
         }
 
 
         public detachControl(element: HTMLElement): void {
         public detachControl(element: HTMLElement): void {
@@ -117,7 +175,7 @@ module BABYLON {
 
 
         public requestVRFullscreen(requestPointerlock: boolean) {
         public requestVRFullscreen(requestPointerlock: boolean) {
             //Backwards comp.
             //Backwards comp.
-            Tools.Warn("requestVRFullscreen is deprecated. call attachControl() to start sending frames to the VR display.")
+            Tools.Warn("requestVRFullscreen is deprecated. call attachControl() inside a user-interaction callback to start sending frames to the VR display.")
             //this.getEngine().switchFullscreen(requestPointerlock);
             //this.getEngine().switchFullscreen(requestPointerlock);
         }
         }
 
 
@@ -132,34 +190,86 @@ module BABYLON {
         }
         }
 
 
         /**
         /**
-         * 
-         * Set the position offset of the VR camera
-         * The offset will be added to the WebVR pose, after scaling it (if set).
-         * 
+         *
+         * @deprecated
+         * This function was used to change the position offset. it is now done using camera.position.
+         *  
          * @param {Vector3} [newPosition] an optional new position. if not provided, the current camera position will be used.
          * @param {Vector3} [newPosition] an optional new position. if not provided, the current camera position will be used.
          * 
          * 
          * @memberOf WebVRFreeCamera
          * @memberOf WebVRFreeCamera
          */
          */
         public setPositionOffset(newPosition?: Vector3) {
         public setPositionOffset(newPosition?: Vector3) {
-            if(newPosition) {
-                this._positionOffset = newPosition;
-            } else {
-                this._positionOffset.copyFrom(this.position);
+            if (newPosition) {
+                this.position.copyFrom(newPosition);
             }
             }
         }
         }
-    }
-    
-    export class WebVRGamepadCamera extends WebVRFreeCamera {
 
 
-        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion:boolean = false, webVROptions: WebVROptions = {}) {
-            super(name, position, scene, compensateDistortion, webVROptions);
-            
-            this.inputs.addGamepad();
+        /**
+         * This function is called by the two RIG cameras.
+         * 'this' is the left or right camera (and NOT (!!!) the WebVRFreeCamera instance)
+         */
+        protected _getWebVRViewMatrix(): Matrix {
+            var viewArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftViewMatrix : this._cameraRigParams["frameData"].rightViewMatrix;
+
+            if (!this.getScene().useRightHandedSystem) {
+                [2, 6, 8, 9, 14].forEach(function (num) {
+                    viewArray[num] *= -1;
+                });
+            }
+            Matrix.FromArrayToRef(viewArray, 0, this._webvrViewMatrix);
+
+            let parentCamera: WebVRFreeCamera = this._cameraRigParams["parentCamera"];
+
+            // should the view matrix be updated with scale and position offset?
+            if (parentCamera.position.lengthSquared() || parentCamera.deviceScaleFactor !== 1) {
+                this._webvrViewMatrix.invert();
+                // scale the position, if set
+                if (parentCamera.deviceScaleFactor) {
+                    this._webvrViewMatrix.m[12] *= parentCamera.deviceScaleFactor;
+                    this._webvrViewMatrix.m[13] *= parentCamera.deviceScaleFactor;
+                    this._webvrViewMatrix.m[14] *= parentCamera.deviceScaleFactor;
+                }
+                // change the position (for "teleporting");
+                this._webvrViewMatrix.m[12] += parentCamera.position.x;
+                this._webvrViewMatrix.m[13] += parentCamera.position.y;
+                this._webvrViewMatrix.m[14] += parentCamera.position.z;
+                this._webvrViewMatrix.invert();
+            }
+            // is rotation offset set? 
+            if (!Quaternion.IsIdentity(this.rotationQuaternion)) {
+                this.rotationQuaternion.toRotationMatrix(this._tempMatrix);
+                this._tempMatrix.multiplyToRef(this._webvrViewMatrix, this._webvrViewMatrix);
+            }
+
+            return this._webvrViewMatrix;
         }
         }
 
 
-        public getClassName(): string {
-            return "WebVRGamepadCamera";
+        public _isSynchronizedViewMatrix() {
+            return false;
         }
         }
-    }    
+
+        protected _getWebVRProjectionMatrix(): Matrix {
+            var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftProjectionMatrix : this._cameraRigParams["frameData"].rightProjectionMatrix;
+            //babylon compatible matrix
+            if (!this.getScene().useRightHandedSystem) {
+                [8, 9, 10, 11].forEach(function (num) {
+                    projectionArray[num] *= -1;
+                });
+            }
+            Matrix.FromArrayToRef(projectionArray, 0, this._projectionMatrix);
+            return this._projectionMatrix;
+        }
+
+        public initControllers() {
+            this.controllers = [];
+            new BABYLON.Gamepads((gp) => {
+                if (gp.type === BABYLON.Gamepad.POSE_ENABLED) {
+                    let webVrController: WebVRController = <WebVRController>gp;
+                    webVrController.attachToPoseControlledCamera(this);
+                    this.controllers.push(webVrController);
+                }
+            });
+        }
+    }
 }
 }
 
 

+ 28 - 24
src/Cameras/babylon.camera.ts

@@ -119,6 +119,8 @@
         public _cameraRigParams: any;
         public _cameraRigParams: any;
         public _rigCameras = new Array<Camera>();
         public _rigCameras = new Array<Camera>();
         public _rigPostProcess: PostProcess;
         public _rigPostProcess: PostProcess;
+        protected _webvrViewMatrix = Matrix.Identity();
+
 
 
         // Cache
         // Cache
         private _computedViewMatrix = Matrix.Identity();
         private _computedViewMatrix = Matrix.Identity();
@@ -127,7 +129,6 @@
         private _worldMatrix: Matrix;
         private _worldMatrix: Matrix;
         public _postProcesses = new Array<PostProcess>();
         public _postProcesses = new Array<PostProcess>();
         private _transformMatrix = Matrix.Zero();
         private _transformMatrix = Matrix.Zero();
-        private _webvrViewMatrix = Matrix.Identity();
 
 
         public _activeMeshes = new SmartArray<Mesh>(256);
         public _activeMeshes = new SmartArray<Mesh>(256);
 
 
@@ -149,7 +150,7 @@
 
 
         public getClassName(): string {
         public getClassName(): string {
             return "Camera";
             return "Camera";
-        }          
+        }
 
 
         /**
         /**
          * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
          * @param {boolean} fullDetails - support for multiple levels of logging within scene loading
@@ -607,19 +608,27 @@
                     if (rigParams.vrDisplay) {
                     if (rigParams.vrDisplay) {
                         //var leftEye = rigParams.vrDisplay.getEyeParameters('left');
                         //var leftEye = rigParams.vrDisplay.getEyeParameters('left');
                         //var rightEye = rigParams.vrDisplay.getEyeParameters('right');
                         //var rightEye = rigParams.vrDisplay.getEyeParameters('right');
+
+                        //Left eye
                         this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
                         this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
                         this._rigCameras[0].setCameraRigParameter("left", true);
                         this._rigCameras[0].setCameraRigParameter("left", true);
                         this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
                         this._rigCameras[0].setCameraRigParameter("frameData", rigParams.frameData);
-                        //this._rigCameras[0].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-leftEye.offset[0], leftEye.offset[1], -leftEye.offset[2]));
+                        this._rigCameras[0].setCameraRigParameter("parentCamera", rigParams.parentCamera);
+                        //this._rigCameras[0].setCameraRigParameter('eyeParameters', leftEye);
                         this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
                         this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
-                        //this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
+                        this._rigCameras[0]._getViewMatrix = this._getWebVRViewMatrix;
+                        this._rigCameras[0]._isSynchronizedViewMatrix = this._isSynchronizedViewMatrix;
+
+                        //Right eye
                         this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
                         this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
+                        //this._rigCameras[1].setCameraRigParameter('eyeParameters', rightEye);
                         this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
                         this._rigCameras[1].setCameraRigParameter("frameData", rigParams.frameData);
-                        //this._rigCameras[1].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-rightEye.offset[0], rightEye.offset[1], -rightEye.offset[2]));
+                        this._rigCameras[1].setCameraRigParameter("parentCamera", rigParams.parentCamera);
                         this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
                         this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
                         this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
-                        //this._rigCameras[1]._getViewMatrix = this._getWebVRViewMatrix;
+                        this._rigCameras[1]._getViewMatrix = this._getWebVRViewMatrix;
+                        this._rigCameras[1]._isSynchronizedViewMatrix = this._isSynchronizedViewMatrix;
                     }
                     }
                     break;
                     break;
 
 
@@ -636,25 +645,20 @@
             return this._projectionMatrix;
             return this._projectionMatrix;
         }
         }
 
 
-        private _getWebVRProjectionMatrix(): Matrix {
-            var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftProjectionMatrix : this._cameraRigParams["frameData"].rightProjectionMatrix;
-            //babylon compatible matrix
-            [8, 9, 10, 11].forEach(function (num) {
-                projectionArray[num] *= -1;
-            });
-            Matrix.FromArrayToRef(projectionArray, 0, this._projectionMatrix);
-            return this._projectionMatrix;
+        /**
+         * This function MUST be overwritten by the different WebVR cameras available.
+         * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
+         */
+        protected _getWebVRProjectionMatrix(): Matrix {
+            return Matrix.Identity();
         }
         }
 
 
-        //Can be used, but we'll use the free camera's view matrix calculation
-        private _getWebVRViewMatrix(): Matrix {
-            var projectionArray = this._cameraRigParams["left"] ? this._cameraRigParams["frameData"].leftViewMatrix : this._cameraRigParams["frameData"].rightViewMatrix;
-            //babylon compatible matrix
-            [8, 9, 10, 11].forEach(function (num) {
-                projectionArray[num] *= -1;
-            });
-            Matrix.FromArrayToRef(projectionArray, 0, this._webvrViewMatrix);
-            return this._webvrViewMatrix;
+        /**
+         * This function MUST be overwritten by the different WebVR cameras available.
+         * The context in which it is running is the RIG camera. So 'this' is the TargetCamera, left or right.
+         */
+        protected _getWebVRViewMatrix(): Matrix {
+            return Matrix.Identity();
         }
         }
 
 
         public setCameraRigParameter(name: string, value: any) {
         public setCameraRigParameter(name: string, value: any) {
@@ -750,7 +754,7 @@
                 case "WebVRFreeCamera":
                 case "WebVRFreeCamera":
                     return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
                     return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
                 case "WebVRGamepadCamera":
                 case "WebVRGamepadCamera":
-                    return () => new WebVRGamepadCamera(name, Vector3.Zero(), scene);
+                    return () => new WebVRFreeCamera(name, Vector3.Zero(), scene);
                 case "VRDeviceOrientationFreeCamera":
                 case "VRDeviceOrientationFreeCamera":
                     return () => new VRDeviceOrientationFreeCamera(name, Vector3.Zero(), scene);
                     return () => new VRDeviceOrientationFreeCamera(name, Vector3.Zero(), scene);
                 case "VRDeviceOrientationGamepadCamera":
                 case "VRDeviceOrientationGamepadCamera":

+ 1 - 1
src/Cameras/babylon.targetCamera.ts

@@ -247,7 +247,7 @@ module BABYLON {
             } else {
             } else {
                 Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
                 Matrix.LookAtLHToRef(this.position, this._currentTarget, this.upVector, this._viewMatrix);
             }
             }
-            
+
             return this._viewMatrix;
             return this._viewMatrix;
         }
         }
 
 

+ 67 - 61
src/Math/babylon.math.ts

@@ -1275,7 +1275,7 @@
 
 
         /**
         /**
          * Returns a new Vector3 set witth the result of the division of the current Vector3 coordinates by the passed ones.  
          * Returns a new Vector3 set witth the result of the division of the current Vector3 coordinates by the passed ones.  
-         */        
+         */
         public divide(otherVector: Vector3): Vector3 {
         public divide(otherVector: Vector3): Vector3 {
             return new Vector3(this.x / otherVector.x, this.y / otherVector.y, this.z / otherVector.z);
             return new Vector3(this.x / otherVector.x, this.y / otherVector.y, this.z / otherVector.z);
         }
         }
@@ -2485,7 +2485,7 @@
         public conjugateToRef(ref: Quaternion): Quaternion {
         public conjugateToRef(ref: Quaternion): Quaternion {
             ref.copyFromFloats(-this.x, -this.y, -this.z, this.w);
             ref.copyFromFloats(-this.x, -this.y, -this.z, this.w);
             return this;
             return this;
-        }   
+        }
         /** 
         /** 
          * Conjugates in place the current Quaternion.
          * Conjugates in place the current Quaternion.
          * Returns the updated Quaternion.  
          * Returns the updated Quaternion.  
@@ -2535,7 +2535,7 @@
          * Returns the current Quaternion.  
          * Returns the current Quaternion.  
          */
          */
         public toEulerAnglesToRef(result: Vector3, order = "YZX"): Quaternion {
         public toEulerAnglesToRef(result: Vector3, order = "YZX"): Quaternion {
-            
+
             var qz = this.z;
             var qz = this.z;
             var qx = this.x;
             var qx = this.x;
             var qy = this.y;
             var qy = this.y;
@@ -2546,25 +2546,25 @@
             var sqx = qx * qx;
             var sqx = qx * qx;
             var sqy = qy * qy;
             var sqy = qy * qy;
 
 
-            var zAxisY = qy*qz - qx*qw;
+            var zAxisY = qy * qz - qx * qw;
             var limit = .4999999;
             var limit = .4999999;
 
 
-            if(zAxisY < -limit){
-                result.y = 2 * Math.atan2(qy,qw);
-                result.x = Math.PI/2;
+            if (zAxisY < -limit) {
+                result.y = 2 * Math.atan2(qy, qw);
+                result.x = Math.PI / 2;
                 result.z = 0;
                 result.z = 0;
-            }else if(zAxisY > limit){
-                result.y = 2 * Math.atan2(qy,qw);
-                result.x = -Math.PI/2;
+            } else if (zAxisY > limit) {
+                result.y = 2 * Math.atan2(qy, qw);
+                result.x = -Math.PI / 2;
                 result.z = 0;
                 result.z = 0;
-            }else{
+            } else {
                 result.z = Math.atan2(2.0 * (qx * qy + qz * qw), (-sqz - sqx + sqy + sqw));
                 result.z = Math.atan2(2.0 * (qx * qy + qz * qw), (-sqz - sqx + sqy + sqw));
                 result.x = Math.asin(-2.0 * (qz * qy - qx * qw));
                 result.x = Math.asin(-2.0 * (qz * qy - qx * qw));
                 result.y = Math.atan2(2.0 * (qz * qx + qy * qw), (sqz - sqx - sqy + sqw));
                 result.y = Math.atan2(2.0 * (qz * qx + qy * qw), (sqz - sqx - sqy + sqw));
             }
             }
 
 
             return this;
             return this;
-            
+
         }
         }
         /**
         /**
          * Updates the passed rotation matrix with the current Quaternion values.  
          * Updates the passed rotation matrix with the current Quaternion values.  
@@ -2680,6 +2680,10 @@
         public static Identity(): Quaternion {
         public static Identity(): Quaternion {
             return new Quaternion(0.0, 0.0, 0.0, 1.0);
             return new Quaternion(0.0, 0.0, 0.0, 1.0);
         }
         }
+
+        public static IsIdentity(quaternion: Quaternion) {
+            return quaternion && quaternion.x === 0 && quaternion.y === 0 && quaternion.z === 0 && quaternion.w === 1;
+        }
         /**
         /**
          * Returns a new Quaternion set from the passed axis (Vector3) and angle in radians (float). 
          * Returns a new Quaternion set from the passed axis (Vector3) and angle in radians (float). 
          */
          */
@@ -2764,7 +2768,7 @@
          * cf to Vector3.RotationFromAxis() documentation.  
          * cf to Vector3.RotationFromAxis() documentation.  
          * Note : axis1, axis2 and axis3 are normalized during this operation.   
          * Note : axis1, axis2 and axis3 are normalized during this operation.   
          */
          */
-         public static RotationQuaternionFromAxis(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Quaternion): Quaternion {
+        public static RotationQuaternionFromAxis(axis1: Vector3, axis2: Vector3, axis3: Vector3, ref: Quaternion): Quaternion {
             var quat = new Quaternion(0.0, 0.0, 0.0, 0.0);
             var quat = new Quaternion(0.0, 0.0, 0.0, 0.0);
             Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             Quaternion.RotationQuaternionFromAxisToRef(axis1, axis2, axis3, quat);
             return quat;
             return quat;
@@ -2781,7 +2785,7 @@
         }
         }
 
 
         public static Slerp(left: Quaternion, right: Quaternion, amount: number): Quaternion {
         public static Slerp(left: Quaternion, right: Quaternion, amount: number): Quaternion {
-            
+
             var result = Quaternion.Identity();
             var result = Quaternion.Identity();
 
 
             Quaternion.SlerpToRef(left, right, amount, result);
             Quaternion.SlerpToRef(left, right, amount, result);
@@ -2790,7 +2794,7 @@
 
 
         }
         }
 
 
-        public static SlerpToRef(left: Quaternion, right: Quaternion, amount: number, result:Quaternion): void {
+        public static SlerpToRef(left: Quaternion, right: Quaternion, amount: number, result: Quaternion): void {
             var num2;
             var num2;
             var num3;
             var num3;
             var num = amount;
             var num = amount;
@@ -2868,9 +2872,9 @@
         public toArray(): Float32Array {
         public toArray(): Float32Array {
             return this.m;
             return this.m;
         }
         }
-         /**
-         * Returns the matrix underlying array.  
-         */       
+        /**
+        * Returns the matrix underlying array.  
+        */
         public asArray(): Float32Array {
         public asArray(): Float32Array {
             return this.toArray();
             return this.toArray();
         }
         }
@@ -2995,10 +2999,10 @@
             this.m[14] = z;
             this.m[14] = z;
             return this;
             return this;
         }
         }
-                /**
-         * Inserts the translation vector in the current Matrix.  
-         * Returns the updated Matrix.  
-         */
+        /**
+ * Inserts the translation vector in the current Matrix.  
+ * Returns the updated Matrix.  
+ */
         public setTranslation(vector3: Vector3): Matrix {
         public setTranslation(vector3: Vector3): Matrix {
             this.m[12] = vector3.x;
             this.m[12] = vector3.x;
             this.m[13] = vector3.y;
             this.m[13] = vector3.y;
@@ -3014,7 +3018,7 @@
         /**
         /**
          * Fill a Vector3 with the extracted translation from the Matrix.  
          * Fill a Vector3 with the extracted translation from the Matrix.  
          */
          */
-        public getTranslationToRef(result:Vector3): Matrix {
+        public getTranslationToRef(result: Vector3): Matrix {
             result.x = this.m[12];
             result.x = this.m[12];
             result.y = this.m[13];
             result.y = this.m[13];
             result.z = this.m[14];
             result.z = this.m[14];
@@ -3030,7 +3034,7 @@
             this.setRowFromFloats(1, 0, 1, 0, 0);
             this.setRowFromFloats(1, 0, 1, 0, 0);
             this.setRowFromFloats(2, 0, 0, 1, 0);
             this.setRowFromFloats(2, 0, 0, 1, 0);
             return this;
             return this;
-        }        
+        }
         /**
         /**
          * Returns a new Matrix set with the multiplication result of the current Matrix and the passed one.  
          * Returns a new Matrix set with the multiplication result of the current Matrix and the passed one.  
          */
          */
@@ -3202,7 +3206,7 @@
         /**
         /**
          * Returns a new Matrix as the extracted rotation matrix from the current one.  
          * Returns a new Matrix as the extracted rotation matrix from the current one.  
          */
          */
-        public getRotationMatrix(): Matrix{
+        public getRotationMatrix(): Matrix {
             var result = Matrix.Identity();
             var result = Matrix.Identity();
             this.getRotationMatrixToRef(result);
             this.getRotationMatrixToRef(result);
             return result;
             return result;
@@ -3211,7 +3215,7 @@
          * Extracts the rotation matrix from the current one and sets it as the passed "result".  
          * Extracts the rotation matrix from the current one and sets it as the passed "result".  
          * Returns the current Matrix.  
          * Returns the current Matrix.  
          */
          */
-        public getRotationMatrixToRef(result:Matrix): Matrix {
+        public getRotationMatrixToRef(result: Matrix): Matrix {
             var m = this.m;
             var m = this.m;
 
 
             var xs = m[0] * m[1] * m[2] * m[3] < 0 ? -1 : 1;
             var xs = m[0] * m[1] * m[2] * m[3] < 0 ? -1 : 1;
@@ -3309,7 +3313,7 @@
             this.m[i + 3] = row.w;
             this.m[i + 3] = row.w;
             return this;
             return this;
         }
         }
-        
+
         /**
         /**
          * Sets the index-th row of the current matrix with the passed 4 x float values.
          * Sets the index-th row of the current matrix with the passed 4 x float values.
          * Returns the updated Matrix.    
          * Returns the updated Matrix.    
@@ -3364,9 +3368,9 @@
             return result;
             return result;
         }
         }
 
 
-          /**
-         * Update a Matrix with values composed by the passed scale (vector3), rotation (quaternion) and translation (vector3).  
-         */
+        /**
+       * Update a Matrix with values composed by the passed scale (vector3), rotation (quaternion) and translation (vector3).  
+       */
         public static ComposeToRef(scale: Vector3, rotation: Quaternion, translation: Vector3, result: Matrix): void {
         public static ComposeToRef(scale: Vector3, rotation: Quaternion, translation: Vector3, result: Matrix): void {
             Matrix.FromValuesToRef(scale.x, 0, 0, 0,
             Matrix.FromValuesToRef(scale.x, 0, 0, 0,
                 0, scale.y, 0, 0,
                 0, scale.y, 0, 0,
@@ -3377,7 +3381,7 @@
             MathTmp.Matrix[1].multiplyToRef(MathTmp.Matrix[0], result);
             MathTmp.Matrix[1].multiplyToRef(MathTmp.Matrix[0], result);
 
 
             result.setTranslation(translation);
             result.setTranslation(translation);
-        }      
+        }
         /**
         /**
          * Returns a new indentity Matrix.  
          * Returns a new indentity Matrix.  
          */
          */
@@ -3750,7 +3754,7 @@
             let a = 2.0 / width;
             let a = 2.0 / width;
             let b = 2.0 / height;
             let b = 2.0 / height;
             let c = 2.0 / (f - n);
             let c = 2.0 / (f - n);
-            let d = -(f + n)/(f - n);
+            let d = -(f + n) / (f - n);
 
 
             BABYLON.Matrix.FromValuesToRef(
             BABYLON.Matrix.FromValuesToRef(
                 a, 0.0, 0.0, 0.0,
                 a, 0.0, 0.0, 0.0,
@@ -3780,7 +3784,7 @@
             let a = 2.0 / (right - left);
             let a = 2.0 / (right - left);
             let b = 2.0 / (top - bottom);
             let b = 2.0 / (top - bottom);
             let c = 2.0 / (f - n);
             let c = 2.0 / (f - n);
-            let d = -(f + n)/(f - n);
+            let d = -(f + n) / (f - n);
             let i0 = (left + right) / (left - right);
             let i0 = (left + right) / (left - right);
             let i1 = (top + bottom) / (bottom - top);
             let i1 = (top + bottom) / (bottom - top);
 
 
@@ -3818,8 +3822,8 @@
 
 
             let a = 2.0 * n / width;
             let a = 2.0 * n / width;
             let b = 2.0 * n / height;
             let b = 2.0 * n / height;
-            let c = (f + n)/(f - n);
-            let d = -2.0 * f * n/(f - n);
+            let c = (f + n) / (f - n);
+            let d = -2.0 * f * n / (f - n);
 
 
             BABYLON.Matrix.FromValuesToRef(
             BABYLON.Matrix.FromValuesToRef(
                 a, 0.0, 0.0, 0.0,
                 a, 0.0, 0.0, 0.0,
@@ -3849,8 +3853,8 @@
             let t = 1.0 / (Math.tan(fov * 0.5));
             let t = 1.0 / (Math.tan(fov * 0.5));
             let a = isVerticalFovFixed ? (t / aspect) : t;
             let a = isVerticalFovFixed ? (t / aspect) : t;
             let b = isVerticalFovFixed ? t : (t * aspect);
             let b = isVerticalFovFixed ? t : (t * aspect);
-            let c = (f + n)/(f - n);
-            let d = -2.0 * f * n/(f - n);
+            let c = (f + n) / (f - n);
+            let d = -2.0 * f * n / (f - n);
 
 
             BABYLON.Matrix.FromValuesToRef(
             BABYLON.Matrix.FromValuesToRef(
                 a, 0.0, 0.0, 0.0,
                 a, 0.0, 0.0, 0.0,
@@ -3883,13 +3887,13 @@
             let t = 1.0 / (Math.tan(fov * 0.5));
             let t = 1.0 / (Math.tan(fov * 0.5));
             let a = isVerticalFovFixed ? (t / aspect) : t;
             let a = isVerticalFovFixed ? (t / aspect) : t;
             let b = isVerticalFovFixed ? t : (t * aspect);
             let b = isVerticalFovFixed ? t : (t * aspect);
-            let c = -(f + n)/(f - n);
-            let d = -2*f*n/(f - n);
+            let c = -(f + n) / (f - n);
+            let d = -2 * f * n / (f - n);
 
 
             BABYLON.Matrix.FromValuesToRef(
             BABYLON.Matrix.FromValuesToRef(
                 a, 0.0, 0.0, 0.0,
                 a, 0.0, 0.0, 0.0,
                 0.0, b, 0.0, 0.0,
                 0.0, b, 0.0, 0.0,
-                0.0, 0.0, c,-1.0,
+                0.0, 0.0, c, -1.0,
                 0.0, 0.0, d, 0.0,
                 0.0, 0.0, d, 0.0,
                 result
                 result
             );
             );
@@ -3897,8 +3901,10 @@
         /**
         /**
          * Sets the passed matrix "result" as a left-handed perspective projection matrix  for WebVR computed from the passed floats : vertical angle of view (fov), width/height ratio (aspect), z near and far limits.  
          * Sets the passed matrix "result" as a left-handed perspective projection matrix  for WebVR computed from the passed floats : vertical angle of view (fov), width/height ratio (aspect), z near and far limits.  
          */
          */
-        public static PerspectiveFovWebVRToRef(fov, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true): void {
-            //left handed
+        public static PerspectiveFovWebVRToRef(fov, znear: number, zfar: number, result: Matrix, rightHanded = false): void {
+
+            var rightHandedFactor = rightHanded ? -1 : 1;
+
             var upTan = Math.tan(fov.upDegrees * Math.PI / 180.0);
             var upTan = Math.tan(fov.upDegrees * Math.PI / 180.0);
             var downTan = Math.tan(fov.downDegrees * Math.PI / 180.0);
             var downTan = Math.tan(fov.downDegrees * Math.PI / 180.0);
             var leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0);
             var leftTan = Math.tan(fov.leftDegrees * Math.PI / 180.0);
@@ -3908,12 +3914,12 @@
             result.m[0] = xScale;
             result.m[0] = xScale;
             result.m[1] = result.m[2] = result.m[3] = result.m[4] = 0.0;
             result.m[1] = result.m[2] = result.m[3] = result.m[4] = 0.0;
             result.m[5] = yScale;
             result.m[5] = yScale;
-            result.m[6] = result.m[7] =  0.0;
-            result.m[8] = ((leftTan - rightTan) * xScale * 0.5);
-            result.m[9] = -((upTan - downTan) * yScale * 0.5);
-            result.m[10] = -(znear + zfar) / (zfar - znear);
+            result.m[6] = result.m[7] = 0.0;
+            result.m[8] = ((leftTan - rightTan) * xScale * 0.5) * rightHandedFactor;
+            result.m[9] = -((upTan - downTan) * yScale * 0.5) * rightHandedFactor;
+            result.m[10] = -(znear + zfar) / (zfar - znear) * rightHandedFactor;
             // result.m[10] = -zfar / (znear - zfar);
             // result.m[10] = -zfar / (znear - zfar);
-            result.m[11] = 1.0;
+            result.m[11] = 1.0 * rightHandedFactor;
             result.m[12] = result.m[13] = result.m[15] = 0.0;
             result.m[12] = result.m[13] = result.m[15] = 0.0;
             result.m[14] = -(2.0 * zfar * znear) / (zfar - znear);
             result.m[14] = -(2.0 * zfar * znear) / (zfar - znear);
             // result.m[14] = (znear * zfar) / (znear - zfar);
             // result.m[14] = (znear * zfar) / (znear - zfar);
@@ -4028,29 +4034,29 @@
          * Sets the passed matrix "mat" as a rotation matrix composed from the 3 passed  left handed axis.  
          * Sets the passed matrix "mat" as a rotation matrix composed from the 3 passed  left handed axis.  
          */
          */
         public static FromXYZAxesToRef(xaxis: Vector3, yaxis: Vector3, zaxis: Vector3, mat: Matrix) {
         public static FromXYZAxesToRef(xaxis: Vector3, yaxis: Vector3, zaxis: Vector3, mat: Matrix) {
-            
+
             mat.m[0] = xaxis.x;
             mat.m[0] = xaxis.x;
             mat.m[1] = xaxis.y;
             mat.m[1] = xaxis.y;
             mat.m[2] = xaxis.z;
             mat.m[2] = xaxis.z;
 
 
             mat.m[3] = 0.0;
             mat.m[3] = 0.0;
-            
+
             mat.m[4] = yaxis.x;
             mat.m[4] = yaxis.x;
             mat.m[5] = yaxis.y;
             mat.m[5] = yaxis.y;
             mat.m[6] = yaxis.z;
             mat.m[6] = yaxis.z;
-            
+
             mat.m[7] = 0.0;
             mat.m[7] = 0.0;
-            
+
             mat.m[8] = zaxis.x;
             mat.m[8] = zaxis.x;
             mat.m[9] = zaxis.y;
             mat.m[9] = zaxis.y;
             mat.m[10] = zaxis.z;
             mat.m[10] = zaxis.z;
-            
+
             mat.m[11] = 0.0;
             mat.m[11] = 0.0;
-            
+
             mat.m[12] = 0.0;
             mat.m[12] = 0.0;
             mat.m[13] = 0.0;
             mat.m[13] = 0.0;
             mat.m[14] = 0.0;
             mat.m[14] = 0.0;
-            
+
             mat.m[15] = 1.0;
             mat.m[15] = 1.0;
 
 
         }
         }
@@ -4058,7 +4064,7 @@
         /**
         /**
          * Sets the passed matrix "result" as a rotation matrix according to the passed quaternion.  
          * Sets the passed matrix "result" as a rotation matrix according to the passed quaternion.  
          */
          */
-        public static FromQuaternionToRef(quat:Quaternion, result:Matrix){
+        public static FromQuaternionToRef(quat: Quaternion, result: Matrix) {
 
 
             var xx = quat.x * quat.x;
             var xx = quat.x * quat.x;
             var yy = quat.y * quat.y;
             var yy = quat.y * quat.y;
@@ -4863,12 +4869,12 @@
             for (var i = 0; i < totalPoints.length - 3; i++) {
             for (var i = 0; i < totalPoints.length - 3; i++) {
                 var amount = 0.0;
                 var amount = 0.0;
                 for (var c = 0; c < nbPoints; c++) {
                 for (var c = 0; c < nbPoints; c++) {
-                    catmullRom.push( Vector3.CatmullRom(totalPoints[i], totalPoints[i + 1], totalPoints[i + 2], totalPoints[i + 3], amount) );
+                    catmullRom.push(Vector3.CatmullRom(totalPoints[i], totalPoints[i + 1], totalPoints[i + 2], totalPoints[i + 3], amount));
                     amount += step
                     amount += step
                 }
                 }
             }
             }
             i--;
             i--;
-            catmullRom.push( Vector3.CatmullRom(totalPoints[i], totalPoints[i + 1], totalPoints[i + 2], totalPoints[i + 3], amount) );
+            catmullRom.push(Vector3.CatmullRom(totalPoints[i], totalPoints[i + 1], totalPoints[i + 2], totalPoints[i + 3], amount));
             return new Curve3(catmullRom);
             return new Curve3(catmullRom);
         }
         }
 
 
@@ -5031,13 +5037,13 @@
         public static Color3: Color3[] = [Color3.Black(), Color3.Black(), Color3.Black()];
         public static Color3: Color3[] = [Color3.Black(), Color3.Black(), Color3.Black()];
         public static Vector2: Vector2[] = [Vector2.Zero(), Vector2.Zero(), Vector2.Zero()];  // 3 temp Vector2 at once should be enough
         public static Vector2: Vector2[] = [Vector2.Zero(), Vector2.Zero(), Vector2.Zero()];  // 3 temp Vector2 at once should be enough
         public static Vector3: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),
         public static Vector3: Vector3[] = [Vector3.Zero(), Vector3.Zero(), Vector3.Zero(),
-            Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];    // 9 temp Vector3 at once should be enough
+        Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero(), Vector3.Zero()];    // 9 temp Vector3 at once should be enough
         public static Vector4: Vector4[] = [Vector4.Zero(), Vector4.Zero(), Vector4.Zero()];  // 3 temp Vector4 at once should be enough
         public static Vector4: Vector4[] = [Vector4.Zero(), Vector4.Zero(), Vector4.Zero()];  // 3 temp Vector4 at once should be enough
         public static Quaternion: Quaternion[] = [Quaternion.Zero(), Quaternion.Zero()];                // 2 temp Quaternion at once should be enough
         public static Quaternion: Quaternion[] = [Quaternion.Zero(), Quaternion.Zero()];                // 2 temp Quaternion at once should be enough
         public static Matrix: Matrix[] = [Matrix.Zero(), Matrix.Zero(),
         public static Matrix: Matrix[] = [Matrix.Zero(), Matrix.Zero(),
-            Matrix.Zero(), Matrix.Zero(),
-            Matrix.Zero(), Matrix.Zero(),
-            Matrix.Zero(), Matrix.Zero()];                      // 6 temp Matrices at once should be enough
+        Matrix.Zero(), Matrix.Zero(),
+        Matrix.Zero(), Matrix.Zero(),
+        Matrix.Zero(), Matrix.Zero()];                      // 6 temp Matrices at once should be enough
     }
     }
     // Same as Tmp but not exported to keep it onyl for math functions to avoid conflicts
     // Same as Tmp but not exported to keep it onyl for math functions to avoid conflicts
     class MathTmp {
     class MathTmp {

+ 348 - 0
src/Tools/babylon.extendedGamepad.ts

@@ -0,0 +1,348 @@
+
+module BABYLON {
+
+    export enum PoseEnabledControllerType {
+        VIVE,
+        OCULUS,
+        GENERIC
+    }
+
+    export interface MutableGamepadButton {
+        value: number;
+        touched: boolean;
+        pressed: boolean;
+    }
+
+    export class PoseEnabledControllerHelper {
+        public static InitiateController(vrGamepad: any) {
+            // for now, only Oculus and Vive are supported
+            if (vrGamepad.id.indexOf('Oculus Touch') !== -1) {
+                return new OculusTouchController(vrGamepad);
+            } else {
+                return new ViveController(vrGamepad);
+            }
+        }
+    }
+
+    export class PoseEnabledController extends Gamepad implements PoseControlled {
+        devicePosition: Vector3;
+        deviceRotationQuaternion: Quaternion;
+        deviceScaleFactor: number = 1;
+
+        public position: Vector3;
+        public rotationQuaternion: Quaternion;
+        public controllerType: PoseEnabledControllerType;
+
+        private _calculatedPosition: Vector3;
+        private _calculatedRotation: Quaternion;
+
+        public rawPose: DevicePose; //GamepadPose;
+
+        private _mesh: AbstractMesh; // a node that will be attached to this Gamepad
+        private _poseControlledCamera: PoseControlled;
+
+        constructor(public vrGamepad) {
+            super(vrGamepad.id, vrGamepad.index, vrGamepad);
+            this.type = Gamepad.POSE_ENABLED;
+            this.controllerType = PoseEnabledControllerType.GENERIC;
+            this.position = Vector3.Zero();
+            this.rotationQuaternion = new Quaternion();
+            this.devicePosition = Vector3.Zero();
+            this.deviceRotationQuaternion = new Quaternion();
+
+            this._calculatedPosition = Vector3.Zero();
+            this._calculatedRotation = new Quaternion();
+        }
+
+        public update() {
+            super.update();
+            // update this device's offset position from the attached camera, if provided
+            if (this._poseControlledCamera) {
+                //this.position.copyFrom(this._poseControlledCamera.position);
+                //this.rotationQuaternion.copyFrom(this._poseControlledCamera.rotationQuaternion);
+                this.deviceScaleFactor = this._poseControlledCamera.deviceScaleFactor;
+            }
+            var pose: GamepadPose = this.vrGamepad.pose;
+            this.updateFromDevice(pose);
+
+            if (this._mesh) {
+                this._mesh.position.copyFrom(this._calculatedPosition);
+                this._mesh.rotationQuaternion.copyFrom(this._calculatedRotation);
+            }
+        }
+
+        updateFromDevice(poseData: DevicePose) {
+            if (poseData) {
+                this.rawPose = poseData;
+                if (poseData.position) {
+                    this.devicePosition.copyFromFloats(poseData.position[0], poseData.position[1], -poseData.position[2]);
+                    if (this._mesh && this._mesh.getScene().useRightHandedSystem) {
+                        this.devicePosition.z *= -1;
+                    }
+
+                    this.devicePosition.scaleToRef(this.deviceScaleFactor, this._calculatedPosition);
+                    this._calculatedPosition.addInPlace(this.position);
+
+                    // scale the position using the scale factor, add the device's position
+                    if (this._poseControlledCamera) {
+                        // this allows total positioning freedom - the device, the camera and the mesh can be individually controlled.
+                        this._calculatedPosition.addInPlace(this._poseControlledCamera.position);
+                    }
+                }
+                if (poseData.orientation) {
+                    this.deviceRotationQuaternion.copyFromFloats(this.rawPose.orientation[0], this.rawPose.orientation[1], -this.rawPose.orientation[2], -this.rawPose.orientation[3]);
+                    if (this._mesh && this._mesh.getScene().useRightHandedSystem) {
+                        this.deviceRotationQuaternion.z *= -1;
+                        this.deviceRotationQuaternion.w *= -1;
+                    }
+
+                    // if the camera is set, rotate to the camera's rotation
+                    this.rotationQuaternion.multiplyToRef(this.deviceRotationQuaternion, this._calculatedRotation);
+                    if (this._poseControlledCamera) {
+                        this._calculatedRotation.multiplyToRef(this._poseControlledCamera.rotationQuaternion, this._calculatedRotation);
+                    }
+                }
+            }
+        }
+
+
+        public attachToMesh(mesh: AbstractMesh) {
+            this._mesh = mesh;
+            if (!this._mesh.rotationQuaternion) {
+                this._mesh.rotationQuaternion = new Quaternion();
+            }
+        }
+
+        public attachToPoseControlledCamera(camera: PoseControlled) {
+            this._poseControlledCamera = camera;
+            this.deviceScaleFactor = camera.deviceScaleFactor;
+        }
+
+        public detachMesh() {
+            this._mesh = undefined;
+        }
+    }
+
+    export interface GamepadButtonChanges {
+        changed: boolean;
+        pressChanged: boolean;
+        touchChanged: boolean;
+        valueChanged: boolean;
+    }
+
+    export abstract class WebVRController extends PoseEnabledController {
+
+        //public onTriggerStateChangedObservable = new Observable<{ state: ExtendedGamepadButton, changes: GamepadButtonChanges }>();
+
+        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>();
+
+        protected _buttons: Array<MutableGamepadButton>;
+
+        private _onButtonStateChange: (controlledIndex: number, buttonIndex: number, state: ExtendedGamepadButton) => void;
+
+        public onButtonStateChange(callback: (controlledIndex: number, buttonIndex: number, state: ExtendedGamepadButton) => void) {
+            this._onButtonStateChange = callback;
+        }
+
+        public pad: StickValues = { x: 0, y: 0 };
+
+        public hand: string; // 'left' or 'right', see https://w3c.github.io/gamepad/extensions.html#gamepadhand-enum
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this._buttons = new Array<ExtendedGamepadButton>(vrGamepad.buttons.length);
+            this.hand = vrGamepad.hand;
+        }
+
+        public update() {
+            super.update();
+            for (var index = 0; index < this._buttons.length; index++) {
+                this._setButtonValue(this.vrGamepad.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;
+                this.pad.y = this.leftStick.y;
+                this.onPadValuesChangedObservable.notifyObservers(this.pad);
+            }
+        }
+
+        protected abstract handleButtonChange(buttonIdx: number, value: ExtendedGamepadButton, changes: GamepadButtonChanges);
+
+        private _setButtonValue(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton, buttonIndex: number) {
+            if (!currentState) {
+                this._buttons[buttonIndex] = {
+                    pressed: newState.pressed,
+                    touched: newState.touched,
+                    value: newState.value
+                }
+                return;
+            }
+            this._checkChanges(newState, currentState);
+            if (this._changes.changed) {
+                this._onButtonStateChange && this._onButtonStateChange(this.index, buttonIndex, newState);
+
+                this.handleButtonChange(buttonIndex, newState, this._changes);
+            }
+            this._buttons[buttonIndex].pressed = newState.pressed;
+            this._buttons[buttonIndex].touched = newState.touched;
+            // oculus triggers are never 0, thou not touched.
+            this._buttons[buttonIndex].value = newState.value < 0.00000001 ? 0 : newState.value;
+        }
+
+        // avoid GC, store state in a tmp object
+        private _changes: GamepadButtonChanges = {
+            pressChanged: false,
+            touchChanged: false,
+            valueChanged: false,
+            changed: false
+        };
+
+        private _checkChanges(newState: ExtendedGamepadButton, currentState: ExtendedGamepadButton) {
+            this._changes.pressChanged = newState.pressed !== currentState.pressed;
+            this._changes.touchChanged = newState.touched !== currentState.touched;
+            this._changes.valueChanged = newState.value !== currentState.value;
+            this._changes.changed = this._changes.pressChanged || this._changes.touchChanged || this._changes.valueChanged;
+            return this._changes;
+        }
+    }
+
+    export class OculusTouchController extends WebVRController {
+
+        public onSecondaryTriggerStateChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        public onThumbRestChangedObservable = new Observable<ExtendedGamepadButton>();
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.OCULUS;
+        }
+
+        // helper getters for left and right hand.
+        public get onAButtonStateChangedObservable() {
+            if (this.hand === 'right') {
+                return this.onMainButtonStateChangedObservable;
+            } else {
+                throw new Error('No A button on left hand');
+            }
+        }
+
+        public get onBButtonStateChangedObservable() {
+            if (this.hand === 'right') {
+                return this.onSecondaryButtonStateChangedObservable;
+            } else {
+                throw new Error('No B button on left hand');
+            }
+        }
+
+        public get onXButtonStateChangedObservable() {
+            if (this.hand === 'left') {
+                return this.onMainButtonStateChangedObservable;
+            } else {
+                throw new Error('No X button on right hand');
+            }
+        }
+
+        public get onYButtonStateChangedObservable() {
+            if (this.hand === 'left') {
+                return this.onSecondaryButtonStateChangedObservable;
+            } else {
+                throw new Error('No Y button on right hand');
+            }
+        }
+
+        /*
+         0) thumb stick (touch, press, value = pressed (0,1)). value is in this.leftStick
+         1) index trigger (touch (?), press (only when value > 0.1), value 0 to 1)
+         2) secondary trigger (same)
+         3) A (right) X (left), touch, pressed = value
+         4) B / Y 
+         5) thumb rest
+        */
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            let notifyObject = state; //{ state: state, changes: changes };
+            switch (buttonIdx) {
+                case 0:
+                    this.onPadStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 1: // index trigger
+                    this.onTriggerStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 2:  // secondary trigger
+                    this.onSecondaryTriggerStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 3:
+                    this.onMainButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 4:
+                    this.onSecondaryButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 5:
+                    this.onThumbRestChangedObservable.notifyObservers(notifyObject);
+                    return;
+            }
+        }
+
+    }
+
+    export class ViveController extends WebVRController {
+
+
+        constructor(vrGamepad) {
+            super(vrGamepad);
+            this.controllerType = PoseEnabledControllerType.VIVE;
+        }
+
+        public get onLeftButtonStateChangedObservable() {
+            return this.onMainButtonStateChangedObservable;
+        }
+
+        public get onRightButtonStateChangedObservable() {
+            return this.onMainButtonStateChangedObservable;
+        }
+
+        public get onMenuButtonStateChangedObservable() {
+            return this.onSecondaryButtonStateChangedObservable;
+        }
+
+        /**
+         * Vive mapping:
+         * 0: touchpad
+         * 1: trigger
+         * 2: left AND right buttons
+         * 3: menu button
+         */
+        protected handleButtonChange(buttonIdx: number, state: ExtendedGamepadButton, changes: GamepadButtonChanges) {
+            let notifyObject = state; //{ state: state, changes: changes };
+            switch (buttonIdx) {
+                case 0:
+                    this.onPadStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 1: // index trigger
+                    this.onTriggerStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 2:  // left AND right button
+                    this.onMainButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+                case 3:
+                    this.onSecondaryButtonStateChangedObservable.notifyObservers(notifyObject);
+                    return;
+            }
+        }
+    }
+
+
+}
+
+interface ExtendedGamepadButton extends GamepadButton {
+    readonly pressed: boolean;
+    readonly touched: boolean;
+    readonly value: number;
+}

+ 39 - 19
src/Tools/babylon.gamepads.ts

@@ -1,29 +1,33 @@
 module BABYLON {
 module BABYLON {
-    export class Gamepads {
-        private babylonGamepads: Array<Gamepad> = [];
+    export class Gamepads<T extends Gamepad> {
+        private babylonGamepads: Array<T> = [];
         private oneGamepadConnected: boolean = false;
         private oneGamepadConnected: boolean = false;
 
 
         private isMonitoring: boolean = false;
         private isMonitoring: boolean = false;
         private gamepadEventSupported: boolean = 'GamepadEvent' in window;
         private gamepadEventSupported: boolean = 'GamepadEvent' in window;
-        private gamepadSupportAvailable: boolean = <boolean>(navigator.getGamepads ||
-            !!navigator.webkitGetGamepads || !!navigator.msGetGamepads || !!navigator.webkitGamepads);
+        private gamepadSupport: () => Array<any> = (navigator.getGamepads ||
+            navigator.webkitGetGamepads || navigator.msGetGamepads || navigator.webkitGamepads);
 
 
-        private _callbackGamepadConnected: (gamepad: Gamepad) => void;
+        private _callbackGamepadConnected: (gamepad: T) => void;
 
 
-        private _onGamepadConnectedEvent: (evt: Event) => void;
+        private _onGamepadConnectedEvent: (evt) => void;
         private _onGamepadDisonnectedEvent: (evt: Event) => void;
         private _onGamepadDisonnectedEvent: (evt: Event) => void;
 
 
         private static gamepadDOMInfo: HTMLElement;
         private static gamepadDOMInfo: HTMLElement;
 
 
 
 
-        constructor(ongamedpadconnected: (gamepad: Gamepad) => void) {
+        constructor(ongamedpadconnected: (gamepad: T) => void) {
             this._callbackGamepadConnected = ongamedpadconnected;
             this._callbackGamepadConnected = ongamedpadconnected;
-            if (this.gamepadSupportAvailable) {
-
+            if (this.gamepadSupport) {
+                //first add already-connected gamepads
+                this._updateGamepadObjects();
+                if (this.babylonGamepads.length) {
+                    this._startMonitoringGamepads();
+                }
                 // Checking if the gamepad connected event is supported (like in Firefox)
                 // Checking if the gamepad connected event is supported (like in Firefox)
                 if (this.gamepadEventSupported) {
                 if (this.gamepadEventSupported) {
                     this._onGamepadConnectedEvent = (evt) => {
                     this._onGamepadConnectedEvent = (evt) => {
-                        this._onGamepadConnected(evt);
+                        this._onGamepadConnected(evt.gamepad);
                     };
                     };
                     this._onGamepadDisonnectedEvent = (evt) => {
                     this._onGamepadDisonnectedEvent = (evt) => {
                         this._onGamepadDisconnected(evt);
                         this._onGamepadDisconnected(evt);
@@ -41,8 +45,8 @@
             if (Gamepads.gamepadDOMInfo) {
             if (Gamepads.gamepadDOMInfo) {
                 document.body.removeChild(Gamepads.gamepadDOMInfo);
                 document.body.removeChild(Gamepads.gamepadDOMInfo);
             }
             }
-            
-            if (this._onGamepadConnectedEvent){
+
+            if (this._onGamepadConnectedEvent) {
                 window.removeEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
                 window.removeEventListener('gamepadconnected', this._onGamepadConnectedEvent, false);
                 window.removeEventListener('gamepaddisconnected', this._onGamepadDisonnectedEvent, false);
                 window.removeEventListener('gamepaddisconnected', this._onGamepadDisonnectedEvent, false);
                 this._onGamepadConnectedEvent = null;
                 this._onGamepadConnectedEvent = null;
@@ -50,13 +54,13 @@
             }
             }
         }
         }
 
 
-        private _onGamepadConnected(evt) {
-            var newGamepad = this._addNewGamepad(evt.gamepad);
+        private _onGamepadConnected(gamepad) {
+            var newGamepad = this._addNewGamepad(gamepad);
             if (this._callbackGamepadConnected) this._callbackGamepadConnected(newGamepad);
             if (this._callbackGamepadConnected) this._callbackGamepadConnected(newGamepad);
             this._startMonitoringGamepads();
             this._startMonitoringGamepads();
         }
         }
 
 
-        private _addNewGamepad(gamepad): Gamepad {
+        private _addNewGamepad(gamepad): T {
             if (!this.oneGamepadConnected) {
             if (!this.oneGamepadConnected) {
                 this.oneGamepadConnected = true;
                 this.oneGamepadConnected = true;
                 if (Gamepads.gamepadDOMInfo) {
                 if (Gamepads.gamepadDOMInfo) {
@@ -70,6 +74,11 @@
             if (xboxOne || (<string>gamepad.id).search("Xbox 360") !== -1 || (<string>gamepad.id).search("xinput") !== -1) {
             if (xboxOne || (<string>gamepad.id).search("Xbox 360") !== -1 || (<string>gamepad.id).search("xinput") !== -1) {
                 newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
                 newGamepad = new Xbox360Pad(gamepad.id, gamepad.index, gamepad, xboxOne);
             }
             }
+            // (<string>gamepad.id).search("Open VR") !== -1 || (<string>gamepad.id).search("Oculus Touch") !== -1
+            // if pose is supported, use the (WebVR) pose enabled controller
+            else if (gamepad.pose) {
+                newGamepad = PoseEnabledControllerHelper.InitiateController(gamepad);
+            }
             else {
             else {
                 newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
                 newGamepad = new GenericPad(gamepad.id, gamepad.index, gamepad);
             }
             }
@@ -147,6 +156,9 @@
         }
         }
     }
     }
     export class Gamepad {
     export class Gamepad {
+
+        public type: number;
+
         private _leftStick: StickValues;
         private _leftStick: StickValues;
         private _rightStick: StickValues;
         private _rightStick: StickValues;
 
 
@@ -158,7 +170,13 @@
         private _onleftstickchanged: (values: StickValues) => void;
         private _onleftstickchanged: (values: StickValues) => void;
         private _onrightstickchanged: (values: StickValues) => void;
         private _onrightstickchanged: (values: StickValues) => void;
 
 
-        constructor(public id: string, public index: number, public browserGamepad, leftStickX:number = 0, leftStickY:number = 1, rightStickX:number = 2, rightStickY:number = 3) {
+        public static GAMEPAD = 0;
+        public static GENERIC = 1;
+        public static XBOX = 2;
+        public static POSE_ENABLED = 3;
+
+        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;
             this._leftStickAxisX = leftStickX;
             this._leftStickAxisY = leftStickY;
             this._leftStickAxisY = leftStickY;
             this._rightStickAxisX = rightStickX;
             this._rightStickAxisX = rightStickX;
@@ -222,6 +240,7 @@
 
 
         constructor(public id: string, public index: number, public gamepad) {
         constructor(public id: string, public index: number, public gamepad) {
             super(id, index, gamepad);
             super(id, index, gamepad);
+            this.type = Gamepad.GENERIC;
             this._buttons = new Array(gamepad.buttons.length);
             this._buttons = new Array(gamepad.buttons.length);
         }
         }
 
 
@@ -293,10 +312,11 @@
         private _dPadLeft: number = 0;
         private _dPadLeft: number = 0;
         private _dPadRight: number = 0;
         private _dPadRight: number = 0;
 
 
-        private _isXboxOnePad:boolean = false;
+        private _isXboxOnePad: boolean = false;
 
 
-        constructor(id: string, index: number, gamepad:any, xboxOne:boolean = false) {
+        constructor(id: string, index: number, gamepad: any, xboxOne: boolean = false) {
             super(id, index, gamepad, 0, 1, (xboxOne ? 3 : 2), (xboxOne ? 4 : 3));
             super(id, index, gamepad, 0, 1, (xboxOne ? 3 : 2), (xboxOne ? 4 : 3));
+            this.type = Gamepad.XBOX;
             this._isXboxOnePad = xboxOne;
             this._isXboxOnePad = xboxOne;
         }
         }
 
 
@@ -495,4 +515,4 @@ interface Navigator {
     webkitGetGamepads(func?: any): any
     webkitGetGamepads(func?: any): any
     msGetGamepads(func?: any): any;
     msGetGamepads(func?: any): any;
     webkitGamepads(func?: any): any;
     webkitGamepads(func?: any): any;
-}
+}

+ 72 - 76
src/babylon.engine.ts

@@ -211,7 +211,7 @@
             if (!lastCreatedEngine) {
             if (!lastCreatedEngine) {
                 return null;
                 return null;
             }
             }
-            
+
             if (lastCreatedEngine.scenes.length === 0) {
             if (lastCreatedEngine.scenes.length === 0) {
                 return null;
                 return null;
             }
             }
@@ -514,19 +514,19 @@
 
 
         private _vaoRecordInProgress = false;
         private _vaoRecordInProgress = false;
         private _mustWipeVertexAttributes = false;
         private _mustWipeVertexAttributes = false;
-        
+
         // Hardware supported Compressed Textures
         // Hardware supported Compressed Textures
         private _texturesSupported = new Array<string>();
         private _texturesSupported = new Array<string>();
-        private _textureFormatInUse : string; 
-        
+        private _textureFormatInUse: string;
+
         public get texturesSupported(): Array<string> {
         public get texturesSupported(): Array<string> {
             return this._texturesSupported;
             return this._texturesSupported;
         }
         }
-        
+
         public get textureFormatInUse(): string {
         public get textureFormatInUse(): string {
             return this._textureFormatInUse;
             return this._textureFormatInUse;
         }
         }
-        
+
         /**
         /**
          * @constructor
          * @constructor
          * @param {HTMLCanvasElement} canvas - the canvas to be used for rendering
          * @param {HTMLCanvasElement} canvas - the canvas to be used for rendering
@@ -558,8 +558,8 @@
                         this._webGLVersion = 2.0;
                         this._webGLVersion = 2.0;
                     }
                     }
                 } catch (e) {
                 } catch (e) {
-                // Do nothing
-                }  
+                    // Do nothing
+                }
             }
             }
 
 
             if (!this._gl) {
             if (!this._gl) {
@@ -576,7 +576,7 @@
             if (!this._gl) {
             if (!this._gl) {
                 throw new Error("WebGL not supported");
                 throw new Error("WebGL not supported");
             }
             }
-            
+
             this._onBlur = () => {
             this._onBlur = () => {
                 this._windowIsBackground = true;
                 this._windowIsBackground = true;
             };
             };
@@ -624,15 +624,15 @@
 
 
             // Extensions
             // Extensions
             this._caps.standardDerivatives = this._webGLVersion > 1 || (this._gl.getExtension('OES_standard_derivatives') !== null);
             this._caps.standardDerivatives = this._webGLVersion > 1 || (this._gl.getExtension('OES_standard_derivatives') !== null);
-            
-            this._caps.astc  = this._gl.getExtension('WEBGL_compressed_texture_astc' ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_astc' );
-            this._caps.s3tc  = this._gl.getExtension('WEBGL_compressed_texture_s3tc' ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc' );
+
+            this._caps.astc = this._gl.getExtension('WEBGL_compressed_texture_astc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_astc');
+            this._caps.s3tc = this._gl.getExtension('WEBGL_compressed_texture_s3tc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_s3tc');
             this._caps.pvrtc = this._gl.getExtension('WEBGL_compressed_texture_pvrtc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
             this._caps.pvrtc = this._gl.getExtension('WEBGL_compressed_texture_pvrtc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_pvrtc');
-            this._caps.etc1  = this._gl.getExtension('WEBGL_compressed_texture_etc1' ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc1' );
-            this._caps.etc2  = this._gl.getExtension('WEBGL_compressed_texture_etc'  ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc'  ) ||
-                               this._gl.getExtension('WEBGL_compressed_texture_es3_0'); // also a requirement of OpenGL ES 3
-            this._caps.atc   = this._gl.getExtension('WEBGL_compressed_texture_atc'  ) || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_atc'  );
-            
+            this._caps.etc1 = this._gl.getExtension('WEBGL_compressed_texture_etc1') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc1');
+            this._caps.etc2 = this._gl.getExtension('WEBGL_compressed_texture_etc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_etc') ||
+                this._gl.getExtension('WEBGL_compressed_texture_es3_0'); // also a requirement of OpenGL ES 3
+            this._caps.atc = this._gl.getExtension('WEBGL_compressed_texture_atc') || this._gl.getExtension('WEBKIT_WEBGL_compressed_texture_atc');
+
             this._caps.textureAnisotropicFilterExtension = this._gl.getExtension('EXT_texture_filter_anisotropic') || this._gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || this._gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
             this._caps.textureAnisotropicFilterExtension = this._gl.getExtension('EXT_texture_filter_anisotropic') || this._gl.getExtension('WEBKIT_EXT_texture_filter_anisotropic') || this._gl.getExtension('MOZ_EXT_texture_filter_anisotropic');
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.maxAnisotropy = this._caps.textureAnisotropicFilterExtension ? this._gl.getParameter(this._caps.textureAnisotropicFilterExtension.MAX_TEXTURE_MAX_ANISOTROPY_EXT) : 0;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
             this._caps.uintIndices = this._webGLVersion > 1 || this._gl.getExtension('OES_element_index_uint') !== null;
@@ -645,58 +645,58 @@
 
 
             this._caps.textureFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_float');
             this._caps.textureFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_float');
             this._caps.textureFloatLinearFiltering = this._caps.textureFloat && this._gl.getExtension('OES_texture_float_linear');
             this._caps.textureFloatLinearFiltering = this._caps.textureFloat && this._gl.getExtension('OES_texture_float_linear');
-            this._caps.textureFloatRender = this._caps.textureFloat && this._canRenderToFloatFramebuffer();            
+            this._caps.textureFloatRender = this._caps.textureFloat && this._canRenderToFloatFramebuffer();
 
 
             this._caps.textureHalfFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_half_float');
             this._caps.textureHalfFloat = this._webGLVersion > 1 || this._gl.getExtension('OES_texture_half_float');
             this._caps.textureHalfFloatLinearFiltering = this._webGLVersion > 1 || (this._caps.textureHalfFloat && this._gl.getExtension('OES_texture_half_float_linear'));
             this._caps.textureHalfFloatLinearFiltering = this._webGLVersion > 1 || (this._caps.textureHalfFloat && this._gl.getExtension('OES_texture_half_float_linear'));
             this._caps.textureHalfFloatRender = this._caps.textureHalfFloat && this._canRenderToHalfFloatFramebuffer();
             this._caps.textureHalfFloatRender = this._caps.textureHalfFloat && this._canRenderToHalfFloatFramebuffer();
-            
+
             this._caps.textureLOD = this._webGLVersion > 1 || this._gl.getExtension('EXT_shader_texture_lod');
             this._caps.textureLOD = this._webGLVersion > 1 || this._gl.getExtension('EXT_shader_texture_lod');
 
 
             // Vertex array object 
             // Vertex array object 
-            if ( this._webGLVersion > 1) {
+            if (this._webGLVersion > 1) {
                 this._caps.vertexArrayObject = true;
                 this._caps.vertexArrayObject = true;
-            } else{
+            } else {
                 var vertexArrayObjectExtension = this._gl.getExtension('OES_vertex_array_object');
                 var vertexArrayObjectExtension = this._gl.getExtension('OES_vertex_array_object');
 
 
                 if (vertexArrayObjectExtension != null) {
                 if (vertexArrayObjectExtension != null) {
-                    this._caps.vertexArrayObject =  true;
+                    this._caps.vertexArrayObject = true;
                     this._gl.createVertexArray = vertexArrayObjectExtension.createVertexArrayOES.bind(vertexArrayObjectExtension);
                     this._gl.createVertexArray = vertexArrayObjectExtension.createVertexArrayOES.bind(vertexArrayObjectExtension);
                     this._gl.bindVertexArray = vertexArrayObjectExtension.bindVertexArrayOES.bind(vertexArrayObjectExtension);
                     this._gl.bindVertexArray = vertexArrayObjectExtension.bindVertexArrayOES.bind(vertexArrayObjectExtension);
                     this._gl.deleteVertexArray = vertexArrayObjectExtension.deleteVertexArrayOES.bind(vertexArrayObjectExtension);
                     this._gl.deleteVertexArray = vertexArrayObjectExtension.deleteVertexArrayOES.bind(vertexArrayObjectExtension);
-                } else{
+                } else {
                     this._caps.vertexArrayObject = false;
                     this._caps.vertexArrayObject = false;
                 }
                 }
             }
             }
             // Instances count            
             // Instances count            
-            if ( this._webGLVersion > 1) {
+            if (this._webGLVersion > 1) {
                 this._caps.instancedArrays = true;
                 this._caps.instancedArrays = true;
-            } else{
+            } else {
                 var instanceExtension = <ANGLE_instanced_arrays>this._gl.getExtension('ANGLE_instanced_arrays');
                 var instanceExtension = <ANGLE_instanced_arrays>this._gl.getExtension('ANGLE_instanced_arrays');
 
 
                 if (instanceExtension != null) {
                 if (instanceExtension != null) {
-                    this._caps.instancedArrays =  true;
+                    this._caps.instancedArrays = true;
                     this._gl.drawArraysInstanced = instanceExtension.drawArraysInstancedANGLE.bind(instanceExtension);
                     this._gl.drawArraysInstanced = instanceExtension.drawArraysInstancedANGLE.bind(instanceExtension);
                     this._gl.drawElementsInstanced = instanceExtension.drawElementsInstancedANGLE.bind(instanceExtension);
                     this._gl.drawElementsInstanced = instanceExtension.drawElementsInstancedANGLE.bind(instanceExtension);
                     this._gl.vertexAttribDivisor = instanceExtension.vertexAttribDivisorANGLE.bind(instanceExtension);
                     this._gl.vertexAttribDivisor = instanceExtension.vertexAttribDivisorANGLE.bind(instanceExtension);
-                } else{
+                } else {
                     this._caps.instancedArrays = false;
                     this._caps.instancedArrays = false;
                 }
                 }
             }
             }
-            
+
             // Intelligently add supported compressed formats in order to check for.
             // Intelligently add supported compressed formats in order to check for.
             // Check for ASTC support first as it is most powerful and to be very cross platform.
             // Check for ASTC support first as it is most powerful and to be very cross platform.
             // Next PVRTC & DXT, which are probably superior to ETC1/2.  
             // Next PVRTC & DXT, which are probably superior to ETC1/2.  
             // Likely no hardware which supports both PVR & DXT, so order matters little.
             // Likely no hardware which supports both PVR & DXT, so order matters little.
             // ETC2 is newer and handles ETC1 (no alpha capability), so check for first.
             // ETC2 is newer and handles ETC1 (no alpha capability), so check for first.
             // ATC before ETC1, since both old (widely supported), but ATC supports alpha, but ETC1 does not
             // ATC before ETC1, since both old (widely supported), but ATC supports alpha, but ETC1 does not
-            if (this._caps.astc ) this.texturesSupported.push('-astc.ktx');
-            if (this._caps.s3tc ) this.texturesSupported.push('-dxt.ktx');
+            if (this._caps.astc) this.texturesSupported.push('-astc.ktx');
+            if (this._caps.s3tc) this.texturesSupported.push('-dxt.ktx');
             if (this._caps.pvrtc) this.texturesSupported.push('-pvrtc.ktx');
             if (this._caps.pvrtc) this.texturesSupported.push('-pvrtc.ktx');
-            if (this._caps.etc2 ) this.texturesSupported.push('-etc2.ktx');
-            if (this._caps.atc  ) this.texturesSupported.push('-atc.ktx');
-            if (this._caps.etc1 ) this.texturesSupported.push('-etc1.ktx');
-            
+            if (this._caps.etc2) this.texturesSupported.push('-etc2.ktx');
+            if (this._caps.atc) this.texturesSupported.push('-atc.ktx');
+            if (this._caps.etc1) this.texturesSupported.push('-etc1.ktx');
+
             if (this._gl.getShaderPrecisionFormat) {
             if (this._gl.getShaderPrecisionFormat) {
                 var highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
                 var highp = this._gl.getShaderPrecisionFormat(this._gl.FRAGMENT_SHADER, this._gl.HIGH_FLOAT);
                 this._caps.highPrecisionShaderSupported = highp.precision !== 0;
                 this._caps.highPrecisionShaderSupported = highp.precision !== 0;
@@ -1178,10 +1178,6 @@
                 this._oldSize = new BABYLON.Size(this.getRenderWidth(), this.getRenderHeight());
                 this._oldSize = new BABYLON.Size(this.getRenderWidth(), this.getRenderHeight());
                 this._oldHardwareScaleFactor = this.getHardwareScalingLevel();
                 this._oldHardwareScaleFactor = this.getHardwareScalingLevel();
 
 
-                //according to the WebVR specs, requestAnimationFrame should be triggered only once.
-                //But actually, no browser follow the specs...
-                //this._vrAnimationFrameHandler = this._vrDisplayEnabled.requestAnimationFrame(this._bindedRenderFunction);
-
                 //get the width and height, change the render size
                 //get the width and height, change the render size
                 var leftEye = this._vrDisplayEnabled.getEyeParameters('left');
                 var leftEye = this._vrDisplayEnabled.getEyeParameters('left');
                 var width, height;
                 var width, height;
@@ -1243,9 +1239,9 @@
             if (texture._MSAAFramebuffer) {
             if (texture._MSAAFramebuffer) {
                 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, texture._MSAAFramebuffer);
                 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, texture._MSAAFramebuffer);
                 gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, texture._framebuffer);
                 gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, texture._framebuffer);
-                gl.blitFramebuffer( 0, 0, texture._width, texture._height, 
-                                    0, 0, texture._width, texture._height,
-                                    gl.COLOR_BUFFER_BIT, gl.NEAREST);
+                gl.blitFramebuffer(0, 0, texture._width, texture._height,
+                    0, 0, texture._width, texture._height,
+                    gl.COLOR_BUFFER_BIT, gl.NEAREST);
             }
             }
 
 
             if (texture.generateMipMaps && !disableGenerateMipMaps) {
             if (texture.generateMipMaps && !disableGenerateMipMaps) {
@@ -1386,14 +1382,14 @@
         public bindArrayBuffer(buffer: WebGLBuffer): void {
         public bindArrayBuffer(buffer: WebGLBuffer): void {
             if (!this._vaoRecordInProgress) {
             if (!this._vaoRecordInProgress) {
                 this._unBindVertexArrayObject();
                 this._unBindVertexArrayObject();
-            }   
+            }
             this.bindBuffer(buffer, this._gl.ARRAY_BUFFER);
             this.bindBuffer(buffer, this._gl.ARRAY_BUFFER);
         }
         }
 
 
         private bindIndexBuffer(buffer: WebGLBuffer): void {
         private bindIndexBuffer(buffer: WebGLBuffer): void {
             if (!this._vaoRecordInProgress) {
             if (!this._vaoRecordInProgress) {
                 this._unBindVertexArrayObject();
                 this._unBindVertexArrayObject();
-            }            
+            }
             this.bindBuffer(buffer, this._gl.ELEMENT_ARRAY_BUFFER);
             this.bindBuffer(buffer, this._gl.ELEMENT_ARRAY_BUFFER);
         }
         }
 
 
@@ -1401,7 +1397,7 @@
             if (this._vaoRecordInProgress || this._currentBoundBuffer[target] !== buffer) {
             if (this._vaoRecordInProgress || this._currentBoundBuffer[target] !== buffer) {
                 this._gl.bindBuffer(target, buffer);
                 this._gl.bindBuffer(target, buffer);
                 this._currentBoundBuffer[target] = buffer;
                 this._currentBoundBuffer[target] = buffer;
-           }
+            }
         }
         }
 
 
         public updateArrayBuffer(data: Float32Array): void {
         public updateArrayBuffer(data: Float32Array): void {
@@ -1430,7 +1426,7 @@
             }
             }
         }
         }
 
 
-        private _bindIndexBufferWithCache(indexBuffer: WebGLBuffer): void {            
+        private _bindIndexBufferWithCache(indexBuffer: WebGLBuffer): void {
             if (indexBuffer == null) {
             if (indexBuffer == null) {
                 return;
                 return;
             }
             }
@@ -1482,10 +1478,10 @@
         public recordVertexArrayObject(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: WebGLBuffer, effect: Effect): WebGLVertexArrayObject {
         public recordVertexArrayObject(vertexBuffers: { [key: string]: VertexBuffer; }, indexBuffer: WebGLBuffer, effect: Effect): WebGLVertexArrayObject {
             var vao = this._gl.createVertexArray();
             var vao = this._gl.createVertexArray();
 
 
-            this._vaoRecordInProgress = true;                
-            
+            this._vaoRecordInProgress = true;
+
             this._gl.bindVertexArray(vao);
             this._gl.bindVertexArray(vao);
-            
+
             this._mustWipeVertexAttributes = true;
             this._mustWipeVertexAttributes = true;
             this._bindVertexBuffersAttributes(vertexBuffers, effect);
             this._bindVertexBuffersAttributes(vertexBuffers, effect);
 
 
@@ -1505,7 +1501,7 @@
                 this._cachedVertexBuffers = null;
                 this._cachedVertexBuffers = null;
                 this._cachedIndexBuffer = null;
                 this._cachedIndexBuffer = null;
 
 
-                this._uintIndicesCurrentlySet = indexBuffer != null && indexBuffer.is32Bits; 
+                this._uintIndicesCurrentlySet = indexBuffer != null && indexBuffer.is32Bits;
                 this._mustWipeVertexAttributes = true;
                 this._mustWipeVertexAttributes = true;
             }
             }
         }
         }
@@ -1554,7 +1550,7 @@
             if (this._cachedVertexBuffers !== vertexBuffers || this._cachedEffectForVertexBuffers !== effect) {
             if (this._cachedVertexBuffers !== vertexBuffers || this._cachedEffectForVertexBuffers !== effect) {
                 this._cachedVertexBuffers = vertexBuffers;
                 this._cachedVertexBuffers = vertexBuffers;
                 this._cachedEffectForVertexBuffers = effect;
                 this._cachedEffectForVertexBuffers = effect;
-                
+
                 this._bindVertexBuffersAttributes(vertexBuffers, effect);
                 this._bindVertexBuffersAttributes(vertexBuffers, effect);
             }
             }
 
 
@@ -1672,7 +1668,7 @@
         public drawPointClouds(verticesStart: number, verticesCount: number, instancesCount?: number): void {
         public drawPointClouds(verticesStart: number, verticesCount: number, instancesCount?: number): void {
             // Apply states
             // Apply states
             this.applyStates();
             this.applyStates();
-            this._drawCalls.addCount(1, false); 
+            this._drawCalls.addCount(1, false);
 
 
             if (instancesCount) {
             if (instancesCount) {
                 this._gl.drawArraysInstanced(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
                 this._gl.drawArraysInstanced(this._gl.POINTS, verticesStart, verticesCount, instancesCount);
@@ -2089,7 +2085,7 @@
          * Current families are astc, dxt, pvrtc, etc2, atc, & etc1.
          * Current families are astc, dxt, pvrtc, etc2, atc, & etc1.
          * @returns The extension selected.
          * @returns The extension selected.
          */
          */
-        public setTextureFormatToUse(formatsAvailable : Array<string>) : string {
+        public setTextureFormatToUse(formatsAvailable: Array<string>): string {
             for (var i = 0, len1 = this.texturesSupported.length; i < len1; i++) {
             for (var i = 0, len1 = this.texturesSupported.length; i < len1; i++) {
                 for (var j = 0, len2 = formatsAvailable.length; j < len2; j++) {
                 for (var j = 0, len2 = formatsAvailable.length; j < len2; j++) {
                     if (this._texturesSupported[i] === formatsAvailable[j].toLowerCase()) {
                     if (this._texturesSupported[i] === formatsAvailable[j].toLowerCase()) {
@@ -2120,7 +2116,7 @@
                     extension = this._textureFormatInUse;
                     extension = this._textureFormatInUse;
                     url = url.substring(0, lastDot) + this._textureFormatInUse;
                     url = url.substring(0, lastDot) + this._textureFormatInUse;
                     isKTX = true;
                     isKTX = true;
-                    
+
                 }
                 }
             } else {
             } else {
                 var oldUrl = url;
                 var oldUrl = url;
@@ -2154,11 +2150,11 @@
                 }
                 }
             };
             };
             var callback: (arrayBuffer: any) => void;
             var callback: (arrayBuffer: any) => void;
-            if (isKTX || isTGA || isDDS){
+            if (isKTX || isTGA || isDDS) {
                 if (isKTX) {
                 if (isKTX) {
                     callback = (data) => {
                     callback = (data) => {
                         var ktx = new Internals.KhronosTextureContainer(data, 1);
                         var ktx = new Internals.KhronosTextureContainer(data, 1);
-    
+
                         prepareWebGLTexture(texture, this._gl, scene, ktx.pixelWidth, ktx.pixelHeight, invertY, false, true, () => {
                         prepareWebGLTexture(texture, this._gl, scene, ktx.pixelWidth, ktx.pixelHeight, invertY, false, true, () => {
                             ktx.uploadLevels(this._gl, !noMipmap);
                             ktx.uploadLevels(this._gl, !noMipmap);
                         }, samplingMode);
                         }, samplingMode);
@@ -2184,14 +2180,14 @@
                             Internals.DDSTools.UploadDDSLevels(this._gl, this.getCaps().s3tc, data, info, loadMipmap, 1);
                             Internals.DDSTools.UploadDDSLevels(this._gl, this.getCaps().s3tc, data, info, loadMipmap, 1);
                         }, samplingMode);
                         }, samplingMode);
                     };
                     };
-            }
+                }
 
 
-            if (!(fromData instanceof Array))
-                Tools.LoadFile(url, data => {
-                    callback(data);
-                }, null, scene.database, true, onerror);
-            else
-                callback(buffer);
+                if (!(fromData instanceof Array))
+                    Tools.LoadFile(url, data => {
+                        callback(data);
+                    }, null, scene.database, true, onerror);
+                else
+                    callback(buffer);
 
 
             } else {
             } else {
                 var onload = (img) => {
                 var onload = (img) => {
@@ -2339,7 +2335,7 @@
 
 
         public updateTextureSamplingMode(samplingMode: number, texture: WebGLTexture): void {
         public updateTextureSamplingMode(samplingMode: number, texture: WebGLTexture): void {
             var filters = getSamplingParameters(samplingMode, texture.generateMipMaps, this._gl);
             var filters = getSamplingParameters(samplingMode, texture.generateMipMaps, this._gl);
- 
+
             if (texture.isCube) {
             if (texture.isCube) {
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture);
                 this._bindTextureDirectly(this._gl.TEXTURE_CUBE_MAP, texture);
 
 
@@ -2354,7 +2350,7 @@
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
                 this._bindTextureDirectly(this._gl.TEXTURE_2D, null);
             }
             }
 
 
-             texture.samplingMode = samplingMode;
+            texture.samplingMode = samplingMode;
         }
         }
 
 
         public updateDynamicTexture(texture: WebGLTexture, canvas: HTMLCanvasElement, invertY: boolean, premulAlpha: boolean = false, format?: number): void {
         public updateDynamicTexture(texture: WebGLTexture, canvas: HTMLCanvasElement, invertY: boolean, premulAlpha: boolean = false, format?: number): void {
@@ -2547,7 +2543,7 @@
             return depthStencilBuffer;
             return depthStencilBuffer;
         }
         }
 
 
-        public updateRenderTargetTextureSampleCount(texture: WebGLTexture, samples: number) : number {
+        public updateRenderTargetTextureSampleCount(texture: WebGLTexture, samples: number): number {
             if (this.webGLVersion < 2) {
             if (this.webGLVersion < 2) {
                 return 1;
                 return 1;
             }
             }
@@ -2693,7 +2689,7 @@
 
 
                     this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                     this._bindTextureDirectly(gl.TEXTURE_CUBE_MAP, texture);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
                     gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1);
-    
+
                     ktx.uploadLevels(this._gl, !noMipmap);
                     ktx.uploadLevels(this._gl, !noMipmap);
 
 
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
                     gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
@@ -2832,7 +2828,7 @@
 
 
             var onerror = () => {
             var onerror = () => {
                 scene._removePendingData(texture);
                 scene._removePendingData(texture);
-                if (onError){
+                if (onError) {
                     onError();
                     onError();
                 }
                 }
             };
             };
@@ -2879,7 +2875,7 @@
                         }
                         }
                     }
                     }
                 }
                 }
-                else {                    
+                else {
                     // Data are known to be in +X +Y +Z -X -Y -Z
                     // Data are known to be in +X +Y +Z -X -Y -Z
                     for (let index = 0; index < facesIndex.length; index++) {
                     for (let index = 0; index < facesIndex.length; index++) {
                         let faceData = rgbeDataArrays[index];
                         let faceData = rgbeDataArrays[index];
@@ -3388,10 +3384,10 @@
             return this._canRenderToFramebuffer(BABYLON.Engine.TEXTURETYPE_FLOAT);
             return this._canRenderToFramebuffer(BABYLON.Engine.TEXTURETYPE_FLOAT);
         }
         }
 
 
-        private _canRenderToHalfFloatFramebuffer(): boolean {       
+        private _canRenderToHalfFloatFramebuffer(): boolean {
             if (this._webGLVersion > 1) {
             if (this._webGLVersion > 1) {
                 return this._caps.colorBufferFloat;
                 return this._caps.colorBufferFloat;
-            }     
+            }
             return this._canRenderToFramebuffer(BABYLON.Engine.TEXTURETYPE_HALF_FLOAT);
             return this._canRenderToFramebuffer(BABYLON.Engine.TEXTURETYPE_HALF_FLOAT);
         }
         }
 
 
@@ -3400,7 +3396,7 @@
             let gl = this._gl;
             let gl = this._gl;
 
 
             //clear existing errors
             //clear existing errors
-            while(gl.getError() !== gl.NO_ERROR){}
+            while (gl.getError() !== gl.NO_ERROR) { }
 
 
             let successful = true;
             let successful = true;
 
 
@@ -3419,13 +3415,13 @@
             successful = successful && (gl.getError() === gl.NO_ERROR);
             successful = successful && (gl.getError() === gl.NO_ERROR);
 
 
             //try render by clearing frame buffer's color buffer
             //try render by clearing frame buffer's color buffer
-            if(successful){
+            if (successful) {
                 gl.clear(gl.COLOR_BUFFER_BIT);
                 gl.clear(gl.COLOR_BUFFER_BIT);
                 successful = successful && (gl.getError() === gl.NO_ERROR);
                 successful = successful && (gl.getError() === gl.NO_ERROR);
             }
             }
 
 
             //try reading from frame to ensure render occurs (just creating the FBO is not sufficient to determine if rendering is supported)
             //try reading from frame to ensure render occurs (just creating the FBO is not sufficient to determine if rendering is supported)
-            if(successful){
+            if (successful) {
                 //in practice it's sufficient to just read from the backbuffer rather than handle potentially issues reading from the texture
                 //in practice it's sufficient to just read from the backbuffer rather than handle potentially issues reading from the texture
                 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
                 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
                 let readFormat = gl.RGBA;
                 let readFormat = gl.RGBA;
@@ -3441,12 +3437,12 @@
             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
             gl.bindFramebuffer(gl.FRAMEBUFFER, null);
 
 
             //clear accumulated errors
             //clear accumulated errors
-            while(!successful && (gl.getError() !== gl.NO_ERROR)) { }
+            while (!successful && (gl.getError() !== gl.NO_ERROR)) { }
 
 
             return successful;
             return successful;
         }
         }
 
 
-        private _getWebGLTextureType(type: number) : number {
+        private _getWebGLTextureType(type: number): number {
             if (type === Engine.TEXTURETYPE_FLOAT) {
             if (type === Engine.TEXTURETYPE_FLOAT) {
                 return this._gl.FLOAT;
                 return this._gl.FLOAT;
             }
             }
@@ -3458,7 +3454,7 @@
             return this._gl.UNSIGNED_BYTE;
             return this._gl.UNSIGNED_BYTE;
         };
         };
 
 
-        private _getRGBABufferInternalSizedFormat(type: number) : number {
+        private _getRGBABufferInternalSizedFormat(type: number): number {
             if (this._webGLVersion === 1) {
             if (this._webGLVersion === 1) {
                 return this._gl.RGBA;
                 return this._gl.RGBA;
             }
             }

+ 15 - 4
src/babylon.mixins.ts

@@ -40,13 +40,13 @@ interface WebGLRenderingContext {
     bindVertexArray(vao: WebGLVertexArrayObject): void;
     bindVertexArray(vao: WebGLVertexArrayObject): void;
     deleteVertexArray(vao: WebGLVertexArrayObject): void;
     deleteVertexArray(vao: WebGLVertexArrayObject): void;
 
 
-    blitFramebuffer(srcX0 : number, srcY0 : number, srcX1 : number, srcY1 : number, dstX0 : number, dstY0 : number, dstX1 : number, dstY1 : number, mask : number, filter : number) : void;
-    renderbufferStorageMultisample(target : number, samples : number, internalformat : number, width : number, height : number) : void;
+    blitFramebuffer(srcX0: number, srcY0: number, srcX1: number, srcY1: number, dstX0: number, dstY0: number, dstX1: number, dstY1: number, mask: number, filter: number): void;
+    renderbufferStorageMultisample(target: number, samples: number, internalformat: number, width: number, height: number): void;
 
 
     MAX_SAMPLES: number;
     MAX_SAMPLES: number;
     RGBA8: number;
     RGBA8: number;
-    READ_FRAMEBUFFER : number;
-    DRAW_FRAMEBUFFER : number;
+    READ_FRAMEBUFFER: number;
+    DRAW_FRAMEBUFFER: number;
 }
 }
 
 
 interface HTMLURL {
 interface HTMLURL {
@@ -179,6 +179,17 @@ interface SIMD {
     Bool8x16: SIMD.Bool8x16Constructor;
     Bool8x16: SIMD.Bool8x16Constructor;
 }
 }
 
 
+interface GamepadPose {
+    hasOrientation: boolean;
+    hasPosition: boolean;
+    position?: Float32Array;
+    linearVelocity?: Float32Array;
+    linearAcceleration?: Float32Array;
+    orientation?: Float32Array;
+    angularVelocity?: Float32Array;
+    angularAcceleration?: Float32Array;
+}
+
 declare namespace SIMD {
 declare namespace SIMD {
     interface Float32x4 {
     interface Float32x4 {
         constructor: Float32x4Constructor;
         constructor: Float32x4Constructor;