@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
341 lines (340 loc) • 16.7 kB
TypeScript
/// <reference types="webxr" />
import { Quaternion, Vector3 } from "three";
import { Context } from "../engine_context.js";
import type { IComponent, INeedleXRSession } from "../engine_types.js";
import { NeedleXRController } from "./NeedleXRController.js";
import { NeedleXRSync } from "./NeedleXRSync.js";
import { SceneTransition } from "./SceneTransition.js";
import type { IXRRig } from "./XRRig.js";
/** @link https://developer.mozilla.org/en-US/docs/Web/API/XRFrame/fillPoses */
declare type FillPosesFunction = (spaces: IterableIterator<XRJointSpace>, referenceSpace: XRSpace, targetArray: Float32Array) => void;
declare type NeedleXRFrame = XRFrame & {
fillPoses?: FillPosesFunction;
};
/** NeedleXRSession event argument.
* Use `args.xr` to access the NeedleXRSession */
export type NeedleXREventArgs = {
readonly xr: NeedleXRSession;
};
export type SessionChangedEvt = (args: NeedleXREventArgs) => void;
export type SessionRequestedEvent = (args: {
readonly mode: XRSessionMode;
readonly init: XRSessionInit;
}) => void;
export type SessionRequestedEndEvent = (args: {
readonly mode: XRSessionMode;
readonly init: XRSessionInit;
newSession: XRSession | null;
}) => void;
/** Result of a XR hit-test
* @property {XRHitTestResult} hit The original XRHitTestResult
* @property {Vector3} position The hit position in world space
* @property {Quaternion} quaternion The hit rotation in world space
*/
export type NeedleXRHitTestResult = {
readonly hit: XRHitTestResult;
readonly position: Vector3;
readonly quaternion: Quaternion;
};
export interface INeedleXRSessionEventReceiver extends Pick<IComponent, "destroyed"> {
get activeAndEnabled(): boolean;
supportsXR?(mode: XRSessionMode): boolean;
/** Called before requesting a XR session */
onBeforeXR?(mode: XRSessionMode, args: XRSessionInit): void;
onEnterXR?(args: NeedleXREventArgs): void;
onUpdateXR?(args: NeedleXREventArgs): void;
onLeaveXR?(args: NeedleXREventArgs): void;
onXRControllerAdded?(args: NeedleXRControllerEventArgs): void;
onXRControllerRemoved?(args: NeedleXRControllerEventArgs): void;
}
/** Contains a reference to the currently active webxr session and the controller that has changed */
export type NeedleXRControllerEventArgs = NeedleXREventArgs & {
controller: NeedleXRController;
change: "added" | "removed";
};
/** Event Arguments when a controller changed event is invoked (added or removed)
* Access the controller via `args.controller`, the `args.change` property indicates if the controller was added or removed
*/
export type ControllerChangedEvt = (args: NeedleXRControllerEventArgs) => void;
/**
* This class manages an XRSession to provide helper methods and events. It provides easy access to the XRInputSources (controllers and hands)
* - Start a XRSession with `NeedleXRSession.start(...)`
* - Stop a XRSession with `NeedleXRSession.stop()`
* - Access a running XRSession with `NeedleXRSession.active`
*
* If a XRSession is active you can use all XR-related event methods on your components to receive XR events e.g. `onEnterXR`, `onUpdateXR`, `onLeaveXR`
* ```ts
* export class MyComponent extends Behaviour {
* // callback invoked whenever the XRSession is started or your component is added to a scene with an active XRSession
* onEnterXR(args: NeedleXREventArgs) {
* console.log("Entered XR");
* // access the NeedleXRSession via args.xr
* }
* // callback invoked whenever a controller is added (or you switch from controller to hand tracking)
* onControllerAdded(args: NeedleXRControllerEventArgs) { }
* }
* ```
*
* ### XRRig
* The XRRig can be accessed via the `rig` property
* Set a custom XRRig via `NeedleXRSession.addRig(...)` or `NeedleXRSession.removeRig(...)`
* By default the active XRRig with the highest priority in the scene is used
*/
export declare class NeedleXRSession implements INeedleXRSession {
private static _sync;
static getXRSync(context: Context): NeedleXRSync;
static get currentSessionRequest(): XRSessionMode | null;
private static _currentSessionRequestMode;
/**
* @returns the active @type {NeedleXRSession} (if any active) or null
*/
static get active(): NeedleXRSession | null;
/** The active xr session mode (if any xr session is active)
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode
*/
static get activeMode(): XRSessionMode | null;
/** XRSystem via navigator.xr access
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSystem
*/
static get xrSystem(): XRSystem | undefined;
/**
* @returns true if the browser supports WebXR (`immersive-vr` or `immersive-ar`)
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSystem/isSessionSupported
*/
static isXRSupported(): Promise<boolean>;
/**
* @returns true if the browser supports immersive-vr (WebXR)
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSystem/isSessionSupported
*/
static isVRSupported(): Promise<boolean>;
/**
* @returns true if the browser supports immersive-ar (WebXR)
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSystem/isSessionSupported
*/
static isARSupported(): Promise<boolean>;
/**
* @param mode The XRSessionMode to check if it is supported
* @returns true if the browser supports the given XRSessionMode
*/
static isSessionSupported(mode: XRSessionMode): Promise<boolean>;
private static _currentSessionRequest?;
private static _activeSession;
/** Register to listen to XRSession start events. Unsubscribe with `offXRSessionStart` */
static onSessionRequestStart(evt: SessionRequestedEvent): void;
/** Unsubscribe from request start evt. Register with `onSessionRequestStart` */
static offSessionRequestStart(evt: SessionRequestedEvent): void;
private static readonly _sessionRequestStartListeners;
/** Called after the session request has finished */
static onSessionRequestEnd(evt: SessionRequestedEndEvent): void;
/** Unsubscribe from request end evt */
static offSessionRequestEnd(evt: SessionRequestedEndEvent): void;
private static readonly _sessionRequestEndListeners;
/** Listen to XR session started. Unsubscribe with `offXRSessionStart` */
static onXRSessionStart(evt: SessionChangedEvt): void;
/** Unsubscribe from XRSession started events */
static offXRSessionStart(evt: SessionChangedEvt): void;
private static readonly _xrStartListeners;
/** Listen to XR session ended. Unsubscribe with `offXRSessionEnd` */
static onXRSessionEnd(evt: SessionChangedEvt): void;
/** Unsubscribe from XRSession started events */
static offXRSessionEnd(evt: SessionChangedEvt): void;
private static readonly _xrEndListeners;
/** Listen to controller added events.
* Events are cleared when starting a new session
**/
static onControllerAdded(evt: ControllerChangedEvt): void;
/** Unsubscribe from controller added evts */
static offControllerAdded(evt: ControllerChangedEvt): void;
private static readonly _controllerAddedListeners;
/** Listen to controller removed events
* Events are cleared when starting a new session
**/
static onControllerRemoved(evt: ControllerChangedEvt): void;
/** Unsubscribe from controller removed events */
static offControllerRemoved(evt: ControllerChangedEvt): void;
private static readonly _controllerRemovedListeners;
/** If the browser supports offerSession - creating a VR or AR button in the browser navigation bar */
static offerSession(mode: XRSessionMode, init: XRSessionInit | "default", context: Context): boolean;
/** @returns a new XRSession init object with defaults */
static getDefaultSessionInit(mode: Omit<XRSessionMode, "inline">): XRSessionInit;
/** start a new webXR session (make sure to stop already running sessions before calling this method)
* @param mode The XRSessionMode to start (e.g. `immersive-vr` or `immersive-ar`) or `ar` to start `immersive-ar` on supported devices OR on iOS devices it will export an interactive USDZ and open in Quicklook.
* Get more information about WebXR modes: https://developer.mozilla.org/en-US/docs/Web/API/XRSessionMode
* @param init The XRSessionInit to use (optional), docs: https://developer.mozilla.org/en-US/docs/Web/API/XRSessionInit
* @param context The Needle Engine context to use
*/
static start(mode: XRSessionMode | "ar", init?: XRSessionInit, context?: Context): Promise<NeedleXRSession | null>;
static setSession(mode: XRSessionMode, session: XRSession, init: XRSessionInit, context: Context): NeedleXRSession;
private static $_stop_request;
/** stops the active XR session */
static stop(): void;
private static onEnd;
/** The needle engine context this session was started from */
readonly context: Context;
get sync(): NeedleXRSync | null;
/** Returns true if the xr session is still active */
get running(): boolean;
/**
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSession
*/
readonly session: XRSession;
/** XR Session Mode: AR or VR */
readonly mode: XRSessionMode;
/**
* The XRSession interface's read-only interactionMode property describes the best space (according to the user agent) for the application to draw an interactive UI for the current session.
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSession/interactionMode
*/
get interactionMode(): "screen-space" | "world-space";
/**
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSession/visibilityState
* @returns {XRVisibilityState} The visibility state of the XRSession
*/
get visibilityState(): XRVisibilityState;
/**
* Check if the session is `visible-blurred` - this means e.g. the keyboard is shown
*/
get isVisibleBlurred(): boolean;
/**
* Check if the session has system keyboard support
*/
get isSystemKeyboardSupported(): boolean;
/**
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRSession/environmentBlendMode
*/
get environmentBlendMode(): XREnvironmentBlendMode;
/**
* The current XR frame
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRFrame
*/
get frame(): NeedleXRFrame;
/** The currently active/connected controllers */
readonly controllers: NeedleXRController[];
/** shorthand to query the left controller. Use `controllers` to get access to all connected controllers */
get leftController(): NeedleXRController | undefined;
/** shorthand to query the right controller. Use `controllers` to get access to all connected controllers */
get rightController(): NeedleXRController | undefined;
/** @returns the given controller if it is connected */
getController(side: XRHandedness | number): NeedleXRController | null;
/** Returns true if running in pass through mode in immersive AR (e.g. user is wearing a headset while in AR) */
get isPassThrough(): boolean;
get isAR(): boolean;
get isVR(): boolean;
/** If the AR mode is not immersive (meaning the user is e.g. holding a phone instead of wearing a AR passthrough headset) */
get isScreenBasedAR(): boolean;
get posePosition(): Vector3;
get poseOrientation(): Quaternion;
/** @returns the context.renderer.xr.getReferenceSpace() result */
get referenceSpace(): XRSpace | null;
/** @returns the XRFrame `viewerpose` using the xr `referenceSpace` */
get viewerPose(): XRViewerPose | undefined;
/** @returns `true` if any image is currently being tracked */
/** returns true if images are currently being tracked */
get isTrackingImages(): boolean;
/** The currently active XR rig */
get rig(): IXRRig | null;
private _rigScale;
private _lastRigScaleUpdate;
/** Get the XR Rig worldscale.
*
* **For AR**
* If you want to modify the scale in AR at runtime get the WebARSessionRoot component via `findObjectOfType(WebARSessionRoot)` and then set the `arScale` value.
*
*/
get rigScale(): number;
/** add a rig to the available XR rigs - if it's priority is higher than the currently active rig it will be enabled */
addRig(rig: IXRRig): void;
/** Remove a rig from the available XR Rigs */
removeRig(rig: IXRRig): void;
/** Sets a XRRig to be active which will parent the camera to this rig */
setRigActive(rig: IXRRig): void;
/**
* @returns the user position in the rig space
*/
getUserOffsetInRig(): Vector3;
private updateActiveXRRig;
private _rigs;
private _viewerHitTestSource;
/** Returns a XR hit test result (if hit-testing is available) in rig space
* @param source If provided, the hit test will be performed for the given controller
*/
getHitTest(source?: NeedleXRController): NeedleXRHitTestResult | null;
private getControllerHitTest;
private convertHitTestResult;
/** convert a XRRigidTransform from XR session space to threejs / Needle Engine XR space */
convertSpace(transform: XRRigidTransform): {
position: Vector3;
quaternion: Quaternion;
};
/** this is the implictly created XR rig */
private readonly _defaultRig;
/** all scripts that receive some sort of XR update event */
private readonly _xr_scripts;
/** scripts that have onUpdateXR event methods */
private readonly _xr_update_scripts;
/** scripts that are in the scene but inactive (e.g. disabled parent gameObject) */
private readonly _inactive_scripts;
private readonly _controllerAdded;
private readonly _controllerRemoved;
private readonly _originalCameraWorldPosition?;
private readonly _originalCameraWorldRotation?;
private readonly _originalCameraWorldScale?;
private readonly _originalCameraParent?;
/** we store the main camera reference here each frame to make sure we have a rendering camera
* this e.g. the case when the XR rig with the camera gets disabled (and thus this.context.mainCamera is unassigned)
*/
private _mainCamera;
private constructor();
/** called when renderer.setSession is fulfilled */
private onRendererSessionSet;
private onInputSourceAdded;
/** Disconnects the controller, invokes events and notifies previou controller (if any) */
private disconnectInputSource;
/** End the XR Session */
end(): void;
private _ended;
private readonly _newControllers;
private onEnd;
private _didStart;
/** Called every frame by the engine */
private onBefore;
private onRenderDebug;
private onBeforeRender;
private onAfterRender;
/** register a new XR script if it hasnt added yet */
private addScript;
/** mark a script as inactive and invokes callbacks */
private markInactive;
private handleInactiveScripts;
private readonly _script_to_remove;
private removeScript;
private invokeCallback_EnterXR;
private invokeCallback_ControllerAdded;
private invokeCallback_ControllerRemoved;
private invokeCallback_LeaveXR;
private syncCameraCullingMask;
private invokeControllerEvent;
private _camera;
private readonly _cameraRenderParent;
private _previousCameraParent;
private readonly _customforward;
private originalCameraNearPlane?;
/** This is used to have the XR system camera look into threejs Z forward direction (instead of -z) */
private applyCustomForward;
private revertCustomForward;
private _viewerPose?;
private readonly _transformOrientation;
private readonly _transformPosition;
private internalUpdateState;
private _transition?;
get transition(): SceneTransition;
/** Call to fade rendering to black for a short moment (the returned promise will be resolved when fully black)
* This can be used to mask scene transitions or teleportation
* @returns a promise that is resolved when the screen is fully black
* @example `fadeTransition().then(() => { <fully_black> })`
*/
fadeTransition(): Promise<void>;
/** e.g. FadeToBlack */
private updateFade;
private onUpdateFade_PostRender;
}
export {};