UNPKG

@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
/// <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 {};