@realitycollective/service-framework-three 1.0.2-preview.1 → 1.0.2-preview.2
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
CHANGED
|
@@ -20,6 +20,11 @@ A native platform, beside IWSDK, three.js and Babylon.js, and byte I/O that work
|
|
|
20
20
|
- `LifecycleContext` now documents its units: `timestamp` and `deltaTime` in milliseconds, `frame` counted from 1, and `source` naming the binding.
|
|
21
21
|
- `@realitycollective/service-framework-three` and `@realitycollective/service-framework-babylon` - the `manager` option on `WebXRRuntimeAdapter` and `BabylonRuntimeAdapter`, the service manager whose focus and pause signals follow a live session's visibility, as the `manager` option already does on the native adapter. `WebXRFocusSink` and `BabylonFocusSink` name the two methods it needs.
|
|
22
22
|
- `WebXRRuntimeAdapter.tick(timestampMs)` and `BabylonRuntimeAdapter.tick()`, now public. Each is the whole frame step `start()` already bound to the owned loop - the visibility gate, the focus/pause signals, the one frame count, `emitFrame` and `renderTick` - pulled out from under `start()` so a host that owns its OWN loop and will never call `start()` (an XR Blocks app, whose `Core` calls `setAnimationLoop` itself) can drive the exact same step by hand from whatever per-frame hook that host provides, rather than reimplementing the gate. The owned loop and a hand-driven app now share one code path; before this, a hand-driven three.js app could only reach the ungated `emitFrame`, so it had no way to reach the focus gate at all.
|
|
23
|
+
- `SESSION_REFERENCE_SPACE` (`"local-floor"`) in `@realitycollective/service-framework`, the WebXR reference space every binding requests: origin where the session began, +Y up with the floor at y = 0, -Z the initial forward direction. `SessionFacet` gains `getMode()` (the live session's mode, or null), `isSupported(mode)` (answers whether the host could start a mode, without changing state, never rejecting) and `recentre()` (moves the viewer's current floor position and yaw to the origin, y untouched, a no-op with no live session). `request()` now documents "end-and-request": asking for a different mode while a session is active ends the live session first and then requests the new one, walking `active`, `ending`, `none`, `requesting`, `active`; asking for the mode already active is a no-op; a host that cannot end its own session refuses with `{ ok: false, reason: "unsupported" }` and leaves the live session running. The pure helpers `recentreOffset(viewer)` and `recentreRig(rig, headLocal)` compute the rigid transform a platform applies, for a reference-space offset and for a moved player rig respectively, and are unit-tested in the core. `runtimeAdapterContractCases()` gains four cases proving every adapter implements all of this the same way, and `MockRuntimeAdapter` implements it in memory with a configurable `supportedModes` option and a `recentreCount`.
|
|
24
|
+
- `@realitycollective/service-framework-three` - `WebXRRuntimeAdapter.session.isSupported` reads `navigator.xr.isSessionSupported`, resolving `false` with no system or on a rejection; `getMode()` and end-and-request now work as the core documents; `recentre()` offsets the renderer's reference space through the new optional `WebXRManagerLike` members `getReferenceSpace`, `setReferenceSpace` and `getFrame`, building the offset with a `rigidTransform` factory that defaults to `globalThis.XRRigidTransform`, and does nothing when any of these is absent. A session request now also calls `setReferenceSpaceType(SESSION_REFERENCE_SPACE)` when the manager carries one, before handing the session to the renderer.
|
|
25
|
+
- `@realitycollective/service-framework-iwsdk` - `IWSDKAdapter` takes an optional second `IWSDKAdapterOptions` argument with `xrSystem`, the `navigator.xr` slice `session.isSupported` reads (defaulting to the global, `null` in Node). `getMode()` and end-and-request now work as the core documents; a world with no `exitXR` refuses a mode switch with `{ ok: false, reason: "unsupported" }` rather than waiting out the session timeout. `recentre()` moves the player rig through the new optional `IWSDKWorldLike` members `player.object3D` and `playerSpaceEntities.head.object3D`, leaving the head's pose local to the rig untouched, and does nothing when either is absent.
|
|
26
|
+
- `@realitycollective/service-framework-babylon` - `BabylonRuntimeAdapter.session.isSupported` is now public, resolving `false` with no experience or on a rejection rather than throwing. `getMode()` and end-and-request now work as the core documents. `recentre()` moves the XR camera through the new optional `BabylonXRExperienceLike.camera` member (`position`, `rotationQuaternion`, `devicePosition`, `deviceRotationQuaternion`), leaving the device's pose local to the camera untouched, and does nothing when the experience carries no camera.
|
|
27
|
+
- `@realitycollective/service-framework-native` - `NativeSessionInfo.supportedModes`, the fact `isSupported` answers from; absent, it reports both immersive modes supported and `"inline"` unsupported. `NativeHost.recentre`, the app's obligation to offset its own reference space so the head's current x, z and yaw become the origin, y untouched; `recentre()` does nothing without it. `getMode()` and end-and-request now work as the core documents: a different mode while active ends the live session first, and an app that refuses instead of ending reports `onSessionRefused("unsupported")` through the same request rather than the refusal being lost.
|
|
23
28
|
|
|
24
29
|
### Changed
|
|
25
30
|
|
|
@@ -29,6 +34,7 @@ A native platform, beside IWSDK, three.js and Babylon.js, and byte I/O that work
|
|
|
29
34
|
- `@realitycollective/service-framework-native` - capabilities re-derive on the `input` slice's source-change signal as well as on session changes, as the IWSDK adapter re-derives on `inputsourceschange`. `handTracking` is true for the `"hand-tracking"` feature, for `XR_EXT_hand_tracking` with system support, or while a hand is among the sources; `planeDetection` is true only for the `"plane-detection"` feature, where it was always false.
|
|
30
35
|
- `@realitycollective/service-framework-native` - a refused session request resolves with the app's reason, `"denied"`, `"unsupported"` or `"error"`, at once. Before this only `"timeout"` and `"error"` could be reached, so a dismissed permission prompt waited ten seconds and read as a timeout.
|
|
31
36
|
- `@realitycollective/service-framework-native` - every member of `NativeHost` and `NativeSessionInfo` states its units and meaning: milliseconds and seconds on `onFrame`, what counts as live, what the blend mode must reflect, and what each signal triggers.
|
|
37
|
+
- **Behaviour change on every platform:** `session.request(mode)` while a session is already active is now a no-op only when `mode` matches the live session. A different mode ends the live session first and then requests the new one ("end-and-request"), instead of the previous no-op that always resolved `{ ok: true }` regardless of the mode asked for. A session the adapter did not itself request - already active at construction, or started through a host's own UI - has no known mode, so a request against it is treated as a switch and goes through the same end-and-request path.
|
|
32
38
|
|
|
33
39
|
### Fixed
|
|
34
40
|
|
|
@@ -69,6 +69,38 @@ export interface WebXRSystemLike {
|
|
|
69
69
|
isSessionSupported(mode: string): Promise<boolean>;
|
|
70
70
|
requestSession(mode: string, init?: unknown): Promise<WebXRSessionLike>;
|
|
71
71
|
}
|
|
72
|
+
/** An `XRRigidTransform`-shaped position, as `recentre()` reads and builds one. */
|
|
73
|
+
export interface WebXRVectorLike {
|
|
74
|
+
readonly x: number;
|
|
75
|
+
readonly y: number;
|
|
76
|
+
readonly z: number;
|
|
77
|
+
}
|
|
78
|
+
/** An `XRRigidTransform`-shaped orientation. */
|
|
79
|
+
export interface WebXRQuaternionLike {
|
|
80
|
+
readonly x: number;
|
|
81
|
+
readonly y: number;
|
|
82
|
+
readonly z: number;
|
|
83
|
+
readonly w: number;
|
|
84
|
+
}
|
|
85
|
+
/** The slice of an `XRRigidTransform` `recentre()` reads off a viewer pose. */
|
|
86
|
+
export interface WebXRRigidTransformLike {
|
|
87
|
+
readonly position: WebXRVectorLike;
|
|
88
|
+
readonly orientation: WebXRQuaternionLike;
|
|
89
|
+
}
|
|
90
|
+
/** The slice of an `XRView`/viewer pose `recentre()` reads. */
|
|
91
|
+
export interface WebXRViewerPoseLike {
|
|
92
|
+
readonly transform: WebXRRigidTransformLike;
|
|
93
|
+
}
|
|
94
|
+
/** The slice of an `XRFrame` `recentre()` reads the viewer pose from. */
|
|
95
|
+
export interface WebXRFrameLike {
|
|
96
|
+
getViewerPose(referenceSpace: WebXRReferenceSpaceLike): WebXRViewerPoseLike | null;
|
|
97
|
+
}
|
|
98
|
+
/** The slice of an `XRReferenceSpace` `recentre()` offsets. */
|
|
99
|
+
export interface WebXRReferenceSpaceLike {
|
|
100
|
+
getOffsetReferenceSpace(originOffset: unknown): WebXRReferenceSpaceLike;
|
|
101
|
+
}
|
|
102
|
+
/** Builds the `XRRigidTransform` `recentre()` hands to `getOffsetReferenceSpace`. */
|
|
103
|
+
export type WebXRRigidTransformFactory = (position: WebXRVectorLike, orientation: WebXRQuaternionLike) => unknown;
|
|
72
104
|
/** The slice of a three.js `renderer.xr` (`WebXRManager`) the adapter drives. */
|
|
73
105
|
export interface WebXRManagerLike {
|
|
74
106
|
/** Hands the renderer the session it should present. */
|
|
@@ -77,6 +109,14 @@ export interface WebXRManagerLike {
|
|
|
77
109
|
getSession(): WebXRSessionLike | null;
|
|
78
110
|
addEventListener(type: WebXRManagerEventType, listener: WebXREventListener): void;
|
|
79
111
|
removeEventListener(type: WebXRManagerEventType, listener: WebXREventListener): void;
|
|
112
|
+
/** Sets the reference space type the manager requests a session with. */
|
|
113
|
+
setReferenceSpaceType?(type: string): void;
|
|
114
|
+
/** The reference space the manager is presenting with, if any. */
|
|
115
|
+
getReferenceSpace?(): WebXRReferenceSpaceLike | null;
|
|
116
|
+
/** Replaces the reference space the manager presents with - `recentre()`'s write. */
|
|
117
|
+
setReferenceSpace?(space: WebXRReferenceSpaceLike): void;
|
|
118
|
+
/** The current `XRFrame`, if the manager is mid-frame. */
|
|
119
|
+
getFrame?(): WebXRFrameLike | null;
|
|
80
120
|
}
|
|
81
121
|
export interface WebXRRuntimeAdapterOptions {
|
|
82
122
|
/** `renderer.xr` - the renderer's WebXR manager. */
|
|
@@ -119,6 +159,12 @@ export interface WebXRRuntimeAdapterOptions {
|
|
|
119
159
|
* before this option existed.
|
|
120
160
|
*/
|
|
121
161
|
readonly manager?: WebXRFocusSink;
|
|
162
|
+
/**
|
|
163
|
+
* Builds the `XRRigidTransform` {@link WebXRRuntimeAdapter.recentre} hands to
|
|
164
|
+
* `getOffsetReferenceSpace`. Defaults to `globalThis.XRRigidTransform` when
|
|
165
|
+
* present; with neither, `recentre()` does nothing.
|
|
166
|
+
*/
|
|
167
|
+
readonly rigidTransform?: WebXRRigidTransformFactory;
|
|
122
168
|
}
|
|
123
169
|
export declare class WebXRRuntimeAdapter implements RuntimeAdapter {
|
|
124
170
|
private readonly frameListeners;
|
|
@@ -132,10 +178,12 @@ export declare class WebXRRuntimeAdapter implements RuntimeAdapter {
|
|
|
132
178
|
private readonly scheduler;
|
|
133
179
|
private readonly sessionInit;
|
|
134
180
|
private readonly manager;
|
|
181
|
+
private readonly rigidTransform;
|
|
135
182
|
private derived;
|
|
136
183
|
private overrides;
|
|
137
184
|
private capabilities;
|
|
138
185
|
private sessionState;
|
|
186
|
+
private mode;
|
|
139
187
|
private boundSession;
|
|
140
188
|
private sessionListeners;
|
|
141
189
|
private animationLoopBound;
|
|
@@ -227,6 +275,16 @@ export declare class WebXRRuntimeAdapter implements RuntimeAdapter {
|
|
|
227
275
|
private handleSessionEnd;
|
|
228
276
|
private updateDerived;
|
|
229
277
|
private publishCapabilities;
|
|
278
|
+
/** Answers `isSupported()` off `navigator.xr`; never rejects, unlike `openSession`'s own check. */
|
|
279
|
+
private checkSupported;
|
|
280
|
+
/**
|
|
281
|
+
* Make the viewer's current floor position and yaw the new origin, by
|
|
282
|
+
* offsetting the manager's reference space. Does nothing when the manager
|
|
283
|
+
* carries none of the optional members this needs, when there is no live
|
|
284
|
+
* frame or reference space, or when no `XRRigidTransform` constructor is
|
|
285
|
+
* available - see {@link WebXRRuntimeAdapterOptions.rigidTransform}.
|
|
286
|
+
*/
|
|
287
|
+
private recentreViewer;
|
|
230
288
|
private requestSession;
|
|
231
289
|
/**
|
|
232
290
|
* Negotiate the session and hand it to the renderer. Nothing here throws at
|
|
@@ -31,8 +31,14 @@
|
|
|
31
31
|
* `tick(timestampMs)` by hand from its own per-frame hook instead, and gets
|
|
32
32
|
* the identical gate rather than reimplementing it.
|
|
33
33
|
*/
|
|
34
|
-
import { DEFAULT_CAPABILITIES, DEFAULT_SESSION_TIMEOUT_MS, deriveCapabilities, mergeSessionInit, } from "@realitycollective/service-framework";
|
|
34
|
+
import { DEFAULT_CAPABILITIES, DEFAULT_SESSION_TIMEOUT_MS, SESSION_REFERENCE_SPACE, deriveCapabilities, mergeSessionInit, recentreOffset, } from "@realitycollective/service-framework";
|
|
35
35
|
import { FIRST_FRAME_DELTA_MS } from "./three-render-loop-bridge.js";
|
|
36
|
+
/** Reads `globalThis.XRRigidTransform`, or `undefined` where there is none (Node). */
|
|
37
|
+
function defaultRigidTransformFactory() {
|
|
38
|
+
const ctor = globalThis
|
|
39
|
+
.XRRigidTransform;
|
|
40
|
+
return ctor ? (position, orientation) => new ctor(position, orientation) : undefined;
|
|
41
|
+
}
|
|
36
42
|
const SESSION_VISIBILITY_VALUES = new Set([
|
|
37
43
|
"visible",
|
|
38
44
|
"visible-blurred",
|
|
@@ -78,10 +84,12 @@ export class WebXRRuntimeAdapter {
|
|
|
78
84
|
scheduler;
|
|
79
85
|
sessionInit;
|
|
80
86
|
manager;
|
|
87
|
+
rigidTransform;
|
|
81
88
|
derived = DEFAULT_CAPABILITIES;
|
|
82
89
|
overrides = {};
|
|
83
90
|
capabilities = DEFAULT_CAPABILITIES;
|
|
84
91
|
sessionState = "none";
|
|
92
|
+
mode = null;
|
|
85
93
|
boundSession = null;
|
|
86
94
|
sessionListeners = [];
|
|
87
95
|
animationLoopBound = false;
|
|
@@ -103,8 +111,11 @@ export class WebXRRuntimeAdapter {
|
|
|
103
111
|
/** Session lifecycle over `navigator.xr` and the renderer's XR manager. */
|
|
104
112
|
session = {
|
|
105
113
|
getState: () => this.sessionState,
|
|
114
|
+
getMode: () => (this.sessionState === "active" ? this.mode : null),
|
|
115
|
+
isSupported: (mode) => this.checkSupported(mode),
|
|
106
116
|
request: (mode, options) => this.requestSession(mode, options),
|
|
107
117
|
end: () => this.endSession(),
|
|
118
|
+
recentre: () => this.recentreViewer(),
|
|
108
119
|
onStateChange: (listener) => {
|
|
109
120
|
this.stateListeners.add(listener);
|
|
110
121
|
return () => {
|
|
@@ -125,6 +136,7 @@ export class WebXRRuntimeAdapter {
|
|
|
125
136
|
this.scheduler = options.scheduler;
|
|
126
137
|
this.sessionInit = options.sessionInit;
|
|
127
138
|
this.manager = options.manager;
|
|
139
|
+
this.rigidTransform = options.rigidTransform ?? defaultRigidTransformFactory();
|
|
128
140
|
this.xr.addEventListener("sessionstart", this.onManagerSessionStart);
|
|
129
141
|
this.xr.addEventListener("sessionend", this.onManagerSessionEnd);
|
|
130
142
|
const session = this.xr.getSession();
|
|
@@ -367,9 +379,61 @@ export class WebXRRuntimeAdapter {
|
|
|
367
379
|
this.capabilities = next;
|
|
368
380
|
this.capabilitiesListeners.forEach((listener) => listener(next));
|
|
369
381
|
}
|
|
382
|
+
/** Answers `isSupported()` off `navigator.xr`; never rejects, unlike `openSession`'s own check. */
|
|
383
|
+
async checkSupported(mode) {
|
|
384
|
+
const system = this.xrSystem;
|
|
385
|
+
if (!system) {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
return await system.isSessionSupported(mode);
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
return false;
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Make the viewer's current floor position and yaw the new origin, by
|
|
397
|
+
* offsetting the manager's reference space. Does nothing when the manager
|
|
398
|
+
* carries none of the optional members this needs, when there is no live
|
|
399
|
+
* frame or reference space, or when no `XRRigidTransform` constructor is
|
|
400
|
+
* available - see {@link WebXRRuntimeAdapterOptions.rigidTransform}.
|
|
401
|
+
*/
|
|
402
|
+
recentreViewer() {
|
|
403
|
+
const { getReferenceSpace, setReferenceSpace, getFrame } = this.xr;
|
|
404
|
+
const factory = this.rigidTransform;
|
|
405
|
+
if (!getReferenceSpace || !setReferenceSpace || !getFrame || !factory) {
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
const space = getReferenceSpace.call(this.xr);
|
|
409
|
+
const frame = getFrame.call(this.xr);
|
|
410
|
+
if (!space || !frame) {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
413
|
+
const pose = frame.getViewerPose(space);
|
|
414
|
+
if (!pose) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
const offset = recentreOffset({
|
|
418
|
+
position: [pose.transform.position.x, pose.transform.position.y, pose.transform.position.z],
|
|
419
|
+
orientation: [
|
|
420
|
+
pose.transform.orientation.x,
|
|
421
|
+
pose.transform.orientation.y,
|
|
422
|
+
pose.transform.orientation.z,
|
|
423
|
+
pose.transform.orientation.w,
|
|
424
|
+
],
|
|
425
|
+
});
|
|
426
|
+
const transform = factory({ x: offset.position[0], y: offset.position[1], z: offset.position[2] }, { x: offset.orientation[0], y: offset.orientation[1], z: offset.orientation[2], w: offset.orientation[3] });
|
|
427
|
+
setReferenceSpace.call(this.xr, space.getOffsetReferenceSpace(transform));
|
|
428
|
+
}
|
|
370
429
|
async requestSession(mode, options) {
|
|
371
430
|
if (this.sessionState === "active") {
|
|
372
|
-
|
|
431
|
+
if (this.mode === mode) {
|
|
432
|
+
return { ok: true };
|
|
433
|
+
}
|
|
434
|
+
// End-and-request: three.js always has an end() to call while a session
|
|
435
|
+
// is live, so the switch is always possible.
|
|
436
|
+
await this.endSession();
|
|
373
437
|
}
|
|
374
438
|
const system = this.xrSystem;
|
|
375
439
|
if (!system) {
|
|
@@ -387,6 +451,7 @@ export class WebXRRuntimeAdapter {
|
|
|
387
451
|
this.setSessionState("none");
|
|
388
452
|
return result;
|
|
389
453
|
}
|
|
454
|
+
this.mode = mode;
|
|
390
455
|
this.setSessionState("active");
|
|
391
456
|
return result;
|
|
392
457
|
}
|
|
@@ -408,6 +473,7 @@ export class WebXRRuntimeAdapter {
|
|
|
408
473
|
}
|
|
409
474
|
const init = mergeSessionInit(this.sessionInit?.(mode), options);
|
|
410
475
|
const session = await system.requestSession(mode, init);
|
|
476
|
+
this.xr.setReferenceSpaceType?.(SESSION_REFERENCE_SPACE);
|
|
411
477
|
await this.xr.setSession(session);
|
|
412
478
|
// A manager that raises `sessionstart` has already attached this session;
|
|
413
479
|
// one that does not is attached here, so both host styles behave alike.
|
|
@@ -454,6 +520,9 @@ export class WebXRRuntimeAdapter {
|
|
|
454
520
|
return;
|
|
455
521
|
}
|
|
456
522
|
this.sessionState = state;
|
|
523
|
+
if (state === "none") {
|
|
524
|
+
this.mode = null;
|
|
525
|
+
}
|
|
457
526
|
this.stateListeners.forEach((listener) => listener(state));
|
|
458
527
|
}
|
|
459
528
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webxr-runtime-adapter.js","sourceRoot":"","sources":["../src/webxr-runtime-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WebXR implementation of {@link RuntimeAdapter}, for a three.js app or any\n * other page that owns its own renderer.\n *\n * The IWSDK binding gets its adapter from IWSDK. Everything else - a plain\n * three.js app, a desktop build with no headset - had nothing, so a service\n * written against `RuntimeAdapter` could not be hosted there. This adapter\n * closes that gap: it orchestrates the entry points the platform already\n * provides (`navigator.xr` for session negotiation, `renderer.xr` for the\n * session the renderer presents, `setAnimationLoop` for frames) and publishes\n * them through the same seam. It renders nothing, plays nothing and owns no\n * scene state.\n *\n * Every host type here is structural, so this package still imports neither\n * `three` nor any WebXR type, and the adapter unit-tests headless.\n *\n * Given a `host` and a `manager`, the owned animation loop also gates on the\n * live session's visibility, as IWSDK's `ServiceBridgeSystem` and the native\n * adapter do: while a session is live, frames and `renderTick` flow only on\n * `\"visible\"`, `emitFocusChange`/`emitPauseChange` fire on every change, and\n * the frame count skips a gated tick. Unlike IWSDK and native, this adapter\n * also serves a desktop page with no session at all, and gating never applies\n * there - a plain three.js page keeps ticking exactly as it did before this\n * existed.\n *\n * That whole step - the gate, the focus/pause signals, the frame count,\n * `emitFrame` and `renderTick` - lives in one place, {@link\n * WebXRRuntimeAdapter.tick}, which `start()` binds to the owned loop. A host\n * that owns its OWN animation loop and will never call `start()` - an XR\n * Blocks app, whose `Core` already calls `setAnimationLoop` itself - calls\n * `tick(timestampMs)` by hand from its own per-frame hook instead, and gets\n * the identical gate rather than reimplementing it.\n */\nimport {\n DEFAULT_CAPABILITIES,\n DEFAULT_SESSION_TIMEOUT_MS,\n deriveCapabilities,\n mergeSessionInit,\n type AdapterCapabilities,\n type CapabilitiesListener,\n type CapabilitySessionLike,\n type FrameInfo,\n type FrameListener,\n type IScheduler,\n type LifecycleContext,\n type RuntimeAdapter,\n type SessionFacet,\n type SessionFailureReason,\n type SessionMode,\n type SessionRequestOptions,\n type SessionResult,\n type SessionState,\n type SessionVisibility,\n type Unsubscribe,\n} from \"@realitycollective/service-framework\";\nimport { FIRST_FRAME_DELTA_MS, type AnimationLoopHostLike } from \"./three-render-loop-bridge.js\";\n\n/** The events an `XRSession` raises that this adapter listens for. */\nexport type WebXRSessionEventType = \"end\" | \"visibilitychange\" | \"inputsourceschange\";\n\n/** The events three.js's `WebXRManager` raises that this adapter listens for. */\nexport type WebXRManagerEventType = \"sessionstart\" | \"sessionend\";\n\n/** Host event callback. The adapter reads the host, not the event object. */\nexport type WebXREventListener = (event?: unknown) => void;\n\n/**\n * The two focus signals the adapter drives while a session is live: a\n * `ServiceManager` is one. Declared locally, as `NativeFocusSink` is in the\n * native package, rather than shared, because the two packages do not depend\n * on each other.\n */\nexport interface WebXRFocusSink {\n emitFocusChange(focused: boolean): void;\n emitPauseChange(context: { readonly paused: boolean }): void;\n}\n\n/**\n * The slice of an `XRSession` the adapter reads. It extends the core's\n * {@link CapabilitySessionLike}, so a live session goes straight to\n * `deriveCapabilities` with no mapping.\n */\nexport interface WebXRSessionLike extends CapabilitySessionLike {\n /** Ends the session. The `end` event is what the adapter acts on. */\n end(): Promise<void>;\n /** `\"visible\"`, `\"visible-blurred\"` or `\"hidden\"` on a live session. */\n readonly visibilityState?: string;\n addEventListener(type: WebXRSessionEventType, listener: WebXREventListener): void;\n removeEventListener(type: WebXRSessionEventType, listener: WebXREventListener): void;\n}\n\n/** The slice of `navigator.xr` the session facet negotiates through. */\nexport interface WebXRSystemLike {\n isSessionSupported(mode: string): Promise<boolean>;\n requestSession(mode: string, init?: unknown): Promise<WebXRSessionLike>;\n}\n\n/** The slice of a three.js `renderer.xr` (`WebXRManager`) the adapter drives. */\nexport interface WebXRManagerLike {\n /** Hands the renderer the session it should present. */\n setSession(session: WebXRSessionLike): Promise<void> | void;\n /** The session the renderer is presenting, or null in 2D. */\n getSession(): WebXRSessionLike | null;\n addEventListener(type: WebXRManagerEventType, listener: WebXREventListener): void;\n removeEventListener(type: WebXRManagerEventType, listener: WebXREventListener): void;\n}\n\nexport interface WebXRRuntimeAdapterOptions {\n /** `renderer.xr` - the renderer's WebXR manager. */\n readonly xr: WebXRManagerLike;\n /**\n * `navigator.xr`. Defaults to the global when omitted, and to `null` where\n * there is no global, which is what a Node test sees. A null system reports\n * every session request as `\"unsupported\"`.\n */\n readonly xrSystem?: WebXRSystemLike | null;\n /**\n * The renderer, or anything else with `setAnimationLoop`. Given one, the\n * adapter owns the loop: {@link WebXRRuntimeAdapter.start} binds it and each\n * callback runs {@link WebXRRuntimeAdapter.tick}. Omit it where something\n * else already owns the loop: call {@link WebXRRuntimeAdapter.tick} by hand\n * from whatever per-frame hook that host provides for the same gate, frame\n * count and `renderTick`, or {@link WebXRRuntimeAdapter.emitFrame} directly\n * for the frame fan-out alone.\n */\n readonly host?: AnimationLoopHostLike;\n /**\n * Given a scheduler, each owned frame also emits the `renderTick` channel\n * with `source: \"three\"`, exactly as `ThreeRenderLoopBridge` does, so an app\n * needs one loop owner rather than two.\n */\n readonly scheduler?: IScheduler;\n /**\n * Supplies the `XRSessionInit` for a mode - required and optional features.\n * Called once per request; the default sends no init at all.\n */\n readonly sessionInit?: (mode: SessionMode) => unknown;\n /**\n * The service manager whose focus and pause signals follow the session's\n * visibility, as `ServiceBridgeSystem`'s `manager` option does on IWSDK and\n * the native adapter's `manager` option does on a native host: both fire on\n * every change of focus, `paused` being `!focused`. Gated ONLY while a\n * session is live - `XRSession.visibilityState` via `visibilitychange` -\n * because unlike IWSDK and native, this adapter also serves a desktop page\n * with no session at all, and that page keeps ticking exactly as it did\n * before this option existed.\n */\n readonly manager?: WebXRFocusSink;\n}\n\ntype SessionStateListener = (state: SessionState) => void;\ntype SessionVisibilityListener = (visibility: SessionVisibility) => void;\n\nconst SESSION_VISIBILITY_VALUES: ReadonlySet<string> = new Set([\n \"visible\",\n \"visible-blurred\",\n \"hidden\",\n]);\n\n/**\n * Map `XRSession.visibilityState` onto the facet's vocabulary. An unrecognised\n * value is reported as `\"hidden\"`, because treating an unknown state as visible\n * would keep game logic running when it should not.\n */\nfunction toSessionVisibility(value: unknown): SessionVisibility {\n if (typeof value === \"string\" && SESSION_VISIBILITY_VALUES.has(value)) {\n return value as SessionVisibility;\n }\n\n return \"hidden\";\n}\n\n/**\n * WebXR reports a blocked request through the error name: `NotAllowedError`\n * when the user or the permission prompt refused, `SecurityError` when the\n * permissions policy did. Both are a refusal rather than a fault.\n */\nfunction toFailureReason(error: unknown): SessionFailureReason {\n const name = (error as { readonly name?: unknown } | null | undefined)?.name;\n\n if (name === \"NotAllowedError\" || name === \"SecurityError\") {\n return \"denied\";\n }\n\n return \"error\";\n}\n\n/** `navigator.xr`, read defensively: there is no navigator in a Node test. */\nfunction defaultXRSystem(): WebXRSystemLike | null {\n const globalNavigator = (globalThis as { navigator?: { xr?: WebXRSystemLike } }).navigator;\n\n return globalNavigator?.xr ?? null;\n}\n\nexport class WebXRRuntimeAdapter implements RuntimeAdapter {\n private readonly frameListeners = new Set<FrameListener>();\n private readonly capabilitiesListeners = new Set<CapabilitiesListener>();\n private readonly stateListeners = new Set<SessionStateListener>();\n private readonly visibilityListeners = new Set<SessionVisibilityListener>();\n private readonly endWaiters = new Set<() => void>();\n\n private readonly xr: WebXRManagerLike;\n private readonly xrSystem: WebXRSystemLike | null;\n private readonly host: AnimationLoopHostLike | undefined;\n private readonly scheduler: IScheduler | undefined;\n private readonly sessionInit: ((mode: SessionMode) => unknown) | undefined;\n private readonly manager: WebXRFocusSink | undefined;\n\n private derived: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private overrides: Partial<AdapterCapabilities> = {};\n private capabilities: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private sessionState: SessionState = \"none\";\n private boundSession: WebXRSessionLike | null = null;\n private sessionListeners: {\n readonly type: WebXRSessionEventType;\n readonly listener: WebXREventListener;\n }[] = [];\n private animationLoopBound = false;\n private frame = 0;\n private lastTimestamp = 0;\n /**\n * Whether the current session is visible/focused; `undefined` while there is\n * no session, meaning ticking is not gated at all. Reset to `undefined` on\n * session end, so a desktop page resumes exactly as before a session ever\n * existed.\n */\n private focused: boolean | undefined;\n\n private readonly onManagerSessionStart: WebXREventListener = () => {\n this.handleSessionStart();\n };\n\n private readonly onManagerSessionEnd: WebXREventListener = () => {\n this.handleSessionEnd();\n };\n\n /** Session lifecycle over `navigator.xr` and the renderer's XR manager. */\n public readonly session: SessionFacet = {\n getState: () => this.sessionState,\n request: (mode, options) => this.requestSession(mode, options),\n end: () => this.endSession(),\n onStateChange: (listener) => {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n },\n onVisibilityChange: (listener) => {\n this.visibilityListeners.add(listener);\n return () => {\n this.visibilityListeners.delete(listener);\n };\n },\n };\n\n public constructor(options: WebXRRuntimeAdapterOptions) {\n this.xr = options.xr;\n this.xrSystem = options.xrSystem === undefined ? defaultXRSystem() : options.xrSystem;\n this.host = options.host;\n this.scheduler = options.scheduler;\n this.sessionInit = options.sessionInit;\n this.manager = options.manager;\n\n this.xr.addEventListener(\"sessionstart\", this.onManagerSessionStart);\n this.xr.addEventListener(\"sessionend\", this.onManagerSessionEnd);\n\n const session = this.xr.getSession();\n\n if (session) {\n this.attachSession(session);\n this.sessionState = \"active\";\n }\n\n this.derived = deriveCapabilities(session);\n this.capabilities = this.derived;\n }\n\n public onFrame(listener: FrameListener): Unsubscribe {\n this.frameListeners.add(listener);\n return () => {\n this.frameListeners.delete(listener);\n };\n }\n\n public getCapabilities(): AdapterCapabilities {\n return this.capabilities;\n }\n\n public onCapabilitiesChange(listener: CapabilitiesListener): Unsubscribe {\n this.capabilitiesListeners.add(listener);\n return () => {\n this.capabilitiesListeners.delete(listener);\n };\n }\n\n /** The session the renderer is presenting, or null in 2D. */\n public getSession(): WebXRSessionLike | null {\n return this.boundSession;\n }\n\n /**\n * Bind the animation loop, if this adapter was given a host: each callback\n * runs {@link WebXRRuntimeAdapter.tick}. With no host this does nothing -\n * something else owns the loop, and drives {@link WebXRRuntimeAdapter.tick}\n * or {@link WebXRRuntimeAdapter.emitFrame} itself.\n *\n * three.js routes `setAnimationLoop` through the session's own\n * `requestAnimationFrame` while presenting, so one call covers both the 2D\n * page and the headset.\n */\n public start(): void {\n if (this.animationLoopBound) {\n return;\n }\n\n this.animationLoopBound = true;\n this.host?.setAnimationLoop((timestamp) => this.tick(timestamp));\n }\n\n /** Release the animation loop. Safe to call when it was never bound. */\n public stop(): void {\n if (!this.animationLoopBound) {\n return;\n }\n\n this.animationLoopBound = false;\n this.host?.setAnimationLoop(null);\n }\n\n /** Push one frame to every subscriber. Call this when you own the loop. */\n public emitFrame(timestamp: number, delta: number, frame?: number): void {\n const info: FrameInfo = frame === undefined ? { timestamp, delta } : { timestamp, delta, frame };\n this.frameListeners.forEach((listener) => listener(info));\n }\n\n /**\n * Re-read the renderer's session and publish any capability change. The\n * adapter does this itself on every session and input-source event; call it\n * directly after the host enables a feature mid-session, or after a host that\n * raises no events changes what it presents.\n */\n public refreshCapabilities(): void {\n const session = this.xr.getSession();\n\n if (session) {\n this.attachSession(session);\n this.updateDerived(session);\n return;\n }\n\n if (this.boundSession) {\n this.handleSessionEnd();\n return;\n }\n\n this.updateDerived(null);\n }\n\n /**\n * Force capability flags regardless of what the session reports. Overrides\n * are a layer on top of the derived values: they win for as long as they are\n * set, survive every later derivation, and are dropped only by\n * {@link WebXRRuntimeAdapter.clearCapabilityOverrides} or\n * {@link WebXRRuntimeAdapter.dispose}.\n */\n public setCapabilities(capabilities: Partial<AdapterCapabilities>): void {\n this.overrides = { ...this.overrides, ...capabilities };\n this.publishCapabilities();\n }\n\n /** Drop every manual override and fall back to the derived capabilities. */\n public clearCapabilityOverrides(): void {\n this.overrides = {};\n this.publishCapabilities();\n }\n\n /**\n * Release everything the adapter holds: the animation loop, both sets of host\n * event listeners, any in-flight `end()`, the manual overrides and every\n * listener. It does not end the session - the app owns that decision.\n */\n public dispose(): void {\n this.stop();\n this.xr.removeEventListener(\"sessionstart\", this.onManagerSessionStart);\n this.xr.removeEventListener(\"sessionend\", this.onManagerSessionEnd);\n this.detachSession();\n this.settleEndWaiters();\n\n this.overrides = {};\n this.frameListeners.clear();\n this.capabilitiesListeners.clear();\n this.stateListeners.clear();\n this.visibilityListeners.clear();\n }\n\n /**\n * Run one frame step: the visibility gate, the focus/pause signals, the one\n * frame count, {@link WebXRRuntimeAdapter.emitFrame} and `renderTick`. This\n * is what {@link WebXRRuntimeAdapter.start} binds to the owned animation\n * loop, and it is public so a host that owns its OWN loop - one that will\n * never call `start()` because it already calls `setAnimationLoop` itself,\n * such as an XR Blocks app - can drive the exact same step by hand from\n * whatever per-frame hook that host provides, rather than reimplementing the\n * gate, the frame count or the `renderTick` shape.\n *\n * `timestampMs` is the loop's own clock reading, the same value `start()`\n * passes from `setAnimationLoop`'s callback.\n */\n public tick(timestampMs: number): void {\n const deltaMs =\n this.lastTimestamp === 0 ? FIRST_FRAME_DELTA_MS : timestampMs - this.lastTimestamp;\n\n this.lastTimestamp = timestampMs;\n\n const session = this.boundSession;\n\n if (session) {\n this.setFocused(toSessionVisibility(session.visibilityState) === \"visible\");\n\n if (!this.focused) {\n // Gated: a live session that is not visible. Skip this tick entirely -\n // no frame, no renderTick, no advance of the frame count - as IWSDK's\n // bridge skips an unfocused frame. A page with no session at all never\n // reaches this branch, so it is never gated.\n return;\n }\n }\n\n this.frame += 1;\n\n // `FrameInfo.delta` is seconds; the scheduler's `LifecycleContext` is in\n // milliseconds, which is the unit `ThreeRenderLoopBridge` already emits.\n this.emitFrame(timestampMs, deltaMs / 1000, this.frame);\n\n const context: LifecycleContext = {\n timestamp: timestampMs,\n deltaTime: deltaMs,\n frame: this.frame,\n source: \"three\",\n };\n\n this.scheduler?.emit(\"renderTick\", context);\n }\n\n /** Emit focus and pause on a change, as `ServiceBridgeSystem` does. */\n private setFocused(focused: boolean): void {\n if (focused === this.focused) {\n return;\n }\n\n this.focused = focused;\n this.manager?.emitFocusChange(focused);\n this.manager?.emitPauseChange({ paused: !focused });\n }\n\n /**\n * Subscribe to one session's own events. The listeners close over the session\n * they belong to, so nothing here has to re-check which session is live.\n */\n private attachSession(session: WebXRSessionLike): void {\n if (session === this.boundSession) {\n return;\n }\n\n this.detachSession();\n this.boundSession = session;\n this.sessionListeners = [\n { type: \"end\", listener: () => this.handleSessionEnd() },\n {\n type: \"visibilitychange\",\n listener: () => this.notifyVisibility(toSessionVisibility(session.visibilityState)),\n },\n { type: \"inputsourceschange\", listener: () => this.updateDerived(session) },\n ];\n\n for (const entry of this.sessionListeners) {\n session.addEventListener(entry.type, entry.listener);\n }\n }\n\n private detachSession(): void {\n const session = this.boundSession;\n\n if (!session) {\n return;\n }\n\n for (const entry of this.sessionListeners) {\n session.removeEventListener(entry.type, entry.listener);\n }\n\n this.sessionListeners = [];\n this.boundSession = null;\n }\n\n private handleSessionStart(): void {\n const session = this.xr.getSession();\n\n if (!session) {\n return;\n }\n\n this.attachSession(session);\n this.setSessionState(\"active\");\n this.updateDerived(session);\n this.notifyVisibility(toSessionVisibility(session.visibilityState));\n }\n\n /**\n * One session ending raises both the session's `end` event and the manager's\n * `sessionend`, so this runs twice per session and is written to be\n * idempotent.\n */\n private handleSessionEnd(): void {\n if (!this.boundSession && this.sessionState === \"none\") {\n return;\n }\n\n this.detachSession();\n this.setSessionState(\"none\");\n this.updateDerived(null);\n this.notifyVisibility(\"non-immersive\");\n this.settleEndWaiters();\n\n // The session that was gating ticks is gone. A page with no session is\n // never gated, so if it had been paused, restore focus now rather than\n // leaving the manager believing it still is.\n if (this.focused === false) {\n this.manager?.emitFocusChange(true);\n this.manager?.emitPauseChange({ paused: false });\n }\n\n this.focused = undefined;\n }\n\n private updateDerived(session: CapabilitySessionLike | null): void {\n this.derived = deriveCapabilities(session);\n this.publishCapabilities();\n }\n\n private publishCapabilities(): void {\n const next: AdapterCapabilities = { ...this.derived, ...this.overrides };\n const current = this.capabilities;\n\n if (\n next.immersive === current.immersive &&\n next.handTracking === current.handTracking &&\n next.planeDetection === current.planeDetection &&\n next.passthrough === current.passthrough &&\n next.environmentBlendMode === current.environmentBlendMode\n ) {\n return;\n }\n\n this.capabilities = next;\n this.capabilitiesListeners.forEach((listener) => listener(next));\n }\n\n private async requestSession(\n mode: SessionMode,\n options?: SessionRequestOptions,\n ): Promise<SessionResult> {\n if (this.sessionState === \"active\") {\n return { ok: true };\n }\n\n const system = this.xrSystem;\n\n if (!system) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n const timeoutMs = options?.timeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;\n this.setSessionState(\"requesting\");\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<SessionResult>((resolve) => {\n timer = setTimeout(() => resolve({ ok: false, reason: \"timeout\" }), timeoutMs);\n });\n\n const result = await Promise.race([this.openSession(system, mode, options), timeout]);\n clearTimeout(timer);\n\n if (!result.ok) {\n this.setSessionState(\"none\");\n return result;\n }\n\n this.setSessionState(\"active\");\n return result;\n }\n\n /**\n * Negotiate the session and hand it to the renderer. Nothing here throws at\n * the caller: a headset that is absent, refused or broken is a normal runtime\n * condition, so every outcome comes back as a {@link SessionResult}.\n *\n * The request's own features are merged over the `sessionInit` hook's result\n * by the core's `mergeSessionInit`, so they add to the app's defaults rather\n * than replacing them. A request that names none passes the hook's result\n * through untouched.\n */\n private async openSession(\n system: WebXRSystemLike,\n mode: SessionMode,\n options?: SessionRequestOptions,\n ): Promise<SessionResult> {\n try {\n const supported = await system.isSessionSupported(mode);\n\n if (!supported) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n const init = mergeSessionInit(this.sessionInit?.(mode), options);\n const session = await system.requestSession(mode, init);\n await this.xr.setSession(session);\n\n // A manager that raises `sessionstart` has already attached this session;\n // one that does not is attached here, so both host styles behave alike.\n this.attachSession(session);\n this.updateDerived(session);\n\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: toFailureReason(error), error };\n }\n }\n\n private async endSession(): Promise<void> {\n const session = this.boundSession;\n\n if (!session) {\n return;\n }\n\n this.setSessionState(\"ending\");\n\n const ended = new Promise<void>((resolve) => {\n this.endWaiters.add(resolve);\n });\n\n try {\n await session.end();\n } catch {\n // Ending is best effort. The `end` event, not this promise, is what the\n // adapter acts on, and a host that refuses still has to be reported.\n }\n\n // The event has normally arrived by now and this is a no-op. A host that\n // raises none would otherwise leave the caller waiting forever, so the\n // settled `end()` call is the fallback signal.\n this.handleSessionEnd();\n\n await ended;\n }\n\n private settleEndWaiters(): void {\n const waiters = Array.from(this.endWaiters);\n this.endWaiters.clear();\n waiters.forEach((resolve) => resolve());\n }\n\n private notifyVisibility(visibility: SessionVisibility): void {\n this.visibilityListeners.forEach((listener) => listener(visibility));\n }\n\n private setSessionState(state: SessionState): void {\n if (this.sessionState === state) {\n return;\n }\n\n this.sessionState = state;\n this.stateListeners.forEach((listener) => listener(state));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"webxr-runtime-adapter.js","sourceRoot":"","sources":["../src/webxr-runtime-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WebXR implementation of {@link RuntimeAdapter}, for a three.js app or any\n * other page that owns its own renderer.\n *\n * The IWSDK binding gets its adapter from IWSDK. Everything else - a plain\n * three.js app, a desktop build with no headset - had nothing, so a service\n * written against `RuntimeAdapter` could not be hosted there. This adapter\n * closes that gap: it orchestrates the entry points the platform already\n * provides (`navigator.xr` for session negotiation, `renderer.xr` for the\n * session the renderer presents, `setAnimationLoop` for frames) and publishes\n * them through the same seam. It renders nothing, plays nothing and owns no\n * scene state.\n *\n * Every host type here is structural, so this package still imports neither\n * `three` nor any WebXR type, and the adapter unit-tests headless.\n *\n * Given a `host` and a `manager`, the owned animation loop also gates on the\n * live session's visibility, as IWSDK's `ServiceBridgeSystem` and the native\n * adapter do: while a session is live, frames and `renderTick` flow only on\n * `\"visible\"`, `emitFocusChange`/`emitPauseChange` fire on every change, and\n * the frame count skips a gated tick. Unlike IWSDK and native, this adapter\n * also serves a desktop page with no session at all, and gating never applies\n * there - a plain three.js page keeps ticking exactly as it did before this\n * existed.\n *\n * That whole step - the gate, the focus/pause signals, the frame count,\n * `emitFrame` and `renderTick` - lives in one place, {@link\n * WebXRRuntimeAdapter.tick}, which `start()` binds to the owned loop. A host\n * that owns its OWN animation loop and will never call `start()` - an XR\n * Blocks app, whose `Core` already calls `setAnimationLoop` itself - calls\n * `tick(timestampMs)` by hand from its own per-frame hook instead, and gets\n * the identical gate rather than reimplementing it.\n */\nimport {\n DEFAULT_CAPABILITIES,\n DEFAULT_SESSION_TIMEOUT_MS,\n SESSION_REFERENCE_SPACE,\n deriveCapabilities,\n mergeSessionInit,\n recentreOffset,\n type AdapterCapabilities,\n type CapabilitiesListener,\n type CapabilitySessionLike,\n type FrameInfo,\n type FrameListener,\n type IScheduler,\n type LifecycleContext,\n type RuntimeAdapter,\n type SessionFacet,\n type SessionFailureReason,\n type SessionMode,\n type SessionRequestOptions,\n type SessionResult,\n type SessionState,\n type SessionVisibility,\n type Unsubscribe,\n} from \"@realitycollective/service-framework\";\nimport { FIRST_FRAME_DELTA_MS, type AnimationLoopHostLike } from \"./three-render-loop-bridge.js\";\n\n/** The events an `XRSession` raises that this adapter listens for. */\nexport type WebXRSessionEventType = \"end\" | \"visibilitychange\" | \"inputsourceschange\";\n\n/** The events three.js's `WebXRManager` raises that this adapter listens for. */\nexport type WebXRManagerEventType = \"sessionstart\" | \"sessionend\";\n\n/** Host event callback. The adapter reads the host, not the event object. */\nexport type WebXREventListener = (event?: unknown) => void;\n\n/**\n * The two focus signals the adapter drives while a session is live: a\n * `ServiceManager` is one. Declared locally, as `NativeFocusSink` is in the\n * native package, rather than shared, because the two packages do not depend\n * on each other.\n */\nexport interface WebXRFocusSink {\n emitFocusChange(focused: boolean): void;\n emitPauseChange(context: { readonly paused: boolean }): void;\n}\n\n/**\n * The slice of an `XRSession` the adapter reads. It extends the core's\n * {@link CapabilitySessionLike}, so a live session goes straight to\n * `deriveCapabilities` with no mapping.\n */\nexport interface WebXRSessionLike extends CapabilitySessionLike {\n /** Ends the session. The `end` event is what the adapter acts on. */\n end(): Promise<void>;\n /** `\"visible\"`, `\"visible-blurred\"` or `\"hidden\"` on a live session. */\n readonly visibilityState?: string;\n addEventListener(type: WebXRSessionEventType, listener: WebXREventListener): void;\n removeEventListener(type: WebXRSessionEventType, listener: WebXREventListener): void;\n}\n\n/** The slice of `navigator.xr` the session facet negotiates through. */\nexport interface WebXRSystemLike {\n isSessionSupported(mode: string): Promise<boolean>;\n requestSession(mode: string, init?: unknown): Promise<WebXRSessionLike>;\n}\n\n/** An `XRRigidTransform`-shaped position, as `recentre()` reads and builds one. */\nexport interface WebXRVectorLike {\n readonly x: number;\n readonly y: number;\n readonly z: number;\n}\n\n/** An `XRRigidTransform`-shaped orientation. */\nexport interface WebXRQuaternionLike {\n readonly x: number;\n readonly y: number;\n readonly z: number;\n readonly w: number;\n}\n\n/** The slice of an `XRRigidTransform` `recentre()` reads off a viewer pose. */\nexport interface WebXRRigidTransformLike {\n readonly position: WebXRVectorLike;\n readonly orientation: WebXRQuaternionLike;\n}\n\n/** The slice of an `XRView`/viewer pose `recentre()` reads. */\nexport interface WebXRViewerPoseLike {\n readonly transform: WebXRRigidTransformLike;\n}\n\n/** The slice of an `XRFrame` `recentre()` reads the viewer pose from. */\nexport interface WebXRFrameLike {\n getViewerPose(referenceSpace: WebXRReferenceSpaceLike): WebXRViewerPoseLike | null;\n}\n\n/** The slice of an `XRReferenceSpace` `recentre()` offsets. */\nexport interface WebXRReferenceSpaceLike {\n getOffsetReferenceSpace(originOffset: unknown): WebXRReferenceSpaceLike;\n}\n\n/** Builds the `XRRigidTransform` `recentre()` hands to `getOffsetReferenceSpace`. */\nexport type WebXRRigidTransformFactory = (\n position: WebXRVectorLike,\n orientation: WebXRQuaternionLike,\n) => unknown;\n\n/** The slice of a three.js `renderer.xr` (`WebXRManager`) the adapter drives. */\nexport interface WebXRManagerLike {\n /** Hands the renderer the session it should present. */\n setSession(session: WebXRSessionLike): Promise<void> | void;\n /** The session the renderer is presenting, or null in 2D. */\n getSession(): WebXRSessionLike | null;\n addEventListener(type: WebXRManagerEventType, listener: WebXREventListener): void;\n removeEventListener(type: WebXRManagerEventType, listener: WebXREventListener): void;\n /** Sets the reference space type the manager requests a session with. */\n setReferenceSpaceType?(type: string): void;\n /** The reference space the manager is presenting with, if any. */\n getReferenceSpace?(): WebXRReferenceSpaceLike | null;\n /** Replaces the reference space the manager presents with - `recentre()`'s write. */\n setReferenceSpace?(space: WebXRReferenceSpaceLike): void;\n /** The current `XRFrame`, if the manager is mid-frame. */\n getFrame?(): WebXRFrameLike | null;\n}\n\n/** Reads `globalThis.XRRigidTransform`, or `undefined` where there is none (Node). */\nfunction defaultRigidTransformFactory(): WebXRRigidTransformFactory | undefined {\n const ctor = (globalThis as { XRRigidTransform?: new (p: unknown, o: unknown) => unknown })\n .XRRigidTransform;\n\n return ctor ? (position, orientation) => new ctor(position, orientation) : undefined;\n}\n\nexport interface WebXRRuntimeAdapterOptions {\n /** `renderer.xr` - the renderer's WebXR manager. */\n readonly xr: WebXRManagerLike;\n /**\n * `navigator.xr`. Defaults to the global when omitted, and to `null` where\n * there is no global, which is what a Node test sees. A null system reports\n * every session request as `\"unsupported\"`.\n */\n readonly xrSystem?: WebXRSystemLike | null;\n /**\n * The renderer, or anything else with `setAnimationLoop`. Given one, the\n * adapter owns the loop: {@link WebXRRuntimeAdapter.start} binds it and each\n * callback runs {@link WebXRRuntimeAdapter.tick}. Omit it where something\n * else already owns the loop: call {@link WebXRRuntimeAdapter.tick} by hand\n * from whatever per-frame hook that host provides for the same gate, frame\n * count and `renderTick`, or {@link WebXRRuntimeAdapter.emitFrame} directly\n * for the frame fan-out alone.\n */\n readonly host?: AnimationLoopHostLike;\n /**\n * Given a scheduler, each owned frame also emits the `renderTick` channel\n * with `source: \"three\"`, exactly as `ThreeRenderLoopBridge` does, so an app\n * needs one loop owner rather than two.\n */\n readonly scheduler?: IScheduler;\n /**\n * Supplies the `XRSessionInit` for a mode - required and optional features.\n * Called once per request; the default sends no init at all.\n */\n readonly sessionInit?: (mode: SessionMode) => unknown;\n /**\n * The service manager whose focus and pause signals follow the session's\n * visibility, as `ServiceBridgeSystem`'s `manager` option does on IWSDK and\n * the native adapter's `manager` option does on a native host: both fire on\n * every change of focus, `paused` being `!focused`. Gated ONLY while a\n * session is live - `XRSession.visibilityState` via `visibilitychange` -\n * because unlike IWSDK and native, this adapter also serves a desktop page\n * with no session at all, and that page keeps ticking exactly as it did\n * before this option existed.\n */\n readonly manager?: WebXRFocusSink;\n /**\n * Builds the `XRRigidTransform` {@link WebXRRuntimeAdapter.recentre} hands to\n * `getOffsetReferenceSpace`. Defaults to `globalThis.XRRigidTransform` when\n * present; with neither, `recentre()` does nothing.\n */\n readonly rigidTransform?: WebXRRigidTransformFactory;\n}\n\ntype SessionStateListener = (state: SessionState) => void;\ntype SessionVisibilityListener = (visibility: SessionVisibility) => void;\n\nconst SESSION_VISIBILITY_VALUES: ReadonlySet<string> = new Set([\n \"visible\",\n \"visible-blurred\",\n \"hidden\",\n]);\n\n/**\n * Map `XRSession.visibilityState` onto the facet's vocabulary. An unrecognised\n * value is reported as `\"hidden\"`, because treating an unknown state as visible\n * would keep game logic running when it should not.\n */\nfunction toSessionVisibility(value: unknown): SessionVisibility {\n if (typeof value === \"string\" && SESSION_VISIBILITY_VALUES.has(value)) {\n return value as SessionVisibility;\n }\n\n return \"hidden\";\n}\n\n/**\n * WebXR reports a blocked request through the error name: `NotAllowedError`\n * when the user or the permission prompt refused, `SecurityError` when the\n * permissions policy did. Both are a refusal rather than a fault.\n */\nfunction toFailureReason(error: unknown): SessionFailureReason {\n const name = (error as { readonly name?: unknown } | null | undefined)?.name;\n\n if (name === \"NotAllowedError\" || name === \"SecurityError\") {\n return \"denied\";\n }\n\n return \"error\";\n}\n\n/** `navigator.xr`, read defensively: there is no navigator in a Node test. */\nfunction defaultXRSystem(): WebXRSystemLike | null {\n const globalNavigator = (globalThis as { navigator?: { xr?: WebXRSystemLike } }).navigator;\n\n return globalNavigator?.xr ?? null;\n}\n\nexport class WebXRRuntimeAdapter implements RuntimeAdapter {\n private readonly frameListeners = new Set<FrameListener>();\n private readonly capabilitiesListeners = new Set<CapabilitiesListener>();\n private readonly stateListeners = new Set<SessionStateListener>();\n private readonly visibilityListeners = new Set<SessionVisibilityListener>();\n private readonly endWaiters = new Set<() => void>();\n\n private readonly xr: WebXRManagerLike;\n private readonly xrSystem: WebXRSystemLike | null;\n private readonly host: AnimationLoopHostLike | undefined;\n private readonly scheduler: IScheduler | undefined;\n private readonly sessionInit: ((mode: SessionMode) => unknown) | undefined;\n private readonly manager: WebXRFocusSink | undefined;\n private readonly rigidTransform: WebXRRigidTransformFactory | undefined;\n\n private derived: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private overrides: Partial<AdapterCapabilities> = {};\n private capabilities: AdapterCapabilities = DEFAULT_CAPABILITIES;\n private sessionState: SessionState = \"none\";\n private mode: SessionMode | null = null;\n private boundSession: WebXRSessionLike | null = null;\n private sessionListeners: {\n readonly type: WebXRSessionEventType;\n readonly listener: WebXREventListener;\n }[] = [];\n private animationLoopBound = false;\n private frame = 0;\n private lastTimestamp = 0;\n /**\n * Whether the current session is visible/focused; `undefined` while there is\n * no session, meaning ticking is not gated at all. Reset to `undefined` on\n * session end, so a desktop page resumes exactly as before a session ever\n * existed.\n */\n private focused: boolean | undefined;\n\n private readonly onManagerSessionStart: WebXREventListener = () => {\n this.handleSessionStart();\n };\n\n private readonly onManagerSessionEnd: WebXREventListener = () => {\n this.handleSessionEnd();\n };\n\n /** Session lifecycle over `navigator.xr` and the renderer's XR manager. */\n public readonly session: SessionFacet = {\n getState: () => this.sessionState,\n getMode: () => (this.sessionState === \"active\" ? this.mode : null),\n isSupported: (mode) => this.checkSupported(mode),\n request: (mode, options) => this.requestSession(mode, options),\n end: () => this.endSession(),\n recentre: () => this.recentreViewer(),\n onStateChange: (listener) => {\n this.stateListeners.add(listener);\n return () => {\n this.stateListeners.delete(listener);\n };\n },\n onVisibilityChange: (listener) => {\n this.visibilityListeners.add(listener);\n return () => {\n this.visibilityListeners.delete(listener);\n };\n },\n };\n\n public constructor(options: WebXRRuntimeAdapterOptions) {\n this.xr = options.xr;\n this.xrSystem = options.xrSystem === undefined ? defaultXRSystem() : options.xrSystem;\n this.host = options.host;\n this.scheduler = options.scheduler;\n this.sessionInit = options.sessionInit;\n this.manager = options.manager;\n this.rigidTransform = options.rigidTransform ?? defaultRigidTransformFactory();\n\n this.xr.addEventListener(\"sessionstart\", this.onManagerSessionStart);\n this.xr.addEventListener(\"sessionend\", this.onManagerSessionEnd);\n\n const session = this.xr.getSession();\n\n if (session) {\n this.attachSession(session);\n this.sessionState = \"active\";\n }\n\n this.derived = deriveCapabilities(session);\n this.capabilities = this.derived;\n }\n\n public onFrame(listener: FrameListener): Unsubscribe {\n this.frameListeners.add(listener);\n return () => {\n this.frameListeners.delete(listener);\n };\n }\n\n public getCapabilities(): AdapterCapabilities {\n return this.capabilities;\n }\n\n public onCapabilitiesChange(listener: CapabilitiesListener): Unsubscribe {\n this.capabilitiesListeners.add(listener);\n return () => {\n this.capabilitiesListeners.delete(listener);\n };\n }\n\n /** The session the renderer is presenting, or null in 2D. */\n public getSession(): WebXRSessionLike | null {\n return this.boundSession;\n }\n\n /**\n * Bind the animation loop, if this adapter was given a host: each callback\n * runs {@link WebXRRuntimeAdapter.tick}. With no host this does nothing -\n * something else owns the loop, and drives {@link WebXRRuntimeAdapter.tick}\n * or {@link WebXRRuntimeAdapter.emitFrame} itself.\n *\n * three.js routes `setAnimationLoop` through the session's own\n * `requestAnimationFrame` while presenting, so one call covers both the 2D\n * page and the headset.\n */\n public start(): void {\n if (this.animationLoopBound) {\n return;\n }\n\n this.animationLoopBound = true;\n this.host?.setAnimationLoop((timestamp) => this.tick(timestamp));\n }\n\n /** Release the animation loop. Safe to call when it was never bound. */\n public stop(): void {\n if (!this.animationLoopBound) {\n return;\n }\n\n this.animationLoopBound = false;\n this.host?.setAnimationLoop(null);\n }\n\n /** Push one frame to every subscriber. Call this when you own the loop. */\n public emitFrame(timestamp: number, delta: number, frame?: number): void {\n const info: FrameInfo = frame === undefined ? { timestamp, delta } : { timestamp, delta, frame };\n this.frameListeners.forEach((listener) => listener(info));\n }\n\n /**\n * Re-read the renderer's session and publish any capability change. The\n * adapter does this itself on every session and input-source event; call it\n * directly after the host enables a feature mid-session, or after a host that\n * raises no events changes what it presents.\n */\n public refreshCapabilities(): void {\n const session = this.xr.getSession();\n\n if (session) {\n this.attachSession(session);\n this.updateDerived(session);\n return;\n }\n\n if (this.boundSession) {\n this.handleSessionEnd();\n return;\n }\n\n this.updateDerived(null);\n }\n\n /**\n * Force capability flags regardless of what the session reports. Overrides\n * are a layer on top of the derived values: they win for as long as they are\n * set, survive every later derivation, and are dropped only by\n * {@link WebXRRuntimeAdapter.clearCapabilityOverrides} or\n * {@link WebXRRuntimeAdapter.dispose}.\n */\n public setCapabilities(capabilities: Partial<AdapterCapabilities>): void {\n this.overrides = { ...this.overrides, ...capabilities };\n this.publishCapabilities();\n }\n\n /** Drop every manual override and fall back to the derived capabilities. */\n public clearCapabilityOverrides(): void {\n this.overrides = {};\n this.publishCapabilities();\n }\n\n /**\n * Release everything the adapter holds: the animation loop, both sets of host\n * event listeners, any in-flight `end()`, the manual overrides and every\n * listener. It does not end the session - the app owns that decision.\n */\n public dispose(): void {\n this.stop();\n this.xr.removeEventListener(\"sessionstart\", this.onManagerSessionStart);\n this.xr.removeEventListener(\"sessionend\", this.onManagerSessionEnd);\n this.detachSession();\n this.settleEndWaiters();\n\n this.overrides = {};\n this.frameListeners.clear();\n this.capabilitiesListeners.clear();\n this.stateListeners.clear();\n this.visibilityListeners.clear();\n }\n\n /**\n * Run one frame step: the visibility gate, the focus/pause signals, the one\n * frame count, {@link WebXRRuntimeAdapter.emitFrame} and `renderTick`. This\n * is what {@link WebXRRuntimeAdapter.start} binds to the owned animation\n * loop, and it is public so a host that owns its OWN loop - one that will\n * never call `start()` because it already calls `setAnimationLoop` itself,\n * such as an XR Blocks app - can drive the exact same step by hand from\n * whatever per-frame hook that host provides, rather than reimplementing the\n * gate, the frame count or the `renderTick` shape.\n *\n * `timestampMs` is the loop's own clock reading, the same value `start()`\n * passes from `setAnimationLoop`'s callback.\n */\n public tick(timestampMs: number): void {\n const deltaMs =\n this.lastTimestamp === 0 ? FIRST_FRAME_DELTA_MS : timestampMs - this.lastTimestamp;\n\n this.lastTimestamp = timestampMs;\n\n const session = this.boundSession;\n\n if (session) {\n this.setFocused(toSessionVisibility(session.visibilityState) === \"visible\");\n\n if (!this.focused) {\n // Gated: a live session that is not visible. Skip this tick entirely -\n // no frame, no renderTick, no advance of the frame count - as IWSDK's\n // bridge skips an unfocused frame. A page with no session at all never\n // reaches this branch, so it is never gated.\n return;\n }\n }\n\n this.frame += 1;\n\n // `FrameInfo.delta` is seconds; the scheduler's `LifecycleContext` is in\n // milliseconds, which is the unit `ThreeRenderLoopBridge` already emits.\n this.emitFrame(timestampMs, deltaMs / 1000, this.frame);\n\n const context: LifecycleContext = {\n timestamp: timestampMs,\n deltaTime: deltaMs,\n frame: this.frame,\n source: \"three\",\n };\n\n this.scheduler?.emit(\"renderTick\", context);\n }\n\n /** Emit focus and pause on a change, as `ServiceBridgeSystem` does. */\n private setFocused(focused: boolean): void {\n if (focused === this.focused) {\n return;\n }\n\n this.focused = focused;\n this.manager?.emitFocusChange(focused);\n this.manager?.emitPauseChange({ paused: !focused });\n }\n\n /**\n * Subscribe to one session's own events. The listeners close over the session\n * they belong to, so nothing here has to re-check which session is live.\n */\n private attachSession(session: WebXRSessionLike): void {\n if (session === this.boundSession) {\n return;\n }\n\n this.detachSession();\n this.boundSession = session;\n this.sessionListeners = [\n { type: \"end\", listener: () => this.handleSessionEnd() },\n {\n type: \"visibilitychange\",\n listener: () => this.notifyVisibility(toSessionVisibility(session.visibilityState)),\n },\n { type: \"inputsourceschange\", listener: () => this.updateDerived(session) },\n ];\n\n for (const entry of this.sessionListeners) {\n session.addEventListener(entry.type, entry.listener);\n }\n }\n\n private detachSession(): void {\n const session = this.boundSession;\n\n if (!session) {\n return;\n }\n\n for (const entry of this.sessionListeners) {\n session.removeEventListener(entry.type, entry.listener);\n }\n\n this.sessionListeners = [];\n this.boundSession = null;\n }\n\n private handleSessionStart(): void {\n const session = this.xr.getSession();\n\n if (!session) {\n return;\n }\n\n this.attachSession(session);\n this.setSessionState(\"active\");\n this.updateDerived(session);\n this.notifyVisibility(toSessionVisibility(session.visibilityState));\n }\n\n /**\n * One session ending raises both the session's `end` event and the manager's\n * `sessionend`, so this runs twice per session and is written to be\n * idempotent.\n */\n private handleSessionEnd(): void {\n if (!this.boundSession && this.sessionState === \"none\") {\n return;\n }\n\n this.detachSession();\n this.setSessionState(\"none\");\n this.updateDerived(null);\n this.notifyVisibility(\"non-immersive\");\n this.settleEndWaiters();\n\n // The session that was gating ticks is gone. A page with no session is\n // never gated, so if it had been paused, restore focus now rather than\n // leaving the manager believing it still is.\n if (this.focused === false) {\n this.manager?.emitFocusChange(true);\n this.manager?.emitPauseChange({ paused: false });\n }\n\n this.focused = undefined;\n }\n\n private updateDerived(session: CapabilitySessionLike | null): void {\n this.derived = deriveCapabilities(session);\n this.publishCapabilities();\n }\n\n private publishCapabilities(): void {\n const next: AdapterCapabilities = { ...this.derived, ...this.overrides };\n const current = this.capabilities;\n\n if (\n next.immersive === current.immersive &&\n next.handTracking === current.handTracking &&\n next.planeDetection === current.planeDetection &&\n next.passthrough === current.passthrough &&\n next.environmentBlendMode === current.environmentBlendMode\n ) {\n return;\n }\n\n this.capabilities = next;\n this.capabilitiesListeners.forEach((listener) => listener(next));\n }\n\n /** Answers `isSupported()` off `navigator.xr`; never rejects, unlike `openSession`'s own check. */\n private async checkSupported(mode: SessionMode): Promise<boolean> {\n const system = this.xrSystem;\n\n if (!system) {\n return false;\n }\n\n try {\n return await system.isSessionSupported(mode);\n } catch {\n return false;\n }\n }\n\n /**\n * Make the viewer's current floor position and yaw the new origin, by\n * offsetting the manager's reference space. Does nothing when the manager\n * carries none of the optional members this needs, when there is no live\n * frame or reference space, or when no `XRRigidTransform` constructor is\n * available - see {@link WebXRRuntimeAdapterOptions.rigidTransform}.\n */\n private recentreViewer(): void {\n const { getReferenceSpace, setReferenceSpace, getFrame } = this.xr;\n const factory = this.rigidTransform;\n\n if (!getReferenceSpace || !setReferenceSpace || !getFrame || !factory) {\n return;\n }\n\n const space = getReferenceSpace.call(this.xr);\n const frame = getFrame.call(this.xr);\n\n if (!space || !frame) {\n return;\n }\n\n const pose = frame.getViewerPose(space);\n\n if (!pose) {\n return;\n }\n\n const offset = recentreOffset({\n position: [pose.transform.position.x, pose.transform.position.y, pose.transform.position.z],\n orientation: [\n pose.transform.orientation.x,\n pose.transform.orientation.y,\n pose.transform.orientation.z,\n pose.transform.orientation.w,\n ],\n });\n\n const transform = factory(\n { x: offset.position[0], y: offset.position[1], z: offset.position[2] },\n { x: offset.orientation[0], y: offset.orientation[1], z: offset.orientation[2], w: offset.orientation[3] },\n );\n\n setReferenceSpace.call(this.xr, space.getOffsetReferenceSpace(transform));\n }\n\n private async requestSession(\n mode: SessionMode,\n options?: SessionRequestOptions,\n ): Promise<SessionResult> {\n if (this.sessionState === \"active\") {\n if (this.mode === mode) {\n return { ok: true };\n }\n\n // End-and-request: three.js always has an end() to call while a session\n // is live, so the switch is always possible.\n await this.endSession();\n }\n\n const system = this.xrSystem;\n\n if (!system) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n const timeoutMs = options?.timeoutMs ?? DEFAULT_SESSION_TIMEOUT_MS;\n this.setSessionState(\"requesting\");\n\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<SessionResult>((resolve) => {\n timer = setTimeout(() => resolve({ ok: false, reason: \"timeout\" }), timeoutMs);\n });\n\n const result = await Promise.race([this.openSession(system, mode, options), timeout]);\n clearTimeout(timer);\n\n if (!result.ok) {\n this.setSessionState(\"none\");\n return result;\n }\n\n this.mode = mode;\n this.setSessionState(\"active\");\n return result;\n }\n\n /**\n * Negotiate the session and hand it to the renderer. Nothing here throws at\n * the caller: a headset that is absent, refused or broken is a normal runtime\n * condition, so every outcome comes back as a {@link SessionResult}.\n *\n * The request's own features are merged over the `sessionInit` hook's result\n * by the core's `mergeSessionInit`, so they add to the app's defaults rather\n * than replacing them. A request that names none passes the hook's result\n * through untouched.\n */\n private async openSession(\n system: WebXRSystemLike,\n mode: SessionMode,\n options?: SessionRequestOptions,\n ): Promise<SessionResult> {\n try {\n const supported = await system.isSessionSupported(mode);\n\n if (!supported) {\n return { ok: false, reason: \"unsupported\" };\n }\n\n const init = mergeSessionInit(this.sessionInit?.(mode), options);\n const session = await system.requestSession(mode, init);\n this.xr.setReferenceSpaceType?.(SESSION_REFERENCE_SPACE);\n await this.xr.setSession(session);\n\n // A manager that raises `sessionstart` has already attached this session;\n // one that does not is attached here, so both host styles behave alike.\n this.attachSession(session);\n this.updateDerived(session);\n\n return { ok: true };\n } catch (error) {\n return { ok: false, reason: toFailureReason(error), error };\n }\n }\n\n private async endSession(): Promise<void> {\n const session = this.boundSession;\n\n if (!session) {\n return;\n }\n\n this.setSessionState(\"ending\");\n\n const ended = new Promise<void>((resolve) => {\n this.endWaiters.add(resolve);\n });\n\n try {\n await session.end();\n } catch {\n // Ending is best effort. The `end` event, not this promise, is what the\n // adapter acts on, and a host that refuses still has to be reported.\n }\n\n // The event has normally arrived by now and this is a no-op. A host that\n // raises none would otherwise leave the caller waiting forever, so the\n // settled `end()` call is the fallback signal.\n this.handleSessionEnd();\n\n await ended;\n }\n\n private settleEndWaiters(): void {\n const waiters = Array.from(this.endWaiters);\n this.endWaiters.clear();\n waiters.forEach((resolve) => resolve());\n }\n\n private notifyVisibility(visibility: SessionVisibility): void {\n this.visibilityListeners.forEach((listener) => listener(visibility));\n }\n\n private setSessionState(state: SessionState): void {\n if (this.sessionState === state) {\n return;\n }\n\n this.sessionState = state;\n\n if (state === \"none\") {\n this.mode = null;\n }\n\n this.stateListeners.forEach((listener) => listener(state));\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realitycollective/service-framework-three",
|
|
3
|
-
"version": "1.0.2-preview.
|
|
3
|
+
"version": "1.0.2-preview.2",
|
|
4
4
|
"description": "three.js render-loop bindings for the Reality Collective TypeScript Service Framework.",
|
|
5
5
|
"author": "Reality Collective",
|
|
6
6
|
"license": "MIT",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"url": "https://github.com/realitycollective/com.realitycollective.service-framework.ts/issues"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@realitycollective/service-framework": "^1.0.2-preview.
|
|
38
|
+
"@realitycollective/service-framework": "^1.0.2-preview.2"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsc -p tsconfig.build.json",
|