Browse Source

Merge pull request #1317 from RaananW/webvr-new-api

Webvr new api
David Catuhe 9 years ago
parent
commit
eac25eff33
4 changed files with 169 additions and 82 deletions
  1. 52 43
      src/Cameras/VR/babylon.webVRCamera.ts
  2. 17 13
      src/Tools/babylon.tools.ts
  3. 99 25
      src/babylon.engine.ts
  4. 1 1
      src/babylon.mixins.ts

+ 52 - 43
src/Cameras/VR/babylon.webVRCamera.ts

@@ -1,60 +1,70 @@
 declare var HMDVRDevice;
-declare var PositionSensorVRDevice;
+declare var VRDisplay;
 
 module BABYLON {
+
+    export interface WebVROptions {
+        trackPosition?: boolean; //update the camera's position
+        positionScale?: number;
+        displayName?: string; //if there are more than one VRDisplays.
+    }
+
     export class WebVRFreeCamera extends FreeCamera {
-        public _hmdDevice = null;
-        public _sensorDevice = null;
+        public _vrDevice = null;
         private _cacheState = null;
-        public _vrEnabled = false;
+        private _vrEnabled = false;
+
+        private _oldSize: BABYLON.Size;
+        private _oldHardwareScaleFactor: number;
 
         private _initialQuaternion: Quaternion;
         private _quaternionCache: Quaternion;
 
-        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault()) {
+        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = true, vrCameraMetrics: VRCameraMetrics = VRCameraMetrics.GetDefault(), private webVROptions: WebVROptions = {}) {
             super(name, position, scene);
 
             vrCameraMetrics.compensateDistortion = compensateDistortion;
             this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: vrCameraMetrics });
 
-            this._getWebVRDevices = this._getWebVRDevices.bind(this);
+            //this._getWebVRDevices = this._getWebVRDevices.bind(this);
+            if (!this.getEngine().vrDisplaysPromise) {
+                Tools.Error("WebVR is not enabled on your browser");
+            } else {
+                //TODO get the metrics updated using the device's eye parameters!
+                this.getEngine().vrDisplaysPromise.then((devices) => {
+                    if (devices.length > 0) {
+                        this._vrEnabled = true;
+                        if (this.webVROptions.displayName) {
+                            devices.some(device => {
+                                if (device.displayName === this.webVROptions.displayName) {
+                                    this._vrDevice = device;
+                                    return true;
+                                } else {
+                                    return false;
+                                }
+                            })
+                        } else {
+                            //choose the first one
+                            this._vrDevice = devices[0];
+                        }
+                    } else {
+                        Tools.Error("No WebVR devices found!");
+                    }
+                })
+            }
 
             this.rotationQuaternion = new Quaternion();
             this._quaternionCache = new Quaternion();
         }
 
