|
@@ -8,41 +8,26 @@ import { Viewport } from '../Maths/math.viewport';
|
|
|
|
|
|
/**
|
|
|
* WebXR Camera which holds the views for the xrSession
|
|
|
- * @see https://doc.babylonjs.com/how_to/webxr
|
|
|
+ * @see https://doc.babylonjs.com/how_to/webxr_camera
|
|
|
*/
|
|
|
export class WebXRCamera extends FreeCamera {
|
|
|
-
|
|
|
- /**
|
|
|
- * Should position compensation execute on first frame.
|
|
|
- * This is used when copying the position from a native (non XR) camera
|
|
|
- */
|
|
|
- public compensateOnFirstFrame: boolean = true;
|
|
|
-
|
|
|
private _firstFrame = false;
|
|
|
- private _referencedPosition: Vector3 = new Vector3();
|
|
|
private _referenceQuaternion: Quaternion = Quaternion.Identity();
|
|
|
+ private _referencedPosition: Vector3 = new Vector3();
|
|
|
private _xrInvPositionCache: Vector3 = new Vector3();
|
|
|
private _xrInvQuaternionCache = Quaternion.Identity();
|
|
|
|
|
|
- private _realWorldHeight: number = 0;
|
|
|
-
|
|
|
/**
|
|
|
- * Prevent the camera from calculating the real-world height
|
|
|
- * If you are not using the user's height disable this for better performance
|
|
|
- */
|
|
|
- public disableRealWorldHeightCalculation: boolean = false;
|
|
|
-
|
|
|
- /**
|
|
|
- * Return the user's height, unrelated to the current ground.
|
|
|
+ * Should position compensation execute on first frame.
|
|
|
+ * This is used when copying the position from a native (non XR) camera
|
|
|
*/
|
|
|
- public get realWorldHeight(): number {
|
|
|
- return this._realWorldHeight;
|
|
|
- }
|
|
|
+ public compensateOnFirstFrame: boolean = true;
|
|
|
|
|
|
/**
|
|
|
* Creates a new webXRCamera, this should only be set at the camera after it has been updated by the xrSessionManager
|
|
|
* @param name the name of the camera
|
|
|
* @param scene the scene to add the camera to
|
|
|
+ * @param _xrSessionManager a constructed xr session manager
|
|
|
*/
|
|
|
constructor(name: string, scene: Scene, private _xrSessionManager: WebXRSessionManager) {
|
|
|
super(name, Vector3.Zero(), scene);
|
|
@@ -73,24 +58,31 @@ export class WebXRCamera extends FreeCamera {
|
|
|
}, undefined, true);
|
|
|
}
|
|
|
|
|
|
- private _updateNumberOfRigCameras(viewCount = 1) {
|
|
|
- while (this.rigCameras.length < viewCount) {
|
|
|
- var newCamera = new TargetCamera("XR-RigCamera: " + this.rigCameras.length, Vector3.Zero(), this.getScene());
|
|
|
- newCamera.minZ = 0.1;
|
|
|
- newCamera.rotationQuaternion = new Quaternion();
|
|
|
- newCamera.updateUpVectorFromRotation = true;
|
|
|
- newCamera.isRigCamera = true;
|
|
|
- newCamera.rigParent = this;
|
|
|
- this.rigCameras.push(newCamera);
|
|
|
- }
|
|
|
- while (this.rigCameras.length > viewCount) {
|
|
|
- var removedCamera = this.rigCameras.pop();
|
|
|
- if (removedCamera) {
|
|
|
- removedCamera.dispose();
|
|
|
- }
|
|
|
+ /**
|
|
|
+ * Return the user's height, unrelated to the current ground.
|
|
|
+ * This will be the y position of this camera, when ground level is 0.
|
|
|
+ */
|
|
|
+ public get realWorldHeight(): number {
|
|
|
+ const basePose = this._xrSessionManager.currentFrame && this._xrSessionManager.currentFrame.getViewerPose(this._xrSessionManager.baseReferenceSpace);
|
|
|
+ if (basePose && basePose.transform) {
|
|
|
+ return basePose.transform.position.y;
|
|
|
+ } else {
|
|
|
+ return 0;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /** @hidden */
|
|
|
+ public _updateForDualEyeDebugging(/*pupilDistance = 0.01*/) {
|
|
|
+ // Create initial camera rigs
|
|
|
+ this._updateNumberOfRigCameras(2);
|
|
|
+ this.rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
|
|
|
+ // this.rigCameras[0].position.x = -pupilDistance / 2;
|
|
|
+ this.rigCameras[0].outputRenderTarget = null;
|
|
|
+ this.rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
|
|
|
+ // this.rigCameras[1].position.x = pupilDistance / 2;
|
|
|
+ this.rigCameras[1].outputRenderTarget = null;
|
|
|
+ }
|
|
|
+
|
|
|
/**
|
|
|
* Sets this camera's transformation based on a non-vr camera
|
|
|
* @param otherCamera the non-vr camera to copy the transformation from
|
|
@@ -111,97 +103,7 @@ export class WebXRCamera extends FreeCamera {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /** @hidden */
|
|
|
- public _updateForDualEyeDebugging(/*pupilDistance = 0.01*/) {
|
|
|
- // Create initial camera rigs
|
|
|
- this._updateNumberOfRigCameras(2);
|
|
|
- this.rigCameras[0].viewport = new Viewport(0, 0, 0.5, 1.0);
|
|
|
- // this.rigCameras[0].position.x = -pupilDistance / 2;
|
|
|
- this.rigCameras[0].outputRenderTarget = null;
|
|
|
- this.rigCameras[1].viewport = new Viewport(0.5, 0, 0.5, 1.0);
|
|
|
- // this.rigCameras[1].position.x = pupilDistance / 2;
|
|
|
- this.rigCameras[1].outputRenderTarget = null;
|
|
|
- }
|
|
|
-
|
|
|
- private _updateReferenceSpace() {
|
|
|
- // were position & rotation updated OUTSIDE of the xr update loop
|
|
|
- if (!this.position.equals(this._referencedPosition) || !this.rotationQuaternion.equals(this._referenceQuaternion)) {
|
|
|
- this.position.subtractToRef(this._referencedPosition, this._referencedPosition);
|
|
|
- this._referenceQuaternion.conjugateInPlace();
|
|
|
- this._referenceQuaternion.multiplyToRef(this.rotationQuaternion, this._referenceQuaternion);
|
|
|
- this._updateReferenceSpaceOffset(this._referencedPosition, this._referenceQuaternion.normalize());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private _updateReferenceSpaceOffset(positionOffset: Vector3, rotationOffset?: Quaternion, ignoreHeight: boolean = false) {
|
|
|
- if (!this._xrSessionManager.referenceSpace || !this._xrSessionManager.currentFrame) {
|
|
|
- return;
|
|
|
- }
|
|
|
- // Compute the origin offset based on player position/orientation.
|
|
|
- this._xrInvPositionCache.copyFrom(positionOffset);
|
|
|
- if (rotationOffset) {
|
|
|
- this._xrInvQuaternionCache.copyFrom(rotationOffset);
|
|
|
- } else {
|
|
|
- this._xrInvQuaternionCache.copyFromFloats(0, 0, 0, 1);
|
|
|
- }
|
|
|
-
|
|
|
- // right handed system
|
|
|
- if (!this._scene.useRightHandedSystem) {
|
|
|
- this._xrInvPositionCache.z *= -1;
|
|
|
- this._xrInvQuaternionCache.z *= -1;
|
|
|
- this._xrInvQuaternionCache.w *= -1;
|
|
|
- }
|
|
|
-
|
|
|
- this._xrInvPositionCache.negateInPlace();
|
|
|
- this._xrInvQuaternionCache.conjugateInPlace();
|
|
|
- // transform point according to rotation with pivot
|
|
|
- this._xrInvPositionCache.rotateByQuaternionToRef(this._xrInvQuaternionCache, this._xrInvPositionCache);
|
|
|
- if (ignoreHeight) {
|
|
|
- this._xrInvPositionCache.y = 0;
|
|
|
- }
|
|
|
- const transform = new XRRigidTransform(
|
|
|
- { ...this._xrInvPositionCache },
|
|
|
- { ...this._xrInvQuaternionCache });
|
|
|
- // Update offset reference to use a new originOffset with the teleported
|
|
|
- // player position and orientation.
|
|
|
- // This new offset needs to be applied to the base ref space.
|
|
|
- const referenceSpace = this._xrSessionManager.referenceSpace.getOffsetReferenceSpace(transform);
|
|
|
-
|
|
|
- const pose = this._xrSessionManager.currentFrame && this._xrSessionManager.currentFrame.getViewerPose(referenceSpace);
|
|
|
-
|
|
|
- if (pose) {
|
|
|
- const pos = new Vector3();
|
|
|
- pos.copyFrom(<any>(pose.transform.position));
|
|
|
- if (!this._scene.useRightHandedSystem) {
|
|
|
- pos.z *= -1;
|
|
|
- }
|
|
|
- this.position.subtractToRef(pos, pos);
|
|
|
- if (!this._scene.useRightHandedSystem) {
|
|
|
- pos.z *= -1;
|
|
|
- }
|
|
|
- pos.negateInPlace();
|
|
|
-
|
|
|
- const transform2 = new XRRigidTransform(
|
|
|
- { ...pos });
|
|
|
- // Update offset reference to use a new originOffset with the teleported
|
|
|
- // player position and orientation.
|
|
|
- // This new offset needs to be applied to the base ref space.
|
|
|
- this._xrSessionManager.referenceSpace = referenceSpace.getOffsetReferenceSpace(transform2);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
private _updateFromXRSession() {
|
|
|
-
|
|
|
- // user height
|
|
|
- if (!this.disableRealWorldHeightCalculation) {
|
|
|
- const basePose = this._xrSessionManager.currentFrame && this._xrSessionManager.currentFrame.getViewerPose(this._xrSessionManager.baseReferenceSpace);
|
|
|
- if (basePose && basePose.transform) {
|
|
|
- this._realWorldHeight = basePose.transform.position.y;
|
|
|
- }
|
|
|
- } else {
|
|
|
- this._realWorldHeight = 0;
|
|
|
- }
|
|
|
-
|
|
|
const pose = this._xrSessionManager.currentFrame && this._xrSessionManager.currentFrame.getViewerPose(this._xrSessionManager.referenceSpace);
|
|
|
|
|
|
if (!pose) {
|
|
@@ -227,7 +129,6 @@ export class WebXRCamera extends FreeCamera {
|
|
|
// avoid using the head rotation on the first frame.
|
|
|
this._referenceQuaternion.copyFromFloats(0, 0, 0, 1);
|
|
|
// update the reference space so that the position will be correct
|
|
|
-
|
|
|
}
|
|
|
else {
|
|
|
this.rotationQuaternion.copyFrom(this._referenceQuaternion);
|
|
@@ -286,4 +187,89 @@ export class WebXRCamera extends FreeCamera {
|
|
|
currentRig.outputRenderTarget = this._xrSessionManager.getRenderTargetTextureForEye(view.eye);
|
|
|
});
|
|
|
}
|
|
|
+
|
|
|
+ private _updateNumberOfRigCameras(viewCount = 1) {
|
|
|
+ while (this.rigCameras.length < viewCount) {
|
|
|
+ var newCamera = new TargetCamera("XR-RigCamera: " + this.rigCameras.length, Vector3.Zero(), this.getScene());
|
|
|
+ newCamera.minZ = 0.1;
|
|
|
+ newCamera.rotationQuaternion = new Quaternion();
|
|
|
+ newCamera.updateUpVectorFromRotation = true;
|
|
|
+ newCamera.isRigCamera = true;
|
|
|
+ newCamera.rigParent = this;
|
|
|
+ this.rigCameras.push(newCamera);
|
|
|
+ }
|
|
|
+ while (this.rigCameras.length > viewCount) {
|
|
|
+ var removedCamera = this.rigCameras.pop();
|
|
|
+ if (removedCamera) {
|
|
|
+ removedCamera.dispose();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _updateReferenceSpace() {
|
|
|
+ // were position & rotation updated OUTSIDE of the xr update loop
|
|
|
+ if (!this.position.equals(this._referencedPosition) || !this.rotationQuaternion.equals(this._referenceQuaternion)) {
|
|
|
+ this.position.subtractToRef(this._referencedPosition, this._referencedPosition);
|
|
|
+ this._referenceQuaternion.conjugateInPlace();
|
|
|
+ this._referenceQuaternion.multiplyToRef(this.rotationQuaternion, this._referenceQuaternion);
|
|
|
+ this._updateReferenceSpaceOffset(this._referencedPosition, this._referenceQuaternion.normalize());
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private _updateReferenceSpaceOffset(positionOffset: Vector3, rotationOffset?: Quaternion, ignoreHeight: boolean = false) {
|
|
|
+ if (!this._xrSessionManager.referenceSpace || !this._xrSessionManager.currentFrame) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // Compute the origin offset based on player position/orientation.
|
|
|
+ this._xrInvPositionCache.copyFrom(positionOffset);
|
|
|
+ if (rotationOffset) {
|
|
|
+ this._xrInvQuaternionCache.copyFrom(rotationOffset);
|
|
|
+ } else {
|
|
|
+ this._xrInvQuaternionCache.copyFromFloats(0, 0, 0, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // right handed system
|
|
|
+ if (!this._scene.useRightHandedSystem) {
|
|
|
+ this._xrInvPositionCache.z *= -1;
|
|
|
+ this._xrInvQuaternionCache.z *= -1;
|
|
|
+ this._xrInvQuaternionCache.w *= -1;
|
|
|
+ }
|
|
|
+
|
|
|
+ this._xrInvPositionCache.negateInPlace();
|
|
|
+ this._xrInvQuaternionCache.conjugateInPlace();
|
|
|
+ // transform point according to rotation with pivot
|
|
|
+ this._xrInvPositionCache.rotateByQuaternionToRef(this._xrInvQuaternionCache, this._xrInvPositionCache);
|
|
|
+ if (ignoreHeight) {
|
|
|
+ this._xrInvPositionCache.y = 0;
|
|
|
+ }
|
|
|
+ const transform = new XRRigidTransform(
|
|
|
+ { ...this._xrInvPositionCache },
|
|
|
+ { ...this._xrInvQuaternionCache });
|
|
|
+ // Update offset reference to use a new originOffset with the teleported
|
|
|
+ // player position and orientation.
|
|
|
+ // This new offset needs to be applied to the base ref space.
|
|
|
+ const referenceSpace = this._xrSessionManager.referenceSpace.getOffsetReferenceSpace(transform);
|
|
|
+
|
|
|
+ const pose = this._xrSessionManager.currentFrame && this._xrSessionManager.currentFrame.getViewerPose(referenceSpace);
|
|
|
+
|
|
|
+ if (pose) {
|
|
|
+ const pos = new Vector3();
|
|
|
+ pos.copyFrom(<any>(pose.transform.position));
|
|
|
+ if (!this._scene.useRightHandedSystem) {
|
|
|
+ pos.z *= -1;
|
|
|
+ }
|
|
|
+ this.position.subtractToRef(pos, pos);
|
|
|
+ if (!this._scene.useRightHandedSystem) {
|
|
|
+ pos.z *= -1;
|
|
|
+ }
|
|
|
+ pos.negateInPlace();
|
|
|
+
|
|
|
+ const transform2 = new XRRigidTransform(
|
|
|
+ { ...pos });
|
|
|
+ // Update offset reference to use a new originOffset with the teleported
|
|
|
+ // player position and orientation.
|
|
|
+ // This new offset needs to be applied to the base ref space.
|
|
|
+ this._xrSessionManager.referenceSpace = referenceSpace.getOffsetReferenceSpace(transform2);
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|