@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.
314 lines (313 loc) • 14 kB
TypeScript
import { Matrix4, Object3D, Quaternion, Ray, Vector3 } from "three";
import { Context } from "../engine_context.js";
import { IPointerHitEventReceiver } from "../engine_input.js";
import type { ButtonName, IGameObject, Vec3, XRControllerButtonName, XRGestureName } from "../engine_types.js";
import type { NeedleXRHitTestResult, NeedleXRSession } from "./NeedleXRSession.js";
/** true when selectstart was ever received.
* On VisionOS 1.1 we always have select events (as per the spec), so this is always true
*/
declare type ControllerAxes = "xr-standard-thumbstick" | "xr-standard-touchpad";
declare type StickName = "xr-standard-thumbstick" | "xr-standard-touchpad";
declare type Mapping = "xr-standard";
declare type ComponentType = "button" | "thumbstick" | "squeeze" | "touchpad";
declare type GamepadKey = "button" | "xAxis" | "yAxis";
declare type NeedleXRControllerButtonName = ButtonName | "primary-button" | "primary";
declare type ComponentMap = {
type: ComponentType;
rootNodeName?: string;
gamepadIndices?: {
[]?: number;
};
visualResponses?: {
[]: {
states: Array<string>;
};
};
};
declare type InputDeviceLayout = {
selectComponentId: string;
components: {
[]: ComponentMap;
};
mapping: Mapping;
gamepad: Array<XRControllerButtonName>;
axes: Array<{
componentId: ControllerAxes;
axis: "x-axis" | "y-axis";
}>;
};
/**
* A NeedleXRController wraps a connected XRInputDevice that is either a physical controller or a hand
* You can access specific buttons using `getButton` and `getStick`
* To get spatial data in rig space (position, rotation) use the `gripPosition`, `gripQuaternion`, `rayPosition` and `rayQuaternion` properties
* To get spatial data in world space use the `gripWorldPosition`, `gripWorldQuaternion`, `rayWorldPosition` and `rayWorldQuaternion` properties
* Inputs will also be emitted as pointer events on `this.context.input` - so you can receive controller inputs on objects using the appropriate input events on your components (e.g. `onPointerDown`, `onPointerUp` etc) - use the `pointerType` property to check if the event is from a controller or not
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource
* @category XR
*/
export declare class NeedleXRController implements IPointerHitEventReceiver {
/** the Needle XR Session */
readonly xr: NeedleXRSession;
get context(): Context;
/**
* https://developer.mozilla.org/en-US/docs/Web/API/XRInputSource
*/
readonly inputSource: XRInputSource;
/** the input source index */
readonly index: number;
/** When enabled the controller will create input events in the Needle Engine input system (e.g. when a button is pressed or the controller is moved)
* You can disable this if you don't want inputs to go through the input system but be aware that this will result in `onPointerDown` component callbacks to not be invoked anymore for this XRController
*/
emitEvents: boolean;
/** Is the controller still connected? */
get connected(): boolean;
private _connected;
get isTracking(): boolean;
private _isTracking;
/** the input source gamepad giving raw access to the gamepad values
* You should usually use the `getButton` and `getStick` methods instead to get access to named buttons and sticks
*/
get gamepad(): Gamepad | undefined;
private __gamepad?;
/** @returns true if this is a hand (otherwise this is a controller) */
get isHand(): boolean;
/**
* If this is a hand then this is the hand info (XRHand)
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRHand
*/
get hand(): XRHand | undefined;
private __hand?;
/** threejs XRHandSpace, shorthand for `context.renderer.xr.getHand(controllerIndex)`
* @link https://threejs.org/docs/#api/en/renderers/webxr/WebXRManager.getHand
*/
get handObject(): import("three").XRHandSpace;
/** The input source profiles */
get profiles(): string[];
/** The device input layout */
get layout(): InputDeviceLayout | undefined;
/** shorthand for `inputSource.targetRayMode` */
get targetRayMode(): (XRTargetRayMode | "transient-pointer");
/** shorthand for `inputSource.targetRaySpace` */
get targetRaySpace(): XRSpace;
/** shorthand for `inputSource.gripSpace` */
get gripSpace(): XRSpace | undefined;
/**
* If the controller if held in the left or right hand (or if it's a left or right hand)
**/
get side(): XRHandedness;
private __side;
/** is right side. shorthand for `side === 'right'` */
get isRight(): boolean;
/** is left side. shorthand for `side === 'left'` */
get isLeft(): boolean;
/** is XR stylus, e.g. Logitech MX Ink */
get isStylus(): boolean;
/** The XRTransientInputHitTestSource can be used to perform hit tests with the controller ray against the real world.
* see https://developer.mozilla.org/en-US/docs/Web/API/XRSession/requestHitTestSourceForTransientInput for more information
* Requires the hit-test feature to be enabled in the XRSession
*
* NOTE: The hit test source should be cancelled once it's not needed anymore. Call `cancelHitTestSource` to do this
*/
getHitTestSource(): XRTransientInputHitTestSource | undefined;
get hasHitTestSource(): XRTransientInputHitTestSource | undefined;
/** Make sure to cancel the hittest source once it's not needed anymore */
cancelHitTestSource(): void;
private _hitTestSource;
private _hasSelectEvent;
get hasSelectEvent(): boolean;
private _isMxInk;
private _isMetaQuestTouchController;
/** Perform a hit test against the XR planes or meshes. shorthand for `xr.getHitTest(controller)`
* @returns the hit test result (with position and rotation in worldspace) or null if no hit was found
*/
getHitTest(): NeedleXRHitTestResult | null;
/** This is cleared at the beginning of each frame */
private readonly _handJointPoses;
/** Get the hand joint pose from the current XRFrame. Results are cached for a frame to avoid calling getJointPose multiple times */
getHandJointPose(joint: XRJointSpace, frame?: XRFrame): XRJointPose | null | undefined;
/** Grip matrix in grip space */
private readonly _gripMatrix;
/** Grip position in grip space */
private readonly _gripPosition;
/** Grip rotation in grip space */
private readonly _gripQuaternion;
private readonly _linearVelocity;
private readonly _rayPositionRaw;
private readonly _rayRotationRaw;
/** ray matrix in grip space */
private readonly _rayMatrix;
/** Ray position in rig space */
private readonly _rayPosition;
/** Ray rotation in rig space */
private readonly _rayQuaternion;
/** Grip position in rig space */
get gripPosition(): Vector3;
/** Grip rotation in rig space */
get gripQuaternion(): Quaternion;
get gripMatrix(): Matrix4;
/** Grip linear velocity in rig space
* @link https://developer.mozilla.org/en-US/docs/Web/API/XRPose/linearVelocity
*/
get gripLinearVelocity(): Vector3;
/** Ray position in rig space */
get rayPosition(): Vector3;
/** Ray rotation in rig space */
get rayQuaternion(): Quaternion;
/** Controller grip position in worldspace */
get gripWorldPosition(): Vector3;
private readonly _gripWorldPosition;
/** Controller grip rotation in wordspace */
get gripWorldQuaternion(): Quaternion;
private readonly _gripWorldQuaternion;
/** Controller ray position in worldspace (this value is calculated once per frame by default - call `updateRayWorldPosition` to force an update) */
get rayWorldPosition(): Vector3;
private readonly _rayWorldPosition;
/** Recalculates the ray world position */
updateRayWorldPosition(): void;
/** Controller ray rotation in wordspace (this value is calculated once per frame by default - call `updateRayWorldQuaternion` to force an update) */
get rayWorldQuaternion(): Quaternion;
private readonly _rayWorldQuaternion;
get pinchPosition(): Vector3;
private readonly _pinchPosition;
/** Recalculates the ray world quaternion */
updateRayWorldQuaternion(): void;
/** The controller ray in worldspace */
get ray(): Ray;
private readonly _ray;
/** Recalculated once per update */
private _hand_wristDotUp;
/**
* The dot product of the hand palm with the up vector.
* This is a number between -1 and 1, where 1 means the palm is directly up and -1 means the palm is directly down (upside down).
* This value is undefined if there's no hand
*/
get handWristDotUp(): number | undefined;
/**
* @returns true if the hand is upside down
*/
get isHandUpsideDown(): boolean;
/**
* @returns true if the hand is upside down and we got a pinch down event this frame.
*/
get isTeleportGesture(): boolean | undefined;
/** The controller object space.
* You can use it to attach objects to the controller.
* Children will be automatically detached and put into the scene when the controller disconnects
*/
get object(): IGameObject;
private readonly _object;
private readonly _gripSpaceObject?;
private readonly _raySpaceObject?;
/** Assigned the model that you use for rendering. This can be used as a hint for other components */
model: Object3D | null;
private readonly _debugAxesHelper;
private readonly _debugGripAxesHelper;
private readonly _debugRayAxesHelper;
/** returns the URL of the default controller model */
getModelUrl(): Promise<string | null>;
constructor(session: NeedleXRSession, device: XRInputSource, index: number);
private _hitTestSourcePromise;
private _requestHitTestSource;
onPointerHits: (_evt: any) => void;
onUpdate(frame: XRFrame): void;
onRenderDebug(): void;
private onUpdateFrame;
/** Called when the input source disconnects */
onDisconnected(): void;
/**
* Get a gamepad button
* @link https://github.com/immersive-web/webxr-gamepads-module/blob/main/gamepads-module-explainer.md
* @param key the controller button name e.g. x-button
* @returns the gamepad button if it exists on the controller - otherwise undefined
*/
getButton(key: NeedleXRControllerButtonName): NeedleGamepadButton | undefined | null;
/** Get a gesture state */
getGesture(key: XRGestureName): NeedleGamepadButton | null;
/**
* Get the pointer id for a specific button of this input device.
* This is useful if you want to check if a button (e.g. trigger) is currently being in use which can be queried on the inputsystem.
* @returns the pointer id for the button or undefined if the button is not supported
* @example
* ```ts
* const pointerId = controller.getPointerId("primary");
* if (pointerId !== undefined) {
* const isUsed = this.context.input.getPointerUsed(pointerId);
* console.log(controller.side, "used?", isUsed);
* }
* ```
*/
getPointerId(button: number): number;
getPointerId(button: NeedleXRControllerButtonName | XRGestureName): number | undefined;
private readonly _needleGamepadButtons;
/** combine the InputState information + the GamepadButton information (since GamepadButtons can not be extended) */
private toNeedleGamepadButton;
/**
* Get the values of a controller joystick
* @link https://github.com/immersive-web/webxr-gamepads-module/blob/main/gamepads-module-explainer.md
* @returns the stick values where x is left/right, y is up/down and z is the button value
*/
getStick(key: StickName | "primary"): Vec3;
private readonly _buttonMap;
private _motioncontroller?;
private _layout;
private getMotionController;
private initialize;
/**
* When enabled the controller will automatically emit pointer down events to the Needle Engine Input System.
* @default true
*/
emitPointerDownEvent: boolean;
/**
* When enabled the controller will automatically emit pointer up events to the Needle Engine Input System.
* @default true
*/
emitPointerUpEvent: boolean;
/**
* When enabled the controller will automatically emit pointer move events to the Needle Engine Input System.
* @default true
*/
emitPointerMoveEvent: boolean;
/**
* The distance threshold for pointer move events. This value is in units in rig space
* @default 0.03
*/
pointerMoveDistanceThreshold: number;
/**
* The angle threshold for pointer move events. This value is in radians.
* @default 0.05
*/
pointerMoveAngleThreshold: number;
private subscribeEvents;
private unsubscribeEvents;
private _selectButtonIndex;
private _squeezeButtonIndex;
private onSelectStart;
private onSelectEnd;
private onSequeezeStart;
private onSequeezeEnd;
/** Index = button index */
private readonly states;
private updateInputEvents;
private _didMoveLastFrame;
private readonly _lastPointerMovePosition;
private readonly _lastPointerMoveQuaternion;
private onUpdateMove;
/** cached spatial pointer init object. We re-use it to not have */
private readonly pointerInit;
private emitPointerEvent;
}
/** Enhanced GamepadButton with `isDown` and `isUp` information */
declare class NeedleGamepadButton {
/** The index of the button in the input gamepad */
readonly index: number | undefined;
readonly name: string;
touched: boolean;
pressed: boolean;
value: number;
/** was the button just pressed down the last update */
isDown: boolean;
/** was the button just released the last update */
isUp: boolean;
constructor(index: number | undefined, name: string);
}
export {};