@realitycollective/babylon-interactions 0.1.0-preview.2 → 0.1.0-preview.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -3
- package/dist/provider.d.ts +2 -16
- package/dist/provider.js +7 -14
- package/dist/provider.js.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -13,20 +13,31 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
13
13
|
- `@realitycollective/iwsdk-interactions` - Meta IWSDK adapter: player-rig poses and stateful gamepads as the provider, IWSDK's own targeting (`Pressed` / `Grabbed` tags) forwarded as pre-resolved hints, and native grab fulfilment when the app enables IWSDK grabbing/physics. One-call `registerInteractions(world)` setup.
|
|
14
14
|
- `@realitycollective/xrblocks-interactions` - EXPERIMENTAL Google XR Blocks adapter, structurally typed against xrblocks v0.20.0 (`Input.getFrame()` ray sources and direct touches, pooled structs copied, no haptics upstream).
|
|
15
15
|
- `demos/playground` - the standalone interaction playground: the full station set from one portable `InteractionDescriptor`, with client-side audio cues, opt-in controller haptics and client-side toss ballistics.
|
|
16
|
-
- `@realitycollective/webxr-interactions` - per-source velocity. `VelocityTracker` differentiates the grip poses the runtime already samples into linear and angular velocity, and the runtime runs one by default (`velocity: false` turns it off, `{ smoothing }` averages it). A source on its first frame, or one that reappears after dropping out, reports no velocity rather than a jump from its last known pose, and a provider that supplies its own velocity keeps it. Read it from `runtime.onSample(...)` or `runtime.getSource(id)`. `
|
|
16
|
+
- `@realitycollective/webxr-interactions` - per-source velocity. `VelocityTracker` differentiates the grip poses the runtime already samples into linear and angular velocity, and the runtime runs one by default (`velocity: false` turns it off, `{ smoothing }` averages it). A source on its first frame, or one that reappears after dropping out, reports no velocity rather than a jump from its last known pose, and a provider that supplies its own velocity keeps it. Read it from `runtime.onSample(...)` or `runtime.getSource(id)`. The differentiation itself is `velocityBetween` from `@realitycollective/webxr-input`, re-exported here; `clampDeadzone` joins the math helpers.
|
|
17
17
|
- `@realitycollective/webxr-interactions` - `routeAudioToSink`, the audio counterpart to `routeHapticsToProvider`: an opt-in that plays the cues named in a map through a client-supplied sink. Cues absent from the map are ignored, so an app sonifies only the moments it has sounds for.
|
|
18
|
-
- `@realitycollective/iwsdk-interactions` and `@realitycollective/threejs-interactions` - presence control. `setPresenceVisible(target, visible)` shows and hides the hand and controller visuals per side; the IWSDK adapter adds `setPresenceModality(mode)` to force hands or controllers over the automatic choice, and the three.js adapter takes the app's own models through `registerVisual(handedness, root)`.
|
|
18
|
+
- `@realitycollective/iwsdk-interactions` and `@realitycollective/threejs-interactions` - presence control. `setPresenceVisible(target, visible)` shows and hides the hand and controller visuals per side; the IWSDK adapter adds `setPresenceModality(mode)` to force hands or controllers over the automatic choice, and the three.js adapter takes the app's own models through `registerVisual(handedness, root)`. Every provider reports whether it can do this through `capabilities.presence`; the XR Blocks adapter reports false, since XR Blocks exposes no way to hide its visuals.
|
|
19
19
|
- `@realitycollective/threejs-interactions` - native WebXR pose velocity. Where the browser reports `linearVelocity` or `angularVelocity` with a grip pose (or with the wrist joint a hand falls back to), the provider passes it through on the snapshot and the core's tracker leaves it alone; where it does not, the tracker derives velocity from consecutive poses as before. The desktop mouse source synthesises nothing: its grip rides the camera ray, so the derived velocity is camera motion, and hand mechanics should be gated on `handedness !== "none"`.
|
|
20
20
|
- `@realitycollective/babylon-interactions` - a Babylon.js adapter, the fourth engine. It reads a `WebXRDefaultExperience` (controllers, motion controller trigger and squeeze, hand-tracking joints, session manager) for input, with `scene.onPointerObservable` as the desktop fallback, haptics through the motion controller's `pulse`, and presence over the visuals Babylon builds - motion controller root meshes and hand meshes. Babylon picks the visual per input source, so there is no modality switch. Hit-testing is a sphere test over registered nodes, with an optional `pickWithRay` hook for mesh-accurate targeting; the transform port moves, rotates and scales Babylon nodes from rest. Structurally typed against the Babylon API rather than importing `@babylonjs/core`, as the XR Blocks adapter is, so an upstream release cannot break the install - and so the whole adapter is covered headlessly by structural fakes. Written against the documented Babylon 7 API and not yet exercised against a real Babylon runtime.
|
|
21
|
-
- Tests for `@realitycollective/iwsdk-interactions`, which had none. `@iwsdk/core` imports headlessly in node, so the provider is covered against a structural fake world with no module mocking, and a parity suite instantiates all
|
|
21
|
+
- Tests for `@realitycollective/iwsdk-interactions`, which had none. `@iwsdk/core` imports headlessly in node, so the provider is covered against a structural fake world with no module mocking, and a parity suite instantiates all four providers and runs the shared contract suite against each one. The package's coverage ratchet moves off zero, and the other three ratchets rise to the floor they now measure.
|
|
22
22
|
|
|
23
23
|
### Changed
|
|
24
24
|
|
|
25
|
+
- Every provider reports presence through `capabilities.presence`, the required key `@realitycollective/webxr-input` 0.1.1 adds to `InputCapabilities`. The class-level `supportsPresence` field the four adapters carried while that key was unpublished is gone. This fixes a client-visible defect on the IWSDK adapter, which built its capabilities without a `presence` key at all: an app that read `capabilities.presence` saw false and hid a feature that worked. The value each provider reports is the one its own field computed - always true on IWSDK, true once Babylon has built a visual to hide, true on three.js once a model is registered, and always false on XR Blocks.
|
|
26
|
+
- `@realitycollective/threejs-interactions` - `registerVisual` and the new `unregisterVisual` now re-derive capabilities and notify `onCapabilitiesChanged`. Presence on this adapter is genuinely conditional: your app builds its own hand and controller models, so `capabilities.presence` is false until you hand the first one over and false again once you take the last one back.
|
|
27
|
+
- The Input 0.1.1 types replace the local stand-ins that were waiting on them. `InputSourceSnapshot` now carries `linearVelocity` and `angularVelocity` itself, so the `InputSourceSnapshotWithVelocity` alias is gone; the local `poseVelocity` is gone in favour of the shipped `velocityBetween`; and the per-adapter `PresenceTarget` and `PresenceModality` declarations are gone. `PresenceModality` now comes from the contracts, and a presence target is written as the contract writes it, `Handedness | "all"`. All three names were re-exported from these packages and are removed from that surface.
|
|
28
|
+
- Every package now requires `@realitycollective/webxr-input` at `^0.1.1` rather than `^0.1.0`, because the code above uses members that only exist from 0.1.1. A fresh install resolved to 0.1.1 either way; the range says so now, so a consumer holding an older lockfile fails at install rather than at run time.
|
|
29
|
+
- The provider parity suite is now three lines over `inputProviderContractCases()` from `@realitycollective/webxr-input`, run against all four adapters, instead of a hand-written copy of the same checks. The shared cases are stricter: they assert the capability record has exactly the contract's keys and that every snapshot is well formed. Two adapter-specific checks stay on top of them.
|
|
30
|
+
- `@realitycollective/iwsdk-interactions` - `setPresenceVisible` and `setPresenceModality` write nothing when nothing changed. The provider holds what it last applied per side - visibility, modality and the hand-tracking capability presence is derived from - and skips a side whose three inputs are unchanged. An app pushing presence from a state subscription, which is the documented pattern, no longer walks every descendant of both visual families on every unrelated state change. The per-frame re-apply during `sample()` stays unconditional, because IWSDK re-asserts its own visuals from its update loop and a capability refresh can change the modality underneath the adapter.
|
|
31
|
+
|
|
25
32
|
- `@realitycollective/iwsdk-interactions` - `pulse` resolves the side from the handedness recorded while sampling, rather than parsing the source id. Parsing the id remains the fallback for an id the provider did not produce.
|
|
26
33
|
|
|
27
34
|
- `@realitycollective/iwsdk-interactions` targets **IWSDK 0.5.x**. Its peer range is now `>=0.5.0 <0.6.0` and it is developed and tested against `@iwsdk/core` 0.5.3. The adapter is source-compatible with 0.4.x and needed no code change, but 0.5 is the only line exercised, so it is the only line supported. Every symbol and runtime member the provider uses is present in both, and it already reads `world.input.xr.gamepads` rather than the accessor 0.5 deprecates.
|
|
28
35
|
- `demos/playground` no longer compiles UIKitML at build time. `@iwsdk/vite-plugin-uikitml` was discontinued at 0.4.2 and only ever did `JSON.stringify(parse(source))`, so the panel is served as `.uikitml` from `public/ui/` and parsed on load. The demo drops that plugin, and its station markup uses `rgba()` rather than `background-opacity`, which the 0.5 parser removed.
|
|
29
36
|
|
|
37
|
+
### Deprecated
|
|
38
|
+
|
|
39
|
+
- `@realitycollective/webxr-interactions` - `InteractionRuntime.onSourcesSampled` is deprecated in favour of `onSample`. They are the same stream, and since `InputSourceSnapshot` carries the velocity fields itself the two signatures are now identical, so only one name is needed. Nothing is removed: existing callers keep working and the method will go in a later major release.
|
|
40
|
+
|
|
30
41
|
### Notes
|
|
31
42
|
|
|
32
43
|
- All five packages depend on `@realitycollective/webxr-input`, released independently from the [WebXR-Input](https://github.com/realitycollective/WebXR-Input) repository. That package must be published before this one.
|
package/dist/provider.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* configuration, and are re-published on session start, session end and
|
|
14
14
|
* controller add/remove.
|
|
15
15
|
*/
|
|
16
|
-
import { type HeadPose, type InputCapabilities, type InputProvider, type InputSourceSnapshot, type Unsubscribe } from "@realitycollective/webxr-input";
|
|
16
|
+
import { type Handedness, type HeadPose, type InputCapabilities, type InputProvider, type InputSourceSnapshot, type PresenceModality, type Unsubscribe } from "@realitycollective/webxr-input";
|
|
17
17
|
import { type BabylonCameraLike, type BabylonSceneLike, type BabylonXRExperienceLike } from "./babylon-types.js";
|
|
18
18
|
export interface BabylonProviderOptions {
|
|
19
19
|
/**
|
|
@@ -35,13 +35,6 @@ export interface BabylonProviderOptions {
|
|
|
35
35
|
*/
|
|
36
36
|
desktopGripDistance?: number;
|
|
37
37
|
}
|
|
38
|
-
/** Which side's visual a presence call targets. `"all"` is both. */
|
|
39
|
-
export type PresenceTarget = "left" | "right" | "none" | "all";
|
|
40
|
-
/**
|
|
41
|
-
* Which family of visuals presence shows. Babylon chooses this per input
|
|
42
|
-
* source, so this provider reports every mode as unavailable.
|
|
43
|
-
*/
|
|
44
|
-
export type PresenceModality = "hands" | "controllers" | "auto";
|
|
45
38
|
/** The desktop pointer source's id - stable for the life of the page. */
|
|
46
39
|
export declare const DESKTOP_SOURCE_ID = "babylon-pointer";
|
|
47
40
|
export declare class BabylonInputProvider implements InputProvider {
|
|
@@ -86,20 +79,13 @@ export declare class BabylonInputProvider implements InputProvider {
|
|
|
86
79
|
* even when the gamepad reports an actuator.
|
|
87
80
|
*/
|
|
88
81
|
pulse(sourceId: string, intensity: number, durationMs: number): boolean;
|
|
89
|
-
/**
|
|
90
|
-
* True when Babylon has built at least one visual this provider can hide:
|
|
91
|
-
* a motion controller root mesh, or a hand mesh from the hand-tracking
|
|
92
|
-
* feature. Becomes `capabilities.presence` at
|
|
93
|
-
* `@realitycollective/webxr-input` 0.1.1.
|
|
94
|
-
*/
|
|
95
|
-
get supportsPresence(): boolean;
|
|
96
82
|
/**
|
|
97
83
|
* Show or hide the visuals Babylon built for the targeted sides. Returns
|
|
98
84
|
* false when the target has nothing to show or hide. `"none"` targets no
|
|
99
85
|
* side, so it reports whether presence is usable at all without changing
|
|
100
86
|
* anything.
|
|
101
87
|
*/
|
|
102
|
-
setPresenceVisible(target:
|
|
88
|
+
setPresenceVisible(target: Handedness | "all", visible: boolean): boolean;
|
|
103
89
|
/**
|
|
104
90
|
* Always false. Babylon picks the visual per input source - a controller
|
|
105
91
|
* gets its motion-controller model, a tracked hand gets the hand mesh -
|
package/dist/provider.js
CHANGED
|
@@ -137,6 +137,10 @@ export class BabylonInputProvider {
|
|
|
137
137
|
// behaviours negotiate on desktop too. Without this every one of them
|
|
138
138
|
// switches itself off off-headset and only press remains usable.
|
|
139
139
|
grabs: desktop ? "poseOnly" : NO_CAPABILITIES.grabs,
|
|
140
|
+
// Set in the loop below: presence is true once Babylon has built at
|
|
141
|
+
// least one visual this provider can hide - a motion controller root
|
|
142
|
+
// mesh, or a hand mesh from the hand-tracking feature.
|
|
143
|
+
presence: false,
|
|
140
144
|
};
|
|
141
145
|
if (inSession) {
|
|
142
146
|
for (const controller of this.controllers()) {
|
|
@@ -157,6 +161,8 @@ export class BabylonInputProvider {
|
|
|
157
161
|
if ((controller.inputSource.gamepad?.hapticActuators?.length ?? 0) > 0) {
|
|
158
162
|
next.haptics = true;
|
|
159
163
|
}
|
|
164
|
+
if (this.visualsFor(controller).length > 0)
|
|
165
|
+
next.presence = true;
|
|
160
166
|
}
|
|
161
167
|
}
|
|
162
168
|
const changed = JSON.stringify(next) !== JSON.stringify(this.capabilities);
|
|
@@ -291,19 +297,6 @@ export class BabylonInputProvider {
|
|
|
291
297
|
return true;
|
|
292
298
|
}
|
|
293
299
|
// -- presence -----------------------------------------------------------------
|
|
294
|
-
/**
|
|
295
|
-
* True when Babylon has built at least one visual this provider can hide:
|
|
296
|
-
* a motion controller root mesh, or a hand mesh from the hand-tracking
|
|
297
|
-
* feature. Becomes `capabilities.presence` at
|
|
298
|
-
* `@realitycollective/webxr-input` 0.1.1.
|
|
299
|
-
*/
|
|
300
|
-
get supportsPresence() {
|
|
301
|
-
for (const controller of this.controllers()) {
|
|
302
|
-
if (this.visualsFor(controller).length > 0)
|
|
303
|
-
return true;
|
|
304
|
-
}
|
|
305
|
-
return false;
|
|
306
|
-
}
|
|
307
300
|
/**
|
|
308
301
|
* Show or hide the visuals Babylon built for the targeted sides. Returns
|
|
309
302
|
* false when the target has nothing to show or hide. `"none"` targets no
|
|
@@ -312,7 +305,7 @@ export class BabylonInputProvider {
|
|
|
312
305
|
*/
|
|
313
306
|
setPresenceVisible(target, visible) {
|
|
314
307
|
if (target === "none")
|
|
315
|
-
return this.
|
|
308
|
+
return this.capabilities.presence;
|
|
316
309
|
let applied = false;
|
|
317
310
|
for (const controller of this.controllers()) {
|
|
318
311
|
const side = normalizeHandedness(controller.inputSource.handedness);
|
package/dist/provider.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * BabylonInputProvider - the Babylon.js input provider.\n *\n * Reads a `WebXRDefaultExperience`: its `input.controllers` for poses and\n * motion-controller components, its session manager for what is live, and\n * the hand-tracking feature for fingertip joints. Falls back to the scene's\n * own pointer (`scene.onPointerObservable`) when no immersive session is\n * running, so the same scene is usable in a flat browser.\n *\n * Nothing here imports `@babylonjs/core` - see `babylon-types.ts`.\n *\n * Capabilities are derived from the LIVE session, never from the requested\n * configuration, and are re-published on session start, session end and\n * controller add/remove.\n */\nimport {\n NO_CAPABILITIES,\n type HeadPose,\n type InputCapabilities,\n type InputProvider,\n type InputSourceSnapshot,\n type Unsubscribe,\n type Vec3Tuple,\n} from \"@realitycollective/webxr-input\";\n// Collapses into `InputSourceSnapshot` at `@realitycollective/webxr-input` 0.1.1, which adds the two velocity fields.\nimport type { InputSourceSnapshotWithVelocity } from \"@realitycollective/webxr-interactions\";\nimport {\n HAND_TRACKING_FEATURE,\n INDEX_TIP_JOINT,\n POINTER_EVENT_TYPES,\n nodeForward,\n defaultForward,\n nodeWorldPose,\n toQuat,\n toVec3,\n type BabylonCameraLike,\n type BabylonHandTrackingLike,\n type BabylonPointerInfoLike,\n type BabylonSceneLike,\n type BabylonTransformNodeLike,\n type BabylonXRControllerLike,\n type BabylonXRExperienceLike,\n type BabylonXRHandLike,\n} from \"./babylon-types.js\";\n\nexport interface BabylonProviderOptions {\n /**\n * The `WebXRDefaultExperience` (or anything with the same surface).\n * Omit it, or pass null, for a desktop-only app: the provider then only\n * ever reports the scene pointer.\n */\n xr?: BabylonXRExperienceLike | null;\n /** The scene. Supplies the pointer fallback, the camera and the engine. */\n scene: BabylonSceneLike;\n /** Head pose camera. Defaults to `scene.activeCamera`. */\n camera?: BabylonCameraLike | null;\n /**\n * Metres along the pointer ray at which the desktop fallback places its\n * synthetic grip pose. Default 1 metre, matching the three.js adapter, so\n * grab, hinge, dial and slide follow the cursor on desktop. Set it near\n * the distance of the things being manipulated; at 0 the grip sits on the\n * camera and a mouse drag reports camera motion only.\n */\n desktopGripDistance?: number;\n}\n\n/** Which side's visual a presence call targets. `\"all\"` is both. */\nexport type PresenceTarget = \"left\" | \"right\" | \"none\" | \"all\";\n\n/**\n * Which family of visuals presence shows. Babylon chooses this per input\n * source, so this provider reports every mode as unavailable.\n */\nexport type PresenceModality = \"hands\" | \"controllers\" | \"auto\";\n\n/** Trigger, then the main component, then nothing. */\nconst TRIGGER_COMPONENT = \"trigger\";\nconst SQUEEZE_COMPONENT = \"squeeze\";\n\n/** The desktop pointer source's id - stable for the life of the page. */\nexport const DESKTOP_SOURCE_ID = \"babylon-pointer\";\n\ninterface PointerState {\n down: boolean;\n x: number;\n y: number;\n ray: { origin: Vec3Tuple; direction: Vec3Tuple } | null;\n}\n\ninterface SampledSource {\n handedness: \"left\" | \"right\" | \"none\";\n controller: BabylonXRControllerLike;\n}\n\nexport class BabylonInputProvider implements InputProvider {\n private readonly options: BabylonProviderOptions;\n private capabilities: InputCapabilities;\n private readonly capsListeners = new Set<(c: InputCapabilities) => void>();\n private readonly sourceListeners = new Set<() => void>();\n /** id -> handedness + controller, recorded while sampling, read by pulse. */\n private readonly sampled = new Map<string, SampledSource>();\n private readonly detach: Unsubscribe[] = [];\n private readonly gripDistance: number;\n /** Read once from `scene.useRightHandedSystem`; flips forward to -Z. */\n private readonly rightHanded: boolean;\n\n private readonly pointer: PointerState = { down: false, x: 0, y: 0, ray: null };\n\n constructor(options: BabylonProviderOptions) {\n this.options = options;\n this.gripDistance = options.desktopGripDistance ?? 1;\n this.rightHanded = options.scene.useRightHandedSystem === true;\n this.capabilities = { ...NO_CAPABILITIES, headPose: true, gaze: true };\n this.attachPointer();\n this.attachSession();\n this.refreshCapabilities();\n }\n\n // -- wiring -------------------------------------------------------------------\n\n private attachPointer(): void {\n const observable = this.options.scene.onPointerObservable;\n if (!observable) return;\n const observer = observable.add((info: BabylonPointerInfoLike) => this.onPointer(info));\n this.detach.push(() => {\n observable.remove(observer);\n });\n }\n\n private onPointer(info: BabylonPointerInfoLike): void {\n const event = info.event;\n if (event?.clientX !== undefined) this.pointer.x = event.clientX;\n if (event?.clientY !== undefined) this.pointer.y = event.clientY;\n // Babylon hands the pick it already did for this event; reuse it rather\n // than picking the scene a second time on the next sample.\n const ray = info.pickInfo?.ray;\n if (ray) {\n this.pointer.ray = {\n origin: toVec3(ray.origin) ?? [0, 0, 0],\n direction: toVec3(ray.direction) ?? defaultForward(this.rightHanded),\n };\n }\n // Only the primary button selects. Babylon reports it as button 0, the\n // same numbering as a DOM PointerEvent.\n if (info.type === POINTER_EVENT_TYPES.down && (event?.button ?? 0) === 0) {\n this.pointer.down = true;\n }\n if (info.type === POINTER_EVENT_TYPES.up && (event?.button ?? 0) === 0) {\n this.pointer.down = false;\n }\n }\n\n private attachSession(): void {\n const manager = this.options.xr?.baseExperience?.sessionManager;\n const input = this.options.xr?.input;\n const republish = () => {\n this.refreshCapabilities();\n for (const listener of [...this.sourceListeners]) listener();\n };\n const subscribe = <T>(\n observable: { add(cb: (value: T) => void): unknown; remove(o: unknown): boolean } | undefined,\n handler: () => void,\n ): void => {\n if (!observable) return;\n const observer = observable.add(() => handler());\n this.detach.push(() => {\n observable.remove(observer);\n });\n };\n subscribe(manager?.onXRSessionInit, republish);\n subscribe(manager?.onXRSessionEnded, () => {\n this.sampled.clear();\n republish();\n });\n subscribe(input?.onControllerAddedObservable, republish);\n subscribe(input?.onControllerRemovedObservable, republish);\n }\n\n // -- capabilities -------------------------------------------------------------\n\n /** The live XR session, or null when none is running. */\n private session(): unknown {\n return this.options.xr?.baseExperience?.sessionManager?.session ?? null;\n }\n\n private controllers(): readonly BabylonXRControllerLike[] {\n return this.options.xr?.input?.controllers ?? [];\n }\n\n /** The hand-tracking feature, when the app enabled it. */\n private handTracking(): BabylonHandTrackingLike | null {\n const manager = this.options.xr?.baseExperience?.featuresManager;\n if (!manager) return null;\n const feature = manager.getEnabledFeature(HAND_TRACKING_FEATURE);\n if (!feature || typeof feature !== \"object\") return null;\n const candidate = feature as Partial<BabylonHandTrackingLike>;\n return typeof candidate.getHandByControllerId === \"function\"\n ? (feature as BabylonHandTrackingLike)\n : null;\n }\n\n private handFor(controller: BabylonXRControllerLike): BabylonXRHandLike | null {\n return this.handTracking()?.getHandByControllerId(controller.uniqueId) ?? null;\n }\n\n private refreshCapabilities(): void {\n const inSession = this.session() !== null;\n const desktop = !inSession && this.options.scene.onPointerObservable !== undefined;\n // Babylon reports no native grab observation, so grabs are always\n // poseOnly: the core drives the transform through the port.\n const next: InputCapabilities = {\n ...NO_CAPABILITIES,\n headPose: true,\n gaze: true,\n pointer2d: desktop,\n // The pointer fallback synthesises a grip on the ray, so hand-driven\n // behaviours negotiate on desktop too. Without this every one of them\n // switches itself off off-headset and only press remains usable.\n grabs: desktop ? \"poseOnly\" : NO_CAPABILITIES.grabs,\n };\n if (inSession) {\n for (const controller of this.controllers()) {\n if (controller.pointer) next.rays = true;\n if (controller.grip ?? controller.pointer) next.grabs = \"poseOnly\";\n if (controller.inputSource.hand) {\n next.handJoints = true;\n next.pokes = true;\n }\n // Pinch strength needs the tracked hand itself, not just the flag on\n // the input source, so it is gated on the feature reporting a hand.\n if (this.handFor(controller)) next.pinch = true;\n if (controller.motionController) next.buttonsAxes = true;\n if ((controller.inputSource.gamepad?.hapticActuators?.length ?? 0) > 0) {\n next.haptics = true;\n }\n }\n }\n const changed = JSON.stringify(next) !== JSON.stringify(this.capabilities);\n this.capabilities = next;\n if (changed) {\n for (const listener of [...this.capsListeners]) listener(next);\n }\n }\n\n getCapabilities(): InputCapabilities {\n return this.capabilities;\n }\n\n onCapabilitiesChanged(listener: (c: InputCapabilities) => void): Unsubscribe {\n this.capsListeners.add(listener);\n return () => this.capsListeners.delete(listener);\n }\n\n onSourcesChanged(listener: () => void): Unsubscribe {\n this.sourceListeners.add(listener);\n return () => this.sourceListeners.delete(listener);\n }\n\n // -- sampling -----------------------------------------------------------------\n\n sample(): readonly InputSourceSnapshot[] {\n this.sampled.clear();\n if (this.session() === null) {\n // A session that ended between frames leaves stale capabilities behind.\n if (this.capabilities.rays) this.refreshCapabilities();\n return this.samplePointer();\n }\n\n const snapshots: InputSourceSnapshotWithVelocity[] = [];\n for (const controller of this.controllers()) {\n const handedness = normalizeHandedness(controller.inputSource.handedness);\n const hand = this.handFor(controller);\n const pointerNode = controller.pointer;\n const snapshot: InputSourceSnapshotWithVelocity = {\n id: controller.uniqueId,\n kind: controller.inputSource.hand ? \"hand\" : \"controller\",\n handedness,\n select: componentValue(\n controller.motionController?.getComponentOfType?.(TRIGGER_COMPONENT) ??\n controller.motionController?.getMainComponent?.() ??\n null,\n ),\n squeeze: componentValue(\n controller.motionController?.getComponentOfType?.(SQUEEZE_COMPONENT) ?? null,\n ),\n };\n if (pointerNode) {\n snapshot.ray = {\n origin: toVec3(pointerNode.getAbsolutePosition()) ?? [0, 0, 0],\n direction: nodeForward(pointerNode, this.rightHanded),\n };\n }\n // Babylon exposes the grip node only for a controller with one; a hand,\n // and a controller without a grip space, carry on the pointer node.\n const gripNode = controller.grip ?? pointerNode;\n if (gripNode) snapshot.gripPose = nodeWorldPose(gripNode);\n const tip = jointPosition(hand, INDEX_TIP_JOINT);\n if (tip) snapshot.indexTip = tip;\n if ((controller.inputSource.gamepad?.hapticActuators?.length ?? 0) > 0) {\n snapshot.hapticsAvailable = true;\n }\n // Babylon reports no per-pose velocity, so nothing is set here and the\n // core's VelocityTracker derives it from consecutive grip poses.\n snapshots.push(snapshot);\n this.sampled.set(snapshot.id, { handedness, controller });\n }\n return snapshots;\n }\n\n /**\n * The desktop pointer, shaped like the three.js adapter's mouse fallback:\n * one source, `handedness: \"none\"`, `kind: \"pointer2d\"`.\n */\n private samplePointer(): readonly InputSourceSnapshot[] {\n if (this.options.scene.onPointerObservable === undefined) return [];\n const ray = this.pointer.ray ?? this.pickRay();\n if (!ray) return [];\n const grip: Vec3Tuple = [\n ray.origin[0] + ray.direction[0] * this.gripDistance,\n ray.origin[1] + ray.direction[1] * this.gripDistance,\n ray.origin[2] + ray.direction[2] * this.gripDistance,\n ];\n return [\n {\n id: DESKTOP_SOURCE_ID,\n kind: \"pointer2d\",\n handedness: \"none\",\n ray: { origin: ray.origin, direction: ray.direction },\n gripPose: { position: grip, quaternion: toQuat(this.camera()?.absoluteRotation) },\n select: this.pointer.down ? 1 : 0,\n // A mouse has one button that means select. Squeeze stays 0 rather\n // than doubling the left button up as both, which would fire a press\n // and a grab together on an interactable that carries both.\n squeeze: 0,\n },\n ];\n }\n\n /** Pick the scene at the last known cursor position for a ray. */\n private pickRay(): { origin: Vec3Tuple; direction: Vec3Tuple } | null {\n const picked = this.options.scene.pick?.(this.pointer.x, this.pointer.y);\n const ray = picked?.ray;\n if (!ray) return null;\n return {\n origin: toVec3(ray.origin) ?? [0, 0, 0],\n direction: toVec3(ray.direction) ?? defaultForward(this.rightHanded),\n };\n }\n\n private camera(): BabylonCameraLike | null {\n return this.options.camera ?? this.options.scene.activeCamera ?? null;\n }\n\n getHeadPose(): HeadPose {\n const camera = this.camera();\n return {\n position: toVec3(camera?.globalPosition ?? camera?.position) ?? [0, 0, 0],\n quaternion: toQuat(camera?.absoluteRotation),\n };\n }\n\n // -- haptics ------------------------------------------------------------------\n\n /**\n * Babylon routes haptics through the motion controller rather than the\n * gamepad actuator, so a source with no motion controller cannot pulse\n * even when the gamepad reports an actuator.\n */\n pulse(sourceId: string, intensity: number, durationMs: number): boolean {\n const controller =\n this.sampled.get(sourceId)?.controller ??\n this.controllers().find((c) => c.uniqueId === sourceId);\n const motionController = controller?.motionController;\n if (!motionController?.pulse) return false;\n void motionController.pulse(Math.min(1, Math.max(0, intensity)), durationMs);\n return true;\n }\n\n // -- presence -----------------------------------------------------------------\n\n /**\n * True when Babylon has built at least one visual this provider can hide:\n * a motion controller root mesh, or a hand mesh from the hand-tracking\n * feature. Becomes `capabilities.presence` at\n * `@realitycollective/webxr-input` 0.1.1.\n */\n get supportsPresence(): boolean {\n for (const controller of this.controllers()) {\n if (this.visualsFor(controller).length > 0) return true;\n }\n return false;\n }\n\n /**\n * Show or hide the visuals Babylon built for the targeted sides. Returns\n * false when the target has nothing to show or hide. `\"none\"` targets no\n * side, so it reports whether presence is usable at all without changing\n * anything.\n */\n setPresenceVisible(target: PresenceTarget, visible: boolean): boolean {\n if (target === \"none\") return this.supportsPresence;\n let applied = false;\n for (const controller of this.controllers()) {\n const side = normalizeHandedness(controller.inputSource.handedness);\n if (target !== \"all\" && target !== side) continue;\n for (const node of this.visualsFor(controller)) {\n node.setEnabled?.(visible);\n applied = true;\n }\n }\n return applied;\n }\n\n /**\n * Always false. Babylon picks the visual per input source - a controller\n * gets its motion-controller model, a tracked hand gets the hand mesh -\n * and exposes no switch between the two. IWSDK builds both families, so\n * its adapter implements this.\n */\n setPresenceModality(_mode: PresenceModality): boolean {\n return false;\n }\n\n /** Every node that represents this controller on screen. */\n private visualsFor(controller: BabylonXRControllerLike): BabylonTransformNodeLike[] {\n const nodes: BabylonTransformNodeLike[] = [];\n const root = controller.motionController?.rootMesh;\n if (root) nodes.push(root);\n const handMesh = this.handFor(controller)?.handMesh;\n if (handMesh) nodes.push(handMesh);\n return nodes;\n }\n\n dispose(): void {\n for (const off of this.detach.splice(0)) off();\n this.sampled.clear();\n this.capsListeners.clear();\n this.sourceListeners.clear();\n }\n}\n\n/** Analog value where the component reports one, else the pressed flag. */\nfunction componentValue(\n component: { value?: number; pressed?: boolean } | null | undefined,\n): number {\n if (!component) return 0;\n if (typeof component.value === \"number\" && component.value > 0) return component.value;\n return component.pressed ? 1 : 0;\n}\n\nfunction jointPosition(\n hand: BabylonXRHandLike | null,\n joint: string,\n): Vec3Tuple | null {\n const mesh = hand?.getJointMesh?.(joint);\n return mesh ? toVec3(mesh.getAbsolutePosition()) : null;\n}\n\nfunction normalizeHandedness(value: string | undefined): \"left\" | \"right\" | \"none\" {\n return value === \"left\" || value === \"right\" ? value : \"none\";\n}\n"]}
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EACL,eAAe,GAShB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,mBAAmB,EACnB,WAAW,EACX,cAAc,EACd,aAAa,EACb,MAAM,EACN,MAAM,GASP,MAAM,oBAAoB,CAAC;AAuB5B,sDAAsD;AACtD,MAAM,iBAAiB,GAAG,SAAS,CAAC;AACpC,MAAM,iBAAiB,GAAG,SAAS,CAAC;AAEpC,yEAAyE;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAcnD,MAAM,OAAO,oBAAoB;IACd,OAAO,CAAyB;IACzC,YAAY,CAAoB;IACvB,aAAa,GAAG,IAAI,GAAG,EAAkC,CAAC;IAC1D,eAAe,GAAG,IAAI,GAAG,EAAc,CAAC;IACzD,6EAA6E;IAC5D,OAAO,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC3C,MAAM,GAAkB,EAAE,CAAC;IAC3B,YAAY,CAAS;IACtC,wEAAwE;IACvD,WAAW,CAAU;IAErB,OAAO,GAAiB,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC;IAEhF,YAAY,OAA+B;QACzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,mBAAmB,IAAI,CAAC,CAAC;QACrD,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,KAAK,CAAC,oBAAoB,KAAK,IAAI,CAAC;QAC/D,IAAI,CAAC,YAAY,GAAG,EAAE,GAAG,eAAe,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QACvE,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,aAAa,EAAE,CAAC;QACrB,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAC7B,CAAC;IAED,gFAAgF;IAExE,aAAa;QACnB,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,mBAAmB,CAAC;QAC1D,IAAI,CAAC,UAAU;YAAE,OAAO;QACxB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,IAA4B,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;QACxF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YACpB,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;IAEO,SAAS,CAAC,IAA4B;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,IAAI,KAAK,EAAE,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;QACjE,IAAI,KAAK,EAAE,OAAO,KAAK,SAAS;YAAE,IAAI,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC;QACjE,wEAAwE;QACxE,2DAA2D;QAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC;QAC/B,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC,OAAO,CAAC,GAAG,GAAG;gBACjB,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;gBACvC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC;aACrE,CAAC;QACJ,CAAC;QACD,uEAAuE;QACvE,wCAAwC;QACxC,IAAI,IAAI,CAAC,IAAI,KAAK,mBAAmB,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACzE,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,KAAK,mBAAmB,CAAC,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;YACvE,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC;QAC5B,CAAC;IACH,CAAC;IAEO,aAAa;QACnB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,cAAc,EAAE,cAAc,CAAC;QAChE,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC;QACrC,MAAM,SAAS,GAAG,GAAG,EAAE;YACrB,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC3B,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,eAAe,CAAC;gBAAE,QAAQ,EAAE,CAAC;QAC/D,CAAC,CAAC;QACF,MAAM,SAAS,GAAG,CAChB,UAA6F,EAC7F,OAAmB,EACb,EAAE;YACR,IAAI,CAAC,UAAU;gBAAE,OAAO;YACxB,MAAM,QAAQ,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;gBACpB,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;QACF,SAAS,CAAC,OAAO,EAAE,eAAe,EAAE,SAAS,CAAC,CAAC;QAC/C,SAAS,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,EAAE;YACxC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACrB,SAAS,EAAE,CAAC;QACd,CAAC,CAAC,CAAC;QACH,SAAS,CAAC,KAAK,EAAE,2BAA2B,EAAE,SAAS,CAAC,CAAC;QACzD,SAAS,CAAC,KAAK,EAAE,6BAA6B,EAAE,SAAS,CAAC,CAAC;IAC7D,CAAC;IAED,gFAAgF;IAEhF,yDAAyD;IACjD,OAAO;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,cAAc,EAAE,cAAc,EAAE,OAAO,IAAI,IAAI,CAAC;IAC1E,CAAC;IAEO,WAAW;QACjB,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,KAAK,EAAE,WAAW,IAAI,EAAE,CAAC;IACnD,CAAC;IAED,0DAA0D;IAClD,YAAY;QAClB,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,cAAc,EAAE,eAAe,CAAC;QACjE,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC;QAC1B,MAAM,OAAO,GAAG,OAAO,CAAC,iBAAiB,CAAC,qBAAqB,CAAC,CAAC;QACjE,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QACzD,MAAM,SAAS,GAAG,OAA2C,CAAC;QAC9D,OAAO,OAAO,SAAS,CAAC,qBAAqB,KAAK,UAAU;YAC1D,CAAC,CAAE,OAAmC;YACtC,CAAC,CAAC,IAAI,CAAC;IACX,CAAC;IAEO,OAAO,CAAC,UAAmC;QACjD,OAAO,IAAI,CAAC,YAAY,EAAE,EAAE,qBAAqB,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC;IACjF,CAAC;IAEO,mBAAmB;QACzB,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC;QAC1C,MAAM,OAAO,GAAG,CAAC,SAAS,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,mBAAmB,KAAK,SAAS,CAAC;QACnF,kEAAkE;QAClE,4DAA4D;QAC5D,MAAM,IAAI,GAAsB;YAC9B,GAAG,eAAe;YAClB,QAAQ,EAAE,IAAI;YACd,IAAI,EAAE,IAAI;YACV,SAAS,EAAE,OAAO;YAClB,qEAAqE;YACrE,sEAAsE;YACtE,iEAAiE;YACjE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,eAAe,CAAC,KAAK;YACnD,oEAAoE;YACpE,qEAAqE;YACrE,uDAAuD;YACvD,QAAQ,EAAE,KAAK;SAChB,CAAC;QACF,IAAI,SAAS,EAAE,CAAC;YACd,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC5C,IAAI,UAAU,CAAC,OAAO;oBAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;gBACzC,IAAI,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,OAAO;oBAAE,IAAI,CAAC,KAAK,GAAG,UAAU,CAAC;gBACnE,IAAI,UAAU,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;oBAChC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;oBACvB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBACpB,CAAC;gBACD,qEAAqE;gBACrE,oEAAoE;gBACpE,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC;oBAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;gBAChD,IAAI,UAAU,CAAC,gBAAgB;oBAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBACzD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;oBACvE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;gBACtB,CAAC;gBACD,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,MAAM,GAAG,CAAC;oBAAE,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;YACnE,CAAC;QACH,CAAC;QACD,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC3E,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QACzB,IAAI,OAAO,EAAE,CAAC;YACZ,KAAK,MAAM,QAAQ,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,CAAC;gBAAE,QAAQ,CAAC,IAAI,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;IAED,eAAe;QACb,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;IAED,qBAAqB,CAAC,QAAwC;QAC5D,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACjC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACnD,CAAC;IAED,gBAAgB,CAAC,QAAoB;QACnC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACnC,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrD,CAAC;IAED,gFAAgF;IAEhF,MAAM;QACJ,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,IAAI,CAAC,OAAO,EAAE,KAAK,IAAI,EAAE,CAAC;YAC5B,wEAAwE;YACxE,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI;gBAAE,IAAI,CAAC,mBAAmB,EAAE,CAAC;YACvD,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;QAC9B,CAAC;QAED,MAAM,SAAS,GAA0B,EAAE,CAAC;QAC5C,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC5C,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;YAC1E,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACtC,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CAAC;YACvC,MAAM,QAAQ,GAAwB;gBACpC,EAAE,EAAE,UAAU,CAAC,QAAQ;gBACvB,IAAI,EAAE,UAAU,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,YAAY;gBACzD,UAAU;gBACV,MAAM,EAAE,cAAc,CACpB,UAAU,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,CAAC,iBAAiB,CAAC;oBAClE,UAAU,CAAC,gBAAgB,EAAE,gBAAgB,EAAE,EAAE;oBACjD,IAAI,CACP;gBACD,OAAO,EAAE,cAAc,CACrB,UAAU,CAAC,gBAAgB,EAAE,kBAAkB,EAAE,CAAC,iBAAiB,CAAC,IAAI,IAAI,CAC7E;aACF,CAAC;YACF,IAAI,WAAW,EAAE,CAAC;gBAChB,QAAQ,CAAC,GAAG,GAAG;oBACb,MAAM,EAAE,MAAM,CAAC,WAAW,CAAC,mBAAmB,EAAE,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;oBAC9D,SAAS,EAAE,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC;iBACtD,CAAC;YACJ,CAAC;YACD,wEAAwE;YACxE,oEAAoE;YACpE,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,IAAI,WAAW,CAAC;YAChD,IAAI,QAAQ;gBAAE,QAAQ,CAAC,QAAQ,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC1D,MAAM,GAAG,GAAG,aAAa,CAAC,IAAI,EAAE,eAAe,CAAC,CAAC;YACjD,IAAI,GAAG;gBAAE,QAAQ,CAAC,QAAQ,GAAG,GAAG,CAAC;YACjC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvE,QAAQ,CAAC,gBAAgB,GAAG,IAAI,CAAC;YACnC,CAAC;YACD,uEAAuE;YACvE,iEAAiE;YACjE,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACzB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;OAGG;IACK,aAAa;QACnB,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,mBAAmB,KAAK,SAAS;YAAE,OAAO,EAAE,CAAC;QACpE,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QAC/C,IAAI,CAAC,GAAG;YAAE,OAAO,EAAE,CAAC;QACpB,MAAM,IAAI,GAAc;YACtB,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY;YACpD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY;YACpD,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,YAAY;SACrD,CAAC;QACF,OAAO;YACL;gBACE,EAAE,EAAE,iBAAiB;gBACrB,IAAI,EAAE,WAAW;gBACjB,UAAU,EAAE,MAAM;gBAClB,GAAG,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE;gBACrD,QAAQ,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,gBAAgB,CAAC,EAAE;gBACjF,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;gBACjC,mEAAmE;gBACnE,qEAAqE;gBACrE,4DAA4D;gBAC5D,OAAO,EAAE,CAAC;aACX;SACF,CAAC;IACJ,CAAC;IAED,kEAAkE;IAC1D,OAAO;QACb,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACzE,MAAM,GAAG,GAAG,MAAM,EAAE,GAAG,CAAC;QACxB,IAAI,CAAC,GAAG;YAAE,OAAO,IAAI,CAAC;QACtB,OAAO;YACL,MAAM,EAAE,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACvC,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC;SACrE,CAAC;IACJ,CAAC;IAEO,MAAM;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,IAAI,IAAI,CAAC;IACxE,CAAC;IAED,WAAW;QACT,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;QAC7B,OAAO;YACL,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,IAAI,MAAM,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;YACzE,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,gBAAgB,CAAC;SAC7C,CAAC;IACJ,CAAC;IAED,gFAAgF;IAEhF;;;;OAIG;IACH,KAAK,CAAC,QAAgB,EAAE,SAAiB,EAAE,UAAkB;QAC3D,MAAM,UAAU,GACd,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,UAAU;YACtC,IAAI,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAC1D,MAAM,gBAAgB,GAAG,UAAU,EAAE,gBAAgB,CAAC;QACtD,IAAI,CAAC,gBAAgB,EAAE,KAAK;YAAE,OAAO,KAAK,CAAC;QAC3C,KAAK,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QAC7E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,gFAAgF;IAEhF;;;;;OAKG;IACH,kBAAkB,CAAC,MAA0B,EAAE,OAAgB;QAC7D,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC;QACzD,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,KAAK,MAAM,UAAU,IAAI,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;YAC5C,MAAM,IAAI,GAAG,mBAAmB,CAAC,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;YACpE,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI;gBAAE,SAAS;YAClD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/C,IAAI,CAAC,UAAU,EAAE,CAAC,OAAO,CAAC,CAAC;gBAC3B,OAAO,GAAG,IAAI,CAAC;YACjB,CAAC;QACH,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;OAKG;IACH,mBAAmB,CAAC,KAAuB;QACzC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,4DAA4D;IACpD,UAAU,CAAC,UAAmC;QACpD,MAAM,KAAK,GAA+B,EAAE,CAAC;QAC7C,MAAM,IAAI,GAAG,UAAU,CAAC,gBAAgB,EAAE,QAAQ,CAAC;QACnD,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC;QACpD,IAAI,QAAQ;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO;QACL,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC;YAAE,GAAG,EAAE,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACrB,IAAI,CAAC,aAAa,CAAC,KAAK,EAAE,CAAC;QAC3B,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC;CACF;AAED,2EAA2E;AAC3E,SAAS,cAAc,CACrB,SAAmE;IAEnE,IAAI,CAAC,SAAS;QAAE,OAAO,CAAC,CAAC;IACzB,IAAI,OAAO,SAAS,CAAC,KAAK,KAAK,QAAQ,IAAI,SAAS,CAAC,KAAK,GAAG,CAAC;QAAE,OAAO,SAAS,CAAC,KAAK,CAAC;IACvF,OAAO,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnC,CAAC;AAED,SAAS,aAAa,CACpB,IAA8B,EAC9B,KAAa;IAEb,MAAM,IAAI,GAAG,IAAI,EAAE,YAAY,EAAE,CAAC,KAAK,CAAC,CAAC;IACzC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1D,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAyB;IACpD,OAAO,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;AAChE,CAAC","sourcesContent":["/**\n * BabylonInputProvider - the Babylon.js input provider.\n *\n * Reads a `WebXRDefaultExperience`: its `input.controllers` for poses and\n * motion-controller components, its session manager for what is live, and\n * the hand-tracking feature for fingertip joints. Falls back to the scene's\n * own pointer (`scene.onPointerObservable`) when no immersive session is\n * running, so the same scene is usable in a flat browser.\n *\n * Nothing here imports `@babylonjs/core` - see `babylon-types.ts`.\n *\n * Capabilities are derived from the LIVE session, never from the requested\n * configuration, and are re-published on session start, session end and\n * controller add/remove.\n */\nimport {\n NO_CAPABILITIES,\n type Handedness,\n type HeadPose,\n type InputCapabilities,\n type InputProvider,\n type InputSourceSnapshot,\n type PresenceModality,\n type Unsubscribe,\n type Vec3Tuple,\n} from \"@realitycollective/webxr-input\";\nimport {\n HAND_TRACKING_FEATURE,\n INDEX_TIP_JOINT,\n POINTER_EVENT_TYPES,\n nodeForward,\n defaultForward,\n nodeWorldPose,\n toQuat,\n toVec3,\n type BabylonCameraLike,\n type BabylonHandTrackingLike,\n type BabylonPointerInfoLike,\n type BabylonSceneLike,\n type BabylonTransformNodeLike,\n type BabylonXRControllerLike,\n type BabylonXRExperienceLike,\n type BabylonXRHandLike,\n} from \"./babylon-types.js\";\n\nexport interface BabylonProviderOptions {\n /**\n * The `WebXRDefaultExperience` (or anything with the same surface).\n * Omit it, or pass null, for a desktop-only app: the provider then only\n * ever reports the scene pointer.\n */\n xr?: BabylonXRExperienceLike | null;\n /** The scene. Supplies the pointer fallback, the camera and the engine. */\n scene: BabylonSceneLike;\n /** Head pose camera. Defaults to `scene.activeCamera`. */\n camera?: BabylonCameraLike | null;\n /**\n * Metres along the pointer ray at which the desktop fallback places its\n * synthetic grip pose. Default 1 metre, matching the three.js adapter, so\n * grab, hinge, dial and slide follow the cursor on desktop. Set it near\n * the distance of the things being manipulated; at 0 the grip sits on the\n * camera and a mouse drag reports camera motion only.\n */\n desktopGripDistance?: number;\n}\n\n/** Trigger, then the main component, then nothing. */\nconst TRIGGER_COMPONENT = \"trigger\";\nconst SQUEEZE_COMPONENT = \"squeeze\";\n\n/** The desktop pointer source's id - stable for the life of the page. */\nexport const DESKTOP_SOURCE_ID = \"babylon-pointer\";\n\ninterface PointerState {\n down: boolean;\n x: number;\n y: number;\n ray: { origin: Vec3Tuple; direction: Vec3Tuple } | null;\n}\n\ninterface SampledSource {\n handedness: \"left\" | \"right\" | \"none\";\n controller: BabylonXRControllerLike;\n}\n\nexport class BabylonInputProvider implements InputProvider {\n private readonly options: BabylonProviderOptions;\n private capabilities: InputCapabilities;\n private readonly capsListeners = new Set<(c: InputCapabilities) => void>();\n private readonly sourceListeners = new Set<() => void>();\n /** id -> handedness + controller, recorded while sampling, read by pulse. */\n private readonly sampled = new Map<string, SampledSource>();\n private readonly detach: Unsubscribe[] = [];\n private readonly gripDistance: number;\n /** Read once from `scene.useRightHandedSystem`; flips forward to -Z. */\n private readonly rightHanded: boolean;\n\n private readonly pointer: PointerState = { down: false, x: 0, y: 0, ray: null };\n\n constructor(options: BabylonProviderOptions) {\n this.options = options;\n this.gripDistance = options.desktopGripDistance ?? 1;\n this.rightHanded = options.scene.useRightHandedSystem === true;\n this.capabilities = { ...NO_CAPABILITIES, headPose: true, gaze: true };\n this.attachPointer();\n this.attachSession();\n this.refreshCapabilities();\n }\n\n // -- wiring -------------------------------------------------------------------\n\n private attachPointer(): void {\n const observable = this.options.scene.onPointerObservable;\n if (!observable) return;\n const observer = observable.add((info: BabylonPointerInfoLike) => this.onPointer(info));\n this.detach.push(() => {\n observable.remove(observer);\n });\n }\n\n private onPointer(info: BabylonPointerInfoLike): void {\n const event = info.event;\n if (event?.clientX !== undefined) this.pointer.x = event.clientX;\n if (event?.clientY !== undefined) this.pointer.y = event.clientY;\n // Babylon hands the pick it already did for this event; reuse it rather\n // than picking the scene a second time on the next sample.\n const ray = info.pickInfo?.ray;\n if (ray) {\n this.pointer.ray = {\n origin: toVec3(ray.origin) ?? [0, 0, 0],\n direction: toVec3(ray.direction) ?? defaultForward(this.rightHanded),\n };\n }\n // Only the primary button selects. Babylon reports it as button 0, the\n // same numbering as a DOM PointerEvent.\n if (info.type === POINTER_EVENT_TYPES.down && (event?.button ?? 0) === 0) {\n this.pointer.down = true;\n }\n if (info.type === POINTER_EVENT_TYPES.up && (event?.button ?? 0) === 0) {\n this.pointer.down = false;\n }\n }\n\n private attachSession(): void {\n const manager = this.options.xr?.baseExperience?.sessionManager;\n const input = this.options.xr?.input;\n const republish = () => {\n this.refreshCapabilities();\n for (const listener of [...this.sourceListeners]) listener();\n };\n const subscribe = <T>(\n observable: { add(cb: (value: T) => void): unknown; remove(o: unknown): boolean } | undefined,\n handler: () => void,\n ): void => {\n if (!observable) return;\n const observer = observable.add(() => handler());\n this.detach.push(() => {\n observable.remove(observer);\n });\n };\n subscribe(manager?.onXRSessionInit, republish);\n subscribe(manager?.onXRSessionEnded, () => {\n this.sampled.clear();\n republish();\n });\n subscribe(input?.onControllerAddedObservable, republish);\n subscribe(input?.onControllerRemovedObservable, republish);\n }\n\n // -- capabilities -------------------------------------------------------------\n\n /** The live XR session, or null when none is running. */\n private session(): unknown {\n return this.options.xr?.baseExperience?.sessionManager?.session ?? null;\n }\n\n private controllers(): readonly BabylonXRControllerLike[] {\n return this.options.xr?.input?.controllers ?? [];\n }\n\n /** The hand-tracking feature, when the app enabled it. */\n private handTracking(): BabylonHandTrackingLike | null {\n const manager = this.options.xr?.baseExperience?.featuresManager;\n if (!manager) return null;\n const feature = manager.getEnabledFeature(HAND_TRACKING_FEATURE);\n if (!feature || typeof feature !== \"object\") return null;\n const candidate = feature as Partial<BabylonHandTrackingLike>;\n return typeof candidate.getHandByControllerId === \"function\"\n ? (feature as BabylonHandTrackingLike)\n : null;\n }\n\n private handFor(controller: BabylonXRControllerLike): BabylonXRHandLike | null {\n return this.handTracking()?.getHandByControllerId(controller.uniqueId) ?? null;\n }\n\n private refreshCapabilities(): void {\n const inSession = this.session() !== null;\n const desktop = !inSession && this.options.scene.onPointerObservable !== undefined;\n // Babylon reports no native grab observation, so grabs are always\n // poseOnly: the core drives the transform through the port.\n const next: InputCapabilities = {\n ...NO_CAPABILITIES,\n headPose: true,\n gaze: true,\n pointer2d: desktop,\n // The pointer fallback synthesises a grip on the ray, so hand-driven\n // behaviours negotiate on desktop too. Without this every one of them\n // switches itself off off-headset and only press remains usable.\n grabs: desktop ? \"poseOnly\" : NO_CAPABILITIES.grabs,\n // Set in the loop below: presence is true once Babylon has built at\n // least one visual this provider can hide - a motion controller root\n // mesh, or a hand mesh from the hand-tracking feature.\n presence: false,\n };\n if (inSession) {\n for (const controller of this.controllers()) {\n if (controller.pointer) next.rays = true;\n if (controller.grip ?? controller.pointer) next.grabs = \"poseOnly\";\n if (controller.inputSource.hand) {\n next.handJoints = true;\n next.pokes = true;\n }\n // Pinch strength needs the tracked hand itself, not just the flag on\n // the input source, so it is gated on the feature reporting a hand.\n if (this.handFor(controller)) next.pinch = true;\n if (controller.motionController) next.buttonsAxes = true;\n if ((controller.inputSource.gamepad?.hapticActuators?.length ?? 0) > 0) {\n next.haptics = true;\n }\n if (this.visualsFor(controller).length > 0) next.presence = true;\n }\n }\n const changed = JSON.stringify(next) !== JSON.stringify(this.capabilities);\n this.capabilities = next;\n if (changed) {\n for (const listener of [...this.capsListeners]) listener(next);\n }\n }\n\n getCapabilities(): InputCapabilities {\n return this.capabilities;\n }\n\n onCapabilitiesChanged(listener: (c: InputCapabilities) => void): Unsubscribe {\n this.capsListeners.add(listener);\n return () => this.capsListeners.delete(listener);\n }\n\n onSourcesChanged(listener: () => void): Unsubscribe {\n this.sourceListeners.add(listener);\n return () => this.sourceListeners.delete(listener);\n }\n\n // -- sampling -----------------------------------------------------------------\n\n sample(): readonly InputSourceSnapshot[] {\n this.sampled.clear();\n if (this.session() === null) {\n // A session that ended between frames leaves stale capabilities behind.\n if (this.capabilities.rays) this.refreshCapabilities();\n return this.samplePointer();\n }\n\n const snapshots: InputSourceSnapshot[] = [];\n for (const controller of this.controllers()) {\n const handedness = normalizeHandedness(controller.inputSource.handedness);\n const hand = this.handFor(controller);\n const pointerNode = controller.pointer;\n const snapshot: InputSourceSnapshot = {\n id: controller.uniqueId,\n kind: controller.inputSource.hand ? \"hand\" : \"controller\",\n handedness,\n select: componentValue(\n controller.motionController?.getComponentOfType?.(TRIGGER_COMPONENT) ??\n controller.motionController?.getMainComponent?.() ??\n null,\n ),\n squeeze: componentValue(\n controller.motionController?.getComponentOfType?.(SQUEEZE_COMPONENT) ?? null,\n ),\n };\n if (pointerNode) {\n snapshot.ray = {\n origin: toVec3(pointerNode.getAbsolutePosition()) ?? [0, 0, 0],\n direction: nodeForward(pointerNode, this.rightHanded),\n };\n }\n // Babylon exposes the grip node only for a controller with one; a hand,\n // and a controller without a grip space, carry on the pointer node.\n const gripNode = controller.grip ?? pointerNode;\n if (gripNode) snapshot.gripPose = nodeWorldPose(gripNode);\n const tip = jointPosition(hand, INDEX_TIP_JOINT);\n if (tip) snapshot.indexTip = tip;\n if ((controller.inputSource.gamepad?.hapticActuators?.length ?? 0) > 0) {\n snapshot.hapticsAvailable = true;\n }\n // Babylon reports no per-pose velocity, so nothing is set here and the\n // core's VelocityTracker derives it from consecutive grip poses.\n snapshots.push(snapshot);\n this.sampled.set(snapshot.id, { handedness, controller });\n }\n return snapshots;\n }\n\n /**\n * The desktop pointer, shaped like the three.js adapter's mouse fallback:\n * one source, `handedness: \"none\"`, `kind: \"pointer2d\"`.\n */\n private samplePointer(): readonly InputSourceSnapshot[] {\n if (this.options.scene.onPointerObservable === undefined) return [];\n const ray = this.pointer.ray ?? this.pickRay();\n if (!ray) return [];\n const grip: Vec3Tuple = [\n ray.origin[0] + ray.direction[0] * this.gripDistance,\n ray.origin[1] + ray.direction[1] * this.gripDistance,\n ray.origin[2] + ray.direction[2] * this.gripDistance,\n ];\n return [\n {\n id: DESKTOP_SOURCE_ID,\n kind: \"pointer2d\",\n handedness: \"none\",\n ray: { origin: ray.origin, direction: ray.direction },\n gripPose: { position: grip, quaternion: toQuat(this.camera()?.absoluteRotation) },\n select: this.pointer.down ? 1 : 0,\n // A mouse has one button that means select. Squeeze stays 0 rather\n // than doubling the left button up as both, which would fire a press\n // and a grab together on an interactable that carries both.\n squeeze: 0,\n },\n ];\n }\n\n /** Pick the scene at the last known cursor position for a ray. */\n private pickRay(): { origin: Vec3Tuple; direction: Vec3Tuple } | null {\n const picked = this.options.scene.pick?.(this.pointer.x, this.pointer.y);\n const ray = picked?.ray;\n if (!ray) return null;\n return {\n origin: toVec3(ray.origin) ?? [0, 0, 0],\n direction: toVec3(ray.direction) ?? defaultForward(this.rightHanded),\n };\n }\n\n private camera(): BabylonCameraLike | null {\n return this.options.camera ?? this.options.scene.activeCamera ?? null;\n }\n\n getHeadPose(): HeadPose {\n const camera = this.camera();\n return {\n position: toVec3(camera?.globalPosition ?? camera?.position) ?? [0, 0, 0],\n quaternion: toQuat(camera?.absoluteRotation),\n };\n }\n\n // -- haptics ------------------------------------------------------------------\n\n /**\n * Babylon routes haptics through the motion controller rather than the\n * gamepad actuator, so a source with no motion controller cannot pulse\n * even when the gamepad reports an actuator.\n */\n pulse(sourceId: string, intensity: number, durationMs: number): boolean {\n const controller =\n this.sampled.get(sourceId)?.controller ??\n this.controllers().find((c) => c.uniqueId === sourceId);\n const motionController = controller?.motionController;\n if (!motionController?.pulse) return false;\n void motionController.pulse(Math.min(1, Math.max(0, intensity)), durationMs);\n return true;\n }\n\n // -- presence -----------------------------------------------------------------\n\n /**\n * Show or hide the visuals Babylon built for the targeted sides. Returns\n * false when the target has nothing to show or hide. `\"none\"` targets no\n * side, so it reports whether presence is usable at all without changing\n * anything.\n */\n setPresenceVisible(target: Handedness | \"all\", visible: boolean): boolean {\n if (target === \"none\") return this.capabilities.presence;\n let applied = false;\n for (const controller of this.controllers()) {\n const side = normalizeHandedness(controller.inputSource.handedness);\n if (target !== \"all\" && target !== side) continue;\n for (const node of this.visualsFor(controller)) {\n node.setEnabled?.(visible);\n applied = true;\n }\n }\n return applied;\n }\n\n /**\n * Always false. Babylon picks the visual per input source - a controller\n * gets its motion-controller model, a tracked hand gets the hand mesh -\n * and exposes no switch between the two. IWSDK builds both families, so\n * its adapter implements this.\n */\n setPresenceModality(_mode: PresenceModality): boolean {\n return false;\n }\n\n /** Every node that represents this controller on screen. */\n private visualsFor(controller: BabylonXRControllerLike): BabylonTransformNodeLike[] {\n const nodes: BabylonTransformNodeLike[] = [];\n const root = controller.motionController?.rootMesh;\n if (root) nodes.push(root);\n const handMesh = this.handFor(controller)?.handMesh;\n if (handMesh) nodes.push(handMesh);\n return nodes;\n }\n\n dispose(): void {\n for (const off of this.detach.splice(0)) off();\n this.sampled.clear();\n this.capsListeners.clear();\n this.sourceListeners.clear();\n }\n}\n\n/** Analog value where the component reports one, else the pressed flag. */\nfunction componentValue(\n component: { value?: number; pressed?: boolean } | null | undefined,\n): number {\n if (!component) return 0;\n if (typeof component.value === \"number\" && component.value > 0) return component.value;\n return component.pressed ? 1 : 0;\n}\n\nfunction jointPosition(\n hand: BabylonXRHandLike | null,\n joint: string,\n): Vec3Tuple | null {\n const mesh = hand?.getJointMesh?.(joint);\n return mesh ? toVec3(mesh.getAbsolutePosition()) : null;\n}\n\nfunction normalizeHandedness(value: string | undefined): \"left\" | \"right\" | \"none\" {\n return value === \"left\" || value === \"right\" ? value : \"none\";\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realitycollective/babylon-interactions",
|
|
3
|
-
"version": "0.1.0-preview.
|
|
3
|
+
"version": "0.1.0-preview.3",
|
|
4
4
|
"description": "Babylon.js adapter for the Reality Collective Interaction Extensions - maps a Babylon WebXR experience (WebXRDefaultExperience controllers, motion controller components, hand-tracking joints) into the engine-free @realitycollective/webxr-interactions core, with a scene pointer fallback on desktop. Structurally typed: no @babylonjs/core import. Re-exports the core.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"realitycollective",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@realitycollective/webxr-input": "^0.1.
|
|
37
|
-
"@realitycollective/webxr-interactions": "^0.1.0-preview.
|
|
36
|
+
"@realitycollective/webxr-input": "^0.1.1",
|
|
37
|
+
"@realitycollective/webxr-interactions": "^0.1.0-preview.3"
|
|
38
38
|
},
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|