import { XRDevice } from 'iwer'; import { Camera, Object3D, WebXRManager } from 'three'; import { StoreApi } from 'zustand/vanilla'; import { XRControllerLayoutLoaderOptions } from './controller/index.js'; import { XRHandLoaderOptions } from './hand/index.js'; import { XRSessionInitOptions } from './init.js'; import { XRInputSourceState, XRInputSourceStateMap } from './input.js'; import { XRLayerEntry } from './layer.js'; import type { EmulatorOptions } from './emulate.js'; declare global { export interface XRSessionEventMap { trackedsourceschange: XRInputSourcesChangeEvent; } export interface XRSession { trackedSources?: ReadonlyArray; } } declare global { type XRBodyJoint = 'root' | 'hips' | 'spine-lower' | 'spine-middle' | 'spine-upper' | 'chest' | 'neck' | 'head' | 'left-shoulder' | 'left-scapula' | 'left-arm-upper' | 'left-arm-lower' | 'left-hand-wrist-twist' | 'right-shoulder' | 'right-scapula' | 'right-arm-upper' | 'right-arm-lower' | 'right-hand-wrist-twist' | 'left-hand-palm' | 'left-hand-wrist' | 'left-hand-thumb-metacarpal' | 'left-hand-thumb-phalanx-proximal' | 'left-hand-thumb-phalanx-distal' | 'left-hand-thumb-tip' | 'left-hand-index-metacarpal' | 'left-hand-index-phalanx-proximal' | 'left-hand-index-phalanx-intermediate' | 'left-hand-index-phalanx-distal' | 'left-hand-index-tip' | 'left-hand-middle-metacarpal' | 'left-hand-middle-phalanx-proximal' | 'left-hand-middle-phalanx-intermediate' | 'left-hand-middle-phalanx-distal' | 'left-hand-middle-tip' | 'left-hand-ring-metacarpal' | 'left-hand-ring-phalanx-proximal' | 'left-hand-ring-phalanx-intermediate' | 'left-hand-ring-phalanx-distal' | 'left-hand-ring-tip' | 'left-hand-little-metacarpal' | 'left-hand-little-phalanx-proximal' | 'left-hand-little-phalanx-intermediate' | 'left-hand-little-phalanx-distal' | 'left-hand-little-tip' | 'right-hand-palm' | 'right-hand-wrist' | 'right-hand-thumb-metacarpal' | 'right-hand-thumb-phalanx-proximal' | 'right-hand-thumb-phalanx-distal' | 'right-hand-thumb-tip' | 'right-hand-index-metacarpal' | 'right-hand-index-phalanx-proximal' | 'right-hand-index-phalanx-intermediate' | 'right-hand-index-phalanx-distal' | 'right-hand-index-tip' | 'right-hand-middle-metacarpal' | 'right-hand-middle-phalanx-proximal' | 'right-hand-middle-phalanx-intermediate' | 'right-hand-middle-phalanx-distal' | 'right-hand-middle-tip' | 'right-hand-ring-metacarpal' | 'right-hand-ring-phalanx-proximal' | 'right-hand-ring-phalanx-intermediate' | 'right-hand-ring-phalanx-distal' | 'right-hand-ring-tip' | 'right-hand-little-metacarpal' | 'right-hand-little-phalanx-proximal' | 'right-hand-little-phalanx-intermediate' | 'right-hand-little-phalanx-distal' | 'right-hand-little-tip' | 'left-upper-leg' | 'left-lower-leg' | 'left-foot-ankle-twist' | 'left-foot-ankle' | 'left-foot-subtalar' | 'left-foot-transverse' | 'left-foot-ball' | 'right-upper-leg' | 'right-lower-leg' | 'right-foot-ankle-twist' | 'right-foot-ankle' | 'right-foot-subtalar' | 'right-foot-transverse' | 'right-foot-ball'; interface XRBodySpace extends XRSpace { readonly jointName: XRBodyJoint; } interface XRBody extends Map { } interface XRFrame { readonly body?: XRBody; } } export type XRState = Readonly<{ body?: XRBody; /** * current `XRSession` */ session?: XRSession; mediaBinding?: XRMediaBinding; /** * `XRReferenceSpace` of the origin in the current session * (this references to the session origin at the floor level) */ originReferenceSpace?: XRReferenceSpace; /** * the 3D object representing the session origin * if the origin is undefined it is implicitly at world position 0,0,0 */ origin?: Object3D; /** * the HTML element for doing dom overlays in handheld AR experiences */ domOverlayRoot?: Element; /** * the session visibility state * e.g. `"visible-blurred"` typically occurs when the user sees an OS overlay */ visibilityState?: XRVisibilityState; /** * the configured xr framerate * caution: the actual framerate of the experience may be lower if it cannot keep up */ frameRate?: number; /** * the xr session mode */ mode: XRSessionMode | null; /** * all xr input sources */ inputSourceStates: ReadonlyArray; /** * the detected `XRPlane`s */ detectedPlanes: ReadonlyArray; /** * the detected `XRMesh`es */ detectedMeshes: ReadonlyArray; /** * active additional webxr layers */ layerEntries: ReadonlyArray; /** * access to the emulator values to change the emulated input device imperatively */ emulator?: XRDevice; } & WithRecord>; export type XRElementImplementations = { [Key in keyof XRInputSourceStateMap]: unknown; }; export type WithRecord = { /** * options for configuring the or provide your own controller implementation * options and implementations can be provided for each handedness individually `{ left: false, right: { ... } }` * @example { rayPointer: false, grabPointer: { cursorModel: { color: "red" } } } * `false` prevents these controllers from beeing used * @default true */ controller: T['controller'] | ({ [Key in XRHandedness]?: T['controller']; } & { default?: T['controller']; }); /** * options for configuring the or provide your own transient pointer implementation * options and implementations can be provided for each handedness individually `{ left: false, right: { ... } }` * `false` prevents these transient pointers from beeing used * @example { rayPointer: { cursorModel: { color: "red" } } } * @default true */ transientPointer: T['transientPointer'] | ({ [Key in XRHandedness]?: T['transientPointer']; } & { default?: T['transientPointer']; }); /** * options for configuring the or provide your own hand implementation * options and implementations can be provided for each handedness individually `{ left: false, right: { ... } }` * `false` prevents these hands from beeing used * @example { rayPointer: false, grabPointer: { cursorModel: { color: "red" } } } * @default true */ hand: T['hand'] | ({ [Key in XRHandedness]?: T['hand']; } & { default?: T['hand']; }); /** * options for configuring the or provide your own gaze implementation * @example { rayPointer: { cursorModel: { color: "red" } } } * `false` prevents these controllers from beeing used * @default true */ gaze: T['gaze']; /** * options for configuring the or provide your own screen input implementation * @example { rayPointer: { cursorModel: { color: "red" } } } * `false` prevents these controllers from beeing used * @default true */ screenInput: T['screenInput']; }; export declare function resolveInputSourceImplementation(implementation: undefined | T | ({ [Key in XRHandedness]?: T | boolean; } & { default?: T | boolean; }) | boolean, handedness: XRHandedness | undefined, defaultValue: T | false): T | false; export type FrameBufferScalingOption = undefined | number | ((maxFrameBufferScaling: number) => number | undefined) | 'high' | 'mid' | 'low'; export type FrameRateOption = ((supportedFrameRates: ArrayLike) => number | false) | 'high' | 'mid' | 'low' | false; export type XRStoreOptions = { /** * Automatically makes a session request to the browser which can provide a custom ui for the user to start the XR experience. * if set to `true` the system will request an "immersive-ar" session if supported, else an "immersive-vr" session * @default true */ offerSession?: XRSessionMode | boolean; /** * emulates a device if WebXR not supported and on localhost * @default "metaQuest3" */ emulate?: EmulatorOptions | boolean; /** * sets the WebXR foveation between 0 and 1 * undefined refers to the default foveation provided by the device/browser * @default undefined */ foveation?: number; /** * sets the framerate of the session * @default "high" */ frameRate?: FrameRateOption; /** * sets the framebuffer scaling of the session * undefined refers to the default framebuffer scaling provided by the device/browser (e.g. 1) * @default undefined */ frameBufferScaling?: FrameBufferScalingOption; /** * session modes that can be entered automatically without manually requesting a session when granted by the system * @default true */ enterGrantedSession?: boolean | Array; /** * allows to use non primary (tracked) input sources * @default false */ secondaryInputSources?: boolean; } & XRControllerLayoutLoaderOptions & XRHandLoaderOptions & Partial> & XRSessionInitOptions; export type XRStore = Omit>, 'destroy'> & { /** * add webxr layer entry */ addLayerEntry(entry: XRLayerEntry): void; /** * remove webxr layer entry */ removeLayerEntry(entry: XRLayerEntry): void; /** * internal function */ setWebXRManager(xr: WebXRManager): void; /** * internal function */ onBeforeFrame(scene: Object3D, camera: Camera, frame: XRFrame | undefined): void; /** * internal function */ onBeforeRender(): void; /** * destroys the store unrepairably (for exiting XR use store.getState().session?.end()) */ destroy(): void; enterXR(mode: XRSessionMode): Promise; enterAR(): Promise; enterVR(): Promise; /** * update the hand configuration or implementation for both or only one hand */ setHand(implementation: T['hand'], handedness?: XRHandedness): void; /** * update the controller configuration or implementation for both or only one controller */ setController(implementation: T['controller'], handedness?: XRHandedness): void; /** * update the gaze configuration or implementation */ setGaze(implementation: T['gaze']): void; /** * update the screen input configuration or implementation */ setScreenInput(implementation: T['screenInput']): void; /** * update the transient pointer configuration or implementation for both or only one hand */ setTransientPointer(implementation: T['transientPointer'], handedness?: XRHandedness): void; setFrameRate(value: FrameRateOption): void; /** * returns a promise that resolves on the next render with the xr frame */ requestFrame(): Promise; }; declare module 'three' { interface Object3D { xrSpace?: XRSpace; } } declare global { interface XRSystem { offerSession?: XRSystem['requestSession']; } } export declare function createXRStore(options?: XRStoreOptions): XRStore;