-        private _getWebVRDevices(devices: Array<any>): void {
-            var size = devices.length;
-            var i = 0;
-
-            // Reset devices.
-            this._sensorDevice = null;
-            this._hmdDevice = null;
-
-            // Search for a HmdDevice.
-            while (i < size && this._hmdDevice === null) {
-                if (devices[i] instanceof HMDVRDevice) {
-                    this._hmdDevice = devices[i];
-                }
-                i++;
-            }
-
-            i = 0;
-
-            while (i < size && this._sensorDevice === null) {
-                if (devices[i] instanceof PositionSensorVRDevice && (!this._hmdDevice || devices[i].hardwareUnitId === this._hmdDevice.hardwareUnitId)) {
-                    this._sensorDevice = devices[i];
-                }
-                i++;
-            }
-
-            this._vrEnabled = this._sensorDevice && this._hmdDevice ? true : false;
-        }
-
         public _checkInputs(): void {
             if (this._vrEnabled) {
-                this._cacheState = this._sensorDevice.getState();
-                this.rotationQuaternion.copyFrom(this._cacheState.orientation);
+                this._cacheState = this._vrDevice.getPose();
+                this.rotationQuaternion.copyFromFloats(this._cacheState.orientation[0], this._cacheState.orientation[1], this._cacheState.orientation[2], this._cacheState.orientation[3]);
+                if (this.webVROptions.trackPosition) {
+                    this.position.copyFromFloats(this._cacheState.position[0], this._cacheState.position[1], -this._cacheState.position[2]);
+                    this.webVROptions.positionScale && this.position.scaleInPlace(this.webVROptions.positionScale)
+                }
                 //Flip in XY plane
                 this.rotationQuaternion.z *= -1;
                 this.rotationQuaternion.w *= -1;
@@ -72,22 +82,21 @@ module BABYLON {
 
             noPreventDefault = Camera.ForceAttachControlToAlwaysPreventDefault ? false : noPreventDefault;
 
-            if (navigator.getVRDevices) {
-                navigator.getVRDevices().then(this._getWebVRDevices);
-            }
-            else if (navigator.mozGetVRDevices) {
-                navigator.mozGetVRDevices(this._getWebVRDevices);
+            if (this._vrEnabled) {
+                this.getEngine().enableVR(this._vrDevice)
             }
         }
 
         public detachControl(element: HTMLElement): void {
             super.detachControl(element);
             this._vrEnabled = false;
+            this.getEngine().disableVR();
         }
 
         public requestVRFullscreen(requestPointerlock: boolean) {
-            if (!this._hmdDevice) return;
-            this.getEngine().switchFullscreen(requestPointerlock, { vrDisplay: this._hmdDevice })
+            //Backwards comp.
+            Tools.Warn("requestVRFullscreen is deprecated. Use engine.switchFullscreen() instead")
+            this.getEngine().switchFullscreen(requestPointerlock);
         }
 
         public getTypeName(): string {

+ 17 - 13
src/Tools/babylon.tools.ts

@@ -251,26 +251,30 @@
             return eventPrefix;
         }
 
-        public static QueueNewFrame(func): void {
-            if (window.requestAnimationFrame)
-                window.requestAnimationFrame(func);
-            else if (window.msRequestAnimationFrame)
-                window.msRequestAnimationFrame(func);
-            else if (window.webkitRequestAnimationFrame)
-                window.webkitRequestAnimationFrame(func);
-            else if (window.mozRequestAnimationFrame)
-                window.mozRequestAnimationFrame(func);
-            else if (window.oRequestAnimationFrame)
-                window.oRequestAnimationFrame(func);
+        /**
+         * @param func - the function to be called
+         * @param requester - the object that will request the next frame. Falls back to window.
+         */
+        public static QueueNewFrame(func, requester: any = window): void {
+            if (requester.requestAnimationFrame)
+                requester.requestAnimationFrame(func);
+            else if (requester.msRequestAnimationFrame)
+                requester.msRequestAnimationFrame(func);
+            else if (requester.webkitRequestAnimationFrame)
+                requester.webkitRequestAnimationFrame(func);
+            else if (requester.mozRequestAnimationFrame)
+                requester.mozRequestAnimationFrame(func);
+            else if (requester.oRequestAnimationFrame)
+                requester.oRequestAnimationFrame(func);
             else {
                 window.setTimeout(func, 16);
             }
         }
 
-        public static RequestFullscreen(element, options?: any): void {
+        public static RequestFullscreen(element): void {
             var requestFunction = element.requestFullscreen || element.msRequestFullscreen || element.webkitRequestFullscreen || element.mozRequestFullScreen;
             if (!requestFunction) return;
-            requestFunction.call(element, options);
+            requestFunction.call(element);
         }
 
         public static ExitFullscreen(): void {

+ 99 - 25
src/babylon.engine.ts

@@ -184,8 +184,8 @@
         public textureLOD: boolean;
         public drawBuffersExtension;
     }
-    
-    export interface EngineOptions extends WebGLContextAttributes{
+
+    export interface EngineOptions extends WebGLContextAttributes {
         limitDeviceRatio?: number;
     }
 
@@ -311,6 +311,17 @@
         public enableOfflineSupport = true;
         public scenes = new Array<Scene>();
 
+        //WebVR 
+
+        //The new WebVR uses promises.
+        //this promise resolves with the current devices available.
+        public vrDisplaysPromise;
+
+        private _vrDisplays;
+        private _vrDisplayEnabled;
+        private _oldSize: BABYLON.Size;
+        private _oldHardwareScaleFactor: number;
+
         // Private Members
         public _gl: WebGLRenderingContext;
         private _renderingCanvas: HTMLCanvasElement;
@@ -328,7 +339,7 @@
         private _caps: EngineCapabilities;
         private _pointerLockRequested: boolean;
         private _alphaTest: boolean;
-        private _isStencilEnable: boolean; 
+        private _isStencilEnable: boolean;
 
         private _loadingScreen: ILoadingScreen;
 
@@ -382,7 +393,7 @@
 
         private _externalData: StringDictionary<Object>;
         private _bindedRenderFunction: any;
-        
+
         /**
          * @constructor
          * @param {HTMLCanvasElement} canvas - the canvas to be used for rendering
@@ -396,7 +407,7 @@
 
             options = options || {};
 
-            if(antialias != null){
+            if (antialias != null) {
                 options.antialias = antialias;
             }
 
@@ -552,6 +563,9 @@
             //default loading screen
             this._loadingScreen = new DefaultLoadingScreen(this._renderingCanvas);
 
+            //Load WebVR Devices
+            this._getVRDisplays();
+
             Tools.Log("Babylon.js engine (v" + Engine.Version + ") launched");
         }
 
@@ -698,11 +712,11 @@
         public setStencilFunction(stencilFunc: number) {
             this._stencilState.stencilFunc = stencilFunc;
         }
-        
+
         public setStencilFunctionReference(reference: number) {
             this._stencilState.stencilFuncRef = reference;
         }
-        
+
         public setStencilFunctionMask(mask: number) {
             this._stencilState.stencilFuncMask = mask;
         }
@@ -770,7 +784,7 @@
 
             if (this._activeRenderLoops.length > 0) {
                 // Register new frame
-                Tools.QueueNewFrame(this._bindedRenderFunction);
+                Tools.QueueNewFrame(this._bindedRenderFunction, this._vrDisplayEnabled);
             } else {
                 this._renderingQueueLaunched = false;
             }
@@ -803,19 +817,19 @@
          * @param {boolean} requestPointerLock - should a pointer lock be requested from the user
          * @param {any} options - an options object to be sent to the requestFullscreen function
          */
-        public switchFullscreen(requestPointerLock: boolean, options?: any): void {
+        public switchFullscreen(requestPointerLock: boolean): void {
             if (this.isFullscreen) {
                 Tools.ExitFullscreen();
             } else {
                 this._pointerLockRequested = requestPointerLock;
-                Tools.RequestFullscreen(this._renderingCanvas, options);
+                Tools.RequestFullscreen(this._renderingCanvas);
             }
         }
 
-        public clear(color: any, backBuffer: boolean, depth: boolean, stencil:boolean = false): void {
+        public clear(color: any, backBuffer: boolean, depth: boolean, stencil: boolean = false): void {
             this.applyStates();
 
-            var mode = 0;            
+            var mode = 0;
             if (backBuffer) {
                 this._gl.clearColor(color.r, color.g, color.b, color.a !== undefined ? color.a : 1.0);
                 mode |= this._gl.COLOR_BUFFER_BIT;
@@ -892,6 +906,11 @@
 
         public endFrame(): void {
             //this.flushFramebuffer();
+
+            //submit frame to the vr device, if enabled
+            if(this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting) {
+                this._vrDisplayEnabled.submitFrame()
+            }
         }
 
         /**
@@ -935,14 +954,66 @@
             }
         }
 
+
+        //WebVR functions
+
+        public enableVR(vrDevice) {
+            this._vrDisplayEnabled = vrDevice;
+            this._vrDisplayEnabled.requestPresent([{ source: this.getRenderingCanvas() }]).then(this._onVRFullScreenTriggered);
+        }
+
+        public disableVR() {
+            if (this._vrDisplayEnabled) {
+                this._vrDisplayEnabled.exitPresent();
+                this._vrDisplayEnabled = null;
+                this._onVRFullScreenTriggered()
+            }
+        }
+
+        private _onVRFullScreenTriggered = () => {
+            if (this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting) {
+                //get the old size before we change
+                this._oldSize = new BABYLON.Size(this.getRenderWidth(), this.getRenderHeight());
+                this._oldHardwareScaleFactor = this.getHardwareScalingLevel();
+
+                //get the width and height, change the render size
+                var leftEye = this._vrDisplayEnabled.getEyeParameters('left');
+                var width, height;
+                this.setHardwareScalingLevel(1);
+                this.setSize(leftEye.renderWidth * 2, leftEye.renderHeight);
+
+            } else {
+                this.setHardwareScalingLevel(this._oldHardwareScaleFactor);
+                this.setSize(this._oldSize.width, this._oldSize.height);
+            }
+        }
+
+        private _getVRDisplays() {
+            var getWebVRDevices = (devices: Array<any>) => {
+                var size = devices.length;
+                var i = 0;
+
+                this._vrDisplays = devices.filter(function (device) {
+                    return devices[i] instanceof VRDisplay;
+                });
+
+                return this._vrDisplays;
+            }
+
+            //using a key due to typescript
+            if (navigator.getVRDisplays) {
+                this.vrDisplaysPromise = navigator.getVRDisplays().then(getWebVRDevices);
+            }
+        }
+
         public bindFramebuffer(texture: WebGLTexture, faceIndex?: number, requiredWidth?: number, requiredHeight?: number): void {
             this._currentRenderTarget = texture;
             this.bindUnboundFramebuffer(texture._framebuffer);
             var gl = this._gl;
             if (texture.isCube) {
-                
+
                 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_CUBE_MAP_POSITIVE_X + faceIndex, texture, 0);
-            } 
+            }
 
             gl.viewport(0, 0, requiredWidth || texture._width, requiredHeight || texture._height);
 
@@ -1134,12 +1205,12 @@
                 var offset = 0;
                 for (var index = 0; index < attributesCount; index++) {
 
-                    if(index < vertexDeclaration.length){
+                    if (index < vertexDeclaration.length) {
 
                         var order = effect.getAttributeLocation(index);
 
                         if (order >= 0) {
-                            if(!this._vertexAttribArraysEnabled[order]){
+                            if (!this._vertexAttribArraysEnabled[order]) {
                                 this._gl.enableVertexAttribArray(order);
                                 this._vertexAttribArraysEnabled[order] = true;
                             }
@@ -1148,11 +1219,11 @@
 
                         offset += vertexDeclaration[index] * 4;
 
-                    }else{
+                    } else {
 
                         //disable effect attributes that have no data
                         var order = effect.getAttributeLocation(index);
-                        if(this._vertexAttribArraysEnabled[order]){
+                        if (this._vertexAttribArraysEnabled[order]) {
                             this._gl.disableVertexAttribArray(order);
                             this._vertexAttribArraysEnabled[order] = false;
                         }
@@ -1183,14 +1254,14 @@
                         var vertexBuffer = vertexBuffers[attributes[index]];
 
                         if (!vertexBuffer) {
-                            if(this._vertexAttribArraysEnabled[order]){
+                            if (this._vertexAttribArraysEnabled[order]) {
                                 this._gl.disableVertexAttribArray(order);
                                 this._vertexAttribArraysEnabled[order] = false;
                             }
                             continue;
                         }
 
-                        if(!this._vertexAttribArraysEnabled[order]){
+                        if (!this._vertexAttribArraysEnabled[order]) {
                             this._gl.enableVertexAttribArray(order);
                             this._vertexAttribArraysEnabled[order] = true;
                         }
@@ -1269,7 +1340,7 @@
                 for (let i = 0; i < offsetLocations.length; i++) {
                     let ai = <InstancingAttributeInfo>offsetLocations[i];
 
-                    if(!this._vertexAttribArraysEnabled[ai.index]){
+                    if (!this._vertexAttribArraysEnabled[ai.index]) {
                         this._gl.enableVertexAttribArray(ai.index);
                         this._vertexAttribArraysEnabled[ai.index] = true;
                     }
@@ -1283,7 +1354,7 @@
                 for (let index = 0; index < 4; index++) {
                     let offsetLocation = <number>offsetLocations[index];
 
-                    if(!this._vertexAttribArraysEnabled[offsetLocation]){
+                    if (!this._vertexAttribArraysEnabled[offsetLocation]) {
                         this._gl.enableVertexAttribArray(offsetLocation);
                         this._vertexAttribArraysEnabled[offsetLocation] = true;
                     }
@@ -2104,7 +2175,7 @@
             this.bindUnboundFramebuffer(framebuffer);
 
             // Manage attachments
-            if (generateStencilBuffer) {                
+            if (generateStencilBuffer) {
                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
             }
             else if (generateDepthBuffer) {
@@ -2134,7 +2205,7 @@
             texture.references = 1;
             texture.samplingMode = samplingMode;
             texture.type = type;
-            
+
             this.resetTextureCache();
 
             this._loadedTexturesCache.push(texture);
@@ -2201,7 +2272,7 @@
             this.bindUnboundFramebuffer(framebuffer);
 
             // Manage attachments
-            if (generateStencilBuffer) {                
+            if (generateStencilBuffer) {
                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_STENCIL_ATTACHMENT, gl.RENDERBUFFER, depthStencilBuffer);
             }
             else if (generateDepthBuffer) {
@@ -2768,6 +2839,9 @@
 
             this._gl = null;
 
+            //WebVR
+            this.disableVR();
+
             // Events
             window.removeEventListener("blur", this._onBlur);
             window.removeEventListener("focus", this._onFocus);

+ 1 - 1
src/babylon.mixins.ts

@@ -97,7 +97,7 @@ interface MSStyleCSSProperties {
 }
 
 interface Navigator {
-    getVRDevices: () => any;
+    getVRDisplays: () => any;
     mozGetVRDevices: (any: any) => any;
     isCocoonJS: boolean;
 }