From 2b253382cbea2e1048f909fd464733ae8d13fa38 Mon Sep 17 00:00:00 2001 From: Felix Zhang Date: Wed, 11 Dec 2024 15:01:43 -0800 Subject: [PATCH] separately export all private keys to eliminate circular dependency warning Summary: Refactor only: export all private keys from private.ts to eliminate the circular dependency warning when building. Reviewed By: zjm-meta Differential Revision: D67112604 Privacy Context Container: L1233623 fbshipit-source-id: cd62a733e9b894a1f664918b4c23bb26ba2707c0 --- src/action/ActionPlayer.ts | 106 +++++---- src/action/ActionRecorder.ts | 48 ++-- src/anchors/XRAnchor.ts | 47 ++-- src/device/XRController.ts | 38 +-- src/device/XRDevice.ts | 231 +++++++++--------- src/device/XRHandInput.ts | 58 +++-- src/device/XRTrackedInput.ts | 87 +++---- src/device/configs/headset/meta.ts | 122 +++++----- src/frameloop/XRFrame.ts | 95 ++++---- src/gamepad/Gamepad.ts | 60 ++--- src/index.ts | 3 + src/initialization/XRSystem.ts | 28 +-- src/input/XRInputSource.ts | 23 +- src/layers/XRWebGLLayer.ts | 36 ++- src/meshes/XRMesh.ts | 21 +- src/planes/XRPlane.ts | 21 +- src/pose/XRJointPose.ts | 9 +- src/pose/XRPose.ts | 15 +- src/pose/XRViewerPose.ts | 11 +- src/primitives/XRRigidTransform.ts | 31 ++- src/private.ts | 32 +++ src/session/XRRenderState.ts | 17 +- src/session/XRSession.ts | 330 ++++++++++++-------------- src/spaces/XRJointSpace.ts | 11 +- src/spaces/XRReferenceSpace.ts | 22 +- src/spaces/XRSpace.ts | 18 +- src/views/XRView.ts | 17 +- src/views/XRViewport.ts | 14 +- tests/frameloop/XRFrame.test.ts | 4 +- tests/initialization/XRSystem.test.ts | 43 ++-- tests/session/XRSession.test.ts | 40 +--- tests/spaces/XRReferenceSpace.test.ts | 16 +- tests/spaces/XRSpace.test.ts | 21 +- 33 files changed, 795 insertions(+), 880 deletions(-) create mode 100644 src/private.ts diff --git a/src/action/ActionPlayer.ts b/src/action/ActionPlayer.ts index 989a276..1c60dc9 100644 --- a/src/action/ActionPlayer.ts +++ b/src/action/ActionPlayer.ts @@ -8,30 +8,32 @@ import { Axis, Button, - PRIVATE as GAMEPAD_PRIVATE, Gamepad, GamepadButton, GamepadMappingType, } from '../gamepad/Gamepad.js'; +import { + P_ACTION_PLAYER, + P_GAMEPAD, + P_JOINT_SPACE, + P_SPACE, +} from '../private.js'; import { XRHand, XRHandJoint } from '../input/XRHand.js'; import { XRHandedness, XRInputSource, XRTargetRayMode, } from '../input/XRInputSource.js'; -import { - PRIVATE as XRJOINTSPACE_PRIVATE, - XRJointSpace, -} from '../spaces/XRJointSpace.js'; import { XRReferenceSpace, XRReferenceSpaceType, } from '../spaces/XRReferenceSpace.js'; -import { PRIVATE as XRSPACE_PRIVATE, XRSpace } from '../spaces/XRSpace.js'; import { mat4, quat, vec3 } from 'gl-matrix'; import { InputSchema } from './ActionRecorder.js'; import { XREye } from '../views/XRView.js'; +import { XRJointSpace } from '../spaces/XRJointSpace.js'; +import { XRSpace } from '../spaces/XRSpace.js'; export interface CompressedRecording { schema: { @@ -41,8 +43,6 @@ export interface CompressedRecording { frames: any[]; } -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/action-player'); - type ProcessedInputData = { targetRayTransform: number[]; gripTransform?: number[]; @@ -52,7 +52,7 @@ type ProcessedInputData = { }; export class ActionPlayer { - [PRIVATE]: { + [P_ACTION_PLAYER]: { refSpace: XRReferenceSpace; inputSources: Map; inputSchemas: Map; @@ -93,7 +93,7 @@ export class ActionPlayer { [XREye.Right]: new XRSpace(viewerSpace), [XREye.None]: new XRSpace(viewerSpace), }; - this[PRIVATE] = { + this[P_ACTION_PLAYER] = { refSpace, inputSources: new Map(), inputSchemas: new Map(), @@ -110,11 +110,11 @@ export class ActionPlayer { }; mat4.fromTranslation( - this[PRIVATE].viewSpaces[XREye.Left][XRSPACE_PRIVATE].offsetMatrix, + this[P_ACTION_PLAYER].viewSpaces[XREye.Left][P_SPACE].offsetMatrix, vec3.fromValues(-ipd / 2, 0, 0), ); mat4.fromTranslation( - this[PRIVATE].viewSpaces[XREye.Right][XRSPACE_PRIVATE].offsetMatrix, + this[P_ACTION_PLAYER].viewSpaces[XREye.Right][P_SPACE].offsetMatrix, vec3.fromValues(ipd / 2, 0, 0), ); @@ -158,69 +158,75 @@ export class ActionPlayer { schema.hasHand ? hand : undefined, ); - this[PRIVATE].inputSources.set(index, { + this[P_ACTION_PLAYER].inputSources.set(index, { active: false, source: inputSource, }); - this[PRIVATE].inputSchemas.set(index, schema); + this[P_ACTION_PLAYER].inputSchemas.set(index, schema); }); } play() { - this[PRIVATE].recordedFramePointer = 0; - this[PRIVATE].playbackTime = this[PRIVATE].startingTimeStamp; - this[PRIVATE].playing = true; - this[PRIVATE].actualTimeStamp = performance.now(); + this[P_ACTION_PLAYER].recordedFramePointer = 0; + this[P_ACTION_PLAYER].playbackTime = + this[P_ACTION_PLAYER].startingTimeStamp; + this[P_ACTION_PLAYER].playing = true; + this[P_ACTION_PLAYER].actualTimeStamp = performance.now(); } stop() { - this[PRIVATE].playing = false; + this[P_ACTION_PLAYER].playing = false; } get playing() { - return this[PRIVATE].playing; + return this[P_ACTION_PLAYER].playing; } get viewerSpace() { - return this[PRIVATE].viewerSpace; + return this[P_ACTION_PLAYER].viewerSpace; } get viewSpaces() { - return this[PRIVATE].viewSpaces; + return this[P_ACTION_PLAYER].viewSpaces; } get inputSources() { - return Array.from(this[PRIVATE].inputSources.values()) + return Array.from(this[P_ACTION_PLAYER].inputSources.values()) .filter((wrapper) => wrapper.active) .map((wrapper) => wrapper.source); } playFrame() { const now = performance.now(); - const delta = now - this[PRIVATE].actualTimeStamp!; - this[PRIVATE].actualTimeStamp = now; - this[PRIVATE].playbackTime! += delta; - if (this[PRIVATE].playbackTime! > this[PRIVATE].endingTimeStamp) { + const delta = now - this[P_ACTION_PLAYER].actualTimeStamp!; + this[P_ACTION_PLAYER].actualTimeStamp = now; + this[P_ACTION_PLAYER].playbackTime! += delta; + if ( + this[P_ACTION_PLAYER].playbackTime! > + this[P_ACTION_PLAYER].endingTimeStamp + ) { this.stop(); return; } while ( - (this[PRIVATE].frames[ - this[PRIVATE].recordedFramePointer + 1 - ][0] as number) < this[PRIVATE].playbackTime + (this[P_ACTION_PLAYER].frames[ + this[P_ACTION_PLAYER].recordedFramePointer + 1 + ][0] as number) < this[P_ACTION_PLAYER].playbackTime ) { - this[PRIVATE].recordedFramePointer++; + this[P_ACTION_PLAYER].recordedFramePointer++; } const lastFrameData = - this[PRIVATE].frames[this[PRIVATE].recordedFramePointer]; + this[P_ACTION_PLAYER].frames[this[P_ACTION_PLAYER].recordedFramePointer]; const nextFrameData = - this[PRIVATE].frames[this[PRIVATE].recordedFramePointer + 1]; + this[P_ACTION_PLAYER].frames[ + this[P_ACTION_PLAYER].recordedFramePointer + 1 + ]; const alpha = - ((this[PRIVATE].playbackTime - lastFrameData[0]) as number) / + ((this[P_ACTION_PLAYER].playbackTime - lastFrameData[0]) as number) / (((nextFrameData[0] as number) - lastFrameData[0]) as number); this.updateXRSpaceFromMergedFrames( - this[PRIVATE].viewerSpace, + this[P_ACTION_PLAYER].viewerSpace, lastFrameData.slice(1, 8), nextFrameData.slice(1, 8), alpha, @@ -242,14 +248,14 @@ export class ActionPlayer { nextFrameInputs.set(index, inputData); } - this[PRIVATE].inputSources.forEach((sourceWrapper) => { + this[P_ACTION_PLAYER].inputSources.forEach((sourceWrapper) => { sourceWrapper.active = false; }); nextFrameInputs.forEach((inputData, index) => { - this[PRIVATE].inputSources.get(index)!.active = true; - const inputSource = this[PRIVATE].inputSources.get(index)!.source; - const schema = this[PRIVATE].inputSchemas.get(index)!; + this[P_ACTION_PLAYER].inputSources.get(index)!.active = true; + const inputSource = this[P_ACTION_PLAYER].inputSources.get(index)!.source; + const schema = this[P_ACTION_PLAYER].inputSchemas.get(index)!; this.updateInputSource( inputSource, schema, @@ -304,7 +310,7 @@ export class ActionPlayer { nextTransformArray, alpha, ); - jointSpace[XRJOINTSPACE_PRIVATE].radius = + jointSpace[P_JOINT_SPACE].radius = (nextRadius - lastRadius) * alpha + lastRadius; } } @@ -313,16 +319,16 @@ export class ActionPlayer { const gamepad = inputSource.gamepad!; nextInputData.buttons!.forEach((states, index) => { const gamepadButton = gamepad.buttons[index]! as GamepadButton; - gamepadButton[GAMEPAD_PRIVATE].pressed = states[0] === 1 ? true : false; - gamepadButton[GAMEPAD_PRIVATE].touched = states[1] === 1 ? true : false; + gamepadButton[P_GAMEPAD].pressed = states[0] === 1 ? true : false; + gamepadButton[P_GAMEPAD].touched = states[1] === 1 ? true : false; const lastValue = lastInputData.buttons![index][2]; const nextValue = states[2]; - gamepadButton[GAMEPAD_PRIVATE].value = + gamepadButton[P_GAMEPAD].value = (nextValue - lastValue) * alpha + lastValue; }); nextInputData.axes!.forEach((nextValue, index) => { const lastValue = lastInputData.axes![index]; - gamepad[GAMEPAD_PRIVATE].axesMap[index.toString()].x = + gamepad[P_GAMEPAD].axesMap[index.toString()].x = (nextValue - lastValue) * alpha + lastValue; }); } @@ -356,18 +362,18 @@ export class ActionPlayer { nextTransform[5], nextTransform[6], ); - vec3.lerp(this[PRIVATE].vec3, f1p, f2p, alpha); - quat.slerp(this[PRIVATE].quat, f1q, f2q, alpha); + vec3.lerp(this[P_ACTION_PLAYER].vec3, f1p, f2p, alpha); + quat.slerp(this[P_ACTION_PLAYER].quat, f1q, f2q, alpha); mat4.fromRotationTranslation( - space[XRSPACE_PRIVATE].offsetMatrix, - this[PRIVATE].quat, - this[PRIVATE].vec3, + space[P_SPACE].offsetMatrix, + this[P_ACTION_PLAYER].quat, + this[P_ACTION_PLAYER].vec3, ); } processRawInputData(inputDataRaw: any[]) { const index = inputDataRaw[0]; - const schema = this[PRIVATE].inputSchemas.get(index)!; + const schema = this[P_ACTION_PLAYER].inputSchemas.get(index)!; const targetRayTransform: number[] = inputDataRaw.slice(1, 8); const inputData: ProcessedInputData = { targetRayTransform }; let dataCounter = 8; diff --git a/src/action/ActionRecorder.ts b/src/action/ActionRecorder.ts index 65ffc51..afc2795 100644 --- a/src/action/ActionRecorder.ts +++ b/src/action/ActionRecorder.ts @@ -7,9 +7,7 @@ import { mat4, quat, vec3 } from 'gl-matrix'; -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/action-recorder', -); +import { P_ACTION_RECORDER } from '../private.js'; export interface InputFrame { index: number; @@ -63,7 +61,7 @@ const compress = (arr: vec3 | quat) => { }; export class ActionRecorder { - [PRIVATE]: { + [P_ACTION_RECORDER]: { session: XRSession; refSpace: XRReferenceSpace; inputMap: Map; @@ -74,7 +72,7 @@ export class ActionRecorder { }; constructor(session: XRSession, refSpace: XRReferenceSpace) { - this[PRIVATE] = { + this[P_ACTION_RECORDER] = { session, refSpace, inputMap: new Map(), @@ -87,8 +85,8 @@ export class ActionRecorder { recordFrame(frame: XRFrame) { const timeStamp = performance.now(); - const viewerMatrix = frame.getViewerPose(this[PRIVATE].refSpace)?.transform - .matrix; + const viewerMatrix = frame.getViewerPose(this[P_ACTION_RECORDER].refSpace) + ?.transform.matrix; if (!viewerMatrix) return; const position = mat4.getTranslation(vec3.create(), viewerMatrix); const quaternion = mat4.getRotation(quat.create(), viewerMatrix); @@ -98,8 +96,8 @@ export class ActionRecorder { quaternion, inputFrames: [], }; - this[PRIVATE].session.inputSources.forEach((inputSource) => { - if (!this[PRIVATE].inputMap.has(inputSource)) { + this[P_ACTION_RECORDER].session.inputSources.forEach((inputSource) => { + if (!this[P_ACTION_RECORDER].inputMap.has(inputSource)) { const schema: InputSchema = { handedness: inputSource.handedness, targetRayMode: inputSource.targetRayMode, @@ -118,15 +116,15 @@ export class ActionRecorder { schema.numButtons = inputSource.gamepad!.buttons.length; schema.numAxes = inputSource.gamepad!.axes.length; } - const index = this[PRIVATE].inputMap.size; - this[PRIVATE].inputMap.set(inputSource, index); - this[PRIVATE].schemaMap.set(index, schema); + const index = this[P_ACTION_RECORDER].inputMap.size; + this[P_ACTION_RECORDER].inputMap.set(inputSource, index); + this[P_ACTION_RECORDER].schemaMap.set(index, schema); } - const index = this[PRIVATE].inputMap.get(inputSource)!; - const schema = this[PRIVATE].schemaMap.get(index)!; + const index = this[P_ACTION_RECORDER].inputMap.get(inputSource)!; + const schema = this[P_ACTION_RECORDER].schemaMap.get(index)!; const targetRayMatrix = frame.getPose( inputSource.targetRaySpace, - this[PRIVATE].refSpace, + this[P_ACTION_RECORDER].refSpace, )?.transform.matrix; if (targetRayMatrix) { const targetRayPosition = mat4.getTranslation( @@ -149,7 +147,7 @@ export class ActionRecorder { if (schema.hasGrip) { const gripMatrix = frame.getPose( inputSource.gripSpace!, - this[PRIVATE].refSpace, + this[P_ACTION_RECORDER].refSpace, )?.transform.matrix; if (gripMatrix) { const position = mat4.getTranslation(vec3.create(), gripMatrix); @@ -169,13 +167,13 @@ export class ActionRecorder { allValid &&= frame.fillPoses( jointSpaces, inputSource.targetRaySpace, - this[PRIVATE].jointTransforms, + this[P_ACTION_RECORDER].jointTransforms, ); // @ts-ignore allValid &&= frame.fillJointRadii( jointSpaces, - this[PRIVATE].jointRadii, + this[P_ACTION_RECORDER].jointRadii, ); if (allValid) { @@ -187,11 +185,11 @@ export class ActionRecorder { }; } = {} as any; for (let offset = 0; offset < 25; offset++) { - const jointMatrix = this[PRIVATE].jointTransforms.slice( + const jointMatrix = this[P_ACTION_RECORDER].jointTransforms.slice( offset * 16, (offset + 1) * 16, ); - const radius = this[PRIVATE].jointRadii[offset]; + const radius = this[P_ACTION_RECORDER].jointRadii[offset]; const position = mat4.getTranslation(vec3.create(), jointMatrix); const quaternion = mat4.getRotation(quat.create(), jointMatrix); const jointName = jointSpaces[offset].jointName as XRHandJoint; @@ -216,7 +214,9 @@ export class ActionRecorder { actionFrame.inputFrames.push(inputFrame); } }); - this[PRIVATE].compressedFrames.push(this.compressActionFrame(actionFrame)); + this[P_ACTION_RECORDER].compressedFrames.push( + this.compressActionFrame(actionFrame), + ); } compressActionFrame(af: ActionFrame): any[] { @@ -227,7 +227,7 @@ export class ActionRecorder { ]; af.inputFrames.forEach((inputFrame) => { const index = inputFrame.index; - const schema = this[PRIVATE].schemaMap.get(index)!; + const schema = this[P_ACTION_RECORDER].schemaMap.get(index)!; const inputOut: any[] = [ index, ...compress(inputFrame.targetRayTransform.position), @@ -266,8 +266,8 @@ export class ActionRecorder { log() { const out = { - schema: Array.from(this[PRIVATE].schemaMap.entries()), - frames: this[PRIVATE].compressedFrames, + schema: Array.from(this[P_ACTION_RECORDER].schemaMap.entries()), + frames: this[P_ACTION_RECORDER].compressedFrames, }; console.log(JSON.stringify(out)); } diff --git a/src/anchors/XRAnchor.ts b/src/anchors/XRAnchor.ts index 505fb8f..e74a4d3 100644 --- a/src/anchors/XRAnchor.ts +++ b/src/anchors/XRAnchor.ts @@ -5,43 +5,41 @@ * LICENSE file in the root directory of this source tree. */ -import { PRIVATE as XRSESSION_PRIVATE, XRSession } from '../session/XRSession'; -import { PRIVATE as XRSPACE_PRIVATE, XRSpace } from '../spaces/XRSpace'; +import { P_ANCHOR, P_DEVICE, P_SESSION, P_SPACE } from '../private.js'; -import { PRIVATE as XRDEVICE_PRIVATE } from '../device/XRDevice'; +import { XRSession } from '../session/XRSession.js'; +import { XRSpace } from '../spaces/XRSpace.js'; import { mat4 } from 'gl-matrix'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-anchor'); - export class XRAnchor { - [PRIVATE]: { + [P_ANCHOR]: { anchorSpace: XRSpace | null; session: XRSession; deleted: boolean; }; constructor(anchorSpace: XRSpace, session: XRSession) { - this[PRIVATE] = { + this[P_ANCHOR] = { anchorSpace, session, deleted: false, }; - session[XRSESSION_PRIVATE].trackedAnchors.add(this); + session[P_SESSION].trackedAnchors.add(this); } get anchorSpace() { - if (this[PRIVATE].deleted) { + if (this[P_ANCHOR].deleted) { throw new DOMException( 'XRAnchor has already been deleted.', 'InvalidStateError', ); } - return this[PRIVATE].anchorSpace!; + return this[P_ANCHOR].anchorSpace!; } requestPersistentHandle() { return new Promise((resolve, reject) => { - if (this[PRIVATE].deleted) { + if (this[P_ANCHOR].deleted) { reject( new DOMException( 'XRAnchor has already been deleted.', @@ -50,7 +48,7 @@ export class XRAnchor { ); } else { const persistentAnchors = - this[PRIVATE].session[XRSESSION_PRIVATE].persistentAnchors; + this[P_ANCHOR].session[P_SESSION].persistentAnchors; for (const [uuid, anchor] of persistentAnchors.entries()) { if (anchor === this) { resolve(uuid); @@ -58,19 +56,23 @@ export class XRAnchor { } } const uuid = crypto.randomUUID(); - XRAnchorUtils.createPersistentAnchor(this[PRIVATE].session, this, uuid); + XRAnchorUtils.createPersistentAnchor( + this[P_ANCHOR].session, + this, + uuid, + ); resolve(uuid); } }); } delete() { - if (this[PRIVATE].deleted) { + if (this[P_ANCHOR].deleted) { return; } - this[PRIVATE].anchorSpace = null; - this[PRIVATE].deleted = true; - this[PRIVATE].session[XRSESSION_PRIVATE].trackedAnchors.delete(this); + this[P_ANCHOR].anchorSpace = null; + this[P_ANCHOR].deleted = true; + this[P_ANCHOR].session[P_SESSION].trackedAnchors.delete(this); } } @@ -85,11 +87,10 @@ export class XRAnchorUtils { localStorage.getItem(PersistentAnchorsStorageKey) || '{}', ) as { [uuid: string]: mat4 }; Object.entries(persistentAnchors).forEach(([uuid, offsetMatrix]) => { - const globalSpace = - session[XRSESSION_PRIVATE].device[XRDEVICE_PRIVATE].globalSpace; + const globalSpace = session[P_SESSION].device[P_DEVICE].globalSpace; const anchorSpace = new XRSpace(globalSpace, offsetMatrix); const anchor = new XRAnchor(anchorSpace, session); - session[XRSESSION_PRIVATE].persistentAnchors.set(uuid, anchor); + session[P_SESSION].persistentAnchors.set(uuid, anchor); }); } @@ -98,13 +99,13 @@ export class XRAnchorUtils { anchor: XRAnchor, uuid: string, ) { - session[XRSESSION_PRIVATE].trackedAnchors.add(anchor); - session[XRSESSION_PRIVATE].persistentAnchors.set(uuid, anchor); + session[P_SESSION].trackedAnchors.add(anchor); + session[P_SESSION].persistentAnchors.set(uuid, anchor); const persistentAnchors = JSON.parse( localStorage.getItem(PersistentAnchorsStorageKey) || '{}', ) as { [uuid: string]: mat4 }; persistentAnchors[uuid] = Array.from( - anchor[PRIVATE].anchorSpace![XRSPACE_PRIVATE].offsetMatrix, + anchor[P_ANCHOR].anchorSpace![P_SPACE].offsetMatrix, ) as mat4; localStorage.setItem( PersistentAnchorsStorageKey, diff --git a/src/device/XRController.ts b/src/device/XRController.ts index 8d6d7ca..76fb09d 100644 --- a/src/device/XRController.ts +++ b/src/device/XRController.ts @@ -5,26 +5,18 @@ * LICENSE file in the root directory of this source tree. */ -import { - PRIVATE as GAMEPAD_PRIVATE, - Gamepad, - GamepadConfig, -} from '../gamepad/Gamepad.js'; +import { Gamepad, GamepadConfig } from '../gamepad/Gamepad.js'; import { GlobalSpace, XRSpace } from '../spaces/XRSpace.js'; +import { P_CONTROLLER, P_GAMEPAD, P_TRACKED_INPUT } from '../private.js'; import { XRHandedness, XRInputSource, XRTargetRayMode, } from '../input/XRInputSource.js'; -import { - PRIVATE as XRTRACKEDINPUT_PRIVATE, - XRTrackedInput, -} from './XRTrackedInput.js'; +import { XRTrackedInput } from './XRTrackedInput.js'; import { mat4 } from 'gl-matrix'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-controller'); - export interface XRControllerConfig { profileId: string; fallbackProfileIds: string[]; @@ -38,7 +30,7 @@ export interface XRControllerConfig { } export class XRController extends XRTrackedInput { - [PRIVATE]: { + [P_CONTROLLER]: { gamepadConfig: GamepadConfig; }; @@ -71,13 +63,13 @@ export class XRController extends XRTrackedInput { ); super(inputSource); - this[PRIVATE] = { + this[P_CONTROLLER] = { gamepadConfig: controllerConfig.layout[handedness]!.gamepad, }; } get gamepadConfig() { - return this[PRIVATE].gamepadConfig; + return this[P_CONTROLLER].gamepadConfig; } updateButtonValue(id: string, value: number) { @@ -86,11 +78,10 @@ export class XRController extends XRTrackedInput { return; } const gamepadButton = - this[XRTRACKEDINPUT_PRIVATE].inputSource.gamepad![GAMEPAD_PRIVATE] - .buttonsMap[id]; + this[P_TRACKED_INPUT].inputSource.gamepad![P_GAMEPAD].buttonsMap[id]; if (gamepadButton) { if ( - gamepadButton[GAMEPAD_PRIVATE].type === 'binary' && + gamepadButton[P_GAMEPAD].type === 'binary' && value != 1 && value != 0 ) { @@ -99,7 +90,7 @@ export class XRController extends XRTrackedInput { ); return; } - gamepadButton[GAMEPAD_PRIVATE].pendingValue = value; + gamepadButton[P_GAMEPAD].pendingValue = value; } else { console.warn(`Current controller does not have button ${id}.`); } @@ -107,10 +98,9 @@ export class XRController extends XRTrackedInput { updateButtonTouch(id: string, touched: boolean) { const gamepadButton = - this[XRTRACKEDINPUT_PRIVATE].inputSource.gamepad![GAMEPAD_PRIVATE] - .buttonsMap[id]; + this[P_TRACKED_INPUT].inputSource.gamepad![P_GAMEPAD].buttonsMap[id]; if (gamepadButton) { - gamepadButton[GAMEPAD_PRIVATE].touched = touched; + gamepadButton[P_GAMEPAD].touched = touched; } else { console.warn(`Current controller does not have button ${id}.`); } @@ -122,8 +112,7 @@ export class XRController extends XRTrackedInput { return; } const axesById = - this[XRTRACKEDINPUT_PRIVATE].inputSource.gamepad![GAMEPAD_PRIVATE] - .axesMap[id]; + this[P_TRACKED_INPUT].inputSource.gamepad![P_GAMEPAD].axesMap[id]; if (axesById) { if (type === 'x-axis') { axesById.x = value; @@ -143,8 +132,7 @@ export class XRController extends XRTrackedInput { return; } const axesById = - this[XRTRACKEDINPUT_PRIVATE].inputSource.gamepad![GAMEPAD_PRIVATE] - .axesMap[id]; + this[P_TRACKED_INPUT].inputSource.gamepad![P_GAMEPAD].axesMap[id]; if (axesById) { axesById.x = x; axesById.y = y; diff --git a/src/device/XRDevice.ts b/src/device/XRDevice.ts index 89821e8..ce75df1 100644 --- a/src/device/XRDevice.ts +++ b/src/device/XRDevice.ts @@ -5,17 +5,19 @@ * LICENSE file in the root directory of this source tree. */ +import { GlobalSpace, XRSpace } from '../spaces/XRSpace.js'; import { - GlobalSpace, - PRIVATE as XRSPACE_PRIVATE, - XRSpace, -} from '../spaces/XRSpace.js'; + P_DEVICE, + P_REF_SPACE, + P_SESSION, + P_SPACE, + P_SYSTEM, +} from '../private.js'; import { Quaternion, Vector3 } from '../utils/Math.js'; import { XRController, XRControllerConfig } from './XRController.js'; import { XREnvironmentBlendMode, XRInteractionMode, - PRIVATE as XRSESSION_PRIVATE, XRSession, XRSessionMode, XRVisibilityState, @@ -29,14 +31,9 @@ import { } from '../input/XRInputSource.js'; import { XRLayer, XRWebGLLayer } from '../layers/XRWebGLLayer.js'; import { - PRIVATE as XRREFERENCESPACE_PRIVATE, XRReferenceSpace, XRReferenceSpaceType, } from '../spaces/XRReferenceSpace.js'; -import { - PRIVATE as XRSYSTEM_PRIVATE, - XRSystem, -} from '../initialization/XRSystem.js'; import { mat4, vec3 } from 'gl-matrix'; import { ActionPlayer } from '../action/ActionPlayer.js'; @@ -53,30 +50,30 @@ import { XRReferenceSpaceEvent } from '../events/XRReferenceSpaceEvent.js'; import { XRRenderState } from '../session/XRRenderState.js'; import { XRRigidTransform } from '../primitives/XRRigidTransform.js'; import { XRSessionEvent } from '../events/XRSessionEvent.js'; +import { XRSystem } from '../initialization/XRSystem.js'; import { XRTrackedInput } from './XRTrackedInput.js'; import { XRViewerPose } from '../pose/XRViewerPose.js'; import { XRViewport } from '../views/XRViewport.js'; -export enum WebXRFeatures { - Viewer = 'viewer', - Local = 'local', - LocalFloor = 'local-floor', - BoundedFloor = 'bounded-floor', - Unbounded = 'unbounded', - DomOverlay = 'dom-overlay', - Anchors = 'anchors', - PlaneDetection = 'plane-detection', - MeshDetection = 'mesh-detection', - HitTest = 'hit-test', - HandTracking = 'hand-tracking', - DepthSensing = 'depth-sensing', -} +export type WebXRFeature = + | 'viewer' + | 'local' + | 'local-floor' + | 'bounded-floor' + | 'unbounded' + | 'dom-overlay' + | 'anchors' + | 'plane-detection' + | 'mesh-detection' + | 'hit-test' + | 'hand-tracking' + | 'depth-sensing'; export interface XRDeviceConfig { name: string; controllerConfig: XRControllerConfig | undefined; supportedSessionModes: XRSessionMode[]; - supportedFeatures: WebXRFeatures[]; + supportedFeatures: WebXRFeature[]; supportedFrameRates: number[]; isSystemKeyboardSupported: boolean; internalNominalFrameRate: number; @@ -96,8 +93,6 @@ export interface XRDeviceOptions { canvasContainer: HTMLDivElement; } -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-device'); - const DEFAULTS = { ipd: 0.063, fovy: Math.PI / 2, @@ -111,7 +106,7 @@ const DEFAULTS = { * Instead, it serves as an user-facing interface to control the emulated XR Device */ export class XRDevice { - [PRIVATE]: { + [P_DEVICE]: { // device config name: string; supportedSessionModes: string[]; @@ -215,7 +210,7 @@ export class XRDevice { canvasContainer.style.overflow = 'hidden'; canvasContainer.style.zIndex = '999'; - this[PRIVATE] = { + this[P_DEVICE] = { name: deviceConfig.name, supportedSessionModes: deviceConfig.supportedSessionModes, supportedFeatures: deviceConfig.supportedFeatures, @@ -237,7 +232,7 @@ export class XRDevice { hands, primaryInputMode: 'controller', pendingReferenceSpaceReset: false, - visibilityState: XRVisibilityState.Visible, + visibilityState: 'visible', pendingVisibilityState: null, xrSystem: null, @@ -257,35 +252,35 @@ export class XRDevice { return new XRViewport( 0, 0, - this[PRIVATE].stereoEnabled ? width / 2 : width, + this[P_DEVICE].stereoEnabled ? width / 2 : width, height, ); case XREye.Right: return new XRViewport( width / 2, 0, - this[PRIVATE].stereoEnabled ? width / 2 : 0, + this[P_DEVICE].stereoEnabled ? width / 2 : 0, height, ); } }, updateViews: () => { // update viewerSpace - const viewerSpace = this[PRIVATE].viewerSpace; + const viewerSpace = this[P_DEVICE].viewerSpace; mat4.fromRotationTranslation( - viewerSpace[XRSPACE_PRIVATE].offsetMatrix, - this[PRIVATE].quaternion.quat, - this[PRIVATE].position.vec3, + viewerSpace[P_SPACE].offsetMatrix, + this[P_DEVICE].quaternion.quat, + this[P_DEVICE].position.vec3, ); // update viewSpaces mat4.fromTranslation( - this[PRIVATE].viewSpaces[XREye.Left][XRSPACE_PRIVATE].offsetMatrix, - vec3.fromValues(-this[PRIVATE].ipd / 2, 0, 0), + this[P_DEVICE].viewSpaces[XREye.Left][P_SPACE].offsetMatrix, + vec3.fromValues(-this[P_DEVICE].ipd / 2, 0, 0), ); mat4.fromTranslation( - this[PRIVATE].viewSpaces[XREye.Right][XRSPACE_PRIVATE].offsetMatrix, - vec3.fromValues(this[PRIVATE].ipd / 2, 0, 0), + this[P_DEVICE].viewSpaces[XREye.Right][P_SPACE].offsetMatrix, + vec3.fromValues(this[P_DEVICE].ipd / 2, 0, 0), ); }, onBaseLayerSet: (baseLayer: XRWebGLLayer | null) => { @@ -293,79 +288,77 @@ export class XRDevice { // backup canvas data const canvas = baseLayer.context.canvas as HTMLCanvasElement; - if (canvas.parentElement !== this[PRIVATE].canvasContainer) { - this[PRIVATE].canvasData = { + if (canvas.parentElement !== this[P_DEVICE].canvasContainer) { + this[P_DEVICE].canvasData = { canvas, parent: canvas.parentElement, width: canvas.width, height: canvas.height, }; - this[PRIVATE].canvasContainer.appendChild(canvas); - document.body.appendChild(this[PRIVATE].canvasContainer); + this[P_DEVICE].canvasContainer.appendChild(canvas); + document.body.appendChild(this[P_DEVICE].canvasContainer); } canvas.width = window.innerWidth; canvas.height = window.innerHeight; }, onSessionEnd: () => { - if (this[PRIVATE].canvasData) { - const { canvas, parent, width, height } = this[PRIVATE].canvasData; + if (this[P_DEVICE].canvasData) { + const { canvas, parent, width, height } = this[P_DEVICE].canvasData; canvas.width = width; canvas.height = height; if (parent) { parent.appendChild(canvas); } else { - this[PRIVATE].canvasContainer.removeChild(canvas); + this[P_DEVICE].canvasContainer.removeChild(canvas); } - document.body.removeChild(this[PRIVATE].canvasContainer); + document.body.removeChild(this[P_DEVICE].canvasContainer); window.dispatchEvent(new Event('resize')); } }, onFrameStart: (frame: XRFrame) => { - if (this[PRIVATE].actionPlayer?.playing) { - this[PRIVATE].actionPlayer.playFrame(); + if (this[P_DEVICE].actionPlayer?.playing) { + this[P_DEVICE].actionPlayer.playFrame(); } else { const session = frame.session; - this[PRIVATE].updateViews(); + this[P_DEVICE].updateViews(); - if (this[PRIVATE].pendingVisibilityState) { - this[PRIVATE].visibilityState = - this[PRIVATE].pendingVisibilityState; - this[PRIVATE].pendingVisibilityState = null; + if (this[P_DEVICE].pendingVisibilityState) { + this[P_DEVICE].visibilityState = + this[P_DEVICE].pendingVisibilityState; + this[P_DEVICE].pendingVisibilityState = null; session.dispatchEvent( new XRSessionEvent('visibilitychange', { session }), ); } - if (this[PRIVATE].visibilityState === XRVisibilityState.Visible) { + if (this[P_DEVICE].visibilityState === 'visible') { this.activeInputs.forEach((activeInput) => { activeInput.onFrameStart(frame); }); } - if (this[PRIVATE].pendingReferenceSpaceReset) { - session[XRSESSION_PRIVATE].referenceSpaces.forEach( - (referenceSpace) => { - switch (referenceSpace[XRREFERENCESPACE_PRIVATE].type) { - case XRReferenceSpaceType.Local: - case XRReferenceSpaceType.LocalFloor: - case XRReferenceSpaceType.BoundedFloor: - case XRReferenceSpaceType.Unbounded: - referenceSpace.dispatchEvent( - new XRReferenceSpaceEvent('reset', { referenceSpace }), - ); - break; - } - }, - ); - this[PRIVATE].pendingReferenceSpaceReset = false; + if (this[P_DEVICE].pendingReferenceSpaceReset) { + session[P_SESSION].referenceSpaces.forEach((referenceSpace) => { + switch (referenceSpace[P_REF_SPACE].type) { + case XRReferenceSpaceType.Local: + case XRReferenceSpaceType.LocalFloor: + case XRReferenceSpaceType.BoundedFloor: + case XRReferenceSpaceType.Unbounded: + referenceSpace.dispatchEvent( + new XRReferenceSpaceEvent('reset', { referenceSpace }), + ); + break; + } + }); + this[P_DEVICE].pendingReferenceSpaceReset = false; } } - this[PRIVATE].updateViews(); + this[P_DEVICE].updateViews(); }, }; - this[PRIVATE].updateViews(); + this[P_DEVICE].updateViews(); globalThis; } @@ -382,13 +375,13 @@ export class XRDevice { configurable: true, }, ); - this[PRIVATE].xrSystem = new XRSystem(this); + this[P_DEVICE].xrSystem = new XRSystem(this); Object.defineProperty(globalThis.navigator, 'xr', { - value: this[PRIVATE].xrSystem, + value: this[P_DEVICE].xrSystem, configurable: true, }); Object.defineProperty(navigator, 'userAgent', { - value: this[PRIVATE].userAgent, + value: this[P_DEVICE].userAgent, writable: false, configurable: false, enumerable: true, @@ -418,83 +411,83 @@ export class XRDevice { } get supportedSessionModes() { - return this[PRIVATE].supportedSessionModes; + return this[P_DEVICE].supportedSessionModes; } get supportedFeatures() { - return this[PRIVATE].supportedFeatures; + return this[P_DEVICE].supportedFeatures; } get supportedFrameRates() { - return this[PRIVATE].supportedFrameRates; + return this[P_DEVICE].supportedFrameRates; } get isSystemKeyboardSupported() { - return this[PRIVATE].isSystemKeyboardSupported; + return this[P_DEVICE].isSystemKeyboardSupported; } get internalNominalFrameRate() { - return this[PRIVATE].internalNominalFrameRate; + return this[P_DEVICE].internalNominalFrameRate; } get stereoEnabled() { - return this[PRIVATE].stereoEnabled; + return this[P_DEVICE].stereoEnabled; } set stereoEnabled(value: boolean) { - this[PRIVATE].stereoEnabled = value; + this[P_DEVICE].stereoEnabled = value; } get ipd() { - return this[PRIVATE].ipd; + return this[P_DEVICE].ipd; } set ipd(value: number) { - this[PRIVATE].ipd = value; + this[P_DEVICE].ipd = value; } get fovy() { - return this[PRIVATE].fovy; + return this[P_DEVICE].fovy; } set fovy(value: number) { - this[PRIVATE].fovy = value; + this[P_DEVICE].fovy = value; } get position(): Vector3 { - return this[PRIVATE].position; + return this[P_DEVICE].position; } get quaternion(): Quaternion { - return this[PRIVATE].quaternion; + return this[P_DEVICE].quaternion; } get viewerSpace() { - if (this[PRIVATE].actionPlayer?.playing) { - return this[PRIVATE].actionPlayer.viewerSpace; + if (this[P_DEVICE].actionPlayer?.playing) { + return this[P_DEVICE].actionPlayer.viewerSpace; } else { - return this[PRIVATE].viewerSpace; + return this[P_DEVICE].viewerSpace; } } get viewSpaces() { - if (this[PRIVATE].actionPlayer?.playing) { - return this[PRIVATE].actionPlayer.viewSpaces; + if (this[P_DEVICE].actionPlayer?.playing) { + return this[P_DEVICE].actionPlayer.viewSpaces; } else { - return this[PRIVATE].viewSpaces; + return this[P_DEVICE].viewSpaces; } } get controllers() { - return this[PRIVATE].controllers; + return this[P_DEVICE].controllers; } get hands() { - return this[PRIVATE].hands; + return this[P_DEVICE].hands; } get primaryInputMode() { - return this[PRIVATE].primaryInputMode; + return this[P_DEVICE].primaryInputMode; } set primaryInputMode(mode: 'controller' | 'hand') { @@ -502,34 +495,34 @@ export class XRDevice { console.warn('primary input mode can only be "controller" or "hand"'); return; } - this[PRIVATE].primaryInputMode = mode; + this[P_DEVICE].primaryInputMode = mode; } get activeInputs(): XRTrackedInput[] { - if (this[PRIVATE].visibilityState !== XRVisibilityState.Visible) { + if (this[P_DEVICE].visibilityState !== 'visible') { return []; } const activeInputs: XRTrackedInput[] = - this[PRIVATE].primaryInputMode === 'controller' - ? Object.values(this[PRIVATE].controllers) - : Object.values(this[PRIVATE].hands); + this[P_DEVICE].primaryInputMode === 'controller' + ? Object.values(this[P_DEVICE].controllers) + : Object.values(this[P_DEVICE].hands); return activeInputs.filter((input) => input.connected); } get inputSources(): XRInputSource[] { - if (this[PRIVATE].actionPlayer?.playing) { - return this[PRIVATE].actionPlayer.inputSources; + if (this[P_DEVICE].actionPlayer?.playing) { + return this[P_DEVICE].actionPlayer.inputSources; } else { return this.activeInputs.map((input) => input.inputSource); } } get canvasContainer(): HTMLDivElement { - return this[PRIVATE].canvasContainer; + return this[P_DEVICE].canvasContainer; } get activeSession(): XRSession | undefined { - return this[PRIVATE].xrSystem?.[XRSYSTEM_PRIVATE].activeSession; + return this[P_DEVICE].xrSystem?.[P_SYSTEM].activeSession; } recenter() { @@ -546,31 +539,33 @@ export class XRDevice { this.quaternion.multiply(deltaQuat); [ - ...Object.values(this[PRIVATE].controllers), - ...Object.values(this[PRIVATE].hands), + ...Object.values(this[P_DEVICE].controllers), + ...Object.values(this[P_DEVICE].hands), ].forEach((activeInput) => { activeInput.position.add(deltaVec); activeInput.quaternion.multiply(deltaQuat); activeInput.position.applyQuaternion(deltaQuat); }); - this[PRIVATE].pendingReferenceSpaceReset = true; + this[P_DEVICE].pendingReferenceSpaceReset = true; } get visibilityState() { - return this[PRIVATE].visibilityState; + return this[P_DEVICE].visibilityState; } // visibility state updates are queued until the XRSession produces frames updateVisibilityState(state: XRVisibilityState) { - if (!Object.values(XRVisibilityState).includes(state)) { + if ( + !Object.values(['visible', 'visible-blurred', 'hidden']).includes(state) + ) { throw new DOMException( 'Invalid XRVisibilityState value', 'NotSupportedError', ); } - if (state !== this[PRIVATE].visibilityState) { - this[PRIVATE].pendingVisibilityState = state; + if (state !== this[P_DEVICE].visibilityState) { + this[P_DEVICE].pendingVisibilityState = state; } } @@ -584,11 +579,11 @@ export class XRDevice { frames: any[]; }, ) { - this[PRIVATE].actionPlayer = new ActionPlayer( + this[P_DEVICE].actionPlayer = new ActionPlayer( refSpace, recording, - this[PRIVATE].ipd, + this[P_DEVICE].ipd, ); - return this[PRIVATE].actionPlayer; + return this[P_DEVICE].actionPlayer; } } diff --git a/src/device/XRHandInput.ts b/src/device/XRHandInput.ts index a9ac42f..39d140d 100644 --- a/src/device/XRHandInput.ts +++ b/src/device/XRHandInput.ts @@ -6,33 +6,29 @@ */ import { - PRIVATE as GAMEPAD_PRIVATE, Gamepad, GamepadConfig, GamepadMappingType, } from '../gamepad/Gamepad.js'; +import { GlobalSpace, XRSpace } from '../spaces/XRSpace.js'; import { - GlobalSpace, - PRIVATE as XRSPACE_PRIVATE, - XRSpace, -} from '../spaces/XRSpace.js'; + P_GAMEPAD, + P_HAND_INPUT, + P_JOINT_SPACE, + P_SPACE, + P_TRACKED_INPUT, +} from '../private.js'; import { XRHand, XRHandJoint } from '../input/XRHand.js'; import { XRHandedness, XRInputSource, XRTargetRayMode, } from '../input/XRInputSource.js'; -import { - PRIVATE as XRJOINTSPACE_PRIVATE, - XRJointSpace, -} from '../spaces/XRJointSpace.js'; -import { - PRIVATE as XRTRACKEDINPUT_PRIVATE, - XRTrackedInput, -} from './XRTrackedInput.js'; import { mat4, quat, vec3 } from 'gl-matrix'; import { XRFrame } from '../frameloop/XRFrame.js'; +import { XRJointSpace } from '../spaces/XRJointSpace.js'; +import { XRTrackedInput } from './XRTrackedInput.js'; import { pinchHandPose } from './configs/hand/pinch.js'; import { pointHandPose } from './configs/hand/point.js'; import { relaxedHandPose } from './configs/hand/relaxed.js'; @@ -121,10 +117,8 @@ const mirrorMatrixToRight = (matrixLeft: mat4) => { } }; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-hand-input'); - export class XRHandInput extends XRTrackedInput { - [PRIVATE]: { + [P_HAND_INPUT]: { poseId: string; poses: { default: HandPose; @@ -173,7 +167,7 @@ export class XRHandInput extends XRTrackedInput { ); super(inputSource); - this[PRIVATE] = { + this[P_HAND_INPUT] = { poseId: 'default', poses: handInputConfig.poses, }; @@ -182,20 +176,20 @@ export class XRHandInput extends XRTrackedInput { } get poseId() { - return this[PRIVATE].poseId; + return this[P_HAND_INPUT].poseId; } set poseId(poseId: string) { - if (!this[PRIVATE].poses[poseId]) { + if (!this[P_HAND_INPUT].poses[poseId]) { console.warn(`Pose config ${poseId} not found`); return; } - this[PRIVATE].poseId = poseId; + this[P_HAND_INPUT].poseId = poseId; } updateHandPose() { - const targetPose = this[PRIVATE].poses[this[PRIVATE].poseId]; - const pinchPose = this[PRIVATE].poses.pinch; + const targetPose = this[P_HAND_INPUT].poses[this[P_HAND_INPUT].poseId]; + const pinchPose = this[P_HAND_INPUT].poses.pinch; Object.values(XRHandJoint).forEach((jointName) => { const targetJointMatrix = targetPose.jointTransforms[jointName].offsetMatrix; @@ -203,21 +197,21 @@ export class XRHandInput extends XRTrackedInput { pinchPose.jointTransforms[jointName].offsetMatrix; const jointSpace = this.inputSource.hand!.get(jointName)!; interpolateMatrix( - jointSpace[XRSPACE_PRIVATE].offsetMatrix, + jointSpace[P_SPACE].offsetMatrix, targetJointMatrix, pinchJointMatrix, this.pinchValue, ); if (this.inputSource.handedness === XRHandedness.Right) { - mirrorMatrixToRight(jointSpace[XRSPACE_PRIVATE].offsetMatrix); + mirrorMatrixToRight(jointSpace[P_SPACE].offsetMatrix); } - jointSpace[XRJOINTSPACE_PRIVATE].radius = + jointSpace[P_JOINT_SPACE].radius = (1 - this.pinchValue) * targetPose.jointTransforms[jointName].radius + this.pinchValue * pinchPose.jointTransforms[jointName].radius; }); if (targetPose.gripOffsetMatrix && pinchPose.gripOffsetMatrix) { interpolateMatrix( - this.inputSource.gripSpace![XRSPACE_PRIVATE].offsetMatrix, + this.inputSource.gripSpace![P_SPACE].offsetMatrix, targetPose.gripOffsetMatrix!, pinchPose.gripOffsetMatrix!, this.pinchValue, @@ -226,8 +220,9 @@ export class XRHandInput extends XRTrackedInput { } get pinchValue() { - return this[XRTRACKEDINPUT_PRIVATE].inputSource.gamepad![GAMEPAD_PRIVATE] - .buttonsMap['pinch']!.value; + return this[P_TRACKED_INPUT].inputSource.gamepad![P_GAMEPAD].buttonsMap[ + 'pinch' + ]!.value; } updatePinchValue(value: number) { @@ -236,9 +231,10 @@ export class XRHandInput extends XRTrackedInput { return; } const gamepadButton = - this[XRTRACKEDINPUT_PRIVATE].inputSource.gamepad![GAMEPAD_PRIVATE] - .buttonsMap['pinch']!; - gamepadButton[GAMEPAD_PRIVATE].pendingValue = value; + this[P_TRACKED_INPUT].inputSource.gamepad![P_GAMEPAD].buttonsMap[ + 'pinch' + ]!; + gamepadButton[P_GAMEPAD].pendingValue = value; } onFrameStart(frame: XRFrame): void { diff --git a/src/device/XRTrackedInput.ts b/src/device/XRTrackedInput.ts index 28dd20e..bd72af5 100644 --- a/src/device/XRTrackedInput.ts +++ b/src/device/XRTrackedInput.ts @@ -5,22 +5,15 @@ * LICENSE file in the root directory of this source tree. */ -import { - PRIVATE as GAMEPAD_PRIVATE, - GamepadButton, -} from '../gamepad/Gamepad.js'; +import { P_GAMEPAD, P_SPACE, P_TRACKED_INPUT } from '../private.js'; import { Quaternion, Vector3 } from '../utils/Math.js'; import { XRHandedness, XRInputSource } from '../input/XRInputSource.js'; +import { GamepadButton } from '../gamepad/Gamepad.js'; import { XRFrame } from '../frameloop/XRFrame.js'; import { XRInputSourceEvent } from '../events/XRInputSourceEvent.js'; -import { PRIVATE as XRSPACE_PRIVATE } from '../spaces/XRSpace.js'; import { mat4 } from 'gl-matrix'; -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/xr-tracked-input', -); - const DEFAULT_TRANSFORM = { [XRHandedness.Left]: { position: new Vector3(-0.25, 1.5, -0.4), @@ -37,7 +30,7 @@ const DEFAULT_TRANSFORM = { }; export class XRTrackedInput { - [PRIVATE]: { + [P_TRACKED_INPUT]: { inputSource: XRInputSource; // input state position: Vector3; @@ -48,7 +41,7 @@ export class XRTrackedInput { }; constructor(inputSource: XRInputSource) { - this[PRIVATE] = { + this[P_TRACKED_INPUT] = { inputSource, position: DEFAULT_TRANSFORM[inputSource.handedness].position.clone(), quaternion: DEFAULT_TRANSFORM[inputSource.handedness].quaternion.clone(), @@ -59,84 +52,78 @@ export class XRTrackedInput { } get position(): Vector3 { - return this[PRIVATE].position; + return this[P_TRACKED_INPUT].position; } get quaternion(): Quaternion { - return this[PRIVATE].quaternion; + return this[P_TRACKED_INPUT].quaternion; } get inputSource(): XRInputSource { - return this[PRIVATE].inputSource; + return this[P_TRACKED_INPUT].inputSource; } get connected() { - return this[PRIVATE].connected; + return this[P_TRACKED_INPUT].connected; } set connected(value: boolean) { - this[PRIVATE].connected = value; - this[PRIVATE].inputSource.gamepad![GAMEPAD_PRIVATE].connected = value; + this[P_TRACKED_INPUT].connected = value; + this[P_TRACKED_INPUT].inputSource.gamepad![P_GAMEPAD].connected = value; } onFrameStart(frame: XRFrame) { - const targetRaySpace = this[PRIVATE].inputSource.targetRaySpace; + const targetRaySpace = this[P_TRACKED_INPUT].inputSource.targetRaySpace; mat4.fromRotationTranslation( - targetRaySpace[XRSPACE_PRIVATE].offsetMatrix, - this[PRIVATE].quaternion.quat, - this[PRIVATE].position.vec3, + targetRaySpace[P_SPACE].offsetMatrix, + this[P_TRACKED_INPUT].quaternion.quat, + this[P_TRACKED_INPUT].position.vec3, ); const session = frame.session; - this[PRIVATE].inputSource.gamepad!.buttons.forEach((button) => { + this[P_TRACKED_INPUT].inputSource.gamepad!.buttons.forEach((button) => { if (button instanceof GamepadButton) { // apply pending values and record last frame values - button[GAMEPAD_PRIVATE].lastFrameValue = button[GAMEPAD_PRIVATE].value; - if (button[GAMEPAD_PRIVATE].pendingValue != null) { - button[GAMEPAD_PRIVATE].value = button[GAMEPAD_PRIVATE].pendingValue; - button[GAMEPAD_PRIVATE].pendingValue = null; + button[P_GAMEPAD].lastFrameValue = button[P_GAMEPAD].value; + if (button[P_GAMEPAD].pendingValue != null) { + button[P_GAMEPAD].value = button[P_GAMEPAD].pendingValue; + button[P_GAMEPAD].pendingValue = null; } // trigger input source events - if (button[GAMEPAD_PRIVATE].eventTrigger != null) { + if (button[P_GAMEPAD].eventTrigger != null) { if ( - button[GAMEPAD_PRIVATE].lastFrameValue === 0 && - button[GAMEPAD_PRIVATE].value > 0 + button[P_GAMEPAD].lastFrameValue === 0 && + button[P_GAMEPAD].value > 0 ) { session.dispatchEvent( - new XRInputSourceEvent(button[GAMEPAD_PRIVATE].eventTrigger, { + new XRInputSourceEvent(button[P_GAMEPAD].eventTrigger, { frame, - inputSource: this[PRIVATE].inputSource, + inputSource: this[P_TRACKED_INPUT].inputSource, }), ); session.dispatchEvent( - new XRInputSourceEvent( - button[GAMEPAD_PRIVATE].eventTrigger + 'start', - { - frame, - inputSource: this[PRIVATE].inputSource, - }, - ), + new XRInputSourceEvent(button[P_GAMEPAD].eventTrigger + 'start', { + frame, + inputSource: this[P_TRACKED_INPUT].inputSource, + }), ); } else if ( - button[GAMEPAD_PRIVATE].lastFrameValue > 0 && - button[GAMEPAD_PRIVATE].value === 0 + button[P_GAMEPAD].lastFrameValue > 0 && + button[P_GAMEPAD].value === 0 ) { session.dispatchEvent( - new XRInputSourceEvent( - button[GAMEPAD_PRIVATE].eventTrigger + 'end', - { - frame, - inputSource: this[PRIVATE].inputSource, - }, - ), + new XRInputSourceEvent(button[P_GAMEPAD].eventTrigger + 'end', { + frame, + inputSource: this[P_TRACKED_INPUT].inputSource, + }), ); } } } }); - this[PRIVATE].inputSourceChanged = - this.connected !== this[PRIVATE].lastFrameConnected; - this[PRIVATE].lastFrameConnected = this.connected; + this[P_TRACKED_INPUT].inputSourceChanged = + this.connected !== this[P_TRACKED_INPUT].lastFrameConnected; + this[P_TRACKED_INPUT].lastFrameConnected = this.connected; } } diff --git a/src/device/configs/headset/meta.ts b/src/device/configs/headset/meta.ts index ad0e4b7..692822b 100644 --- a/src/device/configs/headset/meta.ts +++ b/src/device/configs/headset/meta.ts @@ -5,11 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import { WebXRFeatures, XRDeviceConfig } from '../../XRDevice.js'; import { XREnvironmentBlendMode, XRInteractionMode, - XRSessionMode, } from '../../../session/XRSession.js'; import { metaQuestTouchPlus, @@ -18,30 +16,28 @@ import { oculusTouchV3, } from '../controller/meta.js'; +import { XRDeviceConfig } from '../../XRDevice.js'; + export const oculusQuest1: XRDeviceConfig = { name: 'Oculus Quest 1', controllerConfig: oculusTouchV2, - supportedSessionModes: [ - XRSessionMode.Inline, - XRSessionMode.ImmersiveVR, - XRSessionMode.ImmersiveAR, - ], + supportedSessionModes: ['inline', 'immersive-vr', 'immersive-ar'], supportedFeatures: [ - WebXRFeatures.Viewer, - WebXRFeatures.Local, - WebXRFeatures.LocalFloor, - WebXRFeatures.BoundedFloor, - WebXRFeatures.Unbounded, - WebXRFeatures.Anchors, - WebXRFeatures.PlaneDetection, - WebXRFeatures.HandTracking, + 'viewer', + 'local', + 'local-floor', + 'bounded-floor', + 'unbounded', + 'anchors', + 'plane-detection', + 'hand-tracking', ], supportedFrameRates: [72, 80, 90], isSystemKeyboardSupported: true, internalNominalFrameRate: 72, environmentBlendModes: { - [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, - [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + ['immersive-vr']: XREnvironmentBlendMode.Opaque, + ['immersive-ar']: XREnvironmentBlendMode.AlphaBlend, }, interactionMode: XRInteractionMode.WorldSpace, userAgent: @@ -51,29 +47,25 @@ export const oculusQuest1: XRDeviceConfig = { export const metaQuest2: XRDeviceConfig = { name: 'Meta Quest 2', controllerConfig: oculusTouchV3, - supportedSessionModes: [ - XRSessionMode.Inline, - XRSessionMode.ImmersiveVR, - XRSessionMode.ImmersiveAR, - ], + supportedSessionModes: ['inline', 'immersive-vr', 'immersive-ar'], supportedFeatures: [ - WebXRFeatures.Viewer, - WebXRFeatures.Local, - WebXRFeatures.LocalFloor, - WebXRFeatures.BoundedFloor, - WebXRFeatures.Unbounded, - WebXRFeatures.Anchors, - WebXRFeatures.PlaneDetection, - WebXRFeatures.MeshDetection, - WebXRFeatures.HitTest, - WebXRFeatures.HandTracking, + 'viewer', + 'local', + 'local-floor', + 'bounded-floor', + 'unbounded', + 'anchors', + 'plane-detection', + 'mesh-detection', + 'hit-test', + 'hand-tracking', ], supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 72, environmentBlendModes: { - [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, - [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + ['immersive-vr']: XREnvironmentBlendMode.Opaque, + ['immersive-ar']: XREnvironmentBlendMode.AlphaBlend, }, interactionMode: XRInteractionMode.WorldSpace, userAgent: @@ -83,29 +75,25 @@ export const metaQuest2: XRDeviceConfig = { export const metaQuestPro: XRDeviceConfig = { name: 'Meta Quest Pro', controllerConfig: metaQuestTouchPro, - supportedSessionModes: [ - XRSessionMode.Inline, - XRSessionMode.ImmersiveVR, - XRSessionMode.ImmersiveAR, - ], + supportedSessionModes: ['inline', 'immersive-vr', 'immersive-ar'], supportedFeatures: [ - WebXRFeatures.Viewer, - WebXRFeatures.Local, - WebXRFeatures.LocalFloor, - WebXRFeatures.BoundedFloor, - WebXRFeatures.Unbounded, - WebXRFeatures.Anchors, - WebXRFeatures.PlaneDetection, - WebXRFeatures.MeshDetection, - WebXRFeatures.HitTest, - WebXRFeatures.HandTracking, + 'viewer', + 'local', + 'local-floor', + 'bounded-floor', + 'unbounded', + 'anchors', + 'plane-detection', + 'mesh-detection', + 'hit-test', + 'hand-tracking', ], supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, environmentBlendModes: { - [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, - [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + ['immersive-vr']: XREnvironmentBlendMode.Opaque, + ['immersive-ar']: XREnvironmentBlendMode.AlphaBlend, }, interactionMode: XRInteractionMode.WorldSpace, userAgent: @@ -115,30 +103,26 @@ export const metaQuestPro: XRDeviceConfig = { export const metaQuest3: XRDeviceConfig = { name: 'Meta Quest 3', controllerConfig: metaQuestTouchPlus, - supportedSessionModes: [ - XRSessionMode.Inline, - XRSessionMode.ImmersiveVR, - XRSessionMode.ImmersiveAR, - ], + supportedSessionModes: ['inline', 'immersive-vr', 'immersive-ar'], supportedFeatures: [ - WebXRFeatures.Viewer, - WebXRFeatures.Local, - WebXRFeatures.LocalFloor, - WebXRFeatures.BoundedFloor, - WebXRFeatures.Unbounded, - WebXRFeatures.Anchors, - WebXRFeatures.PlaneDetection, - WebXRFeatures.MeshDetection, - WebXRFeatures.HitTest, - WebXRFeatures.HandTracking, - WebXRFeatures.DepthSensing, + 'viewer', + 'local', + 'local-floor', + 'bounded-floor', + 'unbounded', + 'anchors', + 'plane-detection', + 'mesh-detection', + 'hit-test', + 'hand-tracking', + 'depth-sensing', ], supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, environmentBlendModes: { - [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, - [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + ['immersive-vr']: XREnvironmentBlendMode.Opaque, + ['immersive-ar']: XREnvironmentBlendMode.AlphaBlend, }, interactionMode: XRInteractionMode.WorldSpace, userAgent: diff --git a/src/frameloop/XRFrame.ts b/src/frameloop/XRFrame.ts index e3cfc0a..0085227 100644 --- a/src/frameloop/XRFrame.ts +++ b/src/frameloop/XRFrame.ts @@ -5,35 +5,28 @@ * LICENSE file in the root directory of this source tree. */ +import { + P_DEVICE, + P_FRAME, + P_JOINT_SPACE, + P_SESSION, + P_SPACE, +} from '../private.js'; import { XRAnchor, XRAnchorSet } from '../anchors/XRAnchor.js'; import { XREye, XRView } from '../views/XRView.js'; -import { - PRIVATE as XRJOINTSPACE_PRIVATE, - XRJointSpace, -} from '../spaces/XRJointSpace.js'; -import { - PRIVATE as XRSESSION_PRIVATE, - XRSession, - XRSessionMode, -} from '../session/XRSession.js'; -import { - PRIVATE as XRSPACE_PRIVATE, - XRSpace, - XRSpaceUtils, -} from '../spaces/XRSpace.js'; +import { XRSpace, XRSpaceUtils } from '../spaces/XRSpace.js'; import { mat4, quat, vec3 } from 'gl-matrix'; -import { PRIVATE as XRDEVICE_PRIVATE } from '../device/XRDevice.js'; import { XRJointPose } from '../pose/XRJointPose.js'; +import { XRJointSpace } from '../spaces/XRJointSpace.js'; import { XRMeshSet } from '../meshes/XRMesh.js'; import { XRPlaneSet } from '../planes/XRPlane.js'; import { XRPose } from '../pose/XRPose.js'; import { XRReferenceSpace } from '../spaces/XRReferenceSpace.js'; import { XRRigidTransform } from '../primitives/XRRigidTransform.js'; +import { XRSession } from '../session/XRSession.js'; import { XRViewerPose } from '../pose/XRViewerPose.js'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-frame'); - const spaceGlobalMatrix = mat4.create(); const baseSpaceGlobalMatrix = mat4.create(); const baseSpaceGlobalMatrixInverse = mat4.create(); @@ -50,7 +43,7 @@ const getOffsetMatrix = ( }; export class XRFrame { - [PRIVATE]: { + [P_FRAME]: { session: XRSession; id: number; active: boolean; @@ -69,7 +62,7 @@ export class XRFrame { animationFrame: boolean, predictedDisplayTime: number, ) { - this[PRIVATE] = { + this[P_FRAME] = { session, id, active, @@ -78,30 +71,30 @@ export class XRFrame { tempMat4: mat4.create(), detectedPlanes: new XRPlaneSet(), detectedMeshes: new XRMeshSet(), - trackedAnchors: session[XRSESSION_PRIVATE].frameTrackedAnchors, + trackedAnchors: session[P_SESSION].frameTrackedAnchors, }; } get session() { - return this[PRIVATE].session; + return this[P_FRAME].session; } get predictedDisplayTime() { - return this[PRIVATE].predictedDisplayTime; + return this[P_FRAME].predictedDisplayTime; } getPose(space: XRSpace, baseSpace: XRSpace) { - if (!this[PRIVATE].active) { + if (!this[P_FRAME].active) { throw new DOMException( 'XRFrame access outside the callback that produced it is invalid.', 'InvalidStateError', ); } - getOffsetMatrix(this[PRIVATE].tempMat4, space, baseSpace); + getOffsetMatrix(this[P_FRAME].tempMat4, space, baseSpace); const position = vec3.create(); - mat4.getTranslation(position, this[PRIVATE].tempMat4); + mat4.getTranslation(position, this[P_FRAME].tempMat4); const orientation = quat.create(); - mat4.getRotation(orientation, this[PRIVATE].tempMat4); + mat4.getRotation(orientation, this[P_FRAME].tempMat4); return new XRPose( new XRRigidTransform( { x: position[0], y: position[1], z: position[2], w: 1.0 }, @@ -112,22 +105,22 @@ export class XRFrame { w: orientation[3], }, ), - space[XRSPACE_PRIVATE].emulated, + space[P_SPACE].emulated, ); } getViewerPose(referenceSpace: XRReferenceSpace) { - if (!this[PRIVATE].animationFrame) { + if (!this[P_FRAME].animationFrame) { throw new DOMException( 'getViewerPose can only be called on XRFrame objects passed to XRSession.requestAnimationFrame callbacks.', 'InvalidStateError', ); } - const session = this[PRIVATE].session; - const device = session[XRSESSION_PRIVATE].device; + const session = this[P_FRAME].session; + const device = session[P_SESSION].device; const pose = this.getPose(device.viewerSpace, referenceSpace); const eyes = - session[XRSESSION_PRIVATE].mode === XRSessionMode.Inline + session[P_SESSION].mode === 'inline' ? [XREye.None] : [XREye.Left, XREye.Right]; @@ -135,8 +128,7 @@ export class XRFrame { eyes.forEach((eye) => { const viewSpace = device.viewSpaces[eye]; const viewPose = this.getPose(viewSpace, referenceSpace); - const projectionMatrix = - session[XRSESSION_PRIVATE].getProjectionMatrix(eye); + const projectionMatrix = session[P_SESSION].getProjectionMatrix(eye); const view = new XRView( eye, new Float32Array(projectionMatrix), @@ -151,14 +143,14 @@ export class XRFrame { getJointPose(joint: XRJointSpace, baseSpace: XRSpace) { const xrPose = this.getPose(joint, baseSpace); - const radius = joint[XRJOINTSPACE_PRIVATE].radius; + const radius = joint[P_JOINT_SPACE].radius; return new XRJointPose(xrPose.transform, radius, false); } fillJointRadii(jointSpaces: XRJointSpace[], radii: Float32Array) { // converting from sequence type to array jointSpaces = Array.from(jointSpaces); - if (!this[PRIVATE].active) { + if (!this[P_FRAME].active) { throw new DOMException( 'XRFrame access outside the callback that produced it is invalid.', 'InvalidStateError', @@ -172,11 +164,11 @@ export class XRFrame { } let allValid = true; for (let offset = 0; offset < jointSpaces.length; offset++) { - if (!jointSpaces[offset][XRJOINTSPACE_PRIVATE].radius) { + if (!jointSpaces[offset][P_JOINT_SPACE].radius) { radii[offset] = NaN; allValid = false; } else { - radii[offset] = jointSpaces[offset][XRJOINTSPACE_PRIVATE].radius; + radii[offset] = jointSpaces[offset][P_JOINT_SPACE].radius; } } return allValid; @@ -185,7 +177,7 @@ export class XRFrame { fillPoses(spaces: XRSpace[], baseSpace: XRSpace, transforms: Float32Array) { // converting from sequence type to array spaces = Array.from(spaces); - if (!this[PRIVATE].active) { + if (!this[P_FRAME].active) { throw new DOMException( 'XRFrame access outside the callback that produced it is invalid.', 'InvalidStateError', @@ -198,47 +190,47 @@ export class XRFrame { ); } spaces.forEach((space, i) => { - getOffsetMatrix(this[PRIVATE].tempMat4, space, baseSpace); + getOffsetMatrix(this[P_FRAME].tempMat4, space, baseSpace); for (let j = 0; j < 16; j++) { - transforms[i * 16 + j] = this[PRIVATE].tempMat4[j]; + transforms[i * 16 + j] = this[P_FRAME].tempMat4[j]; } }); return true; } get detectedPlanes() { - if (!this[PRIVATE].active) { + if (!this[P_FRAME].active) { throw new DOMException( 'XRFrame access outside the callback that produced it is invalid.', 'InvalidStateError', ); } - return this[PRIVATE].detectedPlanes; + return this[P_FRAME].detectedPlanes; } get detectedMeshes() { - if (!this[PRIVATE].active) { + if (!this[P_FRAME].active) { throw new DOMException( 'XRFrame access outside the callback that produced it is invalid.', 'InvalidStateError', ); } - return this[PRIVATE].detectedMeshes; + return this[P_FRAME].detectedMeshes; } get trackedAnchors() { - if (!this[PRIVATE].active) { + if (!this[P_FRAME].active) { throw new DOMException( 'XRFrame access outside the callback that produced it is invalid.', 'InvalidStateError', ); } - return this[PRIVATE].trackedAnchors; + return this[P_FRAME].trackedAnchors; } createAnchor(pose: XRRigidTransform, space: XRSpace) { return new Promise((resolve, reject) => { - if (!this[PRIVATE].active) { + if (!this[P_FRAME].active) { reject( new DOMException( 'XRFrame access outside the callback that produced it is invalid.', @@ -247,15 +239,14 @@ export class XRFrame { ); } else { const globalSpace = - this[PRIVATE].session[XRSESSION_PRIVATE].device[XRDEVICE_PRIVATE] - .globalSpace; + this[P_FRAME].session[P_SESSION].device[P_DEVICE].globalSpace; const tempSpace = new XRSpace(space, pose.matrix); const globalOffsetMatrix = XRSpaceUtils.calculateGlobalOffsetMatrix(tempSpace); const anchorSpace = new XRSpace(globalSpace, globalOffsetMatrix); - const anchor = new XRAnchor(anchorSpace, this[PRIVATE].session); - this[PRIVATE].session[XRSESSION_PRIVATE].trackedAnchors.add(anchor); - this[PRIVATE].session[XRSESSION_PRIVATE].newAnchors.set(anchor, { + const anchor = new XRAnchor(anchorSpace, this[P_FRAME].session); + this[P_FRAME].session[P_SESSION].trackedAnchors.add(anchor); + this[P_FRAME].session[P_SESSION].newAnchors.set(anchor, { resolve, reject, }); diff --git a/src/gamepad/Gamepad.ts b/src/gamepad/Gamepad.ts index 6d3e625..67029aa 100644 --- a/src/gamepad/Gamepad.ts +++ b/src/gamepad/Gamepad.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/gamepad'); +import { P_GAMEPAD } from '../private.js'; export enum GamepadMappingType { None = '', @@ -31,7 +31,7 @@ export interface GamepadConfig { } export class GamepadButton { - [PRIVATE]: { + [P_GAMEPAD]: { type: 'analog' | 'binary' | 'manual'; eventTrigger: 'select' | 'squeeze' | null; pressed: boolean; @@ -45,7 +45,7 @@ export class GamepadButton { type: 'analog' | 'binary' | 'manual', eventTrigger: 'select' | 'squeeze' | null, ) { - this[PRIVATE] = { + this[P_GAMEPAD] = { type, eventTrigger, pressed: false, @@ -57,23 +57,23 @@ export class GamepadButton { } get pressed() { - if (this[PRIVATE].type === 'manual') { - return this[PRIVATE].pressed; + if (this[P_GAMEPAD].type === 'manual') { + return this[P_GAMEPAD].pressed; } else { - return this[PRIVATE].value > 0; + return this[P_GAMEPAD].value > 0; } } get touched() { - if (this[PRIVATE].type === 'manual') { - return this[PRIVATE].touched; + if (this[P_GAMEPAD].type === 'manual') { + return this[P_GAMEPAD].touched; } else { - return this[PRIVATE].touched || this.pressed; + return this[P_GAMEPAD].touched || this.pressed; } } get value() { - return this[PRIVATE].value; + return this[P_GAMEPAD].value; } } @@ -84,7 +84,7 @@ export class EmptyGamepadButton { } export class Gamepad { - [PRIVATE]: { + [P_GAMEPAD]: { id: string; index: number; connected: boolean; @@ -106,7 +106,7 @@ export class Gamepad { id: string = '', index: number = -1, ) { - this[PRIVATE] = { + this[P_GAMEPAD] = { id, index, connected: false, @@ -120,10 +120,10 @@ export class Gamepad { }; gamepadConfig.buttons.forEach((buttonConfig) => { if (buttonConfig === null) { - this[PRIVATE].buttonsSequence.push(null); + this[P_GAMEPAD].buttonsSequence.push(null); } else { - this[PRIVATE].buttonsSequence.push(buttonConfig.id); - this[PRIVATE].buttonsMap[buttonConfig.id] = new GamepadButton( + this[P_GAMEPAD].buttonsSequence.push(buttonConfig.id); + this[P_GAMEPAD].buttonsMap[buttonConfig.id] = new GamepadButton( buttonConfig.type, buttonConfig.eventTrigger ?? null, ); @@ -131,39 +131,39 @@ export class Gamepad { }); gamepadConfig.axes.forEach((axisConfig) => { if (axisConfig === null) { - this[PRIVATE].axesSequence.push(null); + this[P_GAMEPAD].axesSequence.push(null); } else { - this[PRIVATE].axesSequence.push(axisConfig.id + axisConfig.type); - if (!this[PRIVATE].axesMap[axisConfig.id]) { - this[PRIVATE].axesMap[axisConfig.id] = { x: 0, y: 0 }; + this[P_GAMEPAD].axesSequence.push(axisConfig.id + axisConfig.type); + if (!this[P_GAMEPAD].axesMap[axisConfig.id]) { + this[P_GAMEPAD].axesMap[axisConfig.id] = { x: 0, y: 0 }; } } }); } get id() { - return this[PRIVATE].id; + return this[P_GAMEPAD].id; } get index() { - return this[PRIVATE].index; + return this[P_GAMEPAD].index; } get connected() { - return this[PRIVATE].connected; + return this[P_GAMEPAD].connected; } get timestamp() { - return this[PRIVATE].timestamp; + return this[P_GAMEPAD].timestamp; } get mapping() { - return this[PRIVATE].mapping; + return this[P_GAMEPAD].mapping; } get axes() { const axes: (number | null)[] = []; - this[PRIVATE].axesSequence.forEach((id) => { + this[P_GAMEPAD].axesSequence.forEach((id) => { if (id === null) { axes.push(null); } else { @@ -172,8 +172,8 @@ export class Gamepad { axes.push( // if axis type is manual, then return the x value axisType === 'y-axis' - ? this[PRIVATE].axesMap[axisId].y - : this[PRIVATE].axesMap[axisId].x, + ? this[P_GAMEPAD].axesMap[axisId].y + : this[P_GAMEPAD].axesMap[axisId].x, ); } }); @@ -181,13 +181,13 @@ export class Gamepad { } get buttons() { - return this[PRIVATE].buttonsSequence.map((id) => - id === null ? new EmptyGamepadButton() : this[PRIVATE].buttonsMap[id], + return this[P_GAMEPAD].buttonsSequence.map((id) => + id === null ? new EmptyGamepadButton() : this[P_GAMEPAD].buttonsMap[id], ); } get hapticActuators() { - return this[PRIVATE].hapticActuators; + return this[P_GAMEPAD].hapticActuators; } get vibrationActuator() { diff --git a/src/index.ts b/src/index.ts index d4616aa..003c929 100644 --- a/src/index.ts +++ b/src/index.ts @@ -65,3 +65,6 @@ export { XRReferenceSpaceEvent } from './events/XRReferenceSpaceEvent.js'; // Action Recording/Playback export { ActionRecorder } from './action/ActionRecorder.js'; + +// Private Keys +export * from './private.js'; diff --git a/src/initialization/XRSystem.ts b/src/initialization/XRSystem.ts index e009bf8..6f05b95 100644 --- a/src/initialization/XRSystem.ts +++ b/src/initialization/XRSystem.ts @@ -5,34 +5,34 @@ * LICENSE file in the root directory of this source tree. */ -import { WebXRFeatures, XRDevice } from '../device/XRDevice.js'; +import type { WebXRFeature, XRDevice } from '../device/XRDevice.js'; import { XRSession, XRSessionInit, XRSessionMode, } from '../session/XRSession.js'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-system'); +import { P_SYSTEM } from '../private.js'; export class XRSystem extends EventTarget { - [PRIVATE]: { + [P_SYSTEM]: { device: XRDevice; activeSession?: XRSession; }; constructor(device: XRDevice) { super(); - this[PRIVATE] = { device }; + this[P_SYSTEM] = { device }; // Initialize device change monitoring here if applicable } isSessionSupported(mode: XRSessionMode): Promise { return new Promise((resolve, _reject) => { - if (mode === XRSessionMode.Inline) { + if (mode === 'inline') { resolve(true); } else { // Check for spatial tracking permission if necessary - resolve(this[PRIVATE].device.supportedSessionModes.includes(mode)); + resolve(this[P_SYSTEM].device.supportedSessionModes.includes(mode)); } }); } @@ -55,7 +55,7 @@ export class XRSystem extends EventTarget { } // Check for active sessions and other constraints here - if (this[PRIVATE].activeSession) { + if (this[P_SYSTEM].activeSession) { reject( new DOMException( 'An active XRSession already exists.', @@ -67,7 +67,7 @@ export class XRSystem extends EventTarget { // Handle required and optional features const { requiredFeatures = [], optionalFeatures = [] } = options; - const { supportedFeatures } = this[PRIVATE].device; + const { supportedFeatures } = this[P_SYSTEM].device; // Check if all required features are supported const allRequiredSupported = requiredFeatures.every((feature) => @@ -88,26 +88,26 @@ export class XRSystem extends EventTarget { ); // Combine required and supported optional features into enabled features - const enabledFeatures = Array.from( + const enabledFeatures: WebXRFeature[] = Array.from( new Set([ ...requiredFeatures, ...supportedOptionalFeatures, - WebXRFeatures.Viewer, - WebXRFeatures.Local, + 'viewer', + 'local', ]), ); // Proceed with session creation const session = new XRSession( - this[PRIVATE].device, + this[P_SYSTEM].device, mode, enabledFeatures, ); - this[PRIVATE].activeSession = session; + this[P_SYSTEM].activeSession = session; // Listen for session end to clear the active session session.addEventListener('end', () => { - this[PRIVATE].activeSession = undefined; + this[P_SYSTEM].activeSession = undefined; }); resolve(session); diff --git a/src/input/XRInputSource.ts b/src/input/XRInputSource.ts index b39fdba..402fe9e 100644 --- a/src/input/XRInputSource.ts +++ b/src/input/XRInputSource.ts @@ -6,6 +6,7 @@ */ import { Gamepad } from '../gamepad/Gamepad.js'; +import { P_INPUT_SOURCE } from '../private.js'; import { XRHand } from './XRHand.js'; import { XRSpace } from '../spaces/XRSpace.js'; @@ -24,12 +25,8 @@ export enum XRTargetRayMode { export class XRInputSourceArray extends Array {} -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/xr-input-source', -); - export class XRInputSource { - [PRIVATE]: { + [P_INPUT_SOURCE]: { handedness: XRHandedness; targetRayMode: XRTargetRayMode; targetRaySpace: XRSpace; @@ -48,7 +45,7 @@ export class XRInputSource { gripSpace?: XRSpace, hand?: XRHand, ) { - this[PRIVATE] = { + this[P_INPUT_SOURCE] = { handedness, targetRayMode, targetRaySpace, @@ -60,30 +57,30 @@ export class XRInputSource { } get handedness() { - return this[PRIVATE].handedness; + return this[P_INPUT_SOURCE].handedness; } get targetRayMode() { - return this[PRIVATE].targetRayMode; + return this[P_INPUT_SOURCE].targetRayMode; } get targetRaySpace() { - return this[PRIVATE].targetRaySpace; + return this[P_INPUT_SOURCE].targetRaySpace; } get gripSpace() { - return this[PRIVATE].gripSpace; + return this[P_INPUT_SOURCE].gripSpace; } get profiles() { - return this[PRIVATE].profiles; + return this[P_INPUT_SOURCE].profiles; } get gamepad() { - return this[PRIVATE].gamepad; + return this[P_INPUT_SOURCE].gamepad; } get hand() { - return this[PRIVATE].hand; + return this[P_INPUT_SOURCE].hand; } } diff --git a/src/layers/XRWebGLLayer.ts b/src/layers/XRWebGLLayer.ts index 47a6155..707b3a7 100644 --- a/src/layers/XRWebGLLayer.ts +++ b/src/layers/XRWebGLLayer.ts @@ -5,18 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -import { - PRIVATE as XRSESSION_PRIVATE, - XRSession, -} from '../session/XRSession.js'; -import { PRIVATE as XRVIEW_PRIVATE, XRView } from '../views/XRView.js'; +import { P_DEVICE, P_SESSION, P_VIEW, P_WEBGL_LAYER } from '../private.js'; -import { PRIVATE as XRDEVICE_PRIVATE } from '../device/XRDevice.js'; +import { XRSession } from '../session/XRSession.js'; +import { XRView } from '../views/XRView.js'; export class XRLayer extends EventTarget {} -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/XRWebGLLayer'); - type LayerInit = { antialias?: boolean; depth?: boolean; @@ -36,7 +31,7 @@ const defaultLayerInit: LayerInit = { }; export class XRWebGLLayer extends XRLayer { - [PRIVATE]: { + [P_WEBGL_LAYER]: { session: XRSession; context: WebGLRenderingContext | WebGL2RenderingContext; antialias: boolean; @@ -49,7 +44,7 @@ export class XRWebGLLayer extends XRLayer { ) { super(); - if (session[XRSESSION_PRIVATE].ended) { + if (session[P_SESSION].ended) { throw new DOMException('Session has ended', 'InvalidStateError'); } @@ -59,7 +54,7 @@ export class XRWebGLLayer extends XRLayer { // Default values for XRWebGLLayerInit, can be overridden by layerInit const config = { ...defaultLayerInit, ...layerInit }; - this[PRIVATE] = { + this[P_WEBGL_LAYER] = { session, context, antialias: config.antialias!, @@ -67,11 +62,11 @@ export class XRWebGLLayer extends XRLayer { } get context() { - return this[PRIVATE].context; + return this[P_WEBGL_LAYER].context; } get antialias() { - return this[PRIVATE].antialias; + return this[P_WEBGL_LAYER].antialias; } get ignoreDepthValues() { @@ -83,24 +78,25 @@ export class XRWebGLLayer extends XRLayer { } get framebufferWidth() { - return this[PRIVATE].context.drawingBufferWidth; + return this[P_WEBGL_LAYER].context.drawingBufferWidth; } get framebufferHeight() { - return this[PRIVATE].context.drawingBufferHeight; + return this[P_WEBGL_LAYER].context.drawingBufferHeight; } getViewport(view: XRView) { - if (view[XRVIEW_PRIVATE].session !== this[PRIVATE].session) { + if (view[P_VIEW].session !== this[P_WEBGL_LAYER].session) { throw new DOMException( "View's session differs from Layer's session", 'InvalidStateError', ); } // TO-DO: check frame - return this[PRIVATE].session[XRSESSION_PRIVATE].device[ - XRDEVICE_PRIVATE - ].getViewport(this, view); + return this[P_WEBGL_LAYER].session[P_SESSION].device[P_DEVICE].getViewport( + this, + view, + ); } static getNativeFramebufferScaleFactor(session: XRSession): number { @@ -110,7 +106,7 @@ export class XRWebGLLayer extends XRLayer { ); } - if (session[XRSESSION_PRIVATE].ended) { + if (session[P_SESSION].ended) { return 0.0; } diff --git a/src/meshes/XRMesh.ts b/src/meshes/XRMesh.ts index f89be33..49a9fd4 100644 --- a/src/meshes/XRMesh.ts +++ b/src/meshes/XRMesh.ts @@ -5,13 +5,12 @@ * LICENSE file in the root directory of this source tree. */ -import { XRSemanticLabels } from '../labels/labels'; -import { XRSpace } from '../spaces/XRSpace'; - -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-mesh'); +import { P_MESH } from '../private.js'; +import { XRSemanticLabels } from '../labels/labels.js'; +import { XRSpace } from '../spaces/XRSpace.js'; export class XRMesh { - [PRIVATE]: { + [P_MESH]: { meshSpace: XRSpace; vertices: Float32Array; indices: Uint32Array; @@ -25,7 +24,7 @@ export class XRMesh { indices: Uint32Array, semanticLabel?: XRSemanticLabels, ) { - this[PRIVATE] = { + this[P_MESH] = { meshSpace, vertices, indices, @@ -35,23 +34,23 @@ export class XRMesh { } get meshSpace() { - return this[PRIVATE].meshSpace; + return this[P_MESH].meshSpace; } get vertices(): Readonly { - return this[PRIVATE].vertices; + return this[P_MESH].vertices; } get indices(): Readonly { - return this[PRIVATE].indices; + return this[P_MESH].indices; } get lastChangedTime() { - return this[PRIVATE].lastChangedTime; + return this[P_MESH].lastChangedTime; } get semanticLabel() { - return this[PRIVATE].semanticLabel; + return this[P_MESH].semanticLabel; } } diff --git a/src/planes/XRPlane.ts b/src/planes/XRPlane.ts index d129a85..cecea4d 100644 --- a/src/planes/XRPlane.ts +++ b/src/planes/XRPlane.ts @@ -5,10 +5,9 @@ * LICENSE file in the root directory of this source tree. */ -import { XRSemanticLabels } from '../labels/labels'; -import { XRSpace } from '../spaces/XRSpace'; - -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-plane'); +import { P_PLANE } from '../private.js'; +import { XRSemanticLabels } from '../labels/labels.js'; +import { XRSpace } from '../spaces/XRSpace.js'; export enum XRPlaneOrientation { Horizontal = 'horizontal', @@ -39,7 +38,7 @@ export const XREntityOrientation: Partial< }; export class XRPlane { - [PRIVATE]: { + [P_PLANE]: { planeSpace: XRSpace; polygon: DOMPointReadOnly[]; lastChangedTime: DOMHighResTimeStamp; @@ -52,7 +51,7 @@ export class XRPlane { polygon: DOMPointReadOnly[], semanticLabel?: XRSemanticLabels, ) { - this[PRIVATE] = { + this[P_PLANE] = { planeSpace, polygon, lastChangedTime: performance.now(), @@ -64,23 +63,23 @@ export class XRPlane { } get planeSpace() { - return this[PRIVATE].planeSpace; + return this[P_PLANE].planeSpace; } get polygon(): ReadonlyArray { - return this[PRIVATE].polygon; + return this[P_PLANE].polygon; } get orientation() { - return this[PRIVATE].orientation; + return this[P_PLANE].orientation; } get lastChangedTime() { - return this[PRIVATE].lastChangedTime; + return this[P_PLANE].lastChangedTime; } get semanticLabel() { - return this[PRIVATE].semanticLabel; + return this[P_PLANE].semanticLabel; } } diff --git a/src/pose/XRJointPose.ts b/src/pose/XRJointPose.ts index 6744e2a..013bb3e 100644 --- a/src/pose/XRJointPose.ts +++ b/src/pose/XRJointPose.ts @@ -5,13 +5,12 @@ * LICENSE file in the root directory of this source tree. */ +import { P_JOINT_POSE } from '../private.js'; import { XRPose } from './XRPose.js'; import { XRRigidTransform } from '../primitives/XRRigidTransform.js'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-joint-pose'); - export class XRJointPose extends XRPose { - [PRIVATE]: { + [P_JOINT_POSE]: { radius: number; }; @@ -23,10 +22,10 @@ export class XRJointPose extends XRPose { angularVelocity: DOMPointReadOnly | undefined = undefined, ) { super(transform, emulatedPosition, linearVelocity, angularVelocity); - this[PRIVATE] = { radius }; + this[P_JOINT_POSE] = { radius }; } get radius() { - return this[PRIVATE].radius; + return this[P_JOINT_POSE].radius; } } diff --git a/src/pose/XRPose.ts b/src/pose/XRPose.ts index 9745c3b..776a3ca 100644 --- a/src/pose/XRPose.ts +++ b/src/pose/XRPose.ts @@ -5,12 +5,11 @@ * LICENSE file in the root directory of this source tree. */ +import { P_POSE } from '../private.js'; import { XRRigidTransform } from '../primitives/XRRigidTransform.js'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-pose'); - export class XRPose { - [PRIVATE]: { + [P_POSE]: { transform: XRRigidTransform; emulatedPosition: boolean; linearVelocity?: DOMPointReadOnly; @@ -23,7 +22,7 @@ export class XRPose { linearVelocity: DOMPointReadOnly | undefined = undefined, angularVelocity: DOMPointReadOnly | undefined = undefined, ) { - this[PRIVATE] = { + this[P_POSE] = { transform, emulatedPosition, linearVelocity, @@ -32,18 +31,18 @@ export class XRPose { } get transform() { - return this[PRIVATE].transform; + return this[P_POSE].transform; } get emulatedPosition() { - return this[PRIVATE].emulatedPosition; + return this[P_POSE].emulatedPosition; } get linearVelocity() { - return this[PRIVATE].linearVelocity; + return this[P_POSE].linearVelocity; } get angularVelocity() { - return this[PRIVATE].angularVelocity; + return this[P_POSE].angularVelocity; } } diff --git a/src/pose/XRViewerPose.ts b/src/pose/XRViewerPose.ts index b8b9435..9c14178 100644 --- a/src/pose/XRViewerPose.ts +++ b/src/pose/XRViewerPose.ts @@ -5,16 +5,13 @@ * LICENSE file in the root directory of this source tree. */ +import { P_VIEWER_POSE } from '../private.js'; import { XRPose } from './XRPose.js'; import { XRRigidTransform } from '../primitives/XRRigidTransform.js'; import { XRView } from '../views/XRView.js'; -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/xr-viewer-pose', -); - export class XRViewerPose extends XRPose { - [PRIVATE]: { + [P_VIEWER_POSE]: { views: readonly XRView[]; }; @@ -26,12 +23,12 @@ export class XRViewerPose extends XRPose { angularVelocity: DOMPointReadOnly | undefined = undefined, ) { super(transform, emulatedPosition, linearVelocity, angularVelocity); - this[PRIVATE] = { + this[P_VIEWER_POSE] = { views: Object.freeze(views), }; } get views() { - return this[PRIVATE].views; + return this[P_VIEWER_POSE].views; } } diff --git a/src/primitives/XRRigidTransform.ts b/src/primitives/XRRigidTransform.ts index 2274adc..b387322 100644 --- a/src/primitives/XRRigidTransform.ts +++ b/src/primitives/XRRigidTransform.ts @@ -8,13 +8,10 @@ import { mat4, quat, vec3 } from 'gl-matrix'; import { DOMPointReadOnly } from '../utils/DOMPointReadOnly.js'; - -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/xr-rigid-transform', -); +import { P_RIGID_TRANSFORM } from '../private.js'; export class XRRigidTransform { - [PRIVATE]: { + [P_RIGID_TRANSFORM]: { matrix: mat4; position: vec3; orientation: quat; @@ -26,7 +23,7 @@ export class XRRigidTransform { const defaultPosition = vec3.fromValues(0, 0, 0); const defaultOrientation = quat.create(); - this[PRIVATE] = { + this[P_RIGID_TRANSFORM] = { matrix: mat4.create(), position: position ? vec3.fromValues(position.x!, position.y!, position.z!) @@ -50,30 +47,30 @@ export class XRRigidTransform { private updateMatrix(): void { mat4.fromRotationTranslation( - this[PRIVATE].matrix, - this[PRIVATE].orientation, - this[PRIVATE].position, + this[P_RIGID_TRANSFORM].matrix, + this[P_RIGID_TRANSFORM].orientation, + this[P_RIGID_TRANSFORM].position, ); } get matrix(): Float32Array { - return this[PRIVATE].matrix as Float32Array; + return this[P_RIGID_TRANSFORM].matrix as Float32Array; } get position(): DOMPointReadOnly { - const pos = this[PRIVATE].position; + const pos = this[P_RIGID_TRANSFORM].position; return new DOMPointReadOnly(pos[0], pos[1], pos[2], 1); } get orientation(): DOMPointReadOnly { - const ori = this[PRIVATE].orientation; + const ori = this[P_RIGID_TRANSFORM].orientation; return new DOMPointReadOnly(ori[0], ori[1], ori[2], ori[3]); } get inverse(): XRRigidTransform { - if (!this[PRIVATE].inverse) { + if (!this[P_RIGID_TRANSFORM].inverse) { const invMatrix = mat4.create(); - if (!mat4.invert(invMatrix, this[PRIVATE].matrix)) { + if (!mat4.invert(invMatrix, this[P_RIGID_TRANSFORM].matrix)) { throw new Error('Matrix is not invertible.'); } @@ -85,7 +82,7 @@ export class XRRigidTransform { mat4.getRotation(invOrientation, invMatrix); // Creating a new XRRigidTransform for the inverse - this[PRIVATE].inverse = new XRRigidTransform( + this[P_RIGID_TRANSFORM].inverse = new XRRigidTransform( new DOMPointReadOnly(invPosition[0], invPosition[1], invPosition[2], 1), new DOMPointReadOnly( invOrientation[0], @@ -96,9 +93,9 @@ export class XRRigidTransform { ); // Setting the inverse of the inverse to be this transform - this[PRIVATE].inverse[PRIVATE].inverse = this; + this[P_RIGID_TRANSFORM].inverse[P_RIGID_TRANSFORM].inverse = this; } - return this[PRIVATE].inverse; + return this[P_RIGID_TRANSFORM].inverse; } } diff --git a/src/private.ts b/src/private.ts new file mode 100644 index 0000000..3abeebf --- /dev/null +++ b/src/private.ts @@ -0,0 +1,32 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +export const P_ACTION_PLAYER = Symbol('@iwer/action-player'); +export const P_ACTION_RECORDER = Symbol('@iwer/action-recorder'); +export const P_ANCHOR = Symbol('@iwer/xr-anchor'); +export const P_CONTROLLER = Symbol('@iwer/xr-controller'); +export const P_DEVICE = Symbol('@iwer/xr-device'); +export const P_HAND_INPUT = Symbol('@iwer/xr-hand-input'); +export const P_TRACKED_INPUT = Symbol('@iwer/xr-tracked-input'); +export const P_FRAME = Symbol('@iwer/xr-frame'); +export const P_GAMEPAD = Symbol('@iwer/gamepad'); +export const P_SYSTEM = Symbol('@iwer/xr-system'); +export const P_INPUT_SOURCE = Symbol('@iwer/xr-input-source'); +export const P_WEBGL_LAYER = Symbol('@iwer/xr-webgl-layer'); +export const P_MESH = Symbol('@iwer/xr-mesh'); +export const P_PLANE = Symbol('@iwer/xr-plane'); +export const P_JOINT_POSE = Symbol('@iwer/xr-joint-pose'); +export const P_POSE = Symbol('@iwer/xr-pose'); +export const P_VIEWER_POSE = Symbol('@iwer/xr-viewer-pose'); +export const P_RIGID_TRANSFORM = Symbol('@iwer/xr-rigid-transform'); +export const P_RENDER_STATE = Symbol('@iwer/xr-render-state'); +export const P_SESSION = Symbol('@iwer/xr-session'); +export const P_JOINT_SPACE = Symbol('@iwer/xr-joint-space'); +export const P_REF_SPACE = Symbol('@iwer/xr-reference-space'); +export const P_SPACE = Symbol('@iwer/xr-space'); +export const P_VIEW = Symbol('@iwer/xr-view'); +export const P_VIEWPORT = Symbol('@iwer/xr-viewport'); diff --git a/src/session/XRRenderState.ts b/src/session/XRRenderState.ts index 8ad1aae..f4c91e7 100644 --- a/src/session/XRRenderState.ts +++ b/src/session/XRRenderState.ts @@ -5,14 +5,11 @@ * LICENSE file in the root directory of this source tree. */ +import { P_RENDER_STATE } from '../private.js'; import { XRWebGLLayer } from '../layers/XRWebGLLayer.js'; -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/xr-render-state', -); - export class XRRenderState { - [PRIVATE]: { + [P_RENDER_STATE]: { depthNear: number; depthFar: number; inlineVerticalFieldOfView: number | null; @@ -20,7 +17,7 @@ export class XRRenderState { }; constructor(init: Partial = {}, oldState?: XRRenderState) { - this[PRIVATE] = { + this[P_RENDER_STATE] = { depthNear: init.depthNear || oldState?.depthNear || 0.1, depthFar: init.depthFar || oldState?.depthFar || 1000.0, inlineVerticalFieldOfView: @@ -32,19 +29,19 @@ export class XRRenderState { } get depthNear(): number { - return this[PRIVATE].depthNear; + return this[P_RENDER_STATE].depthNear; } get depthFar(): number { - return this[PRIVATE].depthFar; + return this[P_RENDER_STATE].depthFar; } get inlineVerticalFieldOfView(): number | null { - return this[PRIVATE].inlineVerticalFieldOfView; + return this[P_RENDER_STATE].inlineVerticalFieldOfView; } get baseLayer(): XRWebGLLayer | null { - return this[PRIVATE].baseLayer; + return this[P_RENDER_STATE].baseLayer; } } diff --git a/src/session/XRSession.ts b/src/session/XRSession.ts index f54d448..a137c0f 100644 --- a/src/session/XRSession.ts +++ b/src/session/XRSession.ts @@ -6,17 +6,15 @@ */ import { - WebXRFeatures, - PRIVATE as XRDEVICE_PRIVATE, - XRDevice, -} from '../device/XRDevice.js'; -import { - PRIVATE as XRANCHOR_PRIVATE, - XRAnchor, - XRAnchorSet, - XRAnchorUtils, -} from '../anchors/XRAnchor.js'; -import { PRIVATE as XRFRAME_PRIVATE, XRFrame } from '../frameloop/XRFrame.js'; + P_ANCHOR, + P_DEVICE, + P_FRAME, + P_SESSION, + P_SPACE, + P_WEBGL_LAYER, +} from '../private.js'; +import type { WebXRFeature, XRDevice } from '../device/XRDevice.js'; +import { XRAnchor, XRAnchorSet, XRAnchorUtils } from '../anchors/XRAnchor.js'; import { XRInputSource, XRInputSourceArray } from '../input/XRInputSource.js'; import { XRInputSourcesChangeEvent, @@ -33,26 +31,17 @@ import { } from '../events/XRSessionEvent.js'; import { XREye } from '../views/XRView.js'; +import { XRFrame } from '../frameloop/XRFrame.js'; import { XRInputSourceEventHandler } from '../events/XRInputSourceEvent.js'; -import { PRIVATE as XRSPACE_PRIVATE } from '../spaces/XRSpace.js'; -import { PRIVATE as XRWEBGLLAYER_PRIVATE } from '../layers/XRWebGLLayer.js'; import { mat4 } from 'gl-matrix'; -export enum XRVisibilityState { - Visible = 'visible', - VisibleBlurred = 'visible-blurred', - Hidden = 'hidden', -} +export type XRVisibilityState = 'visible' | 'visible-blurred' | 'hidden'; -export enum XRSessionMode { - Inline = 'inline', - ImmersiveVR = 'immersive-vr', - ImmersiveAR = 'immersive-ar', -} +export type XRSessionMode = 'inline' | 'immersive-vr' | 'immersive-ar'; export type XRSessionInit = { - requiredFeatures?: string[]; - optionalFeatures?: string[]; + requiredFeatures?: WebXRFeature[]; + optionalFeatures?: WebXRFeature[]; }; export enum XREnvironmentBlendMode { @@ -77,10 +66,8 @@ type CallbackData = { cancelled: boolean; }; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-session'); - export class XRSession extends EventTarget { - [PRIVATE]: { + [P_SESSION]: { device: XRDevice; enabledFeatures: Array; isSystemKeyboardSupported: boolean; @@ -141,7 +128,7 @@ export class XRSession extends EventTarget { enabledFeatures: string[], ) { super(); - this[PRIVATE] = { + this[P_SESSION] = { device, mode, renderState: new XRRenderState(), @@ -155,10 +142,10 @@ export class XRSession extends EventTarget { [XREye.None]: mat4.create(), }, getProjectionMatrix: (eye: XREye) => { - return this[PRIVATE].projectionMatrices[eye]; + return this[P_SESSION].projectionMatrices[eye]; }, referenceSpaceIsSupported: (referenceSpaceType: XRReferenceSpaceType) => { - if (!this[PRIVATE].enabledFeatures.includes(referenceSpaceType)) { + if (!this[P_SESSION].enabledFeatures.includes(referenceSpaceType)) { return false; } switch (referenceSpaceType) { @@ -168,30 +155,30 @@ export class XRSession extends EventTarget { case XRReferenceSpaceType.LocalFloor: case XRReferenceSpaceType.BoundedFloor: case XRReferenceSpaceType.Unbounded: - return this[PRIVATE].mode != XRSessionMode.Inline; + return this[P_SESSION].mode != 'inline'; } }, frameHandle: 0, frameCallbacks: [], currentFrameCallbacks: null, onDeviceFrame: () => { - if (this[PRIVATE].ended) { + if (this[P_SESSION].ended) { return; } - this[PRIVATE].deviceFrameHandle = globalThis.requestAnimationFrame( - this[PRIVATE].onDeviceFrame, + this[P_SESSION].deviceFrameHandle = globalThis.requestAnimationFrame( + this[P_SESSION].onDeviceFrame, ); - if (this[PRIVATE].pendingRenderState != null) { - this[PRIVATE].renderState = this[PRIVATE].pendingRenderState; - this[PRIVATE].pendingRenderState = null; - this[PRIVATE].device[XRDEVICE_PRIVATE].onBaseLayerSet( - this[PRIVATE].renderState.baseLayer, + if (this[P_SESSION].pendingRenderState != null) { + this[P_SESSION].renderState = this[P_SESSION].pendingRenderState; + this[P_SESSION].pendingRenderState = null; + this[P_SESSION].device[P_DEVICE].onBaseLayerSet( + this[P_SESSION].renderState.baseLayer, ); } - const baseLayer = this[PRIVATE].renderState.baseLayer; + const baseLayer = this[P_SESSION].renderState.baseLayer; if (baseLayer === null) { return; } @@ -226,7 +213,7 @@ export class XRSession extends EventTarget { * prevent rendering artifacts from past frames. It ensures that each new frame starts * with a clean slate. */ - if (this[PRIVATE].mode != XRSessionMode.Inline) { + if (this[P_SESSION].mode != 'inline') { const currentClearColor = context.getParameter( context.COLOR_CLEAR_VALUE, ); @@ -255,45 +242,46 @@ export class XRSession extends EventTarget { } // Calculate projection matrices - const { depthNear, depthFar } = this[PRIVATE].renderState; + const { depthNear, depthFar } = this[P_SESSION].renderState; const { width, height } = canvas; - if (this[PRIVATE].mode !== XRSessionMode.Inline) { + if (this[P_SESSION].mode !== 'inline') { const aspect = - (width * (this[PRIVATE].device.stereoEnabled ? 0.5 : 1.0)) / height; + (width * (this[P_SESSION].device.stereoEnabled ? 0.5 : 1.0)) / + height; mat4.perspective( - this[PRIVATE].projectionMatrices[XREye.Left], - this[PRIVATE].device.fovy, + this[P_SESSION].projectionMatrices[XREye.Left], + this[P_SESSION].device.fovy, aspect, depthNear, depthFar, ); mat4.copy( - this[PRIVATE].projectionMatrices[XREye.Right], - this[PRIVATE].projectionMatrices[XREye.Left], + this[P_SESSION].projectionMatrices[XREye.Right], + this[P_SESSION].projectionMatrices[XREye.Left], ); } else { const aspect = width / height; mat4.perspective( - this[PRIVATE].projectionMatrices[XREye.None], - this[PRIVATE].renderState.inlineVerticalFieldOfView!, + this[P_SESSION].projectionMatrices[XREye.None], + this[P_SESSION].renderState.inlineVerticalFieldOfView!, aspect, depthNear, depthFar, ); } - this[PRIVATE].updateTrackedAnchors(); + this[P_SESSION].updateTrackedAnchors(); const frame = new XRFrame( this, - this[PRIVATE].frameHandle, + this[P_SESSION].frameHandle, true, true, performance.now(), ); - this[PRIVATE].device[XRDEVICE_PRIVATE].onFrameStart(frame); - this[PRIVATE].updateActiveInputSources(); + this[P_SESSION].device[P_DEVICE].onFrameStart(frame); + this[P_SESSION].updateActiveInputSources(); /* * For each entry in callbacks, in order: @@ -303,10 +291,10 @@ export class XRSession extends EventTarget { */ // - Let callbacks be a list of the entries in session’s list of animation frame // callback, in the order in which they were added to the list. - const callbacks = (this[PRIVATE].currentFrameCallbacks = - this[PRIVATE].frameCallbacks); + const callbacks = (this[P_SESSION].currentFrameCallbacks = + this[P_SESSION].frameCallbacks); // - Set session’s list of animation frame callbacks to the empty list. - this[PRIVATE].frameCallbacks = []; + this[P_SESSION].frameCallbacks = []; const rightNow = performance.now(); for (let i = 0; i < callbacks.length; i++) { try { @@ -317,28 +305,27 @@ export class XRSession extends EventTarget { console.error(err); } } - this[PRIVATE].currentFrameCallbacks = null; + this[P_SESSION].currentFrameCallbacks = null; // - Set frame’s active boolean to false. - frame[XRFRAME_PRIVATE].active = false; + frame[P_FRAME].active = false; }, nominalFrameRate: device.internalNominalFrameRate, referenceSpaces: [], inputSourceArray: [], activeInputSources: [], updateActiveInputSources: () => { - const handTrackingOn = this[PRIVATE].enabledFeatures.includes( - WebXRFeatures.HandTracking, - ); - const prevInputs = this[PRIVATE].activeInputSources; - const currInputs = this[PRIVATE].device.inputSources.filter( + const handTrackingOn = + this[P_SESSION].enabledFeatures.includes('hand-tracking'); + const prevInputs = this[P_SESSION].activeInputSources; + const currInputs = this[P_SESSION].device.inputSources.filter( (inputSource) => !inputSource.hand || handTrackingOn, ); const added = currInputs.filter((item) => !prevInputs.includes(item)); const removed = prevInputs.filter((item) => !currInputs.includes(item)); - this[PRIVATE].activeInputSources = currInputs; + this[P_SESSION].activeInputSources = currInputs; if (added.length > 0 || removed.length > 0) { this.dispatchEvent( @@ -355,13 +342,13 @@ export class XRSession extends EventTarget { newAnchors: new Map(), frameTrackedAnchors: new XRAnchorSet(), updateTrackedAnchors: () => { - if (this[PRIVATE].enabledFeatures.includes('anchors')) { - this[PRIVATE].frameTrackedAnchors.clear(); - Array.from(this[PRIVATE].trackedAnchors).forEach((anchor) => { - if (anchor[XRANCHOR_PRIVATE].deleted) { - this[PRIVATE].trackedAnchors.delete(anchor); - if (this[PRIVATE].newAnchors.has(anchor)) { - const { reject } = this[PRIVATE].newAnchors.get(anchor)!; + if (this[P_SESSION].enabledFeatures.includes('anchors')) { + this[P_SESSION].frameTrackedAnchors.clear(); + Array.from(this[P_SESSION].trackedAnchors).forEach((anchor) => { + if (anchor[P_ANCHOR].deleted) { + this[P_SESSION].trackedAnchors.delete(anchor); + if (this[P_SESSION].newAnchors.has(anchor)) { + const { reject } = this[P_SESSION].newAnchors.get(anchor)!; reject( new DOMException( 'Anchor is no longer tracked', @@ -370,11 +357,11 @@ export class XRSession extends EventTarget { ); } } else { - this[PRIVATE].frameTrackedAnchors.add(anchor); - if (this[PRIVATE].newAnchors.has(anchor)) { - const { resolve } = this[PRIVATE].newAnchors.get(anchor)!; + this[P_SESSION].frameTrackedAnchors.add(anchor); + if (this[P_SESSION].newAnchors.has(anchor)) { + const { resolve } = this[P_SESSION].newAnchors.get(anchor)!; resolve(anchor); - this[PRIVATE].newAnchors.delete(anchor); + this[P_SESSION].newAnchors.delete(anchor); } } }); @@ -395,66 +382,65 @@ export class XRSession extends EventTarget { XRAnchorUtils.recoverPersistentAnchorsFromStorage(this); // start the frameloop - this[PRIVATE].onDeviceFrame(); + this[P_SESSION].onDeviceFrame(); } get visibilityState(): XRVisibilityState { - return this[PRIVATE].device.visibilityState; + return this[P_SESSION].device.visibilityState; } get frameRate(): number | undefined { - return this[PRIVATE].nominalFrameRate; + return this[P_SESSION].nominalFrameRate; } get supportedFrameRates(): Float32Array | undefined { - return new Float32Array(this[PRIVATE].device.supportedFrameRates); + return new Float32Array(this[P_SESSION].device.supportedFrameRates); } get renderState(): XRRenderState { - return this[PRIVATE].renderState; + return this[P_SESSION].renderState; } get inputSources(): XRInputSourceArray { // use the same array object - this[PRIVATE].inputSourceArray.length = 0; - if (!this[PRIVATE].ended && this[PRIVATE].mode !== XRSessionMode.Inline) { - this[PRIVATE].inputSourceArray.push(...this[PRIVATE].activeInputSources); + this[P_SESSION].inputSourceArray.length = 0; + if (!this[P_SESSION].ended && this[P_SESSION].mode !== 'inline') { + this[P_SESSION].inputSourceArray.push( + ...this[P_SESSION].activeInputSources, + ); } - return this[PRIVATE].inputSourceArray; + return this[P_SESSION].inputSourceArray; } get enabledFeatures(): Array { - return this[PRIVATE].enabledFeatures; + return this[P_SESSION].enabledFeatures; } get isSystemKeyboardSupported(): boolean { - return this[PRIVATE].isSystemKeyboardSupported; + return this[P_SESSION].isSystemKeyboardSupported; } get environmentBlendMode(): XREnvironmentBlendMode { return ( - this[PRIVATE].device[XRDEVICE_PRIVATE].environmentBlendModes[ - this[PRIVATE].mode + this[P_SESSION].device[P_DEVICE].environmentBlendModes[ + this[P_SESSION].mode ] ?? XREnvironmentBlendMode.Opaque ); } get interactionMode(): XRInteractionMode { - return this[PRIVATE].device[XRDEVICE_PRIVATE].interactionMode; + return this[P_SESSION].device[P_DEVICE].interactionMode; } updateRenderState(state: XRRenderStateInit = {}): void { - if (this[PRIVATE].ended) { + if (this[P_SESSION].ended) { throw new DOMException( 'XRSession has already ended.', 'InvalidStateError', ); } - if ( - state.baseLayer && - state.baseLayer[XRWEBGLLAYER_PRIVATE].session !== this - ) { + if (state.baseLayer && state.baseLayer[P_WEBGL_LAYER].session !== this) { throw new DOMException( 'Base layer was created by a different XRSession', 'InvalidStateError', @@ -463,7 +449,7 @@ export class XRSession extends EventTarget { if ( state.inlineVerticalFieldOfView != null && - this[PRIVATE].mode !== XRSessionMode.Inline + this[P_SESSION].mode !== 'inline' ) { throw new DOMException( 'InlineVerticalFieldOfView must not be set for an immersive session', @@ -474,25 +460,25 @@ export class XRSession extends EventTarget { const compoundStateInit: XRRenderStateInit = { baseLayer: state.baseLayer || - this[PRIVATE].pendingRenderState?.baseLayer || + this[P_SESSION].pendingRenderState?.baseLayer || undefined, depthFar: state.depthFar || - this[PRIVATE].pendingRenderState?.depthFar || + this[P_SESSION].pendingRenderState?.depthFar || undefined, depthNear: state.depthNear || - this[PRIVATE].pendingRenderState?.depthNear || + this[P_SESSION].pendingRenderState?.depthNear || undefined, inlineVerticalFieldOfView: state.inlineVerticalFieldOfView || - this[PRIVATE].pendingRenderState?.inlineVerticalFieldOfView || + this[P_SESSION].pendingRenderState?.inlineVerticalFieldOfView || undefined, }; - this[PRIVATE].pendingRenderState = new XRRenderState( + this[P_SESSION].pendingRenderState = new XRRenderState( compoundStateInit, - this[PRIVATE].renderState, + this[P_SESSION].renderState, ); } @@ -500,11 +486,11 @@ export class XRSession extends EventTarget { // display frame rate of the device will be executed async updateTargetFrameRate(rate: number): Promise { return new Promise((resolve, reject) => { - if (this[PRIVATE].ended) { + if (this[P_SESSION].ended) { reject( new DOMException('XRSession has already ended.', 'InvalidStateError'), ); - } else if (!this[PRIVATE].device.supportedFrameRates.includes(rate)) { + } else if (!this[P_SESSION].device.supportedFrameRates.includes(rate)) { reject( new DOMException( 'Requested frame rate not supported.', @@ -512,12 +498,12 @@ export class XRSession extends EventTarget { ), ); } else { - if (this[PRIVATE].nominalFrameRate === rate) { + if (this[P_SESSION].nominalFrameRate === rate) { console.log( `Requested frame rate is the same as the current nominal frame rate, no update made`, ); } else { - this[PRIVATE].nominalFrameRate = rate; + this[P_SESSION].nominalFrameRate = rate; this.dispatchEvent( new XRSessionEvent('frameratechange', { session: this }), ); @@ -533,8 +519,8 @@ export class XRSession extends EventTarget { ): Promise { return new Promise((resolve, reject) => { if ( - this[PRIVATE].ended || - !this[PRIVATE].referenceSpaceIsSupported(type) + this[P_SESSION].ended || + !this[P_SESSION].referenceSpaceIsSupported(type) ) { reject( new DOMException( @@ -547,14 +533,14 @@ export class XRSession extends EventTarget { let referenceSpace: XRReferenceSpace; switch (type) { case XRReferenceSpaceType.Viewer: - referenceSpace = this[PRIVATE].device.viewerSpace; + referenceSpace = this[P_SESSION].device.viewerSpace; break; case XRReferenceSpaceType.Local: // creating an XRReferenceSpace with the current headset transform in global space referenceSpace = new XRReferenceSpace( type, - this[PRIVATE].device[XRDEVICE_PRIVATE].globalSpace, - this[PRIVATE].device.viewerSpace[XRSPACE_PRIVATE].offsetMatrix, + this[P_SESSION].device[P_DEVICE].globalSpace, + this[P_SESSION].device.viewerSpace[P_SPACE].offsetMatrix, ); break; case XRReferenceSpaceType.LocalFloor: @@ -563,21 +549,21 @@ export class XRSession extends EventTarget { // TO-DO: add boundary geometry for bounded-floor referenceSpace = new XRReferenceSpace( type, - this[PRIVATE].device[XRDEVICE_PRIVATE].globalSpace, + this[P_SESSION].device[P_DEVICE].globalSpace, ); break; } - this[PRIVATE].referenceSpaces.push(referenceSpace); + this[P_SESSION].referenceSpaces.push(referenceSpace); resolve(referenceSpace); }); } requestAnimationFrame(callback: XRFrameRequestCallback): number { - if (this[PRIVATE].ended) { + if (this[P_SESSION].ended) { return 0; } - const frameHandle = ++this[PRIVATE].frameHandle; - this[PRIVATE].frameCallbacks.push({ + const frameHandle = ++this[P_SESSION].frameHandle; + this[P_SESSION].frameCallbacks.push({ handle: frameHandle, callback, cancelled: false, @@ -587,7 +573,7 @@ export class XRSession extends EventTarget { cancelAnimationFrame(handle: number): void { // Remove the callback with that handle from the queue - let callbacks: CallbackData[] | null = this[PRIVATE].frameCallbacks; + let callbacks: CallbackData[] | null = this[P_SESSION].frameCallbacks; let index = callbacks.findIndex((d) => d && d.handle === handle); if (index > -1) { callbacks[index].cancelled = true; @@ -595,7 +581,7 @@ export class XRSession extends EventTarget { } // If cancelAnimationFrame is called from within a frame callback, also check // the remaining callbacks for the current frame: - callbacks = this[PRIVATE].currentFrameCallbacks; + callbacks = this[P_SESSION].currentFrameCallbacks; if (callbacks) { index = callbacks.findIndex((d) => d && d.handle === handle); if (index > -1) { @@ -607,13 +593,13 @@ export class XRSession extends EventTarget { async end(): Promise { return new Promise((resolve, reject) => { - if (this[PRIVATE].ended || this[PRIVATE].deviceFrameHandle === null) { + if (this[P_SESSION].ended || this[P_SESSION].deviceFrameHandle === null) { reject( new DOMException('XRSession has already ended.', 'InvalidStateError'), ); } else { - globalThis.cancelAnimationFrame(this[PRIVATE].deviceFrameHandle!); - this[PRIVATE].device[XRDEVICE_PRIVATE].onSessionEnd(); + globalThis.cancelAnimationFrame(this[P_SESSION].deviceFrameHandle!); + this[P_SESSION].device[P_DEVICE].onSessionEnd(); this.dispatchEvent(new XRSessionEvent('end', { session: this })); resolve(); } @@ -622,25 +608,25 @@ export class XRSession extends EventTarget { // anchors get persistentAnchors(): Readonly { - return Array.from(this[PRIVATE].persistentAnchors.keys()); + return Array.from(this[P_SESSION].persistentAnchors.keys()); } restorePersistentAnchor(uuid: string): Promise { return new Promise((resolve, reject) => { - if (!this[PRIVATE].persistentAnchors.has(uuid)) { + if (!this[P_SESSION].persistentAnchors.has(uuid)) { reject( new DOMException( `Persistent anchor with uuid ${uuid} not found.`, 'InvalidStateError', ), ); - } else if (this[PRIVATE].ended) { + } else if (this[P_SESSION].ended) { reject( new DOMException('XRSession has already ended.', 'InvalidStateError'), ); } else { - const anchor = this[PRIVATE].persistentAnchors.get(uuid)!; - if (this[PRIVATE].newAnchors.has(anchor)) { + const anchor = this[P_SESSION].persistentAnchors.get(uuid)!; + if (this[P_SESSION].newAnchors.has(anchor)) { reject( new DOMException( `Multiple concurrent attempts detected to restore the anchor with UUID: ${uuid}.`, @@ -648,8 +634,8 @@ export class XRSession extends EventTarget { ), ); } else { - this[PRIVATE].trackedAnchors.add(anchor); - this[PRIVATE].newAnchors.set(anchor, { resolve, reject }); + this[P_SESSION].trackedAnchors.add(anchor); + this[P_SESSION].newAnchors.set(anchor, { resolve, reject }); } } }); @@ -657,7 +643,7 @@ export class XRSession extends EventTarget { deletePersistentAnchor(uuid: string): Promise { return new Promise((resolve, reject) => { - if (!this[PRIVATE].persistentAnchors.has(uuid)) { + if (!this[P_SESSION].persistentAnchors.has(uuid)) { reject( new DOMException( `Persistent anchor with uuid ${uuid} not found.`, @@ -665,8 +651,8 @@ export class XRSession extends EventTarget { ), ); } else { - const anchor = this[PRIVATE].persistentAnchors.get(uuid)!; - this[PRIVATE].persistentAnchors.delete(uuid); + const anchor = this[P_SESSION].persistentAnchors.get(uuid)!; + this[P_SESSION].persistentAnchors.delete(uuid); anchor.delete(); resolve(undefined); } @@ -675,167 +661,167 @@ export class XRSession extends EventTarget { // events get onend() { - return this[PRIVATE].onend ?? (() => {}); + return this[P_SESSION].onend ?? (() => {}); } set onend(callback: XRSessionEventHandler) { - if (this[PRIVATE].onend) { - this.removeEventListener('end', this[PRIVATE].onend as EventListener); + if (this[P_SESSION].onend) { + this.removeEventListener('end', this[P_SESSION].onend as EventListener); } - this[PRIVATE].onend = callback; + this[P_SESSION].onend = callback; if (callback) { this.addEventListener('end', callback as EventListener); } } get oninputsourceschange() { - return this[PRIVATE].oninputsourceschange ?? (() => {}); + return this[P_SESSION].oninputsourceschange ?? (() => {}); } set oninputsourceschange(callback: XRInputSourcesChangeEventHandler) { - if (this[PRIVATE].oninputsourceschange) { + if (this[P_SESSION].oninputsourceschange) { this.removeEventListener( 'inputsourceschange', - this[PRIVATE].oninputsourceschange as EventListener, + this[P_SESSION].oninputsourceschange as EventListener, ); } - this[PRIVATE].oninputsourceschange = callback; + this[P_SESSION].oninputsourceschange = callback; if (callback) { this.addEventListener('inputsourceschange', callback as EventListener); } } get onselect() { - return this[PRIVATE].onselect ?? (() => {}); + return this[P_SESSION].onselect ?? (() => {}); } set onselect(callback: XRInputSourceEventHandler) { - if (this[PRIVATE].onselect) { + if (this[P_SESSION].onselect) { this.removeEventListener( 'select', - this[PRIVATE].onselect as EventListener, + this[P_SESSION].onselect as EventListener, ); } - this[PRIVATE].onselect = callback; + this[P_SESSION].onselect = callback; if (callback) { this.addEventListener('select', callback as EventListener); } } get onselectstart() { - return this[PRIVATE].onselectstart ?? (() => {}); + return this[P_SESSION].onselectstart ?? (() => {}); } set onselectstart(callback: XRInputSourceEventHandler) { - if (this[PRIVATE].onselectstart) { + if (this[P_SESSION].onselectstart) { this.removeEventListener( 'selectstart', - this[PRIVATE].onselectstart as EventListener, + this[P_SESSION].onselectstart as EventListener, ); } - this[PRIVATE].onselectstart = callback; + this[P_SESSION].onselectstart = callback; if (callback) { this.addEventListener('selectstart', callback as EventListener); } } get onselectend() { - return this[PRIVATE].onselectend ?? (() => {}); + return this[P_SESSION].onselectend ?? (() => {}); } set onselectend(callback: XRInputSourceEventHandler) { - if (this[PRIVATE].onselectend) { + if (this[P_SESSION].onselectend) { this.removeEventListener( 'selectend', - this[PRIVATE].onselectend as EventListener, + this[P_SESSION].onselectend as EventListener, ); } - this[PRIVATE].onselectend = callback; + this[P_SESSION].onselectend = callback; if (callback) { this.addEventListener('selectend', callback as EventListener); } } get onsqueeze() { - return this[PRIVATE].onsqueeze ?? (() => {}); + return this[P_SESSION].onsqueeze ?? (() => {}); } set onsqueeze(callback: XRInputSourceEventHandler) { - if (this[PRIVATE].onsqueeze) { + if (this[P_SESSION].onsqueeze) { this.removeEventListener( 'squeeze', - this[PRIVATE].onsqueeze as EventListener, + this[P_SESSION].onsqueeze as EventListener, ); } - this[PRIVATE].onsqueeze = callback; + this[P_SESSION].onsqueeze = callback; if (callback) { this.addEventListener('squeeze', callback as EventListener); } } get onsqueezestart() { - return this[PRIVATE].onsqueezestart ?? (() => {}); + return this[P_SESSION].onsqueezestart ?? (() => {}); } set onsqueezestart(callback: XRInputSourceEventHandler) { - if (this[PRIVATE].onsqueezestart) { + if (this[P_SESSION].onsqueezestart) { this.removeEventListener( 'squeezestart', - this[PRIVATE].onsqueezestart as EventListener, + this[P_SESSION].onsqueezestart as EventListener, ); } - this[PRIVATE].onsqueezestart = callback; + this[P_SESSION].onsqueezestart = callback; if (callback) { this.addEventListener('squeezestart', callback as EventListener); } } get onsqueezeend() { - return this[PRIVATE].onsqueezeend ?? (() => {}); + return this[P_SESSION].onsqueezeend ?? (() => {}); } set onsqueezeend(callback: XRInputSourceEventHandler) { - if (this[PRIVATE].onsqueezeend) { + if (this[P_SESSION].onsqueezeend) { this.removeEventListener( 'squeezeend', - this[PRIVATE].onsqueezeend as EventListener, + this[P_SESSION].onsqueezeend as EventListener, ); } - this[PRIVATE].onsqueezeend = callback; + this[P_SESSION].onsqueezeend = callback; if (callback) { this.addEventListener('squeezeend', callback as EventListener); } } get onvisibilitychange() { - return this[PRIVATE].onvisibilitychange ?? (() => {}); + return this[P_SESSION].onvisibilitychange ?? (() => {}); } set onvisibilitychange(callback: XRSessionEventHandler) { - if (this[PRIVATE].onvisibilitychange) { + if (this[P_SESSION].onvisibilitychange) { this.removeEventListener( 'visibilitychange', - this[PRIVATE].onvisibilitychange as EventListener, + this[P_SESSION].onvisibilitychange as EventListener, ); } - this[PRIVATE].onvisibilitychange = callback; + this[P_SESSION].onvisibilitychange = callback; if (callback) { this.addEventListener('visibilitychange', callback as EventListener); } } get onframeratechange() { - return this[PRIVATE].onframeratechange ?? (() => {}); + return this[P_SESSION].onframeratechange ?? (() => {}); } set onframeratechange(callback: XRSessionEventHandler) { - if (this[PRIVATE].onframeratechange) { + if (this[P_SESSION].onframeratechange) { this.removeEventListener( 'frameratechange', - this[PRIVATE].onframeratechange as EventListener, + this[P_SESSION].onframeratechange as EventListener, ); } - this[PRIVATE].onframeratechange = callback; + this[P_SESSION].onframeratechange = callback; if (callback) { this.addEventListener('frameratechange', callback as EventListener); } diff --git a/src/spaces/XRJointSpace.ts b/src/spaces/XRJointSpace.ts index 8e733c4..248a5fb 100644 --- a/src/spaces/XRJointSpace.ts +++ b/src/spaces/XRJointSpace.ts @@ -5,16 +5,13 @@ * LICENSE file in the root directory of this source tree. */ +import { P_JOINT_SPACE } from '../private.js'; import { XRHandJoint } from '../input/XRHand.js'; import { XRSpace } from './XRSpace.js'; import { mat4 } from 'gl-matrix'; -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/xr-joint-space', -); - export class XRJointSpace extends XRSpace { - [PRIVATE]: { + [P_JOINT_SPACE]: { jointName: XRHandJoint; radius: number; }; @@ -25,10 +22,10 @@ export class XRJointSpace extends XRSpace { offsetMatrix?: mat4, ) { super(parentSpace, offsetMatrix); - this[PRIVATE] = { jointName, radius: 0 }; + this[P_JOINT_SPACE] = { jointName, radius: 0 }; } get jointName() { - return this[PRIVATE].jointName; + return this[P_JOINT_SPACE].jointName; } } diff --git a/src/spaces/XRReferenceSpace.ts b/src/spaces/XRReferenceSpace.ts index 3c56c40..ea9b4eb 100644 --- a/src/spaces/XRReferenceSpace.ts +++ b/src/spaces/XRReferenceSpace.ts @@ -5,14 +5,11 @@ * LICENSE file in the root directory of this source tree. */ +import { P_REF_SPACE } from '../private.js'; import { XRReferenceSpaceEventHandler } from '../events/XRReferenceSpaceEvent.js'; import { XRSpace } from './XRSpace.js'; import { mat4 } from 'gl-matrix'; -export const PRIVATE = Symbol( - '@immersive-web-emulation-runtime/xr-reference-space', -); - export enum XRReferenceSpaceType { Viewer = 'viewer', Local = 'local', @@ -22,7 +19,7 @@ export enum XRReferenceSpaceType { } export class XRReferenceSpace extends XRSpace { - [PRIVATE]: { + [P_REF_SPACE]: { type: XRReferenceSpaceType; onreset: XRReferenceSpaceEventHandler | null; } = { @@ -36,18 +33,21 @@ export class XRReferenceSpace extends XRSpace { offsetMatrix?: mat4, ) { super(parentSpace, offsetMatrix); - this[PRIVATE].type = type; + this[P_REF_SPACE].type = type; } get onreset(): XRReferenceSpaceEventHandler { - return this[PRIVATE].onreset ?? (() => {}); + return this[P_REF_SPACE].onreset ?? (() => {}); } set onreset(callback: XRReferenceSpaceEventHandler) { - if (this[PRIVATE].onreset) { - this.removeEventListener('reset', this[PRIVATE].onreset as EventListener); + if (this[P_REF_SPACE].onreset) { + this.removeEventListener( + 'reset', + this[P_REF_SPACE].onreset as EventListener, + ); } - this[PRIVATE].onreset = callback; + this[P_REF_SPACE].onreset = callback; if (callback) { this.addEventListener('reset', callback as EventListener); } @@ -57,6 +57,6 @@ export class XRReferenceSpace extends XRSpace { getOffsetReferenceSpace(originOffset: mat4): XRReferenceSpace { // Create a new XRReferenceSpace with the originOffset as its offsetMatrix // The new space's parent is set to 'this' (the current XRReferenceSpace) - return new XRReferenceSpace(this[PRIVATE].type, this, originOffset); + return new XRReferenceSpace(this[P_REF_SPACE].type, this, originOffset); } } diff --git a/src/spaces/XRSpace.ts b/src/spaces/XRSpace.ts index 4b7a51e..19d1053 100644 --- a/src/spaces/XRSpace.ts +++ b/src/spaces/XRSpace.ts @@ -7,10 +7,10 @@ import { mat4, quat, vec3 } from 'gl-matrix'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-space'); +import { P_SPACE } from '../private.js'; export class XRSpace extends EventTarget { - [PRIVATE]: { + [P_SPACE]: { parentSpace: XRSpace | undefined; offsetMatrix: mat4; emulated: boolean; @@ -18,7 +18,7 @@ export class XRSpace extends EventTarget { constructor(parentSpace?: XRSpace, offsetMatrix?: mat4) { super(); - this[PRIVATE] = { + this[P_SPACE] = { parentSpace, offsetMatrix: offsetMatrix ? mat4.clone(offsetMatrix) : mat4.create(), emulated: true, @@ -35,13 +35,13 @@ export class GlobalSpace extends XRSpace { export class XRSpaceUtils { // Update the position component of the offsetMatrix of a given XRSpace static updateOffsetPosition(space: XRSpace, position: vec3): void { - const offsetMatrix = space[PRIVATE].offsetMatrix; + const offsetMatrix = space[P_SPACE].offsetMatrix; mat4.fromTranslation(offsetMatrix, position); } // Update the rotation component of the offsetMatrix of a given XRSpace using a quaternion static updateOffsetQuaternion(space: XRSpace, quaternion: quat): void { - const offsetMatrix = space[PRIVATE].offsetMatrix; + const offsetMatrix = space[P_SPACE].offsetMatrix; const translation = vec3.create(); mat4.getTranslation(translation, offsetMatrix); mat4.fromRotationTranslation(offsetMatrix, quaternion, translation); @@ -49,7 +49,7 @@ export class XRSpaceUtils { // Update the offsetMatrix of a given XRSpace directly static updateOffsetMatrix(space: XRSpace, matrix: mat4): void { - const offsetMatrix = space[PRIVATE].offsetMatrix; + const offsetMatrix = space[P_SPACE].offsetMatrix; mat4.copy(offsetMatrix, matrix); } @@ -58,11 +58,11 @@ export class XRSpaceUtils { space: XRSpace, globalOffset: mat4 = mat4.create(), ): mat4 { - const parentOffset = space[PRIVATE].parentSpace - ? XRSpaceUtils.calculateGlobalOffsetMatrix(space[PRIVATE].parentSpace) + const parentOffset = space[P_SPACE].parentSpace + ? XRSpaceUtils.calculateGlobalOffsetMatrix(space[P_SPACE].parentSpace) : mat4.create(); // Identity matrix for GlobalSpace - mat4.multiply(globalOffset, parentOffset, space[PRIVATE].offsetMatrix); + mat4.multiply(globalOffset, parentOffset, space[P_SPACE].offsetMatrix); return globalOffset; } } diff --git a/src/views/XRView.ts b/src/views/XRView.ts index 1bbba4b..b5a9cb5 100644 --- a/src/views/XRView.ts +++ b/src/views/XRView.ts @@ -5,11 +5,10 @@ * LICENSE file in the root directory of this source tree. */ +import { P_VIEW } from '../private.js'; import { XRRigidTransform } from '../primitives/XRRigidTransform.js'; import { XRSession } from '../session/XRSession.js'; -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-view'); - export enum XREye { None = 'none', Left = 'left', @@ -17,7 +16,7 @@ export enum XREye { } export class XRView { - [PRIVATE]: { + [P_VIEW]: { eye: XREye; projectionMatrix: Float32Array; transform: XRRigidTransform; @@ -32,7 +31,7 @@ export class XRView { transform: XRRigidTransform, session: XRSession, ) { - this[PRIVATE] = { + this[P_VIEW] = { eye, projectionMatrix, transform, @@ -43,19 +42,19 @@ export class XRView { } get eye(): XREye { - return this[PRIVATE].eye; + return this[P_VIEW].eye; } get projectionMatrix(): Float32Array { - return this[PRIVATE].projectionMatrix; + return this[P_VIEW].projectionMatrix; } get transform(): XRRigidTransform { - return this[PRIVATE].transform; + return this[P_VIEW].transform; } get recommendedViewportScale(): number | null { - return this[PRIVATE].recommendedViewportScale; + return this[P_VIEW].recommendedViewportScale; } requestViewportScale(scale: number | null): void { @@ -63,6 +62,6 @@ export class XRView { console.warn('Invalid scale value. Scale must be > 0 and <= 1.'); return; } - this[PRIVATE].requestedViewportScale = scale; + this[P_VIEW].requestedViewportScale = scale; } } diff --git a/src/views/XRViewport.ts b/src/views/XRViewport.ts index 61777c8..e5159b5 100644 --- a/src/views/XRViewport.ts +++ b/src/views/XRViewport.ts @@ -5,10 +5,10 @@ * LICENSE file in the root directory of this source tree. */ -export const PRIVATE = Symbol('@immersive-web-emulation-runtime/xr-viewport'); +import { P_VIEWPORT } from '../private.js'; export class XRViewport { - [PRIVATE]: { + [P_VIEWPORT]: { x: number; y: number; width: number; @@ -16,22 +16,22 @@ export class XRViewport { }; constructor(x: number, y: number, width: number, height: number) { - this[PRIVATE] = { x, y, width, height }; + this[P_VIEWPORT] = { x, y, width, height }; } get x(): number { - return this[PRIVATE].x; + return this[P_VIEWPORT].x; } get y(): number { - return this[PRIVATE].y; + return this[P_VIEWPORT].y; } get width(): number { - return this[PRIVATE].width; + return this[P_VIEWPORT].width; } get height(): number { - return this[PRIVATE].height; + return this[P_VIEWPORT].height; } } diff --git a/tests/frameloop/XRFrame.test.ts b/tests/frameloop/XRFrame.test.ts index adaace5..ae1ee88 100644 --- a/tests/frameloop/XRFrame.test.ts +++ b/tests/frameloop/XRFrame.test.ts @@ -9,12 +9,12 @@ import { XRReferenceSpace, XRReferenceSpaceType, } from '../../src/spaces/XRReferenceSpace'; -import { XRSession, XRSessionMode } from '../../src/session/XRSession'; import { XRDevice } from '../../src/device/XRDevice'; import { XRFrame } from '../../src/frameloop/XRFrame'; import { XRPose } from '../../src/pose/XRPose'; import { XRRenderStateInit } from '../../src/session/XRRenderState'; +import { XRSession } from '../../src/session/XRSession'; import { XRSpace } from '../../src/spaces/XRSpace'; import { XRSystem } from '../../src/initialization/XRSystem'; import { XRViewerPose } from '../../src/pose/XRViewerPose'; @@ -60,7 +60,7 @@ describe('XRFrame', () => { }; xrDevice = new XRDevice(metaQuest3); xrSystem = new XRSystem(xrDevice); - xrSession = await xrSystem.requestSession(XRSessionMode.ImmersiveVR); + xrSession = await xrSystem.requestSession('immersive-vr'); refSpace = await xrSession.requestReferenceSpace( XRReferenceSpaceType.Local, ); diff --git a/tests/initialization/XRSystem.test.ts b/tests/initialization/XRSystem.test.ts index 8588b75..8560f2b 100644 --- a/tests/initialization/XRSystem.test.ts +++ b/tests/initialization/XRSystem.test.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import { WebXRFeatures, XRDevice } from '../../src/device/XRDevice'; +import { XRDevice, XRDeviceConfig } from '../../src/device/XRDevice'; import { XREnvironmentBlendMode, XRInteractionMode, @@ -18,42 +18,33 @@ import { metaQuestTouchPlus } from '../../src/device/configs/controller/meta'; describe('XRSystem', () => { let xrDevice: XRDevice; let xrSystem: XRSystem; - const arvrDeviceConfig = { + const arvrDeviceConfig: XRDeviceConfig = { name: 'AR VR Device', controllerConfig: metaQuestTouchPlus, - supportedSessionModes: [ - XRSessionMode.Inline, - XRSessionMode.ImmersiveVR, - XRSessionMode.ImmersiveAR, - ], - supportedFeatures: [ - WebXRFeatures.Viewer, - WebXRFeatures.Local, - WebXRFeatures.LocalFloor, - WebXRFeatures.HandTracking, - ], + supportedSessionModes: ['inline', 'immersive-vr', 'immersive-ar'], + supportedFeatures: ['viewer', 'local', 'local-floor', 'hand-tracking'], supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, environmentBlendModes: { - [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, - [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + 'immersive-vr': XREnvironmentBlendMode.Opaque, + 'immersive-ar': XREnvironmentBlendMode.AlphaBlend, }, interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Test user agent', }; - const vrOnlyDeviceConfig = { + const vrOnlyDeviceConfig: XRDeviceConfig = { name: 'VR Device', controllerConfig: metaQuestTouchPlus, - supportedSessionModes: [XRSessionMode.Inline, XRSessionMode.ImmersiveVR], + supportedSessionModes: ['inline', 'immersive-vr'], supportedFeatures: arvrDeviceConfig.supportedFeatures, supportedFrameRates: arvrDeviceConfig.supportedFrameRates, isSystemKeyboardSupported: true, internalNominalFrameRate: 90, environmentBlendModes: { - [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, - [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + 'immersive-vr': XREnvironmentBlendMode.Opaque, + 'immersive-ar': XREnvironmentBlendMode.AlphaBlend, }, interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Test user agent', @@ -111,7 +102,7 @@ describe('XRSystem', () => { xrSystem = new XRSystem(xrDevice); await expect( xrSystem.requestSession('immersive-ar' as XRSessionMode, { - requiredFeatures: [WebXRFeatures.Anchors], + requiredFeatures: ['anchors'], }), ).rejects.toThrow(); }); @@ -123,8 +114,8 @@ describe('XRSystem', () => { 'immersive-ar' as XRSessionMode, ); expect(session).not.toBeNull(); - expect(session.enabledFeatures).toContain(WebXRFeatures.Viewer); - expect(session.enabledFeatures).toContain(WebXRFeatures.Local); + expect(session.enabledFeatures).toContain('viewer'); + expect(session.enabledFeatures).toContain('local'); }); test('requestSession should return XRSession with correct list of enabled features', async () => { @@ -133,12 +124,12 @@ describe('XRSystem', () => { const session = await xrSystem.requestSession( 'immersive-ar' as XRSessionMode, { - requiredFeatures: [WebXRFeatures.HandTracking], - optionalFeatures: [WebXRFeatures.Anchors], + requiredFeatures: ['hand-tracking'], + optionalFeatures: ['anchors'], }, ); expect(session).not.toBeNull(); - expect(session.enabledFeatures).toContain(WebXRFeatures.HandTracking); - expect(session.enabledFeatures).not.toContain(WebXRFeatures.Anchors); + expect(session.enabledFeatures).toContain('hand-tracking'); + expect(session.enabledFeatures).not.toContain('anchors'); }); }); diff --git a/tests/session/XRSession.test.ts b/tests/session/XRSession.test.ts index bb3de15..0421a08 100644 --- a/tests/session/XRSession.test.ts +++ b/tests/session/XRSession.test.ts @@ -5,17 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import { - WebXRFeatures, - XRDevice, - XRDeviceConfig, -} from '../../src/device/XRDevice'; +import { XRDevice, XRDeviceConfig } from '../../src/device/XRDevice'; import { XREnvironmentBlendMode, XRInteractionMode, XRSession, - XRSessionMode, - XRVisibilityState, } from '../../src/session/XRSession'; import { XRReferenceSpace, @@ -73,29 +67,21 @@ describe('XRSession', () => { const testDeviceConfig: XRDeviceConfig = { name: 'Test Device', controllerConfig: metaQuestTouchPlus, - supportedSessionModes: [ - XRSessionMode.Inline, - XRSessionMode.ImmersiveVR, - XRSessionMode.ImmersiveAR, - ], - supportedFeatures: [ - WebXRFeatures.Viewer, - WebXRFeatures.Local, - WebXRFeatures.LocalFloor, - ], + supportedSessionModes: ['inline', 'immersive-vr', 'immersive-ar'], + supportedFeatures: ['viewer', 'local', 'local-floor'], supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, environmentBlendModes: { - [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, - [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + 'immersive-vr': XREnvironmentBlendMode.Opaque, + 'immersive-ar': XREnvironmentBlendMode.AlphaBlend, }, interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Test user agent', }; xrDevice = new XRDevice(testDeviceConfig); xrSystem = new XRSystem(xrDevice); - xrSession = await xrSystem.requestSession(XRSessionMode.ImmersiveVR); + xrSession = await xrSystem.requestSession('immersive-vr'); xrRenderStateInit = {}; xrRenderStateInit.baseLayer = new XRWebGLLayer( xrSession, @@ -185,7 +171,7 @@ describe('XRSession', () => { test('Inline XRSession.requestReferenceSpace should reject except for viewer', async () => { xrSession.end(); - const inlineSession = await xrSystem.requestSession(XRSessionMode.Inline); + const inlineSession = await xrSystem.requestSession('inline'); expect( await inlineSession.requestReferenceSpace(XRReferenceSpaceType.Viewer), ).toBeDefined(); @@ -205,7 +191,7 @@ describe('XRSession', () => { test('Inline XRSession.requestReferenceSpace should reject except for viewer', async () => { xrSession.end(); - const inlineSession = await xrSystem.requestSession(XRSessionMode.Inline); + const inlineSession = await xrSystem.requestSession('inline'); expect( await inlineSession.requestReferenceSpace(XRReferenceSpaceType.Viewer), ).toBeDefined(); @@ -243,8 +229,8 @@ describe('XRSession', () => { ).rejects.toThrow(); xrSession.end(); // should return if local-floor is requested - xrSession = await xrSystem.requestSession(XRSessionMode.ImmersiveVR, { - requiredFeatures: [WebXRFeatures.LocalFloor], + xrSession = await xrSystem.requestSession('immersive-vr', { + requiredFeatures: ['local-floor'], }); expect( await xrSession.requestReferenceSpace(XRReferenceSpaceType.LocalFloor), @@ -269,10 +255,10 @@ describe('XRSession', () => { test('Visibility change on XRSession will fire the visibilitychange XRSessionEvent', async () => { const mockCallback = jest.fn().mockImplementation(() => { - expect(xrSession.visibilityState).toBe(XRVisibilityState.VisibleBlurred); + expect(xrSession.visibilityState).toBe('visible-blurred'); }); xrSession.addEventListener('visibilitychange', mockCallback); - xrDevice.updateVisibilityState(XRVisibilityState.VisibleBlurred); + xrDevice.updateVisibilityState('visible-blurred'); const onFrame = (_time: number, _frame: XRFrame) => { xrSession.end(); }; @@ -354,7 +340,7 @@ describe('XRSession', () => { test('XRSession.environmentBlendMode returns the correct value', async () => { expect(xrSession.environmentBlendMode).toBe(XREnvironmentBlendMode.Opaque); xrSession.end(); - xrSession = await xrSystem.requestSession(XRSessionMode.ImmersiveAR); + xrSession = await xrSystem.requestSession('immersive-ar'); expect(xrSession.environmentBlendMode).toBe( XREnvironmentBlendMode.AlphaBlend, ); diff --git a/tests/spaces/XRReferenceSpace.test.ts b/tests/spaces/XRReferenceSpace.test.ts index b29eb31..c0d5748 100644 --- a/tests/spaces/XRReferenceSpace.test.ts +++ b/tests/spaces/XRReferenceSpace.test.ts @@ -5,13 +5,9 @@ * LICENSE file in the root directory of this source tree. */ +import { GlobalSpace, XRSpaceUtils } from '../../src/spaces/XRSpace'; +import { P_REF_SPACE, P_SPACE } from '../../src/private'; import { - GlobalSpace, - PRIVATE as XRSPACE_PRIVATE, - XRSpaceUtils, -} from '../../src/spaces/XRSpace'; -import { - PRIVATE, XRReferenceSpace, XRReferenceSpaceType, } from '../../src/spaces/XRReferenceSpace'; @@ -31,8 +27,8 @@ describe('XRReferenceSpace', () => { test('XRReferenceSpace should initialize correctly', () => { expect(xrReferenceSpace).toBeDefined(); - expect(xrReferenceSpace[PRIVATE].type).toBe(XRReferenceSpaceType.Local); - expect(xrReferenceSpace[XRSPACE_PRIVATE].parentSpace).toBe(globalSpace); + expect(xrReferenceSpace[P_REF_SPACE].type).toBe(XRReferenceSpaceType.Local); + expect(xrReferenceSpace[P_SPACE].parentSpace).toBe(globalSpace); }); test('getOffsetReferenceSpace should create a new XRReferenceSpace with correct offset', () => { @@ -42,9 +38,9 @@ describe('XRReferenceSpace', () => { const offsetSpace = xrReferenceSpace.getOffsetReferenceSpace(offsetMatrix); expect(offsetSpace).toBeInstanceOf(XRReferenceSpace); - expect(offsetSpace[XRSPACE_PRIVATE].parentSpace).toBe(xrReferenceSpace); + expect(offsetSpace[P_SPACE].parentSpace).toBe(xrReferenceSpace); expect( - mat4.equals(offsetSpace[XRSPACE_PRIVATE].offsetMatrix, offsetMatrix), + mat4.equals(offsetSpace[P_SPACE].offsetMatrix, offsetMatrix), ).toBeTruthy(); }); diff --git a/tests/spaces/XRSpace.test.ts b/tests/spaces/XRSpace.test.ts index 4581a54..01be445 100644 --- a/tests/spaces/XRSpace.test.ts +++ b/tests/spaces/XRSpace.test.ts @@ -5,14 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import { - GlobalSpace, - PRIVATE, - XRSpace, - XRSpaceUtils, -} from '../../src/spaces/XRSpace'; +import { GlobalSpace, XRSpace, XRSpaceUtils } from '../../src/spaces/XRSpace'; import { mat4, quat, vec3 } from 'gl-matrix'; +import { P_SPACE } from '../../src/private'; + describe('XRSpace and XRSpaceUtils', () => { let globalSpace: GlobalSpace; let xrSpace: XRSpace; @@ -24,15 +21,15 @@ describe('XRSpace and XRSpaceUtils', () => { test('XRSpace should initialize with default values', () => { expect( - mat4.equals(xrSpace[PRIVATE].offsetMatrix, mat4.create()), + mat4.equals(xrSpace[P_SPACE].offsetMatrix, mat4.create()), ).toBeTruthy(); - expect(xrSpace[PRIVATE].parentSpace).toBeUndefined(); + expect(xrSpace[P_SPACE].parentSpace).toBeUndefined(); }); test('GlobalSpace should initialize as an XRSpace with no parent', () => { - expect(globalSpace[PRIVATE].parentSpace).toBeUndefined(); + expect(globalSpace[P_SPACE].parentSpace).toBeUndefined(); expect( - mat4.equals(globalSpace[PRIVATE].offsetMatrix, mat4.create()), + mat4.equals(globalSpace[P_SPACE].offsetMatrix, mat4.create()), ).toBeTruthy(); }); @@ -43,7 +40,7 @@ describe('XRSpace and XRSpaceUtils', () => { const expectedMatrix = mat4.create(); mat4.fromTranslation(expectedMatrix, position); expect( - mat4.equals(xrSpace[PRIVATE].offsetMatrix, expectedMatrix), + mat4.equals(xrSpace[P_SPACE].offsetMatrix, expectedMatrix), ).toBeTruthy(); }); @@ -55,7 +52,7 @@ describe('XRSpace and XRSpaceUtils', () => { const expectedMatrix = mat4.create(); mat4.fromQuat(expectedMatrix, quaternion); expect( - mat4.equals(xrSpace[PRIVATE].offsetMatrix, expectedMatrix), + mat4.equals(xrSpace[P_SPACE].offsetMatrix, expectedMatrix), ).toBeTruthy(); });