@realitycollective/native-interactions 0.1.1-preview.2 → 0.1.1

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.
@@ -1,8 +1,16 @@
1
1
  import { copyRay, copyVec3, resolveHostSlice, } from "./native-types.js";
2
2
  export class NativeHitTester {
3
3
  host;
4
+ hitCone;
4
5
  constructor(options = {}) {
5
6
  this.host = resolveHostSlice("interactions", options.interactions);
7
+ const host = this.host;
8
+ if (host.hitCone) {
9
+ this.hitCone = (ray, halfAngle, maxLength) => {
10
+ const hit = host.hitCone(copyRay(ray), halfAngle, maxLength);
11
+ return hit ? toInteractableHit(hit) : null;
12
+ };
13
+ }
6
14
  }
7
15
  hitRay(ray) {
8
16
  const hit = this.host.hitRay(copyRay(ray));
@@ -1 +1 @@
1
- {"version":3,"file":"hit-tester.js","sourceRoot":"","sources":["../src/hit-tester.ts"],"names":[],"mappings":"AASA,OAAO,EACL,OAAO,EACP,QAAQ,EACR,gBAAgB,GAGjB,MAAM,mBAAmB,CAAC;AAO3B,MAAM,OAAO,eAAe;IACT,IAAI,CAAwB;IAE7C,YAAY,UAAkC,EAAE;QAC9C,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC,cAAc,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,CAAC,GAAa;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3C,OAAO,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;IAED,YAAY,CAAC,KAAgB,EAAE,MAAc;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;QAC5D,OAAO,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;CACF;AAED,SAAS,iBAAiB,CAAC,GAAc;IACvC,OAAO,EAAE,cAAc,EAAE,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;AAC9F,CAAC","sourcesContent":["/**\n * NativeHitTester - resolves the core's ray and proximity queries against\n * the native host's `interactions` slice. The native app owns the scene and\n * physics, so targeting is entirely the host's answer; this class only\n * copies the result across and renames its `targetId` to the core's\n * `interactableId`.\n */\nimport type { RayTuple, Vec3Tuple } from \"@realitycollective/webxr-input\";\nimport type { HitTester, InteractableHit } from \"@realitycollective/webxr-interactions\";\nimport {\n copyRay,\n copyVec3,\n resolveHostSlice,\n type NativeHit,\n type NativeInteractionHost,\n} from \"./native-types.js\";\n\nexport interface NativeHitTesterOptions {\n /** The `interactions` slice. Omit to read `globalThis.__rcHost.interactions`. */\n interactions?: NativeInteractionHost;\n}\n\nexport class NativeHitTester implements HitTester {\n private readonly host: NativeInteractionHost;\n\n constructor(options: NativeHitTesterOptions = {}) {\n this.host = resolveHostSlice(\"interactions\", options.interactions);\n }\n\n hitRay(ray: RayTuple): InteractableHit | null {\n const hit = this.host.hitRay(copyRay(ray));\n return hit ? toInteractableHit(hit) : null;\n }\n\n hitProximity(point: Vec3Tuple, radius: number): InteractableHit | null {\n const hit = this.host.hitProximity(copyVec3(point), radius);\n return hit ? toInteractableHit(hit) : null;\n }\n}\n\nfunction toInteractableHit(hit: NativeHit): InteractableHit {\n return { interactableId: hit.targetId, distance: hit.distance, point: copyVec3(hit.point) };\n}\n"]}
1
+ {"version":3,"file":"hit-tester.js","sourceRoot":"","sources":["../src/hit-tester.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,OAAO,EACP,QAAQ,EACR,gBAAgB,GAGjB,MAAM,mBAAmB,CAAC;AAO3B,MAAM,OAAO,eAAe;IACT,IAAI,CAAwB;IACpC,OAAO,CAAmF;IAEnG,YAAY,UAAkC,EAAE;QAC9C,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC,cAAc,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACvB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,OAAO,GAAG,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE;gBAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;gBAC9D,OAAO,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC7C,CAAC,CAAC;QACJ,CAAC;IACH,CAAC;IAED,MAAM,CAAC,GAAa;QAClB,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3C,OAAO,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;IAED,YAAY,CAAC,KAAgB,EAAE,MAAc;QAC3C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC;QAC5D,OAAO,GAAG,CAAC,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAC7C,CAAC;CACF;AAED,SAAS,iBAAiB,CAAC,GAAc;IACvC,OAAO,EAAE,cAAc,EAAE,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,GAAG,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;AAC9F,CAAC","sourcesContent":["/**\n * NativeHitTester - resolves the core's ray, proximity and cone queries\n * against the native host's `interactions` slice. The native app owns the\n * scene and physics, so targeting is entirely the host's answer; this class\n * only copies the result across and renames its `targetId` to the core's\n * `interactableId`. `hitCone` is present only when the host slice carries\n * one, so the runtime falls back to `hitRay` for eye gaze on a host without.\n */\nimport type { RayTuple, Vec3Tuple } from \"@realitycollective/webxr-input\";\nimport type { HitTester, InteractableHit } from \"@realitycollective/webxr-interactions\";\nimport {\n copyRay,\n copyVec3,\n resolveHostSlice,\n type NativeHit,\n type NativeInteractionHost,\n} from \"./native-types.js\";\n\nexport interface NativeHitTesterOptions {\n /** The `interactions` slice. Omit to read `globalThis.__rcHost.interactions`. */\n interactions?: NativeInteractionHost;\n}\n\nexport class NativeHitTester implements HitTester {\n private readonly host: NativeInteractionHost;\n readonly hitCone?: (ray: RayTuple, halfAngle: number, maxLength: number) => InteractableHit | null;\n\n constructor(options: NativeHitTesterOptions = {}) {\n this.host = resolveHostSlice(\"interactions\", options.interactions);\n const host = this.host;\n if (host.hitCone) {\n this.hitCone = (ray, halfAngle, maxLength) => {\n const hit = host.hitCone!(copyRay(ray), halfAngle, maxLength);\n return hit ? toInteractableHit(hit) : null;\n };\n }\n }\n\n hitRay(ray: RayTuple): InteractableHit | null {\n const hit = this.host.hitRay(copyRay(ray));\n return hit ? toInteractableHit(hit) : null;\n }\n\n hitProximity(point: Vec3Tuple, radius: number): InteractableHit | null {\n const hit = this.host.hitProximity(copyVec3(point), radius);\n return hit ? toInteractableHit(hit) : null;\n }\n}\n\nfunction toInteractableHit(hit: NativeHit): InteractableHit {\n return { interactableId: hit.targetId, distance: hit.distance, point: copyVec3(hit.point) };\n}\n"]}
package/dist/host.d.ts CHANGED
@@ -14,13 +14,39 @@
14
14
  * app's own frame callback. Without it, call `update(dt)` yourself with
15
15
  * seconds.
16
16
  */
17
- import { InteractionRuntime, type DwellConfig, type InteractableDescriptor } from "@realitycollective/webxr-interactions";
17
+ import { InteractionRuntime, type DwellConfig, type InteractableDescriptor, type NearPointerOptions, type PhysicsBodySpec, type PhysicsShapeSpec, type PointerArbiter, type PointerDisplay, type PointerDisplayConfig } from "@realitycollective/webxr-interactions";
18
18
  import { NativeHitTester, type NativeHitTesterOptions } from "./hit-tester.js";
19
+ import { NativePhysicsFacility } from "./physics-facility.js";
19
20
  import { NativeInputProvider, type NativeInputProviderOptions } from "./provider.js";
20
21
  import { NativeTransformPort } from "./transform-port.js";
21
- import { type NativeFrameSource } from "./native-types.js";
22
+ import { type NativeFrameSource, type NativePhysicsHost } from "./native-types.js";
22
23
  export interface NativeInteractionsOptions extends NativeInputProviderOptions, NativeHitTesterOptions {
23
24
  dwellDefaults?: DwellConfig;
25
+ /** The near-pointer distances (touch hover, touch press, grab radius). Defaults are IWSDK 1.0.0's. */
26
+ nearPointer?: Partial<NearPointerOptions>;
27
+ /**
28
+ * The pointer arbiter shared with the UI Extensions native host, so one
29
+ * decision per source covers panels and interactables (the runtime's
30
+ * `pointers` option). Omit when the app has no panels.
31
+ */
32
+ pointers?: PointerArbiter;
33
+ /**
34
+ * The app's pointer display settings (the runtime's `pointerDisplay`
35
+ * option): ray never, always or while hitting; cursors on objects and on
36
+ * panels; the ray and cursor look. Defaults are IWSDK 1.0.0's. The host is
37
+ * handed the resolved drawing every frame and the settings on every change.
38
+ */
39
+ pointerDisplay?: PointerDisplay | Partial<PointerDisplayConfig>;
40
+ /**
41
+ * The `physics` slice: the host's default engine (Jolt on Quest and
42
+ * Android, RealityKit on visionOS) behind the core contract, or an app's
43
+ * own `PhysicsFacility` in its place. Omit to read
44
+ * `globalThis.__rcHost.physics`. Without either, `physics` is null and no
45
+ * target can carry a body; the host conformance kit fails such a host.
46
+ */
47
+ physics?: NativePhysicsHost;
48
+ /** Step the physics from `update(dt)`. Default true; false when the host steps its engine itself. */
49
+ stepPhysics?: boolean;
24
50
  /**
25
51
  * Drive `update(dt)` from the native app's frame callback. Default false -
26
52
  * the app calls `update` from its own loop.
@@ -36,15 +62,30 @@ export interface NativeRegisterOptions {
36
62
  * 0.1, the radius IWSDK gives a target that declares none.
37
63
  */
38
64
  targetRadius?: number;
65
+ /**
66
+ * Give the target a body in the `physics` slice at its current world pose
67
+ * (core defaults: dynamic, no damping, gravity factor 1). A target whose
68
+ * body the host already holds (`physics.hasBody(id)`) needs neither this
69
+ * nor `shape`: its port is bound to that body as it is.
70
+ */
71
+ body?: PhysicsBodySpec;
72
+ /** The body's collider (default `"auto"`, the host's collider for the object). Implies `body`. */
73
+ shape?: PhysicsShapeSpec;
39
74
  }
40
75
  export declare class NativeInteractions {
41
76
  readonly runtime: InteractionRuntime;
42
77
  readonly provider: NativeInputProvider;
43
78
  readonly hitTester: NativeHitTester;
79
+ /** The platform's physics over the `physics` slice, or null when the host installs none. */
80
+ readonly physics: NativePhysicsFacility | null;
81
+ private readonly stepPhysics;
44
82
  private readonly ports;
83
+ private readonly ownedBodies;
45
84
  private readonly options;
46
85
  private readonly interactionsHost;
47
86
  private detachHost;
87
+ private readonly unsubscribeVisuals;
88
+ private readonly unsubscribeDisplay;
48
89
  constructor(options?: NativeInteractionsOptions);
49
90
  /**
50
91
  * Register an interactable. The native app knows it by `descriptor.id`,
package/dist/host.js CHANGED
@@ -16,9 +16,10 @@
16
16
  */
17
17
  import { InteractionRuntime, } from "@realitycollective/webxr-interactions";
18
18
  import { NativeHitTester } from "./hit-tester.js";
19
+ import { NativePhysicsFacility } from "./physics-facility.js";
19
20
  import { NativeInputProvider } from "./provider.js";
20
21
  import { NativeTransformPort } from "./transform-port.js";
21
- import { installedHost, resolveHostSlice, } from "./native-types.js";
22
+ import { findHostSlice, installedHost, resolveHostSlice, } from "./native-types.js";
22
23
  /** The radius a target is hit-tested with when it declares none: IWSDK's `targetRadius ?? 0.1`. */
23
24
  const DEFAULT_TARGET_RADIUS = 0.1;
24
25
  /** Longest frame the host-attached loop will report, in seconds. */
@@ -27,10 +28,16 @@ export class NativeInteractions {
27
28
  runtime;
28
29
  provider;
29
30
  hitTester;
31
+ /** The platform's physics over the `physics` slice, or null when the host installs none. */
32
+ physics;
33
+ stepPhysics;
30
34
  ports = new Map();
35
+ ownedBodies = new Set();
31
36
  options;
32
37
  interactionsHost;
33
38
  detachHost = null;
39
+ unsubscribeVisuals;
40
+ unsubscribeDisplay;
34
41
  constructor(options = {}) {
35
42
  this.options = options;
36
43
  this.interactionsHost = resolveHostSlice("interactions", options.interactions);
@@ -40,7 +47,35 @@ export class NativeInteractions {
40
47
  provider: this.provider,
41
48
  hitTester: this.hitTester,
42
49
  ...(options.dwellDefaults ? { dwellDefaults: options.dwellDefaults } : {}),
50
+ ...(options.nearPointer ? { nearPointer: options.nearPointer } : {}),
51
+ ...(options.pointers ? { pointers: options.pointers } : {}),
52
+ ...(options.pointerDisplay ? { pointerDisplay: options.pointerDisplay } : {}),
43
53
  });
54
+ const physicsHost = findHostSlice("physics", options.physics);
55
+ this.physics = physicsHost ? new NativePhysicsFacility({ physics: physicsHost }) : null;
56
+ this.stepPhysics = options.stepPhysics ?? true;
57
+ // The host is handed the ray and cursor DRAWING every frame, per source
58
+ // (`NativeInputHost.applyPointerVisuals`): the arbiter's decision under
59
+ // the app's pointer display settings, fully resolved. It draws exactly that.
60
+ const input = resolveHostSlice("input", options.input);
61
+ this.unsubscribeVisuals = this.runtime.onPointerDrawing((drawings) => {
62
+ if (!input.applyPointerVisuals)
63
+ return;
64
+ for (const drawing of drawings) {
65
+ const visuals = this.runtime.getPointerVisuals(drawing.sourceId);
66
+ input.applyPointerVisuals(drawing.sourceId, {
67
+ ...drawing,
68
+ rayColor: [drawing.rayColor[0], drawing.rayColor[1], drawing.rayColor[2]],
69
+ activePointer: visuals?.activePointer ?? null,
70
+ targetKind: visuals?.targetKind ?? null,
71
+ targetId: visuals?.targetId ?? null,
72
+ hitDistance: visuals?.hitDistance ?? null,
73
+ });
74
+ }
75
+ });
76
+ const display = this.runtime.getPointerDisplay();
77
+ input.applyPointerDisplay?.({ ...display.get() });
78
+ this.unsubscribeDisplay = display.onChange((config) => input.applyPointerDisplay?.({ ...config }));
44
79
  if (options.attachToHost)
45
80
  this.attachToHost(options);
46
81
  }
@@ -50,7 +85,19 @@ export class NativeInteractions {
50
85
  * 0.1 m, so a bare target is a 10 cm sphere as on IWSDK.
51
86
  */
52
87
  register(descriptor, options = {}) {
53
- const port = new NativeTransformPort(descriptor.id, this.options.interactions ? { interactions: this.options.interactions } : {});
88
+ const id = descriptor.id;
89
+ if (options.body || options.shape) {
90
+ if (!this.physics) {
91
+ throw new Error(`@realitycollective/native-interactions: "${id}" asks for a physics body but no physics slice is installed; the host installs globalThis.__rcHost.physics, or pass \`physics\` to createNativeInteractions`);
92
+ }
93
+ this.physics.addBody(id, this.interactionsHost.getWorldPose(id), options.body, options.shape);
94
+ this.ownedBodies.add(id);
95
+ }
96
+ const bound = this.physics && this.physics.hasBody(id) ? this.physics : null;
97
+ const port = new NativeTransformPort(id, {
98
+ ...(this.options.interactions ? { interactions: this.options.interactions } : {}),
99
+ ...(bound ? { physics: { facility: bound, bodyId: id } } : {}),
100
+ });
54
101
  this.interactionsHost.setTargetRadius(descriptor.id, options.targetRadius ?? DEFAULT_TARGET_RADIUS);
55
102
  this.ports.set(descriptor.id, port);
56
103
  this.runtime.registerInteractable(descriptor, { transform: port });
@@ -59,16 +106,24 @@ export class NativeInteractions {
59
106
  unregister(id) {
60
107
  this.runtime.unregisterInteractable(id);
61
108
  this.ports.delete(id);
109
+ if (this.ownedBodies.delete(id))
110
+ this.physics?.removeBody(id);
62
111
  }
63
112
  getPort(id) {
64
113
  return this.ports.get(id);
65
114
  }
66
115
  update(dt) {
116
+ // The provider's eye-gaze filter and grace integrate over the same step.
117
+ this.provider.setFrameDelta(dt);
67
118
  this.runtime.update(dt);
119
+ if (this.stepPhysics)
120
+ this.physics?.step(dt);
68
121
  }
69
122
  dispose() {
70
123
  this.detachHost?.();
71
124
  this.detachHost = null;
125
+ this.unsubscribeVisuals();
126
+ this.unsubscribeDisplay();
72
127
  this.runtime.dispose();
73
128
  this.provider.dispose();
74
129
  }
package/dist/host.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * createNativeInteractions - one-call setup for the native adapter, with the\n * same shape as the Babylon, three.js, IWSDK and XR Blocks setups.\n *\n * ```ts\n * const interactions = createNativeInteractions({ attachToHost: true });\n * interactions.register({ id: \"button\", behaviours: [{ kind: \"press\" }] });\n * interactions.runtime.onEvent((event) => console.log(event.type));\n * ```\n *\n * The native app owns the scene, so an interactable is registered by id\n * alone: the app answers hit queries and pose reads for that id through the\n * `interactions` slice. With `attachToHost` the loop drives itself from the\n * app's own frame callback. Without it, call `update(dt)` yourself with\n * seconds.\n */\nimport {\n InteractionRuntime,\n type DwellConfig,\n type InteractableDescriptor,\n} from \"@realitycollective/webxr-interactions\";\nimport { NativeHitTester, type NativeHitTesterOptions } from \"./hit-tester.js\";\nimport { NativeInputProvider, type NativeInputProviderOptions } from \"./provider.js\";\nimport { NativeTransformPort } from \"./transform-port.js\";\nimport {\n installedHost,\n resolveHostSlice,\n type NativeFrameSource,\n type NativeInteractionHost,\n} from \"./native-types.js\";\n\nexport interface NativeInteractionsOptions\n extends NativeInputProviderOptions,\n NativeHitTesterOptions {\n dwellDefaults?: DwellConfig;\n /**\n * Drive `update(dt)` from the native app's frame callback. Default false -\n * the app calls `update` from its own loop.\n */\n attachToHost?: boolean;\n /** Where frames come from for `attachToHost`. Omit to use `globalThis.__rcHost`. */\n frames?: NativeFrameSource;\n}\n\n/** Per-registration options, as IWSDK's `register` takes them. */\nexport interface NativeRegisterOptions {\n /**\n * The radius, in metres, the host hit-tests this target with. Default\n * 0.1, the radius IWSDK gives a target that declares none.\n */\n targetRadius?: number;\n}\n\n/** The radius a target is hit-tested with when it declares none: IWSDK's `targetRadius ?? 0.1`. */\nconst DEFAULT_TARGET_RADIUS = 0.1;\n\n/** Longest frame the host-attached loop will report, in seconds. */\nconst MAX_FRAME_SECONDS = 0.1;\n\nexport class NativeInteractions {\n readonly runtime: InteractionRuntime;\n readonly provider: NativeInputProvider;\n readonly hitTester: NativeHitTester;\n private readonly ports = new Map<string, NativeTransformPort>();\n private readonly options: NativeInteractionsOptions;\n private readonly interactionsHost: NativeInteractionHost;\n private detachHost: (() => void) | null = null;\n\n constructor(options: NativeInteractionsOptions = {}) {\n this.options = options;\n this.interactionsHost = resolveHostSlice(\"interactions\", options.interactions);\n this.provider = new NativeInputProvider(options);\n this.hitTester = new NativeHitTester(options);\n this.runtime = new InteractionRuntime({\n provider: this.provider,\n hitTester: this.hitTester,\n ...(options.dwellDefaults ? { dwellDefaults: options.dwellDefaults } : {}),\n });\n if (options.attachToHost) this.attachToHost(options);\n }\n\n /**\n * Register an interactable. The native app knows it by `descriptor.id`,\n * and is told the radius to hit-test it with: `options.targetRadius`, or\n * 0.1 m, so a bare target is a 10 cm sphere as on IWSDK.\n */\n register(descriptor: InteractableDescriptor, options: NativeRegisterOptions = {}): NativeTransformPort {\n const port = new NativeTransformPort(\n descriptor.id,\n this.options.interactions ? { interactions: this.options.interactions } : {},\n );\n this.interactionsHost.setTargetRadius(descriptor.id, options.targetRadius ?? DEFAULT_TARGET_RADIUS);\n this.ports.set(descriptor.id, port);\n this.runtime.registerInteractable(descriptor, { transform: port });\n return port;\n }\n\n unregister(id: string): void {\n this.runtime.unregisterInteractable(id);\n this.ports.delete(id);\n }\n\n getPort(id: string): NativeTransformPort | undefined {\n return this.ports.get(id);\n }\n\n update(dt: number): void {\n this.runtime.update(dt);\n }\n\n dispose(): void {\n this.detachHost?.();\n this.detachHost = null;\n this.runtime.dispose();\n this.provider.dispose();\n }\n\n private attachToHost(options: NativeInteractionsOptions): void {\n const frames = options.frames ?? installedHost();\n if (!frames?.onFrame) {\n throw new Error(\n \"@realitycollective/native-interactions: attachToHost needs a frame source, and globalThis.__rcHost.onFrame is not installed. Pass `frames`, or call update(dt) yourself.\",\n );\n }\n // The app reports the frame delta in seconds. A long frame is clamped so\n // behaviours integrate over a sane step rather than jumping.\n this.detachHost = frames.onFrame((_timestampMs, deltaS) => {\n this.update(Math.min(MAX_FRAME_SECONDS, Math.max(0, deltaS)));\n });\n }\n}\n\nexport function createNativeInteractions(\n options: NativeInteractionsOptions = {},\n): NativeInteractions {\n return new NativeInteractions(options);\n}\n"]}
1
+ {"version":3,"file":"host.js","sourceRoot":"","sources":["../src/host.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * createNativeInteractions - one-call setup for the native adapter, with the\n * same shape as the Babylon, three.js, IWSDK and XR Blocks setups.\n *\n * ```ts\n * const interactions = createNativeInteractions({ attachToHost: true });\n * interactions.register({ id: \"button\", behaviours: [{ kind: \"press\" }] });\n * interactions.runtime.onEvent((event) => console.log(event.type));\n * ```\n *\n * The native app owns the scene, so an interactable is registered by id\n * alone: the app answers hit queries and pose reads for that id through the\n * `interactions` slice. With `attachToHost` the loop drives itself from the\n * app's own frame callback. Without it, call `update(dt)` yourself with\n * seconds.\n */\nimport {\n InteractionRuntime,\n type DwellConfig,\n type InteractableDescriptor,\n type NearPointerOptions,\n type PhysicsBodySpec,\n type PhysicsShapeSpec,\n type PointerArbiter,\n type PointerDisplay,\n type PointerDisplayConfig,\n} from \"@realitycollective/webxr-interactions\";\nimport { NativeHitTester, type NativeHitTesterOptions } from \"./hit-tester.js\";\nimport { NativePhysicsFacility } from \"./physics-facility.js\";\nimport { NativeInputProvider, type NativeInputProviderOptions } from \"./provider.js\";\nimport { NativeTransformPort } from \"./transform-port.js\";\nimport {\n findHostSlice,\n installedHost,\n resolveHostSlice,\n type NativeFrameSource,\n type NativeInteractionHost,\n type NativePhysicsHost,\n} from \"./native-types.js\";\n\nexport interface NativeInteractionsOptions\n extends NativeInputProviderOptions,\n NativeHitTesterOptions {\n dwellDefaults?: DwellConfig;\n /** The near-pointer distances (touch hover, touch press, grab radius). Defaults are IWSDK 1.0.0's. */\n nearPointer?: Partial<NearPointerOptions>;\n /**\n * The pointer arbiter shared with the UI Extensions native host, so one\n * decision per source covers panels and interactables (the runtime's\n * `pointers` option). Omit when the app has no panels.\n */\n pointers?: PointerArbiter;\n /**\n * The app's pointer display settings (the runtime's `pointerDisplay`\n * option): ray never, always or while hitting; cursors on objects and on\n * panels; the ray and cursor look. Defaults are IWSDK 1.0.0's. The host is\n * handed the resolved drawing every frame and the settings on every change.\n */\n pointerDisplay?: PointerDisplay | Partial<PointerDisplayConfig>;\n /**\n * The `physics` slice: the host's default engine (Jolt on Quest and\n * Android, RealityKit on visionOS) behind the core contract, or an app's\n * own `PhysicsFacility` in its place. Omit to read\n * `globalThis.__rcHost.physics`. Without either, `physics` is null and no\n * target can carry a body; the host conformance kit fails such a host.\n */\n physics?: NativePhysicsHost;\n /** Step the physics from `update(dt)`. Default true; false when the host steps its engine itself. */\n stepPhysics?: boolean;\n /**\n * Drive `update(dt)` from the native app's frame callback. Default false -\n * the app calls `update` from its own loop.\n */\n attachToHost?: boolean;\n /** Where frames come from for `attachToHost`. Omit to use `globalThis.__rcHost`. */\n frames?: NativeFrameSource;\n}\n\n/** Per-registration options, as IWSDK's `register` takes them. */\nexport interface NativeRegisterOptions {\n /**\n * The radius, in metres, the host hit-tests this target with. Default\n * 0.1, the radius IWSDK gives a target that declares none.\n */\n targetRadius?: number;\n /**\n * Give the target a body in the `physics` slice at its current world pose\n * (core defaults: dynamic, no damping, gravity factor 1). A target whose\n * body the host already holds (`physics.hasBody(id)`) needs neither this\n * nor `shape`: its port is bound to that body as it is.\n */\n body?: PhysicsBodySpec;\n /** The body's collider (default `\"auto\"`, the host's collider for the object). Implies `body`. */\n shape?: PhysicsShapeSpec;\n}\n\n/** The radius a target is hit-tested with when it declares none: IWSDK's `targetRadius ?? 0.1`. */\nconst DEFAULT_TARGET_RADIUS = 0.1;\n\n/** Longest frame the host-attached loop will report, in seconds. */\nconst MAX_FRAME_SECONDS = 0.1;\n\nexport class NativeInteractions {\n readonly runtime: InteractionRuntime;\n readonly provider: NativeInputProvider;\n readonly hitTester: NativeHitTester;\n /** The platform's physics over the `physics` slice, or null when the host installs none. */\n readonly physics: NativePhysicsFacility | null;\n private readonly stepPhysics: boolean;\n private readonly ports = new Map<string, NativeTransformPort>();\n private readonly ownedBodies = new Set<string>();\n private readonly options: NativeInteractionsOptions;\n private readonly interactionsHost: NativeInteractionHost;\n private detachHost: (() => void) | null = null;\n private readonly unsubscribeVisuals: () => void;\n private readonly unsubscribeDisplay: () => void;\n\n constructor(options: NativeInteractionsOptions = {}) {\n this.options = options;\n this.interactionsHost = resolveHostSlice(\"interactions\", options.interactions);\n this.provider = new NativeInputProvider(options);\n this.hitTester = new NativeHitTester(options);\n this.runtime = new InteractionRuntime({\n provider: this.provider,\n hitTester: this.hitTester,\n ...(options.dwellDefaults ? { dwellDefaults: options.dwellDefaults } : {}),\n ...(options.nearPointer ? { nearPointer: options.nearPointer } : {}),\n ...(options.pointers ? { pointers: options.pointers } : {}),\n ...(options.pointerDisplay ? { pointerDisplay: options.pointerDisplay } : {}),\n });\n const physicsHost = findHostSlice(\"physics\", options.physics);\n this.physics = physicsHost ? new NativePhysicsFacility({ physics: physicsHost }) : null;\n this.stepPhysics = options.stepPhysics ?? true;\n // The host is handed the ray and cursor DRAWING every frame, per source\n // (`NativeInputHost.applyPointerVisuals`): the arbiter's decision under\n // the app's pointer display settings, fully resolved. It draws exactly that.\n const input = resolveHostSlice(\"input\", options.input);\n this.unsubscribeVisuals = this.runtime.onPointerDrawing((drawings) => {\n if (!input.applyPointerVisuals) return;\n for (const drawing of drawings) {\n const visuals = this.runtime.getPointerVisuals(drawing.sourceId);\n input.applyPointerVisuals(drawing.sourceId, {\n ...drawing,\n rayColor: [drawing.rayColor[0], drawing.rayColor[1], drawing.rayColor[2]],\n activePointer: visuals?.activePointer ?? null,\n targetKind: visuals?.targetKind ?? null,\n targetId: visuals?.targetId ?? null,\n hitDistance: visuals?.hitDistance ?? null,\n });\n }\n });\n const display = this.runtime.getPointerDisplay();\n input.applyPointerDisplay?.({ ...display.get() });\n this.unsubscribeDisplay = display.onChange((config) => input.applyPointerDisplay?.({ ...config }));\n if (options.attachToHost) this.attachToHost(options);\n }\n\n /**\n * Register an interactable. The native app knows it by `descriptor.id`,\n * and is told the radius to hit-test it with: `options.targetRadius`, or\n * 0.1 m, so a bare target is a 10 cm sphere as on IWSDK.\n */\n register(descriptor: InteractableDescriptor, options: NativeRegisterOptions = {}): NativeTransformPort {\n const id = descriptor.id;\n if (options.body || options.shape) {\n if (!this.physics) {\n throw new Error(\n `@realitycollective/native-interactions: \"${id}\" asks for a physics body but no physics slice is installed; the host installs globalThis.__rcHost.physics, or pass \\`physics\\` to createNativeInteractions`,\n );\n }\n this.physics.addBody(id, this.interactionsHost.getWorldPose(id), options.body, options.shape);\n this.ownedBodies.add(id);\n }\n const bound = this.physics && this.physics.hasBody(id) ? this.physics : null;\n const port = new NativeTransformPort(id, {\n ...(this.options.interactions ? { interactions: this.options.interactions } : {}),\n ...(bound ? { physics: { facility: bound, bodyId: id } } : {}),\n });\n this.interactionsHost.setTargetRadius(descriptor.id, options.targetRadius ?? DEFAULT_TARGET_RADIUS);\n this.ports.set(descriptor.id, port);\n this.runtime.registerInteractable(descriptor, { transform: port });\n return port;\n }\n\n unregister(id: string): void {\n this.runtime.unregisterInteractable(id);\n this.ports.delete(id);\n if (this.ownedBodies.delete(id)) this.physics?.removeBody(id);\n }\n\n getPort(id: string): NativeTransformPort | undefined {\n return this.ports.get(id);\n }\n\n update(dt: number): void {\n // The provider's eye-gaze filter and grace integrate over the same step.\n this.provider.setFrameDelta(dt);\n this.runtime.update(dt);\n if (this.stepPhysics) this.physics?.step(dt);\n }\n\n dispose(): void {\n this.detachHost?.();\n this.detachHost = null;\n this.unsubscribeVisuals();\n this.unsubscribeDisplay();\n this.runtime.dispose();\n this.provider.dispose();\n }\n\n private attachToHost(options: NativeInteractionsOptions): void {\n const frames = options.frames ?? installedHost();\n if (!frames?.onFrame) {\n throw new Error(\n \"@realitycollective/native-interactions: attachToHost needs a frame source, and globalThis.__rcHost.onFrame is not installed. Pass `frames`, or call update(dt) yourself.\",\n );\n }\n // The app reports the frame delta in seconds. A long frame is clamped so\n // behaviours integrate over a sane step rather than jumping.\n this.detachHost = frames.onFrame((_timestampMs, deltaS) => {\n this.update(Math.min(MAX_FRAME_SECONDS, Math.max(0, deltaS)));\n });\n }\n}\n\nexport function createNativeInteractions(\n options: NativeInteractionsOptions = {},\n): NativeInteractions {\n return new NativeInteractions(options);\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -9,10 +9,11 @@
9
9
  * internal.
10
10
  */
11
11
  export * from "@realitycollective/webxr-interactions";
12
- export type { NativeFrameSource, NativeHit, NativeHostSlices, NativeInputFacts, NativeInputHost, NativeInteractionHost, NativeInteractionsTestHost, NativePresenceShown, } from "./native-types.js";
12
+ export type { NativeFrameSource, NativeHit, NativeHostSlices, NativeInputFacts, NativeInputHost, NativeInteractionHost, NativeInteractionsTestHost, NativePhysicsHost, NativePointerVisuals, NativePresenceShown, } from "./native-types.js";
13
13
  export * from "./provider.js";
14
14
  export * from "./hit-tester.js";
15
15
  export * from "./transform-port.js";
16
+ export * from "./physics-facility.js";
16
17
  export * from "./host.js";
17
18
  export { nativeInteractionsHostConformanceCases } from "./conformance.js";
18
19
  export type { NativeInteractionsHostConformanceCase, NativeInteractionsHostConformanceSetup, } from "./conformance.js";
package/dist/index.js CHANGED
@@ -12,6 +12,7 @@ export * from "@realitycollective/webxr-interactions";
12
12
  export * from "./provider.js";
13
13
  export * from "./hit-tester.js";
14
14
  export * from "./transform-port.js";
15
+ export * from "./physics-facility.js";
15
16
  export * from "./host.js";
16
17
  export { nativeInteractionsHostConformanceCases } from "./conformance.js";
17
18
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,uCAAuC,CAAC;AAWtD,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,sCAAsC,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * Public entry point of the native adapter: the same kinds of export as the\n * Babylon, three.js, IWSDK and XR Blocks adapters. The core is re-exported;\n * the provider, hit tester and transform port implement its contracts; the\n * setup entry point wires them together; and the slice types describe what\n * the native app installs on `globalThis.__rcHost`. One addition the web\n * adapters do not need: the host conformance kit a native app runs on its\n * device against its real slices. Slice reading and tuple copying stay\n * internal.\n */\nexport * from \"@realitycollective/webxr-interactions\";\nexport type {\n NativeFrameSource,\n NativeHit,\n NativeHostSlices,\n NativeInputFacts,\n NativeInputHost,\n NativeInteractionHost,\n NativeInteractionsTestHost,\n NativePresenceShown,\n} from \"./native-types.js\";\nexport * from \"./provider.js\";\nexport * from \"./hit-tester.js\";\nexport * from \"./transform-port.js\";\nexport * from \"./host.js\";\nexport { nativeInteractionsHostConformanceCases } from \"./conformance.js\";\nexport type {\n NativeInteractionsHostConformanceCase,\n NativeInteractionsHostConformanceSetup,\n} from \"./conformance.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,cAAc,uCAAuC,CAAC;AAatD,cAAc,eAAe,CAAC;AAC9B,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,sCAAsC,EAAE,MAAM,kBAAkB,CAAC","sourcesContent":["/**\n * Public entry point of the native adapter: the same kinds of export as the\n * Babylon, three.js, IWSDK and XR Blocks adapters. The core is re-exported;\n * the provider, hit tester and transform port implement its contracts; the\n * setup entry point wires them together; and the slice types describe what\n * the native app installs on `globalThis.__rcHost`. One addition the web\n * adapters do not need: the host conformance kit a native app runs on its\n * device against its real slices. Slice reading and tuple copying stay\n * internal.\n */\nexport * from \"@realitycollective/webxr-interactions\";\nexport type {\n NativeFrameSource,\n NativeHit,\n NativeHostSlices,\n NativeInputFacts,\n NativeInputHost,\n NativeInteractionHost,\n NativeInteractionsTestHost,\n NativePhysicsHost,\n NativePointerVisuals,\n NativePresenceShown,\n} from \"./native-types.js\";\nexport * from \"./provider.js\";\nexport * from \"./hit-tester.js\";\nexport * from \"./transform-port.js\";\nexport * from \"./physics-facility.js\";\nexport * from \"./host.js\";\nexport { nativeInteractionsHostConformanceCases } from \"./conformance.js\";\nexport type {\n NativeInteractionsHostConformanceCase,\n NativeInteractionsHostConformanceSetup,\n} from \"./conformance.js\";\n"]}