소스 검색

Merge pull request #1319 from RaananW/webvr-new-api-changes

Webvr new api changes
David Catuhe 9 년 전
부모
커밋
95053f2758

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

@@ -21,6 +21,7 @@
  - Text2D super sampling to enhance quality in World Space Canvas
  - World Space Canvas is now rendering in an adaptive way for its resolution to fit the on screen projected one to achieve a good rendering quality
  - Transparent Primitives are now drawn with Instanced Array when supported
+- WebVR Camera was updated to be conform with the current specs. ([RaananW](https://github.com/RaananW)) 
 
 ### Exporters
     

+ 32 - 39
src/Cameras/VR/babylon.webVRCamera.ts

@@ -17,36 +17,42 @@ module BABYLON {
         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(), private webVROptions: WebVROptions = {}) {
+        constructor(name: string, position: Vector3, scene: Scene, compensateDistortion = false, private webVROptions: WebVROptions = {}) {
             super(name, position, scene);
 
-            vrCameraMetrics.compensateDistortion = compensateDistortion;
-            this.setCameraRigMode(Camera.RIG_MODE_VR, { vrCameraMetrics: vrCameraMetrics });
+            //enable VR
+            this.getEngine().initWebVR();
 
-            //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!
+                //TODO also check that the device has the right capabilities!
                 this.getEngine().vrDisplaysPromise.then((devices) => {
                     if (devices.length > 0) {
                         this._vrEnabled = true;
                         if (this.webVROptions.displayName) {
-                            devices.some(device => {
+                            var found = devices.some(device => {
                                 if (device.displayName === this.webVROptions.displayName) {
                                     this._vrDevice = device;
                                     return true;
                                 } else {
                                     return false;
                                 }
-                            })
+                            });
+                            if (!found) {
+                                this._vrDevice = devices[0];
+                                Tools.Warn("Display " + this.webVROptions.displayName + " was not found. Using " + this._vrDevice.displayName);
+                            }
                         } else {
                             //choose the first one
                             this._vrDevice = devices[0];
                         }
+
+                        //reset the rig parameters.
+                        this.setCameraRigMode(Camera.RIG_MODE_WEBVR, { vrDisplay: this._vrDevice });
                     } else {
                         Tools.Error("No WebVR devices found!");
                     }
@@ -59,19 +65,20 @@ module BABYLON {
 
         public _checkInputs(): void {
             if (this._vrEnabled) {
-                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;
-                if (this._initialQuaternion) {
-                    this._quaternionCache.copyFrom(this.rotationQuaternion);
-                    this._initialQuaternion.multiplyToRef(this.rotationQuaternion, this.rotationQuaternion);
+                var currentPost = this._vrDevice.getPose();
+                //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]);
+                        this.webVROptions.positionScale && this.position.scaleInPlace(this.webVROptions.positionScale)
+                    }
+                    //Flip in XY plane
+                    this.rotationQuaternion.z *= -1;
+                    this.rotationQuaternion.w *= -1;
                 }
+
             }
 
             super._checkInputs();
@@ -95,32 +102,18 @@ module BABYLON {
 
         public requestVRFullscreen(requestPointerlock: boolean) {
             //Backwards comp.
-            Tools.Warn("requestVRFullscreen is deprecated. Use engine.switchFullscreen() instead")
-            this.getEngine().switchFullscreen(requestPointerlock);
+            Tools.Warn("requestVRFullscreen is deprecated. call attachControl() to start sending frames to the VR display.")
+            //this.getEngine().switchFullscreen(requestPointerlock);
         }
 
         public getTypeName(): string {
             return "WebVRFreeCamera";
         }
 
-        public resetToCurrentRotation(axis: BABYLON.Axis = BABYLON.Axis.Y) {
-            //can only work if this camera has a rotation quaternion already.
-            if (!this.rotationQuaternion) return;
-
-            if (!this._initialQuaternion) {
-                this._initialQuaternion = new BABYLON.Quaternion();
-            }
-
-            this._initialQuaternion.copyFrom(this._quaternionCache || this.rotationQuaternion);
-
-            ['x', 'y', 'z'].forEach((axisName) => {
-                if (!axis[axisName]) {
-                    this._initialQuaternion[axisName] = 0;
-                } else {
-                    this._initialQuaternion[axisName] *= -1;
-                }
-            });
-            this._initialQuaternion.normalize();
+        public resetToCurrentRotation() {
+            //uses the vrDisplay's "resetPose()".
+            //pitch and roll won't be affected.
+            this._vrDevice.resetPose();
         }
     }
 }

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

@@ -15,6 +15,7 @@
         private static _RIG_MODE_STEREOSCOPIC_SIDEBYSIDE_CROSSEYED = 12;
         private static _RIG_MODE_STEREOSCOPIC_OVERUNDER = 13;
         private static _RIG_MODE_VR = 20;
+        private static _RIG_MODE_WEBVR = 21;
 
         public static get PERSPECTIVE_CAMERA(): number {
             return Camera._PERSPECTIVE_CAMERA;
@@ -56,6 +57,10 @@
             return Camera._RIG_MODE_VR;
         }
 
+        public static get RIG_MODE_WEBVR(): number {
+            return Camera._RIG_MODE_WEBVR;
+        }
+
         public static ForceAttachControlToAlwaysPreventDefault = false;
 
         // Members
@@ -533,6 +538,23 @@
                         this._rigCameras[1]._rigPostProcess = new VRDistortionCorrectionPostProcess("VR_Distort_Compensation_Right", this._rigCameras[1], true, metrics);
                     }
                     break;
+                case Camera.RIG_MODE_WEBVR:
+                    if (rigParams.vrDisplay) {
+                        var leftEye = rigParams.vrDisplay.getEyeParameters('left');
+                        var rightEye = rigParams.vrDisplay.getEyeParameters('right');
+                        this._rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
+                        this._rigCameras[0].setCameraRigParameter("vrFieldOfView", leftEye.fieldOfView);
+                        this._rigCameras[0].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-leftEye.offset[0], leftEye.offset[1], -leftEye.offset[2]));
+                        this._rigCameras[0]._cameraRigParams.vrWorkMatrix = new Matrix();
+                        this._rigCameras[0].getProjectionMatrix = this._getWebVRProjectionMatrix;
+                        this._rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
+                        this._rigCameras[1].setCameraRigParameter("vrFieldOfView", rightEye.fieldOfView);
+                        this._rigCameras[1].setCameraRigParameter("vrOffsetMatrix", Matrix.Translation(-rightEye.offset[0], rightEye.offset[1], -rightEye.offset[2]));
+                        this._rigCameras[1]._cameraRigParams.vrWorkMatrix = new Matrix();
+                        this._rigCameras[1].getProjectionMatrix = this._getWebVRProjectionMatrix;
+                    }
+                    break;
+
             }
 
             this._cascadePostProcessesToRigCams();
@@ -546,6 +568,12 @@
             return this._projectionMatrix;
         }
 
+        private _getWebVRProjectionMatrix(): Matrix {
+            Matrix.PerspectiveFovWebVRToRef(this._cameraRigParams['vrFieldOfView'], this.minZ, this.maxZ, this._cameraRigParams.vrWorkMatrix);
+            this._cameraRigParams.vrWorkMatrix.multiplyToRef(this._cameraRigParams['vrOffsetMatrix'], this._projectionMatrix);
+            return this._projectionMatrix;
+        }
+
         public setCameraRigParameter(name: string, value: any) {
             if (!this._cameraRigParams) {
                 this._cameraRigParams = {};

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

@@ -238,7 +238,7 @@
         public createRigCamera(name: string, cameraIndex: number): Camera {
             if (this.cameraRigMode !== Camera.RIG_MODE_NONE) {
                 var rigCamera = new TargetCamera(name, this.position.clone(), this.getScene());
-                if (this.cameraRigMode === Camera.RIG_MODE_VR) {
+                if (this.cameraRigMode === Camera.RIG_MODE_VR || this.cameraRigMode === Camera.RIG_MODE_WEBVR) {
                     if (!this.rotationQuaternion) {
                         this.rotationQuaternion = new Quaternion();
                     }
@@ -274,6 +274,7 @@
                     break;
 
                 case Camera.RIG_MODE_VR:
+                case Camera.RIG_MODE_WEBVR:
                     camLeft.rotationQuaternion.copyFrom(this.rotationQuaternion);
                     camRight.rotationQuaternion.copyFrom(this.rotationQuaternion);
                     camLeft.position.copyFrom(this.position);

+ 22 - 1
src/Math/babylon.math.ts

@@ -1731,7 +1731,7 @@
         }
 
         public copyFrom(src: Size) {
-            this.width  = src.width;
+            this.width = src.width;
             this.height = src.height;
         }
 
@@ -2920,6 +2920,27 @@
             result.m[14] = (znear * zfar) / (znear - zfar);
         }
 
+        public static PerspectiveFovWebVRToRef(fov, znear: number, zfar: number, result: Matrix, isVerticalFovFixed = true) {
+            var upTan = Math.tan(fov.upDegrees * 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 rightTan = Math.tan(fov.rightDegrees * Math.PI / 180.0);
+            var xScale = 2.0 / (leftTan + rightTan);
+            var yScale = 2.0 / (upTan + downTan);
+            result.m[0] = xScale;
+            result.m[1] = result.m[2] = result.m[3] = result.m[4] = 0.0;
+            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[10] = -zfar / (znear - zfar);
+            result.m[11] = 1.0;
+            result.m[12] = result.m[13] = result.m[15] = 0.0;
+            //result.m[14] = -(2.0 * zfar * znear) / (zfar - znear);
+            result.m[14] = (znear * zfar) / (znear - zfar);
+        }
+
         public static GetFinalMatrix(viewport: Viewport, world: Matrix, view: Matrix, projection: Matrix, zmin: number, zmax: number): Matrix {
             var cw = viewport.width;
             var ch = viewport.height;

+ 4 - 1
src/Tools/babylon.tools.ts

@@ -256,7 +256,10 @@
          * @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)
+            //if WebVR is enabled AND presenting, requestAnimationFrame is triggered when enabled.
+            /*if(requester.isPresenting) {
+                return;
+            } else*/ if (requester.requestAnimationFrame)
                 requester.requestAnimationFrame(func);
             else if (requester.msRequestAnimationFrame)
                 requester.msRequestAnimationFrame(func);

+ 20 - 6
src/babylon.engine.ts

@@ -187,6 +187,7 @@
 
     export interface EngineOptions extends WebGLContextAttributes {
         limitDeviceRatio?: number;
+        autoEnableWebVR?: boolean;
     }
 
     /**
@@ -321,6 +322,7 @@
         private _vrDisplayEnabled;
         private _oldSize: BABYLON.Size;
         private _oldHardwareScaleFactor: number;
+        private _vrAnimationFrameHandler: number;
 
         // Private Members
         public _gl: WebGLRenderingContext;
@@ -564,7 +566,9 @@
             this._loadingScreen = new DefaultLoadingScreen(this._renderingCanvas);
 
             //Load WebVR Devices
-            this._getVRDisplays();
+            if (options.autoEnableWebVR) {
+                this.initWebVR();
+            }
 
             Tools.Log("Babylon.js engine (v" + Engine.Version + ") launched");
         }
@@ -908,7 +912,7 @@
             //this.flushFramebuffer();
 
             //submit frame to the vr device, if enabled
-            if(this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting) {
+            if (this._vrDisplayEnabled && this._vrDisplayEnabled.isPresenting) {
                 this._vrDisplayEnabled.submitFrame()
             }
         }
@@ -957,6 +961,12 @@
 
         //WebVR functions
 
+        public initWebVR() {
+            if (!this.vrDisplaysPromise) {
+                this._getVRDisplays();
+            }
+        }
+
         public enableVR(vrDevice) {
             this._vrDisplayEnabled = vrDevice;
             this._vrDisplayEnabled.requestPresent([{ source: this.getRenderingCanvas() }]).then(this._onVRFullScreenTriggered);
@@ -964,9 +974,7 @@
 
         public disableVR() {
             if (this._vrDisplayEnabled) {
-                this._vrDisplayEnabled.exitPresent();
-                this._vrDisplayEnabled = null;
-                this._onVRFullScreenTriggered()
+                this._vrDisplayEnabled.exitPresent().then(this._onVRFullScreenTriggered);
             }
         }
 
@@ -976,15 +984,21 @@
                 this._oldSize = new BABYLON.Size(this.getRenderWidth(), this.getRenderHeight());
                 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
                 var leftEye = this._vrDisplayEnabled.getEyeParameters('left');
                 var width, height;
                 this.setHardwareScalingLevel(1);
                 this.setSize(leftEye.renderWidth * 2, leftEye.renderHeight);
-
             } else {
+                //When the specs are implemented, need to uncomment this.
+                //this._vrDisplayEnabled.cancelAnimationFrame(this._vrAnimationFrameHandler);
                 this.setHardwareScalingLevel(this._oldHardwareScaleFactor);
                 this.setSize(this._oldSize.width, this._oldSize.height);
+                this._vrDisplayEnabled = undefined;
             }
         }