diff --git a/src/device/XRDevice.ts b/src/device/XRDevice.ts index 161e338..89821e8 100644 --- a/src/device/XRDevice.ts +++ b/src/device/XRDevice.ts @@ -12,6 +12,14 @@ import { } from '../spaces/XRSpace.js'; import { Quaternion, Vector3 } from '../utils/Math.js'; import { XRController, XRControllerConfig } from './XRController.js'; +import { + XREnvironmentBlendMode, + XRInteractionMode, + PRIVATE as XRSESSION_PRIVATE, + XRSession, + XRSessionMode, + XRVisibilityState, +} from '../session/XRSession.js'; import { XREye, XRView } from '../views/XRView.js'; import { XRHandInput, oculusHandConfig } from './XRHandInput.js'; import { @@ -25,12 +33,6 @@ import { XRReferenceSpace, XRReferenceSpaceType, } from '../spaces/XRReferenceSpace.js'; -import { - PRIVATE as XRSESSION_PRIVATE, - XRSession, - XRSessionMode, - XRVisibilityState, -} from '../session/XRSession.js'; import { PRIVATE as XRSYSTEM_PRIVATE, XRSystem, @@ -78,6 +80,10 @@ export interface XRDeviceConfig { supportedFrameRates: number[]; isSystemKeyboardSupported: boolean; internalNominalFrameRate: number; + environmentBlendModes: Partial<{ + [sessionMode in XRSessionMode]: XREnvironmentBlendMode; + }>; + interactionMode: XRInteractionMode; userAgent: string; } @@ -113,6 +119,10 @@ export class XRDevice { supportedFrameRates: number[]; isSystemKeyboardSupported: boolean; internalNominalFrameRate: number; + environmentBlendModes: Partial<{ + [sessionMode in XRSessionMode]: XREnvironmentBlendMode; + }>; + interactionMode: XRInteractionMode; userAgent: string; // device state @@ -212,6 +222,8 @@ export class XRDevice { supportedFrameRates: deviceConfig.supportedFrameRates, isSystemKeyboardSupported: deviceConfig.isSystemKeyboardSupported, internalNominalFrameRate: deviceConfig.internalNominalFrameRate, + environmentBlendModes: deviceConfig.environmentBlendModes, + interactionMode: deviceConfig.interactionMode, userAgent: deviceConfig.userAgent, position: diff --git a/src/device/configs/headset/meta.ts b/src/device/configs/headset/meta.ts index 11bb094..ad0e4b7 100644 --- a/src/device/configs/headset/meta.ts +++ b/src/device/configs/headset/meta.ts @@ -6,6 +6,11 @@ */ import { WebXRFeatures, XRDeviceConfig } from '../../XRDevice.js'; +import { + XREnvironmentBlendMode, + XRInteractionMode, + XRSessionMode, +} from '../../../session/XRSession.js'; import { metaQuestTouchPlus, metaQuestTouchPro, @@ -13,8 +18,6 @@ import { oculusTouchV3, } from '../controller/meta.js'; -import { XRSessionMode } from '../../../session/XRSession.js'; - export const oculusQuest1: XRDeviceConfig = { name: 'Oculus Quest 1', controllerConfig: oculusTouchV2, @@ -36,6 +39,11 @@ export const oculusQuest1: XRDeviceConfig = { supportedFrameRates: [72, 80, 90], isSystemKeyboardSupported: true, internalNominalFrameRate: 72, + environmentBlendModes: { + [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, + [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + }, + interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Mozilla/5.0 (X11; Linux x86_64; Quest 1) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/33.0.0.x.x.x Chrome/126.0.6478.122 VR Safari/537.36', }; @@ -63,6 +71,11 @@ export const metaQuest2: XRDeviceConfig = { supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 72, + environmentBlendModes: { + [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, + [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + }, + interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Mozilla/5.0 (X11; Linux x86_64; Quest 2) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/33.0.0.x.x.x Chrome/126.0.6478.122 VR Safari/537.36', }; @@ -90,6 +103,11 @@ export const metaQuestPro: XRDeviceConfig = { supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, + environmentBlendModes: { + [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, + [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + }, + interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Mozilla/5.0 (X11; Linux x86_64; Quest Pro) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/33.0.0.x.x.x Chrome/126.0.6478.122 VR Safari/537.36', }; @@ -118,6 +136,11 @@ export const metaQuest3: XRDeviceConfig = { supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, + environmentBlendModes: { + [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, + [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + }, + interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Mozilla/5.0 (X11; Linux x86_64; Quest 3) AppleWebKit/537.36 (KHTML, like Gecko) OculusBrowser/33.0.0.x.x.x Chrome/126.0.6478.122 VR Safari/537.36', }; diff --git a/src/session/XRSession.ts b/src/session/XRSession.ts index 1f9f0bf..634649b 100644 --- a/src/session/XRSession.ts +++ b/src/session/XRSession.ts @@ -49,6 +49,17 @@ export type XRSessionInit = { optionalFeatures?: string[]; }; +export enum XREnvironmentBlendMode { + Opaque = 'opaque', + AlphaBlend = 'alpha-blend', + Additive = 'additive', +} + +export enum XRInteractionMode { + ScreenSpace = 'screen-space', + WorldSpace = 'world-space', +} + type XRFrameRequestCallback = ( time: DOMHighResTimeStamp, frame: XRFrame, @@ -368,6 +379,18 @@ export class XRSession extends EventTarget { return this[PRIVATE].isSystemKeyboardSupported; } + get environmentBlendMode(): XREnvironmentBlendMode { + return ( + this[PRIVATE].device[XRDEVICE_PRIVATE].environmentBlendModes[ + this[PRIVATE].mode + ] ?? XREnvironmentBlendMode.Opaque + ); + } + + get interactionMode(): XRInteractionMode { + return this[PRIVATE].device[XRDEVICE_PRIVATE].interactionMode; + } + updateRenderState(state: XRRenderStateInit = {}): void { if (this[PRIVATE].ended) { throw new DOMException( diff --git a/tests/initialization/XRSystem.test.ts b/tests/initialization/XRSystem.test.ts index b351214..8588b75 100644 --- a/tests/initialization/XRSystem.test.ts +++ b/tests/initialization/XRSystem.test.ts @@ -6,8 +6,12 @@ */ import { WebXRFeatures, XRDevice } from '../../src/device/XRDevice'; +import { + XREnvironmentBlendMode, + XRInteractionMode, + XRSessionMode, +} from '../../src/session/XRSession'; -import { XRSessionMode } from '../../src/session/XRSession'; import { XRSystem } from '../../src/initialization/XRSystem'; import { metaQuestTouchPlus } from '../../src/device/configs/controller/meta'; @@ -31,6 +35,11 @@ describe('XRSystem', () => { supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, + environmentBlendModes: { + [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, + [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + }, + interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Test user agent', }; @@ -42,6 +51,11 @@ describe('XRSystem', () => { supportedFrameRates: arvrDeviceConfig.supportedFrameRates, isSystemKeyboardSupported: true, internalNominalFrameRate: 90, + environmentBlendModes: { + [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, + [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + }, + interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Test user agent', }; diff --git a/tests/session/XRSession.test.ts b/tests/session/XRSession.test.ts index e05be8e..bb3de15 100644 --- a/tests/session/XRSession.test.ts +++ b/tests/session/XRSession.test.ts @@ -10,6 +10,13 @@ import { XRDevice, XRDeviceConfig, } from '../../src/device/XRDevice'; +import { + XREnvironmentBlendMode, + XRInteractionMode, + XRSession, + XRSessionMode, + XRVisibilityState, +} from '../../src/session/XRSession'; import { XRReferenceSpace, XRReferenceSpaceType, @@ -18,11 +25,6 @@ import { XRRenderState, XRRenderStateInit, } from '../../src/session/XRRenderState'; -import { - XRSession, - XRSessionMode, - XRVisibilityState, -} from '../../src/session/XRSession'; import { XRFrame } from '../../src/frameloop/XRFrame'; import { XRInputSourceArray } from '../../src/input/XRInputSource'; @@ -71,7 +73,11 @@ describe('XRSession', () => { const testDeviceConfig: XRDeviceConfig = { name: 'Test Device', controllerConfig: metaQuestTouchPlus, - supportedSessionModes: [XRSessionMode.Inline, XRSessionMode.ImmersiveVR], + supportedSessionModes: [ + XRSessionMode.Inline, + XRSessionMode.ImmersiveVR, + XRSessionMode.ImmersiveAR, + ], supportedFeatures: [ WebXRFeatures.Viewer, WebXRFeatures.Local, @@ -80,6 +86,11 @@ describe('XRSession', () => { supportedFrameRates: [72, 80, 90, 120], isSystemKeyboardSupported: true, internalNominalFrameRate: 90, + environmentBlendModes: { + [XRSessionMode.ImmersiveVR]: XREnvironmentBlendMode.Opaque, + [XRSessionMode.ImmersiveAR]: XREnvironmentBlendMode.AlphaBlend, + }, + interactionMode: XRInteractionMode.WorldSpace, userAgent: 'Test user agent', }; xrDevice = new XRDevice(testDeviceConfig); @@ -339,4 +350,17 @@ describe('XRSession', () => { expect(mockOnSqueezeStart).toHaveBeenCalled(); expect(mockOnSqueezeEnd).toHaveBeenCalled(); }); + + test('XRSession.environmentBlendMode returns the correct value', async () => { + expect(xrSession.environmentBlendMode).toBe(XREnvironmentBlendMode.Opaque); + xrSession.end(); + xrSession = await xrSystem.requestSession(XRSessionMode.ImmersiveAR); + expect(xrSession.environmentBlendMode).toBe( + XREnvironmentBlendMode.AlphaBlend, + ); + }); + + test('XRSession.interactionMode returns the correct value', async () => { + expect(xrSession.interactionMode).toBe(XRInteractionMode.WorldSpace); + }); });