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.

164 lines 6.18 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; import { SkinnedMesh } from "three"; import { RaycastOptions } from "../../engine/engine_physics.js"; import { serializable } from "../../engine/engine_serialization.js"; import { NeedleXRSession } from "../../engine/engine_xr.js"; import { Behaviour } from "../Component.js"; import { EventSystem } from "./EventSystem.js"; /** * [ObjectRaycaster](https://engine.needle.tools/docs/api/ObjectRaycaster) Base class for raycasters that detect pointer interactions. * Derive from this class to create custom raycasting logic. * * **Built-in raycasters:** * - {@link ObjectRaycaster} - Raycasts against 3D objects * - {@link GraphicRaycaster} - Raycasts against UI elements * - {@link SpatialGrabRaycaster} - Sphere overlap for XR grab * * **Important:** If you override `awake`, `onEnable`, or `onDisable`, * call the base class methods to ensure proper registration with {@link EventSystem}. * * @category Interactivity * @group Components * @see {@link EventSystem} for the event dispatch system */ export class Raycaster extends Behaviour { awake() { EventSystem.createIfNoneExists(this.context); } onEnable() { EventSystem.get(this.context)?.register(this); } onDisable() { EventSystem.get(this.context)?.unregister(this); } } /** * ObjectRaycaster enables pointer interactions with 3D objects. * Add this component to any object that needs click/hover detection. * * **Usage:** * Objects with ObjectRaycaster will receive pointer events when * they implement interfaces like {@link IPointerClickHandler}. * * **Note:** * In older Needle Engine versions the ObjectRaycaster was required to be added to the Scene. * This is no longer the case - the EventSystem will automatically handle raycasts. * * * @category Interactivity * @group Components * @see {@link IPointerClickHandler} for click events * @see {@link DragControls} for drag interactions */ export class ObjectRaycaster extends Raycaster { targets = null; raycastHits = []; ignoreSkinnedMeshes = false; start() { this.targets = [this.gameObject]; } performRaycast(opts = null) { if (!this.targets) return null; opts ??= new RaycastOptions(); opts.targets = this.targets; opts.results = this.raycastHits; opts.useAcceleratedRaycast = true; const orig = opts.testObject; if (this.ignoreSkinnedMeshes) { opts.testObject = obj => { // if we are set to ignore skinned meshes, we return false for them if (obj instanceof SkinnedMesh) { return "continue in children"; } // call the original testObject function if (orig) return orig(obj); // otherwise allow raycasting return true; }; } const hits = this.context.physics.raycast(opts); opts.testObject = orig; return hits; } } __decorate([ serializable() ], ObjectRaycaster.prototype, "ignoreSkinnedMeshes", void 0); /** * GraphicRaycaster enables pointer interactions with UI elements. * Add this to a {@link Canvas} or UI hierarchy to enable button clicks, * hover effects, and other UI interactions. * * **Requirements:** * - Must be on the same object as a Canvas or on a parent * - UI elements need proper RectTransform setup * * @example Enable UI interaction * ```ts * // Add to Canvas object * canvas.addComponent(GraphicRaycaster); * // Now buttons and other UI elements will respond to clicks * ``` * * @summary Raycaster for UI elements * @category User Interface * @group Components * @see {@link Canvas} for UI root * @see {@link Button} for clickable UI * @see {@link EventSystem} for event handling */ export class GraphicRaycaster extends ObjectRaycaster { // eventCamera: Camera | null = null; // ignoreReversedGraphics: boolean = false; // rootRaycaster: GraphicRaycaster | null = null; constructor() { super(); this.ignoreSkinnedMeshes = true; } } /** * SpatialGrabRaycaster enables direct grab interactions in VR/AR. * Uses sphere overlap detection around the controller/hand position * to allow grabbing objects by reaching into them. * * **Features:** * - Active only during XR sessions * - Can be globally disabled via `SpatialGrabRaycaster.allow` * - Works alongside ray-based interaction * * @category XR * @group Components * @see {@link WebXR} for XR session management * @see {@link DragControls} for object manipulation */ export class SpatialGrabRaycaster extends Raycaster { /** * Use to disable SpatialGrabRaycaster globally */ static allow = true; performRaycast(_opts) { // ensure we're in XR, otherwise return if (!NeedleXRSession.active) return null; if (!SpatialGrabRaycaster.allow) return null; if (!_opts?.ray) return null; // TODO this raycast should actually start from gripWorldPosition, not the ray origin, for // cases like transient-pointer on VisionOS where the ray starts at the head and not the hand const rayOrigin = _opts.ray.origin; const radius = 0.015; // TODO if needed, check if the input source is a XR controller or hand // draw gizmo around ray origin // Gizmos.DrawSphere(rayOrigin, radius, 0x00ff0022); return this.context.physics.sphereOverlap(rayOrigin, radius, false, true); } } //# sourceMappingURL=Raycaster.js.map