@pmndrs/xr
Version:
VR/AR for threejs
252 lines (251 loc) • 11.5 kB
TypeScript
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<XRInputSource>;
}
}
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<XRBodyJoint, XRBodySpace> {
}
interface XRFrame {
readonly body?: XRBody;
}
}
export type XRState<T extends XRElementImplementations> = 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<XRInputSourceState>;
/**
* the detected `XRPlane`s
*/
detectedPlanes: ReadonlyArray<XRPlane>;
/**
* the detected `XRMesh`es
*/
detectedMeshes: ReadonlyArray<XRMesh>;
/**
* active additional webxr layers
*/
layerEntries: ReadonlyArray<XRLayerEntry>;
/**
* access to the emulator values to change the emulated input device imperatively
*/
emulator?: XRDevice;
} & WithRecord<T>>;
export type XRElementImplementations = {
[Key in keyof XRInputSourceStateMap]: unknown;
};
export type WithRecord<T extends XRElementImplementations> = {
/**
* options for configuring the <DefaultXRController/> 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 <DefaultXRTransientPointer/> 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 <DefaultXRHand/> 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 <DefaultXRGaze/> 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 <DefaultXRScreenInput/> 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<T extends object | Function>(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>) => number | false) | 'high' | 'mid' | 'low' | false;
export type XRStoreOptions<T extends XRElementImplementations> = {
/**
* 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<XRSessionMode>;
/**
* allows to use non primary (tracked) input sources
* @default false
*/
secondaryInputSources?: boolean;
} & XRControllerLayoutLoaderOptions & XRHandLoaderOptions & Partial<WithRecord<T>> & XRSessionInitOptions;
export type XRStore<T extends XRElementImplementations> = Omit<StoreApi<XRState<T>>, '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<XRSession | undefined>;
enterAR(): Promise<XRSession | undefined>;
enterVR(): Promise<XRSession | undefined>;
/**
* 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<XRFrame>;
};
declare module 'three' {
interface Object3D {
xrSpace?: XRSpace;
}
}
declare global {
interface XRSystem {
offerSession?: XRSystem['requestSession'];
}
}
export declare function createXRStore<T extends XRElementImplementations>(options?: XRStoreOptions<T>): XRStore<T>;