@realitycollective/iwsdk-environment 0.1.1-preview.0 → 0.1.1-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 +19 -0
- package/README.md +3 -3
- package/dist/audio-port.d.ts +9 -2
- package/dist/audio-port.js +36 -12
- package/dist/audio-port.js.map +1 -1
- package/dist/create-system.d.ts +1 -1
- package/dist/create-system.js +1 -1
- package/dist/create-system.js.map +1 -1
- package/dist/environment-port.d.ts +1 -1
- package/dist/environment-port.js +2 -2
- package/dist/environment-port.js.map +1 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.js +14 -1
- package/dist/index.js.map +1 -1
- package/dist/scene-port.d.ts +66 -0
- package/dist/scene-port.js +163 -0
- package/dist/scene-port.js.map +1 -0
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -8,8 +8,27 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
|
|
|
8
8
|
|
|
9
9
|
### Added
|
|
10
10
|
|
|
11
|
+
- `@realitycollective/native-environment` - `nativeEnvironmentHostConformanceCases()`, the host conformance kit a native app runs on its device against its real `environment` and `audio` slices, with the readbacks of the new `NativeEnvironmentTestHost` and `NativeAudioTestHost` types. It drives the real core directors over the host and checks what the host draws and sounds: every slot applied, passthrough suppressing sky and fog and restoring them unchanged, a transition's in-between values reaching the host, the remembered occlusion spec applied when passthrough starts, and the `overlap`, `restart`, `ignore`, `minIntervalMs` and stop policies. A host without `applyOcclusion` or without an `audio` slice fails the cases that need them, so those gaps show on the device.
|
|
12
|
+
- `@realitycollective/native-environment` - binding cases for what a native host receives from the director: the five slots in the order sky, fog, ambient, key, ibl (only the slots that changed); `null` for sky and fog while passthrough is on and the spec back after; the remembered occlusion spec on passthrough, with the sky suppressed once the host reports occlusion `active`; a transition with no duration snapping; and each of the four easings' value halfway through.
|
|
13
|
+
- `@realitycollective/webxr-environment` - **scene management**. `SceneManager` holds the rules and a new `ScenePort` builds and destroys content on each host. It covers a scene list (`register`, `list`), single and additive loads, `unload` of any scene in the stack with the rest keeping their order, preloading (`activate: false`, then `activate`) for an instant switch, an active scene (`getActiveScene`, `setActiveScene`) that receives new instances and whose `environment` drives the director with the configured transition, `instantiate` and `destroy` of named assets, `setNodeActive`, `makePersistent` (addressed afterwards under `PERSISTENT_SCENE_ID`), `bindNode` to tie something such as an interaction target to a node while its scene is shown, and `onSceneLoaded` / `onSceneUnloaded` / `onActiveSceneChanged`. Loads and unloads run in call order; a repeat load returns the same promise; an id not in the list rejects; a scene that repeats a node id fails to load. `dispose` leaves nothing on the host and restores the environment a scene first overrode. The owner placed it here on 2026-09-25, and `docs/BOUNDARY.md` records what moved in and what stays out.
|
|
14
|
+
- `@realitycollective/webxr-environment` - `sceneManagerContractCases()`, 18 cases covering every rule above, with `SCENE_CONTRACT_FIXTURES`. Unlike the port suites they run the real manager over a host's real port, with an inspector that answers from the host itself, including whether its own hit testing would find a node. Each case fails against a port or a manager built to break it.
|
|
15
|
+
- `@realitycollective/threejs-environment` - `ThreeScenePort` and `createThreeScenes(root, options)`. Each scene is a group under the root, loaded with `GLTFLoader` by default or an app's own `load`; node ids come from `userData.nodeId` or the object's name. A hidden scene or inactive node stays in the graph, invisible, with an empty layer mask so no `Raycaster` finds it and `pointerEvents = "none"` for pmndrs pointer events; each object's own values come back when it is shown.
|
|
16
|
+
- `@realitycollective/iwsdk-environment` - `IWSDKScenePort` and `createIWSDKScenes(world, options)`. Scenes are built by IWSDK's own `SceneJSONImporter`, each under its own persistent root beside `world.loadLevel` rather than replacing it. The `LevelTag` the importer adds is removed, so an unrelated `loadLevel` cannot destroy a managed scene. Every entity a scene owns is tracked, because destroying an IWSDK entity does not destroy the entities under it. Instances default to a clone of the preloaded glTF in IWSDK's `AssetManager`.
|
|
17
|
+
- `@realitycollective/xrblocks-environment` - `createXRBlocksScenes(root, options)`, which is `ThreeScenePort` outright, as `createXRBlocksAudio` is `ThreeAudioPort`.
|
|
18
|
+
- `@realitycollective/native-environment` - the `scenes` slice on `__rcHost`, `NativeScenePort` and `createNativeScenes`. The native app builds each scene from its `src` with its own loaders and physics from the physics components in it; scenes and nodes cross as the app's own string keys, and a node's key is its target id in the `interactions` slice. `test/helpers.ts` ships `createFakeScenesHost`, an in-memory app that conforms. All four platforms pass all 18 scene cases.
|
|
11
19
|
- `@realitycollective/webxr-environment` - `environmentPortContractCases()`, `audioPortContractCases()` and `worldSensingPortContractCases()`, the shared `EnvironmentPort` / `AudioPort` / `WorldSensingPort` conformance suites, shipped as data rather than as tests, in the style of `webxr-uiextensions`'s `contract-cases.ts`. Each case is a `name` plus a `run(subject)` that throws a plain `Error` on failure, so an adapter runs it in its own test runner. They check what the core relies on and nothing more: every sky, fog, ambient, key and IBL variant applies without throwing; a port that senses answers a request through `observe` with a report naming a known feature and state; a one-shot voice's `ended` runs exactly once and a stopped voice is never driven to end afterwards; a world-sensing port's `createAnchor` never rejects and its `stopHitTest` / `removeAnchor` tolerate an unknown id. All four platforms - `threejs-`, `iwsdk-`, `xrblocks-environment` and `native-environment` - now run all three suites, and all four already passed every case. `contract-cases.ts` is inside the core's 100% coverage gate.
|
|
12
20
|
- `@realitycollective/native-environment` - the native host adapter, for an app that embeds a JavaScript engine such as Hermes over OpenXR or CompositorServices, owns rendering and audio itself, and installs `globalThis.__rcHost`. `NativeEnvironmentPort`, `NativeAudioPort` and `NativeWorldSensingPort` forward every slot, request and callback to the host's `environment`, `audio` and `sensing` slices as plain data, deciding nothing: a `src` string is resolved and decoded entirely by the native app, and depth occlusion, light estimation and world sensing are grown on the port only when the host implements them, so the director's own "the port does not have this" reporting is what an app sees. Audio keeps every voice's `ended` callback in JavaScript, keyed by `voiceId`, because a callback the core hands to a port cannot itself cross this boundary. `environment` and `audio` are required by the port that reads them and throw, naming the slice, when neither an injected host nor `globalThis.__rcHost` has one; `sensing` is genuinely optional and is instead reported `unsupported`.
|
|
21
|
+
- `@realitycollective/webxr-environment` - `AudioStartReaper` and `DEFAULT_AUDIO_START_TIMEOUT_MS` (10 000 ms), an engine-free helper any `AudioPort` composes to release a voice it started but never confirmed as playing. A looping voice, and one whose cue declares its own `durationMs`, are never reaped this way. `audioPortContractCases()` gained a case proving a voice that never starts is released once a port's own start timeout elapses, on every platform that polls with `update`.
|
|
22
|
+
- `@realitycollective/threejs-environment` - a start-timeout on `ThreeAudioPort` (`ThreeAudioPortOptions.startTimeoutMs`, default 10 000 ms), so a held play whose buffer decode hangs forever - not just one that fails outright - is eventually released instead of leaking. `@realitycollective/xrblocks-environment` inherits it through `ThreeAudioPort`.
|
|
23
|
+
- `@realitycollective/native-environment` - the same start-timeout backstop on `NativeAudioPort` (`NativeAudioPortOptions.startTimeoutMs`): the host is still the one expected to report a failed start promptly through `onVoiceEnded`, and this is what stands between that expectation and a permanent leak on a host that forgets. A cue that legitimately plays longer than the timeout should declare its own `durationMs`.
|
|
24
|
+
- `@realitycollective/native-environment` - four readbacks on `NativeEnvironmentTestHost` (`drawnSkyKind`, `drawnFogKind`, `drawnKeyLightCastsShadow`, `drawnIblActive`) plus `appliedLightEstimation`, reporting what a test host is actually drawing rather than merely what it was handed, and new host-conformance cases built on them: every sky kind (solid, gradient, texture) and every fog kind (linear, exponential) is drawn as the kind it was given rather than flattened to one; a key light's `castShadow` actually turns the host's shadow on and off; an ibl spec makes materials reflect it and `null` turns that back off; and light estimation reaches `applyLightEstimation`, with the host's own measurement, once it produces one, overlaying ambient, key and ibl - a host that reports the feature unavailable, or implements neither method at all, is accepted as legitimate, and only a host with both methods that does nothing at all fails.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- `@realitycollective/iwsdk-environment` - `IWSDKAudioPort`'s start-timeout reap of a voice that never starts now runs through the shared `AudioStartReaper`. Its own behaviour - 10 000 ms default, a looping voice never reaped this way - is unchanged.
|
|
29
|
+
|
|
30
|
+
- The reference is now IWSDK 1.0. `@realitycollective/iwsdk-environment` peers on `@iwsdk/core >=1.0.0 <2.0.0` (was `>=0.5.0 <0.6.0`) and is built and tested against 1.0.0. IWSDK 1.0 still has no light or reflection estimation, so both stay reported unsupported.
|
|
31
|
+
- `@realitycollective/native-environment` - every member of the `environment`, `audio`, `sensing` and `scenes` slices now carries a comment stating its units, sign, defaults and the IWSDK member it stands in for: what `null` means for each slot, that the director has already applied suppression, the easings and the policies, the key light's direction convention (the way the light travels), linear colours in 0..1, and that a scenes host reports every node id, repeats included, because the manager fails a load whose ids repeat. Documentation only.
|
|
13
32
|
|
|
14
33
|
### Fixed
|
|
15
34
|
|
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ It drives IWSDK's own machinery rather than reaching past it to three.js:
|
|
|
39
39
|
- **Image-based lighting** - `IBLGradient` and `IBLTexture`. `kind: "room"` is native here: IWSDK's own component takes the string `"room"` for its built-in probe, so the one kind three.js has to approximate is the one this host has for free.
|
|
40
40
|
- **Authored skies** - `DomeTexture`, with blurriness and rotation. The gradient dome is removed rather than left underneath, so the two never fight over the background.
|
|
41
41
|
- **Depth occlusion** - `DepthSensingSystem` for the system-level settings and `DepthOccludable` per entity. IWSDK opts entities in one at a time by patching their materials, so `scope: "all"` needs an `occludables` option naming which entities the real world may hide - the entities are content, and content is yours. Whatever the port adds it remembers, so turning occlusion off never strips a component you added yourself. Without that option it reports `unavailable` and says so.
|
|
42
|
-
- **Light estimation** - none. IWSDK
|
|
42
|
+
- **Light estimation** - none. IWSDK has no WebXR light estimation at all (checked in 0.5.3 and 1.0.0), and the port reports `unsupported` with that sentence rather than going quiet. Filed upstream as item 6.1. An `ibl: { kind: "estimated" }` asked for here is refused the same way, and the environment map you set yourself is left where it was.
|
|
43
43
|
- **The room** - hits carry no distance here, because IWSDK moves a target entity to the hit and keeps the ray to itself; guessing from the head would be a different number wearing the same name. `IWSDKWorldSensingPort` queries the entities IWSDK's `SceneUnderstandingSystem` already created rather than reaching past it to the session, so planes, meshes and anchors come with IWSDK's own measurements. A hit test becomes an entity carrying `EnvironmentRaycastTarget`, which is how IWSDK casts; creating an anchor means creating an entity with `XRAnchor`, which is how IWSDK anchors.
|
|
44
44
|
- **Audio** - one entity per voice, carrying `AudioSource` with `playbackMode` pinned to `Overlap`, plus the cue's own reference distance, rolloff, maximum distance, `DistanceModel` and cone. IWSDK hands the entity transform to the same three.js positional audio the other adapter uses, so a `facing` turns the entity and the cone angles are written in degrees. The core has already applied the cue's retrigger policy, and letting IWSDK apply its own on top would make `restart` mean two different things on two engines.
|
|
45
45
|
|
|
@@ -49,7 +49,7 @@ Every one of those is an IWSDK platform component with no app-side equivalent. T
|
|
|
49
49
|
|
|
50
50
|
## Knowing when a sound finished
|
|
51
51
|
|
|
52
|
-
IWSDK reports whether a source *is* playing, never that it has just stopped. So the port polls in the tick it is already given, and reports the end once a voice has been seen playing and then is not. A voice that never starts at all - a missing file, a failed decode - is reaped after `startTimeoutMs` (10 s by default) with a warning, rather than being tracked forever.
|
|
52
|
+
IWSDK reports whether a source *is* playing, never that it has just stopped. So the port polls in the tick it is already given, and reports the end once a voice has been seen playing and then is not. A voice that never starts at all - a missing file, a failed decode - is reaped after `startTimeoutMs` (10 s by default) with a warning, rather than being tracked forever. That reaping is a shared rule now: `AudioStartReaper` in `@realitycollective/webxr-environment` runs it here and on every other platform, so the same wait and the same exemptions (a looping voice, a cue with its own `durationMs`) apply everywhere. This port's own contribution stays the same - confirming a voice started the moment `AudioUtils.isPlaying` first says so.
|
|
53
53
|
|
|
54
54
|
## Passthrough
|
|
55
55
|
|
|
@@ -65,7 +65,7 @@ A session belongs to the platform layer, not to the environment. `setPassthrough
|
|
|
65
65
|
|
|
66
66
|
## Peer dependencies
|
|
67
67
|
|
|
68
|
-
`@iwsdk/core >= 0.
|
|
68
|
+
`@iwsdk/core >= 1.0.0 < 2.0.0`, developed and tested against 1.0.0.
|
|
69
69
|
|
|
70
70
|
`three >= 0.170.0`, which every IWSDK application already carries (IWSDK projects alias it to `super-three`). The adapter imports three.js classes from `three` directly rather than through `@iwsdk/core`'s star re-export, so it prebundles even when an application excludes `three` from Vite's dependency optimizer to transform its source.
|
|
71
71
|
|
package/dist/audio-port.d.ts
CHANGED
|
@@ -19,7 +19,12 @@
|
|
|
19
19
|
* So this port polls in `update()` - which the director already calls - and
|
|
20
20
|
* reports the end once a voice has been observed playing and then is not.
|
|
21
21
|
* A voice that never starts at all (a missing file, a decode failure) would
|
|
22
|
-
* otherwise be tracked forever, so it is reaped after `startTimeoutMs
|
|
22
|
+
* otherwise be tracked forever, so it is reaped after `startTimeoutMs` - by
|
|
23
|
+
* the shared `AudioStartReaper` every `AudioPort` in this family now composes
|
|
24
|
+
* (see its file comment), not by bookkeeping local to this file. This port's
|
|
25
|
+
* own contribution is calling `confirmStarted` the instant `AudioUtils.isPlaying`
|
|
26
|
+
* first reports true, exactly the signal `AudioStartReaper` needs and exactly
|
|
27
|
+
* what this file always polled for anyway - only the giving-up is shared now.
|
|
23
28
|
*/
|
|
24
29
|
import { type Entity, type World } from "@iwsdk/core";
|
|
25
30
|
import type { AudioPort, AudioVoiceRequest } from "@realitycollective/webxr-environment";
|
|
@@ -29,7 +34,9 @@ export interface IWSDKAudioPortOptions {
|
|
|
29
34
|
/**
|
|
30
35
|
* How long to wait for a voice to start before giving up on it,
|
|
31
36
|
* milliseconds. Default 10 000 - generous, because a cold asset fetch on a
|
|
32
|
-
* headset over hotel wifi is slower than anyone's patience.
|
|
37
|
+
* headset over hotel wifi is slower than anyone's patience. See
|
|
38
|
+
* `AudioStartReaper` / `DEFAULT_AUDIO_START_TIMEOUT_MS` in
|
|
39
|
+
* `@realitycollective/webxr-environment`, which every platform now shares.
|
|
33
40
|
*/
|
|
34
41
|
readonly startTimeoutMs?: number;
|
|
35
42
|
}
|
package/dist/audio-port.js
CHANGED
|
@@ -19,20 +19,26 @@
|
|
|
19
19
|
* So this port polls in `update()` - which the director already calls - and
|
|
20
20
|
* reports the end once a voice has been observed playing and then is not.
|
|
21
21
|
* A voice that never starts at all (a missing file, a decode failure) would
|
|
22
|
-
* otherwise be tracked forever, so it is reaped after `startTimeoutMs
|
|
22
|
+
* otherwise be tracked forever, so it is reaped after `startTimeoutMs` - by
|
|
23
|
+
* the shared `AudioStartReaper` every `AudioPort` in this family now composes
|
|
24
|
+
* (see its file comment), not by bookkeeping local to this file. This port's
|
|
25
|
+
* own contribution is calling `confirmStarted` the instant `AudioUtils.isPlaying`
|
|
26
|
+
* first reports true, exactly the signal `AudioStartReaper` needs and exactly
|
|
27
|
+
* what this file always polled for anyway - only the giving-up is shared now.
|
|
23
28
|
*/
|
|
24
29
|
import { AudioSource, AudioUtils, DistanceModel, PlaybackMode, Transform, } from "@iwsdk/core";
|
|
25
30
|
import { Quaternion, Vector3 } from "three";
|
|
31
|
+
import { AudioStartReaper } from "@realitycollective/webxr-environment";
|
|
26
32
|
export class IWSDKAudioPort {
|
|
27
33
|
#world;
|
|
28
34
|
#parent;
|
|
29
|
-
#
|
|
35
|
+
#reaper;
|
|
30
36
|
#voices = new Map();
|
|
31
37
|
#disposed = false;
|
|
32
38
|
constructor(world, options = {}) {
|
|
33
39
|
this.#world = world;
|
|
34
40
|
this.#parent = options.parent;
|
|
35
|
-
this.#
|
|
41
|
+
this.#reaper = new AudioStartReaper(options.startTimeoutMs === undefined ? {} : { startTimeoutMs: options.startTimeoutMs });
|
|
36
42
|
}
|
|
37
43
|
start(request) {
|
|
38
44
|
if (this.#disposed) {
|
|
@@ -91,7 +97,8 @@ export class IWSDKAudioPort {
|
|
|
91
97
|
coneOuterGain: attenuation.cone.outsideGain,
|
|
92
98
|
}),
|
|
93
99
|
});
|
|
94
|
-
this.#voices.set(request.voiceId, { entity, request, started: false
|
|
100
|
+
this.#voices.set(request.voiceId, { entity, request, started: false });
|
|
101
|
+
this.#reaper.track(request.voiceId, request, () => this.#onStartTimeout(request.voiceId));
|
|
95
102
|
AudioUtils.play(entity);
|
|
96
103
|
}
|
|
97
104
|
stop(voiceId) {
|
|
@@ -99,6 +106,7 @@ export class IWSDKAudioPort {
|
|
|
99
106
|
if (voice === undefined)
|
|
100
107
|
return;
|
|
101
108
|
this.#voices.delete(voiceId);
|
|
109
|
+
this.#reaper.release(voiceId);
|
|
102
110
|
this.#retire(voice);
|
|
103
111
|
}
|
|
104
112
|
setGain(voiceId, gain) {
|
|
@@ -108,7 +116,10 @@ export class IWSDKAudioPort {
|
|
|
108
116
|
AudioUtils.setVolume(voice.entity, gain);
|
|
109
117
|
}
|
|
110
118
|
update(deltaMs) {
|
|
111
|
-
if (this.#disposed
|
|
119
|
+
if (this.#disposed)
|
|
120
|
+
return;
|
|
121
|
+
this.#reaper.update(deltaMs);
|
|
122
|
+
if (this.#voices.size === 0)
|
|
112
123
|
return;
|
|
113
124
|
for (const [id, voice] of [...this.#voices]) {
|
|
114
125
|
// A looping voice ends when someone stops it, never on its own.
|
|
@@ -116,24 +127,37 @@ export class IWSDKAudioPort {
|
|
|
116
127
|
continue;
|
|
117
128
|
const playing = AudioUtils.isPlaying(voice.entity);
|
|
118
129
|
if (playing) {
|
|
119
|
-
voice.started
|
|
130
|
+
if (!voice.started) {
|
|
131
|
+
voice.started = true;
|
|
132
|
+
this.#reaper.confirmStarted(id);
|
|
133
|
+
}
|
|
120
134
|
continue;
|
|
121
135
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
136
|
+
// Never (yet) observed playing: still within its start window, or
|
|
137
|
+
// already handed to `#onStartTimeout` by the reaper above - either way
|
|
138
|
+
// there is nothing for THIS loop to do with it.
|
|
139
|
+
if (!voice.started)
|
|
140
|
+
continue;
|
|
128
141
|
this.#voices.delete(id);
|
|
129
142
|
this.#retire(voice);
|
|
130
143
|
voice.request.ended();
|
|
131
144
|
}
|
|
132
145
|
}
|
|
146
|
+
/** The shared reaper gave up on a voice that never started; see its file comment for the rule. */
|
|
147
|
+
#onStartTimeout(voiceId) {
|
|
148
|
+
const voice = this.#voices.get(voiceId);
|
|
149
|
+
if (voice === undefined)
|
|
150
|
+
return;
|
|
151
|
+
console.warn(`[iwsdk-environment] cue "${voice.request.cue.id}" never started (${voice.request.cue.src})`);
|
|
152
|
+
this.#voices.delete(voiceId);
|
|
153
|
+
this.#retire(voice);
|
|
154
|
+
voice.request.ended();
|
|
155
|
+
}
|
|
133
156
|
dispose() {
|
|
134
157
|
if (this.#disposed)
|
|
135
158
|
return;
|
|
136
159
|
this.#disposed = true;
|
|
160
|
+
this.#reaper.clear();
|
|
137
161
|
for (const [id, voice] of [...this.#voices]) {
|
|
138
162
|
this.#voices.delete(id);
|
|
139
163
|
this.#retire(voice);
|
package/dist/audio-port.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audio-port.js","sourceRoot":"","sources":["../src/audio-port.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `AudioPort` for Meta IWSDK, over `AudioSource` + `AudioUtils`.\n *\n * ---------------------------------------------------------------------------\n * ONE ENTITY PER VOICE\n * ---------------------------------------------------------------------------\n * IWSDK's audio is a component on an entity, with its own pooling and its own\n * retrigger policy per source. Reusing one entity per CUE would hand those\n * decisions back to IWSDK and make `restart` and `ignore` mean something\n * different here than on three.js, which is the divergence this stack exists\n * to remove. So a voice is an entity: the director's policy wins, every voice\n * is individually stoppable, and IWSDK's own `playbackMode` is pinned to\n * `Overlap` so it never second-guesses the core.\n *\n * ---------------------------------------------------------------------------\n * KNOWING WHEN A SOUND FINISHED\n * ---------------------------------------------------------------------------\n * IWSDK reports whether a source IS playing, never that it has just stopped.\n * So this port polls in `update()` - which the director already calls - and\n * reports the end once a voice has been observed playing and then is not.\n * A voice that never starts at all (a missing file, a decode failure) would\n * otherwise be tracked forever, so it is reaped after `startTimeoutMs`.\n */\nimport {\n AudioSource,\n AudioUtils,\n DistanceModel,\n PlaybackMode,\n Transform,\n type Entity,\n type World,\n} from \"@iwsdk/core\";\nimport { Quaternion, Vector3 } from \"three\";\nimport type { AudioPort, AudioVoiceRequest } from \"@realitycollective/webxr-environment\";\n\nexport interface IWSDKAudioPortOptions {\n /** Parent for the voice entities. Defaults to the world's default parent. */\n readonly parent?: Entity;\n /**\n * How long to wait for a voice to start before giving up on it,\n * milliseconds. Default 10 000 - generous, because a cold asset fetch on a\n * headset over hotel wifi is slower than anyone's patience.\n */\n readonly startTimeoutMs?: number;\n}\n\ninterface Voice {\n readonly entity: Entity;\n readonly request: AudioVoiceRequest;\n started: boolean;\n waitedMs: number;\n}\n\nexport class IWSDKAudioPort implements AudioPort {\n readonly #world: World;\n readonly #parent: Entity | undefined;\n readonly #startTimeoutMs: number;\n readonly #voices = new Map<number, Voice>();\n\n #disposed = false;\n\n constructor(world: World, options: IWSDKAudioPortOptions = {}) {\n this.#world = world;\n this.#parent = options.parent;\n this.#startTimeoutMs = options.startTimeoutMs ?? 10_000;\n }\n\n start(request: AudioVoiceRequest): void {\n if (this.#disposed) {\n request.ended();\n return;\n }\n const entity = this.#world.createTransformEntity(\n undefined,\n this.#parent === undefined ? undefined : { parent: this.#parent },\n );\n const positional = request.at !== null || (request.cue.positional ?? false);\n if (request.at !== null) {\n const view = entity.getVectorView(Transform, \"position\");\n view[0] = request.at[0];\n view[1] = request.at[1];\n view[2] = request.at[2];\n }\n // IWSDK's audio system hands the entity's transform to the same three.js\n // `PositionalAudio` the other adapter uses, so forward is +Z here too and\n // the entity is turned to point that axis the way the sound travels.\n const towards = request.facing ?? null;\n if (towards !== null) {\n const facing = TEMP_FACING.set(towards[0], towards[1], towards[2]);\n if (facing.lengthSq() > 0) {\n TEMP_ROTATION.setFromUnitVectors(AUDIO_FORWARD, facing.normalize());\n const view = entity.getVectorView(Transform, \"orientation\");\n view[0] = TEMP_ROTATION.x;\n view[1] = TEMP_ROTATION.y;\n view[2] = TEMP_ROTATION.z;\n view[3] = TEMP_ROTATION.w;\n }\n }\n const attenuation = request.spatial;\n entity.addComponent(AudioSource, {\n src: request.cue.src,\n volume: request.gain,\n loop: request.loop,\n positional,\n autoplay: false,\n // The core already applied the cue's policy before we got here.\n playbackMode: PlaybackMode.Overlap,\n // Anything the cue did not describe is left to IWSDK's own defaults,\n // which is why these are spread in rather than defaulted here: this port\n // has no opinion about how far a sound carries, and inventing one would\n // make the same cue sound different on each engine.\n ...(attenuation?.refDistance === undefined ? {} : { refDistance: attenuation.refDistance }),\n ...(attenuation?.rolloffFactor === undefined\n ? {}\n : { rolloffFactor: attenuation.rolloffFactor }),\n ...(attenuation?.maxDistance === undefined ? {} : { maxDistance: attenuation.maxDistance }),\n ...(attenuation?.model === undefined ? {} : { distanceModel: distanceModel(attenuation.model) }),\n // Radians in the contract, degrees on the component - IWSDK passes these\n // straight to the same Web Audio panner three.js uses.\n ...(attenuation?.cone === undefined\n ? {}\n : {\n coneInnerAngle: toDegrees(attenuation.cone.inner),\n coneOuterAngle: toDegrees(attenuation.cone.outer),\n coneOuterGain: attenuation.cone.outsideGain,\n }),\n });\n this.#voices.set(request.voiceId, { entity, request, started: false, waitedMs: 0 });\n AudioUtils.play(entity);\n }\n\n stop(voiceId: number): void {\n const voice = this.#voices.get(voiceId);\n if (voice === undefined) return;\n this.#voices.delete(voiceId);\n this.#retire(voice);\n }\n\n setGain(voiceId: number, gain: number): void {\n const voice = this.#voices.get(voiceId);\n if (voice === undefined) return;\n AudioUtils.setVolume(voice.entity, gain);\n }\n\n update(deltaMs: number): void {\n if (this.#disposed || this.#voices.size === 0) return;\n for (const [id, voice] of [...this.#voices]) {\n // A looping voice ends when someone stops it, never on its own.\n if (voice.request.loop) continue;\n const playing = AudioUtils.isPlaying(voice.entity);\n if (playing) {\n voice.started = true;\n continue;\n }\n if (!voice.started) {\n voice.waitedMs += deltaMs;\n if (voice.waitedMs < this.#startTimeoutMs) continue;\n console.warn(\n `[iwsdk-environment] cue \"${voice.request.cue.id}\" never started (${voice.request.cue.src})`,\n );\n }\n this.#voices.delete(id);\n this.#retire(voice);\n voice.request.ended();\n }\n }\n\n dispose(): void {\n if (this.#disposed) return;\n this.#disposed = true;\n for (const [id, voice] of [...this.#voices]) {\n this.#voices.delete(id);\n this.#retire(voice);\n }\n }\n\n #retire(voice: Voice): void {\n AudioUtils.stop(voice.entity);\n voice.entity.destroy();\n }\n}\n\n/** Our distance model names onto IWSDK's enum, which holds the same three. */\nfunction distanceModel(model: \"linear\" | \"inverse\" | \"exponential\"): string {\n if (model === \"linear\") return DistanceModel.Linear;\n if (model === \"exponential\") return DistanceModel.Exponential;\n return DistanceModel.Inverse;\n}\n\n/** Web Audio points a source along +Z, and IWSDK's audio system follows it. */\nconst AUDIO_FORWARD = new Vector3(0, 0, 1);\nconst TEMP_FACING = new Vector3();\nconst TEMP_ROTATION = new Quaternion();\n\n/** Radians in the contract; degrees on the component. */\nfunction toDegrees(radians: number): number {\n return (radians * 180) / Math.PI;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"audio-port.js","sourceRoot":"","sources":["../src/audio-port.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `AudioPort` for Meta IWSDK, over `AudioSource` + `AudioUtils`.\n *\n * ---------------------------------------------------------------------------\n * ONE ENTITY PER VOICE\n * ---------------------------------------------------------------------------\n * IWSDK's audio is a component on an entity, with its own pooling and its own\n * retrigger policy per source. Reusing one entity per CUE would hand those\n * decisions back to IWSDK and make `restart` and `ignore` mean something\n * different here than on three.js, which is the divergence this stack exists\n * to remove. So a voice is an entity: the director's policy wins, every voice\n * is individually stoppable, and IWSDK's own `playbackMode` is pinned to\n * `Overlap` so it never second-guesses the core.\n *\n * ---------------------------------------------------------------------------\n * KNOWING WHEN A SOUND FINISHED\n * ---------------------------------------------------------------------------\n * IWSDK reports whether a source IS playing, never that it has just stopped.\n * So this port polls in `update()` - which the director already calls - and\n * reports the end once a voice has been observed playing and then is not.\n * A voice that never starts at all (a missing file, a decode failure) would\n * otherwise be tracked forever, so it is reaped after `startTimeoutMs` - by\n * the shared `AudioStartReaper` every `AudioPort` in this family now composes\n * (see its file comment), not by bookkeeping local to this file. This port's\n * own contribution is calling `confirmStarted` the instant `AudioUtils.isPlaying`\n * first reports true, exactly the signal `AudioStartReaper` needs and exactly\n * what this file always polled for anyway - only the giving-up is shared now.\n */\nimport {\n AudioSource,\n AudioUtils,\n DistanceModel,\n PlaybackMode,\n Transform,\n type Entity,\n type World,\n} from \"@iwsdk/core\";\nimport { Quaternion, Vector3 } from \"three\";\nimport type { AudioPort, AudioVoiceRequest } from \"@realitycollective/webxr-environment\";\nimport { AudioStartReaper } from \"@realitycollective/webxr-environment\";\n\nexport interface IWSDKAudioPortOptions {\n /** Parent for the voice entities. Defaults to the world's default parent. */\n readonly parent?: Entity;\n /**\n * How long to wait for a voice to start before giving up on it,\n * milliseconds. Default 10 000 - generous, because a cold asset fetch on a\n * headset over hotel wifi is slower than anyone's patience. See\n * `AudioStartReaper` / `DEFAULT_AUDIO_START_TIMEOUT_MS` in\n * `@realitycollective/webxr-environment`, which every platform now shares.\n */\n readonly startTimeoutMs?: number;\n}\n\ninterface Voice {\n readonly entity: Entity;\n readonly request: AudioVoiceRequest;\n started: boolean;\n}\n\nexport class IWSDKAudioPort implements AudioPort {\n readonly #world: World;\n readonly #parent: Entity | undefined;\n readonly #reaper: AudioStartReaper;\n readonly #voices = new Map<number, Voice>();\n\n #disposed = false;\n\n constructor(world: World, options: IWSDKAudioPortOptions = {}) {\n this.#world = world;\n this.#parent = options.parent;\n this.#reaper = new AudioStartReaper(\n options.startTimeoutMs === undefined ? {} : { startTimeoutMs: options.startTimeoutMs },\n );\n }\n\n start(request: AudioVoiceRequest): void {\n if (this.#disposed) {\n request.ended();\n return;\n }\n const entity = this.#world.createTransformEntity(\n undefined,\n this.#parent === undefined ? undefined : { parent: this.#parent },\n );\n const positional = request.at !== null || (request.cue.positional ?? false);\n if (request.at !== null) {\n const view = entity.getVectorView(Transform, \"position\");\n view[0] = request.at[0];\n view[1] = request.at[1];\n view[2] = request.at[2];\n }\n // IWSDK's audio system hands the entity's transform to the same three.js\n // `PositionalAudio` the other adapter uses, so forward is +Z here too and\n // the entity is turned to point that axis the way the sound travels.\n const towards = request.facing ?? null;\n if (towards !== null) {\n const facing = TEMP_FACING.set(towards[0], towards[1], towards[2]);\n if (facing.lengthSq() > 0) {\n TEMP_ROTATION.setFromUnitVectors(AUDIO_FORWARD, facing.normalize());\n const view = entity.getVectorView(Transform, \"orientation\");\n view[0] = TEMP_ROTATION.x;\n view[1] = TEMP_ROTATION.y;\n view[2] = TEMP_ROTATION.z;\n view[3] = TEMP_ROTATION.w;\n }\n }\n const attenuation = request.spatial;\n entity.addComponent(AudioSource, {\n src: request.cue.src,\n volume: request.gain,\n loop: request.loop,\n positional,\n autoplay: false,\n // The core already applied the cue's policy before we got here.\n playbackMode: PlaybackMode.Overlap,\n // Anything the cue did not describe is left to IWSDK's own defaults,\n // which is why these are spread in rather than defaulted here: this port\n // has no opinion about how far a sound carries, and inventing one would\n // make the same cue sound different on each engine.\n ...(attenuation?.refDistance === undefined ? {} : { refDistance: attenuation.refDistance }),\n ...(attenuation?.rolloffFactor === undefined\n ? {}\n : { rolloffFactor: attenuation.rolloffFactor }),\n ...(attenuation?.maxDistance === undefined ? {} : { maxDistance: attenuation.maxDistance }),\n ...(attenuation?.model === undefined ? {} : { distanceModel: distanceModel(attenuation.model) }),\n // Radians in the contract, degrees on the component - IWSDK passes these\n // straight to the same Web Audio panner three.js uses.\n ...(attenuation?.cone === undefined\n ? {}\n : {\n coneInnerAngle: toDegrees(attenuation.cone.inner),\n coneOuterAngle: toDegrees(attenuation.cone.outer),\n coneOuterGain: attenuation.cone.outsideGain,\n }),\n });\n this.#voices.set(request.voiceId, { entity, request, started: false });\n this.#reaper.track(request.voiceId, request, () => this.#onStartTimeout(request.voiceId));\n AudioUtils.play(entity);\n }\n\n stop(voiceId: number): void {\n const voice = this.#voices.get(voiceId);\n if (voice === undefined) return;\n this.#voices.delete(voiceId);\n this.#reaper.release(voiceId);\n this.#retire(voice);\n }\n\n setGain(voiceId: number, gain: number): void {\n const voice = this.#voices.get(voiceId);\n if (voice === undefined) return;\n AudioUtils.setVolume(voice.entity, gain);\n }\n\n update(deltaMs: number): void {\n if (this.#disposed) return;\n this.#reaper.update(deltaMs);\n if (this.#voices.size === 0) return;\n for (const [id, voice] of [...this.#voices]) {\n // A looping voice ends when someone stops it, never on its own.\n if (voice.request.loop) continue;\n const playing = AudioUtils.isPlaying(voice.entity);\n if (playing) {\n if (!voice.started) {\n voice.started = true;\n this.#reaper.confirmStarted(id);\n }\n continue;\n }\n // Never (yet) observed playing: still within its start window, or\n // already handed to `#onStartTimeout` by the reaper above - either way\n // there is nothing for THIS loop to do with it.\n if (!voice.started) continue;\n this.#voices.delete(id);\n this.#retire(voice);\n voice.request.ended();\n }\n }\n\n /** The shared reaper gave up on a voice that never started; see its file comment for the rule. */\n #onStartTimeout(voiceId: number): void {\n const voice = this.#voices.get(voiceId);\n if (voice === undefined) return;\n console.warn(\n `[iwsdk-environment] cue \"${voice.request.cue.id}\" never started (${voice.request.cue.src})`,\n );\n this.#voices.delete(voiceId);\n this.#retire(voice);\n voice.request.ended();\n }\n\n dispose(): void {\n if (this.#disposed) return;\n this.#disposed = true;\n this.#reaper.clear();\n for (const [id, voice] of [...this.#voices]) {\n this.#voices.delete(id);\n this.#retire(voice);\n }\n }\n\n #retire(voice: Voice): void {\n AudioUtils.stop(voice.entity);\n voice.entity.destroy();\n }\n}\n\n/** Our distance model names onto IWSDK's enum, which holds the same three. */\nfunction distanceModel(model: \"linear\" | \"inverse\" | \"exponential\"): string {\n if (model === \"linear\") return DistanceModel.Linear;\n if (model === \"exponential\") return DistanceModel.Exponential;\n return DistanceModel.Inverse;\n}\n\n/** Web Audio points a source along +Z, and IWSDK's audio system follows it. */\nconst AUDIO_FORWARD = new Vector3(0, 0, 1);\nconst TEMP_FACING = new Vector3();\nconst TEMP_ROTATION = new Quaternion();\n\n/** Radians in the contract; degrees on the component. */\nfunction toDegrees(radians: number): number {\n return (radians * 180) / Math.PI;\n}\n"]}
|
package/dist/create-system.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `createSystem` from `@iwsdk/core`, with its return type written out.
|
|
3
3
|
*
|
|
4
|
-
* Why this file exists. IWSDK 0.5.3 declares `createSystem` in
|
|
4
|
+
* Why this file exists. IWSDK (0.5.3, and still 1.0.0) declares `createSystem` in
|
|
5
5
|
* `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,
|
|
6
6
|
* and that file imports `World` from './world' with no extension, the only
|
|
7
7
|
* extensionless relative import in the package. This repository builds with
|
package/dist/create-system.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `createSystem` from `@iwsdk/core`, with its return type written out.
|
|
3
3
|
*
|
|
4
|
-
* Why this file exists. IWSDK 0.5.3 declares `createSystem` in
|
|
4
|
+
* Why this file exists. IWSDK (0.5.3, and still 1.0.0) declares `createSystem` in
|
|
5
5
|
* `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,
|
|
6
6
|
* and that file imports `World` from './world' with no extension, the only
|
|
7
7
|
* extensionless relative import in the package. This repository builds with
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-system.js","sourceRoot":"","sources":["../src/create-system.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAA2B,MAAM,aAAa,CAAC;AAGzF,MAAM,UAAU,YAAY,CAC1B,OAAW,EACX,MAAU;IAEV,OAAO,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["/**\n * `createSystem` from `@iwsdk/core`, with its return type written out.\n *\n * Why this file exists. IWSDK 0.5.3 declares `createSystem` in\n * `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,\n * and that file imports `World` from './world' with no extension, the only\n * extensionless relative import in the package. This repository builds with\n * `moduleResolution: NodeNext`, under which an extensionless relative import\n * in an ES module does not resolve (TS2835), so inside the host's own\n * declaration `World` is an unresolved name. `skipLibCheck` hides the error,\n * and TypeScript's declaration emitter preserves an unresolved name verbatim,\n * so every `export class X extends createSystem(...)` in this package was\n * emitted with a bare `World` in its base type. A consumer that checks\n * library files (`skipLibCheck: false`, or a package emitting declarations on\n * top of ours) fails with TS2304 on its first import of this package. A file\n * that happened to import `World` for another signature resolved the bare\n * name by accident, which is how the sibling adapters looked clean.\n *\n * The annotation below names `World` through `@iwsdk/core`'s barrel, whose\n * imports all carry extensions, so the emitter writes\n * `import(\"@iwsdk/core\").World` wherever the result is extended. Every system\n * in this package takes `createSystem` from here, never from `@iwsdk/core`.\n * `elics` is imported for its types only: it is `@iwsdk/core`'s own\n * dependency, the import is erased at build time, and the emitted\n * declarations already name `import(\"elics\").SystemConstructor`.\n *\n * Delete this file, and import `createSystem` from `@iwsdk/core` again, once\n * IWSDK ships `./world.js` there. `scripts/declaration-check.mjs`, run by\n * `verify:pack`, is the check that fails while the bare name is emitted.\n */\nimport { createSystem as createIWSDKSystem, type System, type World } from \"@iwsdk/core\";\nimport type { SystemConstructor, SystemQueries, SystemSchema } from \"elics\";\n\nexport function createSystem<S extends SystemSchema, Q extends SystemQueries>(\n queries?: Q,\n schema?: S,\n): SystemConstructor<S, Q, World, System<S, Q>> {\n return createIWSDKSystem(queries, schema);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"create-system.js","sourceRoot":"","sources":["../src/create-system.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,EAAE,YAAY,IAAI,iBAAiB,EAA2B,MAAM,aAAa,CAAC;AAGzF,MAAM,UAAU,YAAY,CAC1B,OAAW,EACX,MAAU;IAEV,OAAO,iBAAiB,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;AAC5C,CAAC","sourcesContent":["/**\n * `createSystem` from `@iwsdk/core`, with its return type written out.\n *\n * Why this file exists. IWSDK (0.5.3, and still 1.0.0) declares `createSystem` in\n * `dist/ecs/system.d.ts` as returning `SystemConstructor<S, Q, World, System<S, Q>>`,\n * and that file imports `World` from './world' with no extension, the only\n * extensionless relative import in the package. This repository builds with\n * `moduleResolution: NodeNext`, under which an extensionless relative import\n * in an ES module does not resolve (TS2835), so inside the host's own\n * declaration `World` is an unresolved name. `skipLibCheck` hides the error,\n * and TypeScript's declaration emitter preserves an unresolved name verbatim,\n * so every `export class X extends createSystem(...)` in this package was\n * emitted with a bare `World` in its base type. A consumer that checks\n * library files (`skipLibCheck: false`, or a package emitting declarations on\n * top of ours) fails with TS2304 on its first import of this package. A file\n * that happened to import `World` for another signature resolved the bare\n * name by accident, which is how the sibling adapters looked clean.\n *\n * The annotation below names `World` through `@iwsdk/core`'s barrel, whose\n * imports all carry extensions, so the emitter writes\n * `import(\"@iwsdk/core\").World` wherever the result is extended. Every system\n * in this package takes `createSystem` from here, never from `@iwsdk/core`.\n * `elics` is imported for its types only: it is `@iwsdk/core`'s own\n * dependency, the import is erased at build time, and the emitted\n * declarations already name `import(\"elics\").SystemConstructor`.\n *\n * Delete this file, and import `createSystem` from `@iwsdk/core` again, once\n * IWSDK ships `./world.js` there. `scripts/declaration-check.mjs`, run by\n * `verify:pack`, is the check that fails while the bare name is emitted.\n */\nimport { createSystem as createIWSDKSystem, type System, type World } from \"@iwsdk/core\";\nimport type { SystemConstructor, SystemQueries, SystemSchema } from \"elics\";\n\nexport function createSystem<S extends SystemSchema, Q extends SystemQueries>(\n queries?: Q,\n schema?: S,\n): SystemConstructor<S, Q, World, System<S, Q>> {\n return createIWSDKSystem(queries, schema);\n}\n"]}
|
|
@@ -79,7 +79,7 @@ export declare class IWSDKEnvironmentPort implements EnvironmentPort {
|
|
|
79
79
|
*/
|
|
80
80
|
applyOcclusion(spec: OcclusionSpec | null): void;
|
|
81
81
|
/**
|
|
82
|
-
* IWSDK
|
|
82
|
+
* IWSDK has no light estimation at all (0.5.3 and 1.0.0) - no probe, no session hook,
|
|
83
83
|
* nothing in its typings. This says so instead of leaving the app to wonder
|
|
84
84
|
* why the room never lights anything, and the request is recorded in
|
|
85
85
|
* docs/UPSTREAM_ENHANCEMENTS.md.
|
package/dist/environment-port.js
CHANGED
|
@@ -238,7 +238,7 @@ export class IWSDKEnvironmentPort {
|
|
|
238
238
|
});
|
|
239
239
|
}
|
|
240
240
|
/**
|
|
241
|
-
* IWSDK
|
|
241
|
+
* IWSDK has no light estimation at all (0.5.3 and 1.0.0) - no probe, no session hook,
|
|
242
242
|
* nothing in its typings. This says so instead of leaving the app to wonder
|
|
243
243
|
* why the room never lights anything, and the request is recorded in
|
|
244
244
|
* docs/UPSTREAM_ENHANCEMENTS.md.
|
|
@@ -255,7 +255,7 @@ export class IWSDKEnvironmentPort {
|
|
|
255
255
|
this.#report({
|
|
256
256
|
feature: "lightEstimation",
|
|
257
257
|
state: "unsupported",
|
|
258
|
-
detail: "IWSDK 0
|
|
258
|
+
detail: "IWSDK 1.0 does not expose WebXR light estimation",
|
|
259
259
|
});
|
|
260
260
|
}
|
|
261
261
|
applyFog(fog) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"environment-port.js","sourceRoot":"","sources":["../src/environment-port.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `EnvironmentPort` for Meta IWSDK.\n *\n * ---------------------------------------------------------------------------\n * IT USES IWSDK'S OWN MACHINERY, NOT THREE.JS BEHIND ITS BACK\n * ---------------------------------------------------------------------------\n * The sky is a `DomeGradient` on the level root and the lights are\n * `AmbientLightComponent` and `DirectionalLightComponent` on entities.\n * Reaching past IWSDK to write `scene.background` or to `scene.add()` a light\n * would work right up until IWSDK's own `EnvironmentSystem` or `LightSystem`\n * disagreed - and one of the things IWSDK's environment system already does\n * correctly is hide authored backgrounds in an AR session, which is behaviour\n * worth inheriting rather than fighting.\n *\n * No geometry is created here. Every component this port touches is an IWSDK\n * platform facility with no app-side equivalent; a floor would be a mesh, and\n * meshes are the app's.\n *\n * Fog is the exception. IWSDK has no fog component, so it is set on\n * `world.scene` directly, with three.js's own `Fog` / `FogExp2` imported from\n * `@iwsdk/core` (the framework's rule - one three.js instance, reached through\n * the framework's re-export).\n *\n * ---------------------------------------------------------------------------\n * WRITING COMPONENT COLOURS\n * ---------------------------------------------------------------------------\n * A `Types.Color` or `Types.Vec3` field must be written through\n * `getVectorView`; `setValue` on one throws. Every colour write here goes\n * through `writeColour` for that reason - it is not a style choice.\n */\nimport {\n AmbientLightComponent,\n DepthOccludable,\n DepthSensingSystem,\n DirectionalLightComponent,\n DomeGradient,\n DomeTexture,\n IBLGradient,\n IBLTexture,\n OcclusionShadersMode,\n Transform,\n type Entity,\n type World,\n} from \"@iwsdk/core\";\nimport { Fog, FogExp2, Quaternion, Vector3 } from \"three\";\nimport type {\n AmbientLightSpec,\n EnvironmentPort,\n EnvironmentPortHost,\n FogSpec,\n IblSpec,\n KeyLightSpec,\n OcclusionSpec,\n ResolvedLightEstimation,\n Rgb,\n SensingReport,\n SkyGradient,\n SkySpec,\n} from \"@realitycollective/webxr-environment\";\nimport { clamp01, lerpRgb } from \"@realitycollective/webxr-environment\";\n\nexport interface IWSDKEnvironmentPortOptions {\n /**\n * Where the light entities are parented. Defaults to the active level root,\n * which is also where the dome lives, so the whole environment is torn down\n * with the level.\n */\n readonly parent?: Entity;\n /** Dome brightness multiplier, used when a sky does not name its own. Default 1. */\n readonly skyIntensity?: number;\n /**\n * Which entities the real world is allowed to hide, for `scope: \"all\"`.\n *\n * IWSDK opts entities INTO occlusion one at a time, by patching the\n * materials under each one, so somebody has to say which entities those\n * are. It cannot be this port: the entities are content, content is the\n * app's, and walking a scene graph looking for meshes would be this package\n * guessing at the app's structure.\n *\n * With no list, `scope: \"all\"` reports `unavailable` and says why. An app\n * that would rather tag its own entities uses `scope: \"tagged\"` and adds\n * `DepthOccludable` itself; then this port only configures the system.\n */\n readonly occludables?: () => Iterable<Entity>;\n}\n\n/** 0..1 softness onto IWSDK's blur radius in pixels, whose default is 20. */\nconst MAX_BLUR_RADIUS = 40;\n\n/**\n * The narrowest thing a vector view is: elics types it as a union of every\n * typed array, and all these helpers need is indexed writes and a length.\n */\ntype NumericView = { [index: number]: number; readonly length: number };\n\n/** Local -Z is where an IWSDK directional light points. */\nconst LIGHT_FORWARD = new Vector3(0, 0, -1);\nconst TEMP_DIRECTION = new Vector3();\nconst TEMP_QUATERNION = new Quaternion();\n\nexport class IWSDKEnvironmentPort implements EnvironmentPort {\n readonly #world: World;\n readonly #parent: Entity | undefined;\n readonly #skyIntensity: number;\n\n readonly #occludables: (() => Iterable<Entity>) | undefined;\n\n #domeEntity: Entity | null = null;\n #iblEntity: Entity | null = null;\n #ambientEntity: Entity | null = null;\n #keyEntity: Entity | null = null;\n #fogKind: FogSpec[\"kind\"] | null = null;\n #host: EnvironmentPortHost | null = null;\n /** Only what THIS port added, so it never removes the app's own component. */\n #occluding: Entity[] = [];\n\n constructor(world: World, options: IWSDKEnvironmentPortOptions = {}) {\n this.#world = world;\n this.#parent = options.parent;\n this.#skyIntensity = options.skyIntensity ?? 1;\n this.#occludables = options.occludables;\n }\n\n observe(host: EnvironmentPortHost): () => void {\n this.#host = host;\n return () => {\n this.#host = null;\n };\n }\n\n applySky(sky: SkySpec | null): void {\n const root = this.#levelRoot();\n if (root === null) return;\n\n if (sky === null) {\n if (this.#domeEntity !== null && this.#domeEntity.hasComponent(DomeGradient)) {\n this.#domeEntity.removeComponent(DomeGradient);\n }\n this.#domeEntity = null;\n return;\n }\n\n if (sky.kind === \"texture\") {\n // An authored sky is a different IWSDK component, so the dome one goes\n // rather than sitting underneath and fighting over the same background.\n if (root.hasComponent(DomeGradient)) root.removeComponent(DomeGradient);\n if (!root.hasComponent(DomeTexture)) root.addComponent(DomeTexture);\n this.#domeEntity = root;\n root.setValue(DomeTexture, \"src\", sky.src);\n root.setValue(DomeTexture, \"intensity\", sky.intensity ?? this.#skyIntensity);\n root.setValue(DomeTexture, \"blurriness\", sky.blur ?? 0);\n writeVec3(root.getVectorView(DomeTexture, \"rotation\"), 0, sky.rotationY ?? 0, 0);\n root.setValue(DomeTexture, \"_needsUpdate\", true);\n return;\n }\n\n if (root.hasComponent(DomeTexture)) root.removeComponent(DomeTexture);\n if (this.#domeEntity !== root || !root.hasComponent(DomeGradient)) {\n if (!root.hasComponent(DomeGradient)) root.addComponent(DomeGradient);\n this.#domeEntity = root;\n }\n\n // IWSDK's dome is a Unity-style sky / equator / ground triple. A solid sky\n // is all three the same. A gradient uses the horizon colour the app gave,\n // and derives one from the ramp when it did not - which is the same colour\n // the three.js adapter puts at the horizon, so the two engines agree there\n // rather than only at the poles.\n if (sky.kind === \"solid\") {\n writeRgb(root.getVectorView(DomeGradient, \"sky\"), sky.colour);\n writeRgb(root.getVectorView(DomeGradient, \"equator\"), sky.colour);\n writeRgb(root.getVectorView(DomeGradient, \"ground\"), sky.colour);\n root.setValue(DomeGradient, \"intensity\", this.#skyIntensity);\n } else {\n writeRgb(root.getVectorView(DomeGradient, \"sky\"), sky.top);\n writeRgb(root.getVectorView(DomeGradient, \"equator\"), equatorOf(sky));\n writeRgb(root.getVectorView(DomeGradient, \"ground\"), sky.bottom);\n root.setValue(DomeGradient, \"intensity\", sky.intensity ?? this.#skyIntensity);\n }\n root.setValue(DomeGradient, \"_needsUpdate\", true);\n }\n\n /**\n * The environment map, as IWSDK's own image-based lighting components.\n *\n * `room` is not an approximation here: IWSDK's `IBLTexture` takes the string\n * `\"room\"` for its built-in probe, so the one kind three.js has to fake is\n * the one kind this host has natively.\n */\n applyIbl(ibl: IblSpec | null): void {\n const root = this.#levelRoot();\n if (root === null) return;\n\n if (ibl === null) {\n if (this.#iblEntity !== null) {\n if (this.#iblEntity.hasComponent(IBLGradient)) this.#iblEntity.removeComponent(IBLGradient);\n if (this.#iblEntity.hasComponent(IBLTexture)) this.#iblEntity.removeComponent(IBLTexture);\n }\n this.#iblEntity = null;\n return;\n }\n this.#iblEntity = root;\n\n if (ibl.kind === \"estimated\") {\n // IWSDK measures no reflections (UPSTREAM_ENHANCEMENTS 6.1), so the slot\n // keeps whatever it had rather than being cleared: an app that asked for\n // measured reflections and got none should not also lose its own map.\n this.#report({\n feature: \"lightEstimation\",\n state: \"unsupported\",\n detail: \"IWSDK measures no reflections, so an estimated environment map cannot be applied\",\n });\n return;\n }\n\n if (ibl.kind === \"gradient\") {\n if (root.hasComponent(IBLTexture)) root.removeComponent(IBLTexture);\n if (!root.hasComponent(IBLGradient)) root.addComponent(IBLGradient);\n writeRgb(root.getVectorView(IBLGradient, \"sky\"), ibl.top);\n writeRgb(root.getVectorView(IBLGradient, \"equator\"), equatorOf(ibl));\n writeRgb(root.getVectorView(IBLGradient, \"ground\"), ibl.bottom);\n root.setValue(IBLGradient, \"intensity\", ibl.intensity ?? 1);\n root.setValue(IBLGradient, \"_needsUpdate\", true);\n return;\n }\n\n if (root.hasComponent(IBLGradient)) root.removeComponent(IBLGradient);\n if (!root.hasComponent(IBLTexture)) root.addComponent(IBLTexture);\n root.setValue(IBLTexture, \"src\", ibl.kind === \"room\" ? \"room\" : ibl.src);\n root.setValue(IBLTexture, \"intensity\", ibl.intensity ?? 1);\n writeVec3(root.getVectorView(IBLTexture, \"rotation\"), 0, ibl.rotationY ?? 0, 0);\n root.setValue(IBLTexture, \"_needsUpdate\", true);\n }\n\n /**\n * Depth occlusion, through IWSDK's `DepthSensingSystem` and the per-entity\n * `DepthOccludable` component.\n *\n * IWSDK patches the materials under each opted-in entity, which is why the\n * scope matters more here than on three.js: there is no global mode to turn\n * on, and an entity nobody opted in is an entity the real world will not\n * hide. Whatever this port adds, it remembers, so turning occlusion off\n * never strips a component the app put there itself.\n */\n applyOcclusion(spec: OcclusionSpec | null): void {\n if (spec === null) {\n this.#releaseOccludables();\n const running = this.#system();\n if (running !== undefined) running.config.enableOcclusion.value = false;\n this.#report({ feature: \"occlusion\", state: \"unavailable\", detail: \"occlusion is off\" });\n return;\n }\n\n const blurRadius =\n spec.softness === undefined ? undefined : clamp01(spec.softness) * MAX_BLUR_RADIUS;\n const system = this.#system();\n if (system === undefined) {\n this.#world.registerSystem(DepthSensingSystem, {\n configData: {\n enableOcclusion: true,\n enableDepthTexture: true,\n useFloat32: spec.source?.format !== \"luminance-alpha\",\n ...(blurRadius === undefined ? {} : { blurRadius }),\n },\n });\n } else {\n system.config.enableOcclusion.value = true;\n system.config.useFloat32.value = spec.source?.format !== \"luminance-alpha\";\n if (blurRadius !== undefined) system.config.blurRadius.value = blurRadius;\n }\n\n const mode = occlusionMode(spec.mode);\n this.#releaseOccludables();\n if (spec.scope === \"tagged\") {\n this.#report({\n feature: \"occlusion\",\n state: \"active\",\n detail: \"occluding the entities the app marked with DepthOccludable\",\n });\n return;\n }\n if (this.#occludables === undefined) {\n this.#report({\n feature: \"occlusion\",\n state: \"unavailable\",\n detail:\n \"IWSDK occludes per entity; pass an occludables option, or use scope: tagged and add DepthOccludable yourself\",\n });\n return;\n }\n let count = 0;\n for (const entity of this.#occludables()) {\n if (entity.hasComponent(DepthOccludable)) continue;\n entity.addComponent(DepthOccludable, { mode });\n this.#occluding.push(entity);\n count += 1;\n }\n this.#report({\n feature: \"occlusion\",\n state: \"active\",\n detail: \"occluding \" + String(count) + \" entities\",\n });\n }\n\n /**\n * IWSDK 0.5.3 has no light estimation at all - no probe, no session hook,\n * nothing in its typings. This says so instead of leaving the app to wonder\n * why the room never lights anything, and the request is recorded in\n * docs/UPSTREAM_ENHANCEMENTS.md.\n */\n applyLightEstimation(estimation: ResolvedLightEstimation | null): void {\n if (estimation === null) {\n this.#report({\n feature: \"lightEstimation\",\n state: \"unavailable\",\n detail: \"light estimation is off\",\n });\n return;\n }\n this.#report({\n feature: \"lightEstimation\",\n state: \"unsupported\",\n detail: \"IWSDK 0.5.3 does not expose WebXR light estimation\",\n });\n }\n\n applyFog(fog: FogSpec | null): void {\n const scene = this.#world.scene;\n if (fog === null) {\n scene.fog = null;\n this.#fogKind = null;\n return;\n }\n if (this.#fogKind !== fog.kind || scene.fog === null) {\n scene.fog =\n fog.kind === \"linear\"\n ? new Fog(rgbToHexNumber(fog.colour), fog.near, fog.far)\n : new FogExp2(rgbToHexNumber(fog.colour), fog.density);\n this.#fogKind = fog.kind;\n return;\n }\n const existing = scene.fog;\n existing.color.setHex(rgbToHexNumber(fog.colour));\n if (fog.kind === \"linear\" && existing instanceof Fog) {\n existing.near = fog.near;\n existing.far = fog.far;\n } else if (fog.kind === \"exponential\" && existing instanceof FogExp2) {\n existing.density = fog.density;\n }\n }\n\n applyAmbient(light: AmbientLightSpec | null): void {\n if (light === null) {\n this.#destroy(\"ambient\");\n return;\n }\n const entity = this.#ensure(\"ambient\");\n if (!entity.hasComponent(AmbientLightComponent)) {\n entity.addComponent(AmbientLightComponent);\n }\n writeRgb(entity.getVectorView(AmbientLightComponent, \"color\"), light.colour);\n entity.setValue(AmbientLightComponent, \"intensity\", light.intensity);\n }\n\n applyKeyLight(light: KeyLightSpec | null): void {\n if (light === null) {\n this.#destroy(\"key\");\n return;\n }\n const entity = this.#ensure(\"key\");\n if (!entity.hasComponent(DirectionalLightComponent)) {\n entity.addComponent(DirectionalLightComponent);\n }\n writeRgb(entity.getVectorView(DirectionalLightComponent, \"color\"), light.colour);\n entity.setValue(DirectionalLightComponent, \"intensity\", light.intensity);\n entity.setValue(DirectionalLightComponent, \"castShadow\", light.castShadow ?? false);\n\n // The spec names the direction light TRAVELS; the component emits along\n // the entity's local -Z, so the entity is rotated to face that way. A\n // zero-length direction would produce a NaN quaternion, so it falls back\n // to straight down.\n const [x, y, z] = light.direction;\n if (Math.hypot(x, y, z) === 0) TEMP_DIRECTION.set(0, -1, 0);\n else TEMP_DIRECTION.set(x, y, z).normalize();\n TEMP_QUATERNION.setFromUnitVectors(LIGHT_FORWARD, TEMP_DIRECTION);\n writeQuaternion(entity.getVectorView(Transform, \"orientation\"), TEMP_QUATERNION);\n }\n\n dispose(): void {\n this.applySky(null);\n this.applyFog(null);\n this.applyAmbient(null);\n this.applyKeyLight(null);\n this.applyIbl(null);\n this.#releaseOccludables();\n this.#host = null;\n }\n\n #system() {\n return this.#world.getSystem(DepthSensingSystem);\n }\n\n #releaseOccludables(): void {\n for (const entity of this.#occluding) {\n if (entity.hasComponent(DepthOccludable)) entity.removeComponent(DepthOccludable);\n }\n this.#occluding = [];\n }\n\n #report(report: SensingReport): void {\n this.#host?.report(report);\n }\n\n #levelRoot(): Entity | null {\n return this.#parent ?? this.#world.activeLevel?.value ?? null;\n }\n\n /** The light entities exist independently of the level, so this cannot fail. */\n #ensure(which: \"ambient\" | \"key\"): Entity {\n const existing = which === \"ambient\" ? this.#ambientEntity : this.#keyEntity;\n if (existing !== null) return existing;\n const parent = this.#levelRoot();\n const entity = this.#world.createTransformEntity(\n undefined,\n parent === null ? undefined : { parent },\n );\n if (which === \"ambient\") this.#ambientEntity = entity;\n else this.#keyEntity = entity;\n return entity;\n }\n\n #destroy(which: \"ambient\" | \"key\"): void {\n const entity = which === \"ambient\" ? this.#ambientEntity : this.#keyEntity;\n entity?.destroy();\n if (which === \"ambient\") this.#ambientEntity = null;\n else this.#keyEntity = null;\n }\n}\n\n/**\n * The colour at the horizon: the app's, or the one the ramp implies.\n *\n * IWSDK's dome and IBL components are both three-stop, so this port always has\n * to supply a middle colour. Deriving it from the same ramp the three.js\n * adapter draws is what stops the two engines disagreeing about a sky the app\n * described once.\n */\nfunction equatorOf(gradient: SkyGradient | { top: Rgb; bottom: Rgb; equator?: Rgb }): Rgb {\n if (gradient.equator !== undefined) return gradient.equator;\n const exponent = \"exponent\" in gradient ? (gradient.exponent ?? 1) : 1;\n return lerpRgb(gradient.bottom, gradient.top, Math.pow(0.5, exponent));\n}\n\n/** Our mode names onto IWSDK's shader modes. */\nfunction occlusionMode(mode: OcclusionSpec[\"mode\"]): string {\n if (mode === \"hard\") return OcclusionShadersMode.HardOcclusion;\n if (mode === \"minmax-soft\") return OcclusionShadersMode.MinMaxSoftOcclusion;\n return OcclusionShadersMode.SoftOcclusion;\n}\n\n/** As `writeRgb`, for a `Types.Vec3` that is not a colour. */\nfunction writeVec3(view: NumericView, x: number, y: number, z: number): void {\n view[0] = x;\n view[1] = y;\n view[2] = z;\n}\n\n/** `[r, g, b]` in 0..1 -> a packed 0xRRGGBB, for the three.js-side setters. */\nfunction rgbToHexNumber(rgb: Rgb): number {\n const channel = (value: number) => Math.round(clamp01(value) * 255);\n return (channel(rgb[0]) << 16) | (channel(rgb[1]) << 8) | channel(rgb[2]);\n}\n\n/**\n * Write an RGBA colour into a component's vector view.\n *\n * The VIEW is passed in rather than the component and field name, so every\n * call site keeps elics's own `getVectorView` typing - which checks that the\n * field exists AND that it is an array type. A helper that took the component\n * would have to erase those generics, and the first misspelled field name\n * would then reach a headset instead of the compiler.\n *\n * Alpha is always 1: the specs describe opaque colours. Writing index 3 of a\n * three-component view is a no-op in JavaScript rather than an error, so no\n * length check is needed for a component whose colour is a `Types.Vec3`.\n */\nfunction writeRgb(view: NumericView, rgb: Rgb): void {\n view[0] = rgb[0];\n view[1] = rgb[1];\n view[2] = rgb[2];\n view[3] = 1;\n}\n\n/** As above, for a `Types.Vec4` orientation. */\nfunction writeQuaternion(view: NumericView, quaternion: Quaternion): void {\n view[0] = quaternion.x;\n view[1] = quaternion.y;\n view[2] = quaternion.z;\n view[3] = quaternion.w;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"environment-port.js","sourceRoot":"","sources":["../src/environment-port.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `EnvironmentPort` for Meta IWSDK.\n *\n * ---------------------------------------------------------------------------\n * IT USES IWSDK'S OWN MACHINERY, NOT THREE.JS BEHIND ITS BACK\n * ---------------------------------------------------------------------------\n * The sky is a `DomeGradient` on the level root and the lights are\n * `AmbientLightComponent` and `DirectionalLightComponent` on entities.\n * Reaching past IWSDK to write `scene.background` or to `scene.add()` a light\n * would work right up until IWSDK's own `EnvironmentSystem` or `LightSystem`\n * disagreed - and one of the things IWSDK's environment system already does\n * correctly is hide authored backgrounds in an AR session, which is behaviour\n * worth inheriting rather than fighting.\n *\n * No geometry is created here. Every component this port touches is an IWSDK\n * platform facility with no app-side equivalent; a floor would be a mesh, and\n * meshes are the app's.\n *\n * Fog is the exception. IWSDK has no fog component, so it is set on\n * `world.scene` directly, with three.js's own `Fog` / `FogExp2` imported from\n * `@iwsdk/core` (the framework's rule - one three.js instance, reached through\n * the framework's re-export).\n *\n * ---------------------------------------------------------------------------\n * WRITING COMPONENT COLOURS\n * ---------------------------------------------------------------------------\n * A `Types.Color` or `Types.Vec3` field must be written through\n * `getVectorView`; `setValue` on one throws. Every colour write here goes\n * through `writeColour` for that reason - it is not a style choice.\n */\nimport {\n AmbientLightComponent,\n DepthOccludable,\n DepthSensingSystem,\n DirectionalLightComponent,\n DomeGradient,\n DomeTexture,\n IBLGradient,\n IBLTexture,\n OcclusionShadersMode,\n Transform,\n type Entity,\n type World,\n} from \"@iwsdk/core\";\nimport { Fog, FogExp2, Quaternion, Vector3 } from \"three\";\nimport type {\n AmbientLightSpec,\n EnvironmentPort,\n EnvironmentPortHost,\n FogSpec,\n IblSpec,\n KeyLightSpec,\n OcclusionSpec,\n ResolvedLightEstimation,\n Rgb,\n SensingReport,\n SkyGradient,\n SkySpec,\n} from \"@realitycollective/webxr-environment\";\nimport { clamp01, lerpRgb } from \"@realitycollective/webxr-environment\";\n\nexport interface IWSDKEnvironmentPortOptions {\n /**\n * Where the light entities are parented. Defaults to the active level root,\n * which is also where the dome lives, so the whole environment is torn down\n * with the level.\n */\n readonly parent?: Entity;\n /** Dome brightness multiplier, used when a sky does not name its own. Default 1. */\n readonly skyIntensity?: number;\n /**\n * Which entities the real world is allowed to hide, for `scope: \"all\"`.\n *\n * IWSDK opts entities INTO occlusion one at a time, by patching the\n * materials under each one, so somebody has to say which entities those\n * are. It cannot be this port: the entities are content, content is the\n * app's, and walking a scene graph looking for meshes would be this package\n * guessing at the app's structure.\n *\n * With no list, `scope: \"all\"` reports `unavailable` and says why. An app\n * that would rather tag its own entities uses `scope: \"tagged\"` and adds\n * `DepthOccludable` itself; then this port only configures the system.\n */\n readonly occludables?: () => Iterable<Entity>;\n}\n\n/** 0..1 softness onto IWSDK's blur radius in pixels, whose default is 20. */\nconst MAX_BLUR_RADIUS = 40;\n\n/**\n * The narrowest thing a vector view is: elics types it as a union of every\n * typed array, and all these helpers need is indexed writes and a length.\n */\ntype NumericView = { [index: number]: number; readonly length: number };\n\n/** Local -Z is where an IWSDK directional light points. */\nconst LIGHT_FORWARD = new Vector3(0, 0, -1);\nconst TEMP_DIRECTION = new Vector3();\nconst TEMP_QUATERNION = new Quaternion();\n\nexport class IWSDKEnvironmentPort implements EnvironmentPort {\n readonly #world: World;\n readonly #parent: Entity | undefined;\n readonly #skyIntensity: number;\n\n readonly #occludables: (() => Iterable<Entity>) | undefined;\n\n #domeEntity: Entity | null = null;\n #iblEntity: Entity | null = null;\n #ambientEntity: Entity | null = null;\n #keyEntity: Entity | null = null;\n #fogKind: FogSpec[\"kind\"] | null = null;\n #host: EnvironmentPortHost | null = null;\n /** Only what THIS port added, so it never removes the app's own component. */\n #occluding: Entity[] = [];\n\n constructor(world: World, options: IWSDKEnvironmentPortOptions = {}) {\n this.#world = world;\n this.#parent = options.parent;\n this.#skyIntensity = options.skyIntensity ?? 1;\n this.#occludables = options.occludables;\n }\n\n observe(host: EnvironmentPortHost): () => void {\n this.#host = host;\n return () => {\n this.#host = null;\n };\n }\n\n applySky(sky: SkySpec | null): void {\n const root = this.#levelRoot();\n if (root === null) return;\n\n if (sky === null) {\n if (this.#domeEntity !== null && this.#domeEntity.hasComponent(DomeGradient)) {\n this.#domeEntity.removeComponent(DomeGradient);\n }\n this.#domeEntity = null;\n return;\n }\n\n if (sky.kind === \"texture\") {\n // An authored sky is a different IWSDK component, so the dome one goes\n // rather than sitting underneath and fighting over the same background.\n if (root.hasComponent(DomeGradient)) root.removeComponent(DomeGradient);\n if (!root.hasComponent(DomeTexture)) root.addComponent(DomeTexture);\n this.#domeEntity = root;\n root.setValue(DomeTexture, \"src\", sky.src);\n root.setValue(DomeTexture, \"intensity\", sky.intensity ?? this.#skyIntensity);\n root.setValue(DomeTexture, \"blurriness\", sky.blur ?? 0);\n writeVec3(root.getVectorView(DomeTexture, \"rotation\"), 0, sky.rotationY ?? 0, 0);\n root.setValue(DomeTexture, \"_needsUpdate\", true);\n return;\n }\n\n if (root.hasComponent(DomeTexture)) root.removeComponent(DomeTexture);\n if (this.#domeEntity !== root || !root.hasComponent(DomeGradient)) {\n if (!root.hasComponent(DomeGradient)) root.addComponent(DomeGradient);\n this.#domeEntity = root;\n }\n\n // IWSDK's dome is a Unity-style sky / equator / ground triple. A solid sky\n // is all three the same. A gradient uses the horizon colour the app gave,\n // and derives one from the ramp when it did not - which is the same colour\n // the three.js adapter puts at the horizon, so the two engines agree there\n // rather than only at the poles.\n if (sky.kind === \"solid\") {\n writeRgb(root.getVectorView(DomeGradient, \"sky\"), sky.colour);\n writeRgb(root.getVectorView(DomeGradient, \"equator\"), sky.colour);\n writeRgb(root.getVectorView(DomeGradient, \"ground\"), sky.colour);\n root.setValue(DomeGradient, \"intensity\", this.#skyIntensity);\n } else {\n writeRgb(root.getVectorView(DomeGradient, \"sky\"), sky.top);\n writeRgb(root.getVectorView(DomeGradient, \"equator\"), equatorOf(sky));\n writeRgb(root.getVectorView(DomeGradient, \"ground\"), sky.bottom);\n root.setValue(DomeGradient, \"intensity\", sky.intensity ?? this.#skyIntensity);\n }\n root.setValue(DomeGradient, \"_needsUpdate\", true);\n }\n\n /**\n * The environment map, as IWSDK's own image-based lighting components.\n *\n * `room` is not an approximation here: IWSDK's `IBLTexture` takes the string\n * `\"room\"` for its built-in probe, so the one kind three.js has to fake is\n * the one kind this host has natively.\n */\n applyIbl(ibl: IblSpec | null): void {\n const root = this.#levelRoot();\n if (root === null) return;\n\n if (ibl === null) {\n if (this.#iblEntity !== null) {\n if (this.#iblEntity.hasComponent(IBLGradient)) this.#iblEntity.removeComponent(IBLGradient);\n if (this.#iblEntity.hasComponent(IBLTexture)) this.#iblEntity.removeComponent(IBLTexture);\n }\n this.#iblEntity = null;\n return;\n }\n this.#iblEntity = root;\n\n if (ibl.kind === \"estimated\") {\n // IWSDK measures no reflections (UPSTREAM_ENHANCEMENTS 6.1), so the slot\n // keeps whatever it had rather than being cleared: an app that asked for\n // measured reflections and got none should not also lose its own map.\n this.#report({\n feature: \"lightEstimation\",\n state: \"unsupported\",\n detail: \"IWSDK measures no reflections, so an estimated environment map cannot be applied\",\n });\n return;\n }\n\n if (ibl.kind === \"gradient\") {\n if (root.hasComponent(IBLTexture)) root.removeComponent(IBLTexture);\n if (!root.hasComponent(IBLGradient)) root.addComponent(IBLGradient);\n writeRgb(root.getVectorView(IBLGradient, \"sky\"), ibl.top);\n writeRgb(root.getVectorView(IBLGradient, \"equator\"), equatorOf(ibl));\n writeRgb(root.getVectorView(IBLGradient, \"ground\"), ibl.bottom);\n root.setValue(IBLGradient, \"intensity\", ibl.intensity ?? 1);\n root.setValue(IBLGradient, \"_needsUpdate\", true);\n return;\n }\n\n if (root.hasComponent(IBLGradient)) root.removeComponent(IBLGradient);\n if (!root.hasComponent(IBLTexture)) root.addComponent(IBLTexture);\n root.setValue(IBLTexture, \"src\", ibl.kind === \"room\" ? \"room\" : ibl.src);\n root.setValue(IBLTexture, \"intensity\", ibl.intensity ?? 1);\n writeVec3(root.getVectorView(IBLTexture, \"rotation\"), 0, ibl.rotationY ?? 0, 0);\n root.setValue(IBLTexture, \"_needsUpdate\", true);\n }\n\n /**\n * Depth occlusion, through IWSDK's `DepthSensingSystem` and the per-entity\n * `DepthOccludable` component.\n *\n * IWSDK patches the materials under each opted-in entity, which is why the\n * scope matters more here than on three.js: there is no global mode to turn\n * on, and an entity nobody opted in is an entity the real world will not\n * hide. Whatever this port adds, it remembers, so turning occlusion off\n * never strips a component the app put there itself.\n */\n applyOcclusion(spec: OcclusionSpec | null): void {\n if (spec === null) {\n this.#releaseOccludables();\n const running = this.#system();\n if (running !== undefined) running.config.enableOcclusion.value = false;\n this.#report({ feature: \"occlusion\", state: \"unavailable\", detail: \"occlusion is off\" });\n return;\n }\n\n const blurRadius =\n spec.softness === undefined ? undefined : clamp01(spec.softness) * MAX_BLUR_RADIUS;\n const system = this.#system();\n if (system === undefined) {\n this.#world.registerSystem(DepthSensingSystem, {\n configData: {\n enableOcclusion: true,\n enableDepthTexture: true,\n useFloat32: spec.source?.format !== \"luminance-alpha\",\n ...(blurRadius === undefined ? {} : { blurRadius }),\n },\n });\n } else {\n system.config.enableOcclusion.value = true;\n system.config.useFloat32.value = spec.source?.format !== \"luminance-alpha\";\n if (blurRadius !== undefined) system.config.blurRadius.value = blurRadius;\n }\n\n const mode = occlusionMode(spec.mode);\n this.#releaseOccludables();\n if (spec.scope === \"tagged\") {\n this.#report({\n feature: \"occlusion\",\n state: \"active\",\n detail: \"occluding the entities the app marked with DepthOccludable\",\n });\n return;\n }\n if (this.#occludables === undefined) {\n this.#report({\n feature: \"occlusion\",\n state: \"unavailable\",\n detail:\n \"IWSDK occludes per entity; pass an occludables option, or use scope: tagged and add DepthOccludable yourself\",\n });\n return;\n }\n let count = 0;\n for (const entity of this.#occludables()) {\n if (entity.hasComponent(DepthOccludable)) continue;\n entity.addComponent(DepthOccludable, { mode });\n this.#occluding.push(entity);\n count += 1;\n }\n this.#report({\n feature: \"occlusion\",\n state: \"active\",\n detail: \"occluding \" + String(count) + \" entities\",\n });\n }\n\n /**\n * IWSDK has no light estimation at all (0.5.3 and 1.0.0) - no probe, no session hook,\n * nothing in its typings. This says so instead of leaving the app to wonder\n * why the room never lights anything, and the request is recorded in\n * docs/UPSTREAM_ENHANCEMENTS.md.\n */\n applyLightEstimation(estimation: ResolvedLightEstimation | null): void {\n if (estimation === null) {\n this.#report({\n feature: \"lightEstimation\",\n state: \"unavailable\",\n detail: \"light estimation is off\",\n });\n return;\n }\n this.#report({\n feature: \"lightEstimation\",\n state: \"unsupported\",\n detail: \"IWSDK 1.0 does not expose WebXR light estimation\",\n });\n }\n\n applyFog(fog: FogSpec | null): void {\n const scene = this.#world.scene;\n if (fog === null) {\n scene.fog = null;\n this.#fogKind = null;\n return;\n }\n if (this.#fogKind !== fog.kind || scene.fog === null) {\n scene.fog =\n fog.kind === \"linear\"\n ? new Fog(rgbToHexNumber(fog.colour), fog.near, fog.far)\n : new FogExp2(rgbToHexNumber(fog.colour), fog.density);\n this.#fogKind = fog.kind;\n return;\n }\n const existing = scene.fog;\n existing.color.setHex(rgbToHexNumber(fog.colour));\n if (fog.kind === \"linear\" && existing instanceof Fog) {\n existing.near = fog.near;\n existing.far = fog.far;\n } else if (fog.kind === \"exponential\" && existing instanceof FogExp2) {\n existing.density = fog.density;\n }\n }\n\n applyAmbient(light: AmbientLightSpec | null): void {\n if (light === null) {\n this.#destroy(\"ambient\");\n return;\n }\n const entity = this.#ensure(\"ambient\");\n if (!entity.hasComponent(AmbientLightComponent)) {\n entity.addComponent(AmbientLightComponent);\n }\n writeRgb(entity.getVectorView(AmbientLightComponent, \"color\"), light.colour);\n entity.setValue(AmbientLightComponent, \"intensity\", light.intensity);\n }\n\n applyKeyLight(light: KeyLightSpec | null): void {\n if (light === null) {\n this.#destroy(\"key\");\n return;\n }\n const entity = this.#ensure(\"key\");\n if (!entity.hasComponent(DirectionalLightComponent)) {\n entity.addComponent(DirectionalLightComponent);\n }\n writeRgb(entity.getVectorView(DirectionalLightComponent, \"color\"), light.colour);\n entity.setValue(DirectionalLightComponent, \"intensity\", light.intensity);\n entity.setValue(DirectionalLightComponent, \"castShadow\", light.castShadow ?? false);\n\n // The spec names the direction light TRAVELS; the component emits along\n // the entity's local -Z, so the entity is rotated to face that way. A\n // zero-length direction would produce a NaN quaternion, so it falls back\n // to straight down.\n const [x, y, z] = light.direction;\n if (Math.hypot(x, y, z) === 0) TEMP_DIRECTION.set(0, -1, 0);\n else TEMP_DIRECTION.set(x, y, z).normalize();\n TEMP_QUATERNION.setFromUnitVectors(LIGHT_FORWARD, TEMP_DIRECTION);\n writeQuaternion(entity.getVectorView(Transform, \"orientation\"), TEMP_QUATERNION);\n }\n\n dispose(): void {\n this.applySky(null);\n this.applyFog(null);\n this.applyAmbient(null);\n this.applyKeyLight(null);\n this.applyIbl(null);\n this.#releaseOccludables();\n this.#host = null;\n }\n\n #system() {\n return this.#world.getSystem(DepthSensingSystem);\n }\n\n #releaseOccludables(): void {\n for (const entity of this.#occluding) {\n if (entity.hasComponent(DepthOccludable)) entity.removeComponent(DepthOccludable);\n }\n this.#occluding = [];\n }\n\n #report(report: SensingReport): void {\n this.#host?.report(report);\n }\n\n #levelRoot(): Entity | null {\n return this.#parent ?? this.#world.activeLevel?.value ?? null;\n }\n\n /** The light entities exist independently of the level, so this cannot fail. */\n #ensure(which: \"ambient\" | \"key\"): Entity {\n const existing = which === \"ambient\" ? this.#ambientEntity : this.#keyEntity;\n if (existing !== null) return existing;\n const parent = this.#levelRoot();\n const entity = this.#world.createTransformEntity(\n undefined,\n parent === null ? undefined : { parent },\n );\n if (which === \"ambient\") this.#ambientEntity = entity;\n else this.#keyEntity = entity;\n return entity;\n }\n\n #destroy(which: \"ambient\" | \"key\"): void {\n const entity = which === \"ambient\" ? this.#ambientEntity : this.#keyEntity;\n entity?.destroy();\n if (which === \"ambient\") this.#ambientEntity = null;\n else this.#keyEntity = null;\n }\n}\n\n/**\n * The colour at the horizon: the app's, or the one the ramp implies.\n *\n * IWSDK's dome and IBL components are both three-stop, so this port always has\n * to supply a middle colour. Deriving it from the same ramp the three.js\n * adapter draws is what stops the two engines disagreeing about a sky the app\n * described once.\n */\nfunction equatorOf(gradient: SkyGradient | { top: Rgb; bottom: Rgb; equator?: Rgb }): Rgb {\n if (gradient.equator !== undefined) return gradient.equator;\n const exponent = \"exponent\" in gradient ? (gradient.exponent ?? 1) : 1;\n return lerpRgb(gradient.bottom, gradient.top, Math.pow(0.5, exponent));\n}\n\n/** Our mode names onto IWSDK's shader modes. */\nfunction occlusionMode(mode: OcclusionSpec[\"mode\"]): string {\n if (mode === \"hard\") return OcclusionShadersMode.HardOcclusion;\n if (mode === \"minmax-soft\") return OcclusionShadersMode.MinMaxSoftOcclusion;\n return OcclusionShadersMode.SoftOcclusion;\n}\n\n/** As `writeRgb`, for a `Types.Vec3` that is not a colour. */\nfunction writeVec3(view: NumericView, x: number, y: number, z: number): void {\n view[0] = x;\n view[1] = y;\n view[2] = z;\n}\n\n/** `[r, g, b]` in 0..1 -> a packed 0xRRGGBB, for the three.js-side setters. */\nfunction rgbToHexNumber(rgb: Rgb): number {\n const channel = (value: number) => Math.round(clamp01(value) * 255);\n return (channel(rgb[0]) << 16) | (channel(rgb[1]) << 8) | channel(rgb[2]);\n}\n\n/**\n * Write an RGBA colour into a component's vector view.\n *\n * The VIEW is passed in rather than the component and field name, so every\n * call site keeps elics's own `getVectorView` typing - which checks that the\n * field exists AND that it is an array type. A helper that took the component\n * would have to erase those generics, and the first misspelled field name\n * would then reach a headset instead of the compiler.\n *\n * Alpha is always 1: the specs describe opaque colours. Writing index 3 of a\n * three-component view is a no-op in JavaScript rather than an error, so no\n * length check is needed for a component whose colour is a `Types.Vec3`.\n */\nfunction writeRgb(view: NumericView, rgb: Rgb): void {\n view[0] = rgb[0];\n view[1] = rgb[1];\n view[2] = rgb[2];\n view[3] = 1;\n}\n\n/** As above, for a `Types.Vec4` orientation. */\nfunction writeQuaternion(view: NumericView, quaternion: Quaternion): void {\n view[0] = quaternion.x;\n view[1] = quaternion.y;\n view[2] = quaternion.z;\n view[3] = quaternion.w;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,16 +5,20 @@
|
|
|
5
5
|
* nothing else.
|
|
6
6
|
*/
|
|
7
7
|
import type { Entity, World } from "@iwsdk/core";
|
|
8
|
-
import type { AudioDirectorOptions, EnvironmentDirectorOptions } from "@realitycollective/webxr-environment";
|
|
9
|
-
import { AudioDirector, EnvironmentDirector } from "@realitycollective/webxr-environment";
|
|
8
|
+
import type { AudioDirectorOptions, EnvironmentDirectorOptions, SceneManagerOptions } from "@realitycollective/webxr-environment";
|
|
9
|
+
import { AudioDirector, EnvironmentDirector, SceneManager } from "@realitycollective/webxr-environment";
|
|
10
10
|
import type { IWSDKEnvironmentPortOptions } from "./environment-port.js";
|
|
11
11
|
import { IWSDKEnvironmentPort } from "./environment-port.js";
|
|
12
12
|
import type { IWSDKAudioPortOptions } from "./audio-port.js";
|
|
13
13
|
import { IWSDKAudioPort } from "./audio-port.js";
|
|
14
|
+
import type { IWSDKScenePortOptions } from "./scene-port.js";
|
|
15
|
+
import { IWSDKScenePort } from "./scene-port.js";
|
|
14
16
|
export type { IWSDKEnvironmentPortOptions } from "./environment-port.js";
|
|
15
17
|
export { IWSDKEnvironmentPort } from "./environment-port.js";
|
|
16
18
|
export type { IWSDKAudioPortOptions } from "./audio-port.js";
|
|
17
19
|
export { IWSDKAudioPort } from "./audio-port.js";
|
|
20
|
+
export type { IWSDKAssetFactory, IWSDKSceneContent, IWSDKSceneLoader, IWSDKScenePortOptions, } from "./scene-port.js";
|
|
21
|
+
export { IWSDKScenePort } from "./scene-port.js";
|
|
18
22
|
export type { IWSDKWorldSensingPortOptions } from "./world-sensing-port.js";
|
|
19
23
|
export { IWSDKWorldSensingPort } from "./world-sensing-port.js";
|
|
20
24
|
export * from "@realitycollective/webxr-environment";
|
|
@@ -67,3 +71,15 @@ export declare class EnvironmentSystem extends EnvironmentSystem_base {
|
|
|
67
71
|
*/
|
|
68
72
|
export declare function registerEnvironment(world: World, options?: IWSDKEnvironmentOptions): IWSDKEnvironmentSetup;
|
|
69
73
|
export type { Entity as IWSDKEntity };
|
|
74
|
+
export interface IWSDKScenesSetup {
|
|
75
|
+
readonly manager: SceneManager<Entity, Entity>;
|
|
76
|
+
readonly port: IWSDKScenePort;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Wire a scene manager to an IWSDK world. Scenes are `iwsdk.scene.v1`
|
|
80
|
+
* documents built by IWSDK's own importer, each under a persistent root of
|
|
81
|
+
* its own, so they live beside `world.loadLevel` rather than replacing it.
|
|
82
|
+
* Pass `environment: setup.environment` from `registerEnvironment` to let the
|
|
83
|
+
* active scene drive the sky, fog and light.
|
|
84
|
+
*/
|
|
85
|
+
export declare function createIWSDKScenes(world: World, options?: SceneManagerOptions & IWSDKScenePortOptions): IWSDKScenesSetup;
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { createSystem } from "./create-system.js";
|
|
2
|
-
import { AudioDirector, EnvironmentDirector } from "@realitycollective/webxr-environment";
|
|
2
|
+
import { AudioDirector, EnvironmentDirector, SceneManager } from "@realitycollective/webxr-environment";
|
|
3
3
|
import { IWSDKEnvironmentPort } from "./environment-port.js";
|
|
4
4
|
import { IWSDKAudioPort } from "./audio-port.js";
|
|
5
|
+
import { IWSDKScenePort } from "./scene-port.js";
|
|
5
6
|
export { IWSDKEnvironmentPort } from "./environment-port.js";
|
|
6
7
|
export { IWSDKAudioPort } from "./audio-port.js";
|
|
8
|
+
export { IWSDKScenePort } from "./scene-port.js";
|
|
7
9
|
export { IWSDKWorldSensingPort } from "./world-sensing-port.js";
|
|
8
10
|
export * from "@realitycollective/webxr-environment";
|
|
9
11
|
const hosts = new WeakMap();
|
|
@@ -77,4 +79,15 @@ export function registerEnvironment(world, options = {}) {
|
|
|
77
79
|
world.registerSystem(EnvironmentSystem);
|
|
78
80
|
return setup;
|
|
79
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Wire a scene manager to an IWSDK world. Scenes are `iwsdk.scene.v1`
|
|
84
|
+
* documents built by IWSDK's own importer, each under a persistent root of
|
|
85
|
+
* its own, so they live beside `world.loadLevel` rather than replacing it.
|
|
86
|
+
* Pass `environment: setup.environment` from `registerEnvironment` to let the
|
|
87
|
+
* active scene drive the sky, fog and light.
|
|
88
|
+
*/
|
|
89
|
+
export function createIWSDKScenes(world, options = {}) {
|
|
90
|
+
const port = new IWSDKScenePort(world, options);
|
|
91
|
+
return { manager: new SceneManager(port, options), port };
|
|
92
|
+
}
|
|
80
93
|
//# 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":"AAOA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * `@realitycollective/iwsdk-environment` - the Meta IWSDK adapter.\n *\n * Re-exports the whole engine-free core, so an app installs this package and\n * nothing else.\n */\nimport type { Entity, World } from \"@iwsdk/core\";\nimport { createSystem } from \"./create-system.js\";\nimport type {\n AudioDirectorOptions,\n EnvironmentDirectorOptions,\n SceneManagerOptions,\n} from \"@realitycollective/webxr-environment\";\nimport { AudioDirector, EnvironmentDirector, SceneManager } from \"@realitycollective/webxr-environment\";\nimport type { IWSDKEnvironmentPortOptions } from \"./environment-port.js\";\nimport { IWSDKEnvironmentPort } from \"./environment-port.js\";\nimport type { IWSDKAudioPortOptions } from \"./audio-port.js\";\nimport { IWSDKAudioPort } from \"./audio-port.js\";\nimport type { IWSDKScenePortOptions } from \"./scene-port.js\";\nimport { IWSDKScenePort } from \"./scene-port.js\";\n\nexport type { IWSDKEnvironmentPortOptions } from \"./environment-port.js\";\nexport { IWSDKEnvironmentPort } from \"./environment-port.js\";\nexport type { IWSDKAudioPortOptions } from \"./audio-port.js\";\nexport { IWSDKAudioPort } from \"./audio-port.js\";\nexport type {\n IWSDKAssetFactory,\n IWSDKSceneContent,\n IWSDKSceneLoader,\n IWSDKScenePortOptions,\n} from \"./scene-port.js\";\nexport { IWSDKScenePort } from \"./scene-port.js\";\nexport type { IWSDKWorldSensingPortOptions } from \"./world-sensing-port.js\";\nexport { IWSDKWorldSensingPort } from \"./world-sensing-port.js\";\n\nexport * from \"@realitycollective/webxr-environment\";\n\nexport interface IWSDKEnvironmentOptions\n extends EnvironmentDirectorOptions,\n IWSDKEnvironmentPortOptions {\n /** Audio director + port options. Omit to set up the environment only. */\n readonly audio?: AudioDirectorOptions & IWSDKAudioPortOptions;\n}\n\nexport interface IWSDKEnvironmentSetup {\n readonly environment: EnvironmentDirector;\n readonly environmentPort: IWSDKEnvironmentPort;\n readonly audio: AudioDirector | null;\n readonly audioPort: IWSDKAudioPort | null;\n /** Tear both directors down and remove everything they created. */\n dispose(): void;\n}\n\nconst hosts = new WeakMap<World, IWSDKEnvironmentSetup>();\n\n/** The setup registered for a world, if `registerEnvironment` has run. */\nexport function environmentFor(world: World): IWSDKEnvironmentSetup | undefined {\n return hosts.get(world);\n}\n\n/**\n * The per-frame tick.\n *\n * Neither director owns a loop, so something has to drive them, and on IWSDK\n * that something is a system - which means the environment stops advancing\n * when the session loses focus, exactly like the rest of the app.\n * `registerEnvironment` registers this for you; it is exported so an app that\n * builds its own system schedule can place it deliberately.\n *\n * IWSDK hands a system its delta in SECONDS and the directors take\n * milliseconds. That conversion happens here, once.\n */\nexport class EnvironmentSystem extends createSystem({}) {\n override update(delta: number): void {\n const setup = hosts.get(this.world as unknown as World);\n if (setup === undefined) return;\n const deltaMs = delta * 1000;\n setup.environment.update(deltaMs);\n setup.audio?.update(deltaMs);\n }\n}\n\n/**\n * One call to wire the environment (and optionally the audio) into an IWSDK\n * world, and register the system that ticks them.\n *\n * ```ts\n * const env = registerEnvironment(world, {\n * presets: STOCK_PRESETS,\n * initial: VOID,\n * audio: { cues: [{ id: \"hum\", src: \"/audio/hum.mp3\", bus: \"ambience\", loop: true }] },\n * });\n * env.environment.transition(\"dusk\", { durationMs: 8000 });\n * ```\n *\n * Passthrough is NOT read from the session here - push it in from whatever\n * already tracks capabilities (`env.environment.setPassthrough(...)`). The\n * session belongs to the platform layer, not to the environment.\n */\nexport function registerEnvironment(\n world: World,\n options: IWSDKEnvironmentOptions = {},\n): IWSDKEnvironmentSetup {\n const existing = hosts.get(world);\n if (existing !== undefined) return existing;\n\n const environmentPort = new IWSDKEnvironmentPort(world, options);\n const environment = new EnvironmentDirector(environmentPort, options);\n\n let audio: AudioDirector | null = null;\n let audioPort: IWSDKAudioPort | null = null;\n if (options.audio !== undefined) {\n audioPort = new IWSDKAudioPort(world, options.audio);\n audio = new AudioDirector(audioPort, options.audio);\n }\n\n const setup: IWSDKEnvironmentSetup = {\n environment,\n environmentPort,\n audio,\n audioPort,\n dispose() {\n audio?.dispose();\n environment.dispose();\n hosts.delete(world);\n },\n };\n hosts.set(world, setup);\n world.registerSystem(EnvironmentSystem);\n return setup;\n}\n\nexport type { Entity as IWSDKEntity };\n\nexport interface IWSDKScenesSetup {\n readonly manager: SceneManager<Entity, Entity>;\n readonly port: IWSDKScenePort;\n}\n\n/**\n * Wire a scene manager to an IWSDK world. Scenes are `iwsdk.scene.v1`\n * documents built by IWSDK's own importer, each under a persistent root of\n * its own, so they live beside `world.loadLevel` rather than replacing it.\n * Pass `environment: setup.environment` from `registerEnvironment` to let the\n * active scene drive the sky, fog and light.\n */\nexport function createIWSDKScenes(\n world: World,\n options: SceneManagerOptions & IWSDKScenePortOptions = {},\n): IWSDKScenesSetup {\n const port = new IWSDKScenePort(world, options);\n return { manager: new SceneManager(port, options), port };\n}\n"]}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `ScenePort` for IWSDK.
|
|
3
|
+
*
|
|
4
|
+
* ---------------------------------------------------------------------------
|
|
5
|
+
* A SCENE IS AN ENTITY TREE BESIDE THE LEVEL, NOT A LEVEL
|
|
6
|
+
* ---------------------------------------------------------------------------
|
|
7
|
+
* IWSDK's `world.loadLevel` replaces the one active level, which is a single
|
|
8
|
+
* load and nothing else. This port builds each scene under its own persistent
|
|
9
|
+
* root entity with IWSDK's own `SceneJSONImporter`, the loader `loadLevel`
|
|
10
|
+
* uses, so additive loads, a stack and preloading all work. The importer tags
|
|
11
|
+
* what it creates with `LevelTag`; the port removes that tag, because a scene
|
|
12
|
+
* this manager owns must not be destroyed by an unrelated `loadLevel`.
|
|
13
|
+
*
|
|
14
|
+
* Every entity the port creates is tracked per scene, because destroying an
|
|
15
|
+
* entity in IWSDK does not destroy the entities parented under it.
|
|
16
|
+
*
|
|
17
|
+
* ---------------------------------------------------------------------------
|
|
18
|
+
* HIDDEN MEANS NOT RENDERED AND NOT HIT-TESTABLE
|
|
19
|
+
* ---------------------------------------------------------------------------
|
|
20
|
+
* A transform entity cannot leave the graph - IWSDK's `TransformSystem`
|
|
21
|
+
* re-parents it on the next frame - so hiding is done in place, as in the
|
|
22
|
+
* three.js port: `visible` (which IWSDK routes into its `Visibility`
|
|
23
|
+
* component), an empty layer mask for three.js raycasters, and
|
|
24
|
+
* `pointerEvents = "none"` for the pmndrs pointer events IWSDK's input system
|
|
25
|
+
* uses, which call `raycast` directly and read only that property.
|
|
26
|
+
*/
|
|
27
|
+
import type { Entity, World } from "@iwsdk/core";
|
|
28
|
+
import type { Object3D } from "three";
|
|
29
|
+
import type { SceneDefinition, ScenePort, WorldPose } from "@realitycollective/webxr-environment";
|
|
30
|
+
/** What a loader built: every node by id, each one an entity. */
|
|
31
|
+
export interface IWSDKSceneContent {
|
|
32
|
+
readonly nodes: readonly {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly entity: Entity;
|
|
35
|
+
}[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Builds a scene's content under `parent`. Default: IWSDK's own
|
|
39
|
+
* `SceneJSONImporter`, reading `src` as an `iwsdk.scene.v1` document.
|
|
40
|
+
*/
|
|
41
|
+
export type IWSDKSceneLoader = (def: SceneDefinition, parent: Entity, onProgress: (progress: number) => void) => Promise<IWSDKSceneContent>;
|
|
42
|
+
/** Makes a fresh object for a named asset, for `instantiate`. */
|
|
43
|
+
export type IWSDKAssetFactory = (asset: string) => Object3D;
|
|
44
|
+
export interface IWSDKScenePortOptions {
|
|
45
|
+
readonly load?: IWSDKSceneLoader;
|
|
46
|
+
/**
|
|
47
|
+
* Resolves an asset name for `instantiate`. Default: a clone of the glTF
|
|
48
|
+
* IWSDK's `AssetManager` holds under that key, which it has once the asset
|
|
49
|
+
* is in the world's manifest and preloaded.
|
|
50
|
+
*/
|
|
51
|
+
readonly assets?: IWSDKAssetFactory;
|
|
52
|
+
}
|
|
53
|
+
export declare class IWSDKScenePort implements ScenePort<Entity, Entity> {
|
|
54
|
+
#private;
|
|
55
|
+
constructor(world: World, options?: IWSDKScenePortOptions);
|
|
56
|
+
build(def: SceneDefinition, visible: boolean, onProgress: (progress: number) => void): Promise<Entity>;
|
|
57
|
+
setVisible(scene: Entity, visible: boolean): void;
|
|
58
|
+
destroy(scene: Entity): void;
|
|
59
|
+
nodeIds(scene: Entity): readonly string[];
|
|
60
|
+
findNode(scene: Entity, nodeId: string): Entity | null;
|
|
61
|
+
setNodeActive(node: Entity, active: boolean): void;
|
|
62
|
+
instantiate(asset: string, pose: WorldPose, scene: Entity, parent: Entity | null): Entity;
|
|
63
|
+
destroyInstance(instance: Entity): void;
|
|
64
|
+
detachNode(scene: Entity, node: Entity): void;
|
|
65
|
+
destroyNode(node: Entity): void;
|
|
66
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { AssetManager, LevelTag, SceneJSONImporter } from "@iwsdk/core";
|
|
2
|
+
import { Group, Matrix4, Quaternion, Vector3 } from "three";
|
|
3
|
+
function isUnder(object, ancestor) {
|
|
4
|
+
for (let at = object; at !== null; at = at.parent)
|
|
5
|
+
if (at === ancestor)
|
|
6
|
+
return true;
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
function defaultAsset(asset) {
|
|
10
|
+
const gltf = AssetManager.getGLTF(asset);
|
|
11
|
+
if (gltf === null) {
|
|
12
|
+
throw new Error(`[iwsdk-environment] cannot instantiate "${asset}": it is not a preloaded glTF in the asset manifest`);
|
|
13
|
+
}
|
|
14
|
+
return gltf.scene.clone();
|
|
15
|
+
}
|
|
16
|
+
export class IWSDKScenePort {
|
|
17
|
+
#world;
|
|
18
|
+
#load;
|
|
19
|
+
#assets;
|
|
20
|
+
#scenes = new Map();
|
|
21
|
+
/** Entities under a node that `detachNode` moved out, destroyed with it. */
|
|
22
|
+
#detached = new Map();
|
|
23
|
+
#off = new WeakSet();
|
|
24
|
+
#saved = new WeakMap();
|
|
25
|
+
constructor(world, options = {}) {
|
|
26
|
+
this.#world = world;
|
|
27
|
+
this.#load =
|
|
28
|
+
options.load ??
|
|
29
|
+
(async (def, parent) => {
|
|
30
|
+
const result = await SceneJSONImporter.load(world, def.src, parent);
|
|
31
|
+
return { nodes: [...result.nodes.values()].map((node) => ({ id: node.nodeId, entity: node.entity })) };
|
|
32
|
+
});
|
|
33
|
+
this.#assets = options.assets ?? defaultAsset;
|
|
34
|
+
}
|
|
35
|
+
async build(def, visible, onProgress) {
|
|
36
|
+
const root = this.#world.createTransformEntity(new Group(), {
|
|
37
|
+
parent: this.#world.sceneEntity,
|
|
38
|
+
persistent: true,
|
|
39
|
+
});
|
|
40
|
+
root.object3D.name = `scene:${def.id}`;
|
|
41
|
+
if (!visible)
|
|
42
|
+
this.#setOff(root.object3D, true);
|
|
43
|
+
let content;
|
|
44
|
+
try {
|
|
45
|
+
content = await this.#load(def, root, onProgress);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
root.destroy();
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
const record = { ids: [], nodes: new Map(), entities: new Set() };
|
|
52
|
+
for (const { id, entity } of content.nodes) {
|
|
53
|
+
if (entity.hasComponent(LevelTag))
|
|
54
|
+
entity.removeComponent(LevelTag);
|
|
55
|
+
record.ids.push(id);
|
|
56
|
+
record.entities.add(entity);
|
|
57
|
+
if (!record.nodes.has(id))
|
|
58
|
+
record.nodes.set(id, entity);
|
|
59
|
+
}
|
|
60
|
+
this.#scenes.set(root, record);
|
|
61
|
+
this.#refresh(root.object3D);
|
|
62
|
+
return root;
|
|
63
|
+
}
|
|
64
|
+
setVisible(scene, visible) {
|
|
65
|
+
this.#setOff(scene.object3D, !visible);
|
|
66
|
+
}
|
|
67
|
+
destroy(scene) {
|
|
68
|
+
const record = this.#scenes.get(scene);
|
|
69
|
+
this.#scenes.delete(scene);
|
|
70
|
+
for (const entity of record?.entities ?? [])
|
|
71
|
+
entity.destroy();
|
|
72
|
+
scene.destroy();
|
|
73
|
+
}
|
|
74
|
+
nodeIds(scene) {
|
|
75
|
+
return this.#scenes.get(scene)?.ids ?? [];
|
|
76
|
+
}
|
|
77
|
+
findNode(scene, nodeId) {
|
|
78
|
+
return this.#scenes.get(scene)?.nodes.get(nodeId) ?? null;
|
|
79
|
+
}
|
|
80
|
+
setNodeActive(node, active) {
|
|
81
|
+
this.#setOff(node.object3D, !active);
|
|
82
|
+
}
|
|
83
|
+
instantiate(asset, pose, scene, parent) {
|
|
84
|
+
const object = this.#assets(asset);
|
|
85
|
+
const target = parent ?? scene;
|
|
86
|
+
const targetObject = target.object3D;
|
|
87
|
+
targetObject.updateWorldMatrix(true, false);
|
|
88
|
+
const world = new Matrix4().compose(new Vector3(pose.position[0], pose.position[1], pose.position[2]), new Quaternion(pose.orientation[0], pose.orientation[1], pose.orientation[2], pose.orientation[3]), new Vector3(1, 1, 1));
|
|
89
|
+
world.premultiply(targetObject.matrixWorld.clone().invert());
|
|
90
|
+
world.decompose(object.position, object.quaternion, object.scale);
|
|
91
|
+
const entity = this.#world.createTransformEntity(object, { parent: target, persistent: true });
|
|
92
|
+
this.#scenes.get(scene)?.entities.add(entity);
|
|
93
|
+
this.#refresh(object);
|
|
94
|
+
return entity;
|
|
95
|
+
}
|
|
96
|
+
destroyInstance(instance) {
|
|
97
|
+
for (const record of this.#scenes.values())
|
|
98
|
+
record.entities.delete(instance);
|
|
99
|
+
instance.destroy();
|
|
100
|
+
}
|
|
101
|
+
detachNode(scene, node) {
|
|
102
|
+
const object = node.object3D;
|
|
103
|
+
this.#world.sceneEntity.object3D.attach(object);
|
|
104
|
+
const owned = new Set();
|
|
105
|
+
const record = this.#scenes.get(scene);
|
|
106
|
+
if (record !== undefined) {
|
|
107
|
+
for (const entity of record.entities) {
|
|
108
|
+
if (entity === node || (entity.object3D !== undefined && isUnder(entity.object3D, object))) {
|
|
109
|
+
record.entities.delete(entity);
|
|
110
|
+
if (entity !== node)
|
|
111
|
+
owned.add(entity);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for (const [id, found] of record.nodes)
|
|
115
|
+
if (found === node)
|
|
116
|
+
record.nodes.delete(id);
|
|
117
|
+
}
|
|
118
|
+
this.#detached.set(node, owned);
|
|
119
|
+
this.#refresh(object);
|
|
120
|
+
}
|
|
121
|
+
destroyNode(node) {
|
|
122
|
+
for (const entity of this.#detached.get(node) ?? [])
|
|
123
|
+
entity.destroy();
|
|
124
|
+
this.#detached.delete(node);
|
|
125
|
+
node.destroy();
|
|
126
|
+
}
|
|
127
|
+
#setOff(object, off) {
|
|
128
|
+
if (off)
|
|
129
|
+
this.#off.add(object);
|
|
130
|
+
else
|
|
131
|
+
this.#off.delete(object);
|
|
132
|
+
object.visible = !off;
|
|
133
|
+
this.#refresh(object);
|
|
134
|
+
}
|
|
135
|
+
#refresh(object) {
|
|
136
|
+
let inherited = false;
|
|
137
|
+
for (let at = object.parent; at !== null; at = at.parent) {
|
|
138
|
+
if (this.#off.has(at)) {
|
|
139
|
+
inherited = true;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
this.#visit(object, inherited);
|
|
144
|
+
}
|
|
145
|
+
#visit(object, inherited) {
|
|
146
|
+
const off = inherited || this.#off.has(object);
|
|
147
|
+
const target = object;
|
|
148
|
+
const saved = this.#saved.get(object);
|
|
149
|
+
if (off && saved === undefined) {
|
|
150
|
+
this.#saved.set(object, { mask: object.layers.mask, pointerEvents: target.pointerEvents });
|
|
151
|
+
object.layers.mask = 0;
|
|
152
|
+
target.pointerEvents = "none";
|
|
153
|
+
}
|
|
154
|
+
else if (!off && saved !== undefined) {
|
|
155
|
+
this.#saved.delete(object);
|
|
156
|
+
object.layers.mask = saved.mask;
|
|
157
|
+
target.pointerEvents = saved.pointerEvents;
|
|
158
|
+
}
|
|
159
|
+
for (const child of object.children)
|
|
160
|
+
this.#visit(child, off);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
//# sourceMappingURL=scene-port.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-port.js","sourceRoot":"","sources":["../src/scene-port.ts"],"names":[],"mappings":"AA2BA,OAAO,EAAE,YAAY,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAgD5D,SAAS,OAAO,CAAC,MAAgB,EAAE,QAAkB;IACnD,KAAK,IAAI,EAAE,GAAoB,MAAM,EAAE,EAAE,KAAK,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM;QAAE,IAAI,EAAE,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;IACrG,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IACzC,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CACb,2CAA2C,KAAK,qDAAqD,CACtG,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;AAC5B,CAAC;AAED,MAAM,OAAO,cAAc;IAChB,MAAM,CAAQ;IACd,KAAK,CAAmB;IACxB,OAAO,CAAoB;IAC3B,OAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;IAClD,4EAA4E;IACnE,SAAS,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC3C,IAAI,GAAG,IAAI,OAAO,EAAY,CAAC;IAC/B,MAAM,GAAG,IAAI,OAAO,EAAmB,CAAC;IAEjD,YAAY,KAAY,EAAE,UAAiC,EAAE;QAC3D,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,KAAK;YACR,OAAO,CAAC,IAAI;gBACZ,CAAC,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE;oBACrB,MAAM,MAAM,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;oBACpE,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC;gBACzG,CAAC,CAAC,CAAC;QACL,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,MAAM,IAAI,YAAY,CAAC;IAChD,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,GAAoB,EAAE,OAAgB,EAAE,UAAsC;QACxF,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,IAAI,KAAK,EAAE,EAAE;YAC1D,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,WAAW;YAC/B,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;QACF,IAAI,CAAC,QAAqB,CAAC,IAAI,GAAG,SAAS,GAAG,CAAC,EAAE,EAAE,CAAC;QACrD,IAAI,CAAC,OAAO;YAAE,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAoB,EAAE,IAAI,CAAC,CAAC;QAC5D,IAAI,OAA0B,CAAC;QAC/B,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC;QACpD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,EAAE,CAAC;YACf,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,MAAM,GAAgB,EAAE,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;QAC/E,KAAK,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,OAAO,CAAC,KAAK,EAAE,CAAC;YAC3C,IAAI,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC;gBAAE,MAAM,CAAC,eAAe,CAAC,QAAQ,CAAC,CAAC;YACpE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpB,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC5B,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QAC1D,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QAC/B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,QAAoB,CAAC,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,OAAgB;QACxC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,QAAoB,EAAE,CAAC,OAAO,CAAC,CAAC;IACrD,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3B,KAAK,MAAM,MAAM,IAAI,MAAM,EAAE,QAAQ,IAAI,EAAE;YAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QAC9D,KAAK,CAAC,OAAO,EAAE,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,KAAa;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,GAAG,IAAI,EAAE,CAAC;IAC5C,CAAC;IAED,QAAQ,CAAC,KAAa,EAAE,MAAc;QACpC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IAC5D,CAAC;IAED,aAAa,CAAC,IAAY,EAAE,MAAe;QACzC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,QAAoB,EAAE,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IAED,WAAW,CAAC,KAAa,EAAE,IAAe,EAAE,KAAa,EAAE,MAAqB;QAC9E,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACnC,MAAM,MAAM,GAAG,MAAM,IAAI,KAAK,CAAC;QAC/B,MAAM,YAAY,GAAG,MAAM,CAAC,QAAoB,CAAC;QACjD,YAAY,CAAC,iBAAiB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,OAAO,EAAE,CAAC,OAAO,CACjC,IAAI,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EACjE,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,EAClG,IAAI,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CACrB,CAAC;QACF,KAAK,CAAC,WAAW,CAAC,YAAY,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC;QAC7D,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QAClE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,qBAAqB,CAAC,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/F,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9C,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QACtB,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,eAAe,CAAC,QAAgB;QAC9B,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE;YAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC7E,QAAQ,CAAC,OAAO,EAAE,CAAC;IACrB,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,IAAY;QACpC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAoB,CAAC;QACxC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,QAAqB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACrC,IAAI,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,EAAE,CAAC;oBAC3F,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;oBAC/B,IAAI,MAAM,KAAK,IAAI;wBAAE,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;YACD,KAAK,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,KAAK;gBAAE,IAAI,KAAK,KAAK,IAAI;oBAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtF,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAChC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,WAAW,CAAC,IAAY;QACtB,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE;YAAE,MAAM,CAAC,OAAO,EAAE,CAAC;QACtE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;IAED,OAAO,CAAC,MAAgB,EAAE,GAAY;QACpC,IAAI,GAAG;YAAE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;;YAC1B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9B,MAAM,CAAC,OAAO,GAAG,CAAC,GAAG,CAAC;QACtB,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACxB,CAAC;IAED,QAAQ,CAAC,MAAgB;QACvB,IAAI,SAAS,GAAG,KAAK,CAAC;QACtB,KAAK,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,EAAE,KAAK,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,MAAM,EAAE,CAAC;YACzD,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBACtB,SAAS,GAAG,IAAI,CAAC;gBACjB,MAAM;YACR,CAAC;QACH,CAAC;QACD,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IACjC,CAAC;IAED,MAAM,CAAC,MAAgB,EAAE,SAAkB;QACzC,MAAM,GAAG,GAAG,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,MAAkC,CAAC;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtC,IAAI,GAAG,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;YAC3F,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;YACvB,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC;QAChC,CAAC;aAAM,IAAI,CAAC,GAAG,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACvC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC3B,MAAM,CAAC,MAAM,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YAChC,MAAM,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;QAC7C,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/D,CAAC;CACF","sourcesContent":["/**\n * `ScenePort` for IWSDK.\n *\n * ---------------------------------------------------------------------------\n * A SCENE IS AN ENTITY TREE BESIDE THE LEVEL, NOT A LEVEL\n * ---------------------------------------------------------------------------\n * IWSDK's `world.loadLevel` replaces the one active level, which is a single\n * load and nothing else. This port builds each scene under its own persistent\n * root entity with IWSDK's own `SceneJSONImporter`, the loader `loadLevel`\n * uses, so additive loads, a stack and preloading all work. The importer tags\n * what it creates with `LevelTag`; the port removes that tag, because a scene\n * this manager owns must not be destroyed by an unrelated `loadLevel`.\n *\n * Every entity the port creates is tracked per scene, because destroying an\n * entity in IWSDK does not destroy the entities parented under it.\n *\n * ---------------------------------------------------------------------------\n * HIDDEN MEANS NOT RENDERED AND NOT HIT-TESTABLE\n * ---------------------------------------------------------------------------\n * A transform entity cannot leave the graph - IWSDK's `TransformSystem`\n * re-parents it on the next frame - so hiding is done in place, as in the\n * three.js port: `visible` (which IWSDK routes into its `Visibility`\n * component), an empty layer mask for three.js raycasters, and\n * `pointerEvents = \"none\"` for the pmndrs pointer events IWSDK's input system\n * uses, which call `raycast` directly and read only that property.\n */\nimport type { Entity, World } from \"@iwsdk/core\";\nimport { AssetManager, LevelTag, SceneJSONImporter } from \"@iwsdk/core\";\nimport type { Object3D } from \"three\";\nimport { Group, Matrix4, Quaternion, Vector3 } from \"three\";\nimport type { SceneDefinition, ScenePort, WorldPose } from \"@realitycollective/webxr-environment\";\n\n/** What a loader built: every node by id, each one an entity. */\nexport interface IWSDKSceneContent {\n readonly nodes: readonly { readonly id: string; readonly entity: Entity }[];\n}\n\n/**\n * Builds a scene's content under `parent`. Default: IWSDK's own\n * `SceneJSONImporter`, reading `src` as an `iwsdk.scene.v1` document.\n */\nexport type IWSDKSceneLoader = (\n def: SceneDefinition,\n parent: Entity,\n onProgress: (progress: number) => void,\n) => Promise<IWSDKSceneContent>;\n\n/** Makes a fresh object for a named asset, for `instantiate`. */\nexport type IWSDKAssetFactory = (asset: string) => Object3D;\n\nexport interface IWSDKScenePortOptions {\n readonly load?: IWSDKSceneLoader;\n /**\n * Resolves an asset name for `instantiate`. Default: a clone of the glTF\n * IWSDK's `AssetManager` holds under that key, which it has once the asset\n * is in the world's manifest and preloaded.\n */\n readonly assets?: IWSDKAssetFactory;\n}\n\ninterface SceneRecord {\n readonly ids: string[];\n readonly nodes: Map<string, Entity>;\n /** Every entity this scene owns, root excluded. */\n readonly entities: Set<Entity>;\n}\n\n/** IWSDK types `pointerEvents` narrowly; the port only saves and restores it. */\ninterface PointerTarget {\n pointerEvents?: unknown;\n}\n\ninterface Saved {\n readonly mask: number;\n readonly pointerEvents: unknown;\n}\n\nfunction isUnder(object: Object3D, ancestor: Object3D): boolean {\n for (let at: Object3D | null = object; at !== null; at = at.parent) if (at === ancestor) return true;\n return false;\n}\n\nfunction defaultAsset(asset: string): Object3D {\n const gltf = AssetManager.getGLTF(asset);\n if (gltf === null) {\n throw new Error(\n `[iwsdk-environment] cannot instantiate \"${asset}\": it is not a preloaded glTF in the asset manifest`,\n );\n }\n return gltf.scene.clone();\n}\n\nexport class IWSDKScenePort implements ScenePort<Entity, Entity> {\n readonly #world: World;\n readonly #load: IWSDKSceneLoader;\n readonly #assets: IWSDKAssetFactory;\n readonly #scenes = new Map<Entity, SceneRecord>();\n /** Entities under a node that `detachNode` moved out, destroyed with it. */\n readonly #detached = new Map<Entity, Set<Entity>>();\n readonly #off = new WeakSet<Object3D>();\n readonly #saved = new WeakMap<Object3D, Saved>();\n\n constructor(world: World, options: IWSDKScenePortOptions = {}) {\n this.#world = world;\n this.#load =\n options.load ??\n (async (def, parent) => {\n const result = await SceneJSONImporter.load(world, def.src, parent);\n return { nodes: [...result.nodes.values()].map((node) => ({ id: node.nodeId, entity: node.entity })) };\n });\n this.#assets = options.assets ?? defaultAsset;\n }\n\n async build(def: SceneDefinition, visible: boolean, onProgress: (progress: number) => void): Promise<Entity> {\n const root = this.#world.createTransformEntity(new Group(), {\n parent: this.#world.sceneEntity,\n persistent: true,\n });\n (root.object3D as Object3D).name = `scene:${def.id}`;\n if (!visible) this.#setOff(root.object3D as Object3D, true);\n let content: IWSDKSceneContent;\n try {\n content = await this.#load(def, root, onProgress);\n } catch (error) {\n root.destroy();\n throw error;\n }\n const record: SceneRecord = { ids: [], nodes: new Map(), entities: new Set() };\n for (const { id, entity } of content.nodes) {\n if (entity.hasComponent(LevelTag)) entity.removeComponent(LevelTag);\n record.ids.push(id);\n record.entities.add(entity);\n if (!record.nodes.has(id)) record.nodes.set(id, entity);\n }\n this.#scenes.set(root, record);\n this.#refresh(root.object3D as Object3D);\n return root;\n }\n\n setVisible(scene: Entity, visible: boolean): void {\n this.#setOff(scene.object3D as Object3D, !visible);\n }\n\n destroy(scene: Entity): void {\n const record = this.#scenes.get(scene);\n this.#scenes.delete(scene);\n for (const entity of record?.entities ?? []) entity.destroy();\n scene.destroy();\n }\n\n nodeIds(scene: Entity): readonly string[] {\n return this.#scenes.get(scene)?.ids ?? [];\n }\n\n findNode(scene: Entity, nodeId: string): Entity | null {\n return this.#scenes.get(scene)?.nodes.get(nodeId) ?? null;\n }\n\n setNodeActive(node: Entity, active: boolean): void {\n this.#setOff(node.object3D as Object3D, !active);\n }\n\n instantiate(asset: string, pose: WorldPose, scene: Entity, parent: Entity | null): Entity {\n const object = this.#assets(asset);\n const target = parent ?? scene;\n const targetObject = target.object3D as Object3D;\n targetObject.updateWorldMatrix(true, false);\n const world = new Matrix4().compose(\n new Vector3(pose.position[0], pose.position[1], pose.position[2]),\n new Quaternion(pose.orientation[0], pose.orientation[1], pose.orientation[2], pose.orientation[3]),\n new Vector3(1, 1, 1),\n );\n world.premultiply(targetObject.matrixWorld.clone().invert());\n world.decompose(object.position, object.quaternion, object.scale);\n const entity = this.#world.createTransformEntity(object, { parent: target, persistent: true });\n this.#scenes.get(scene)?.entities.add(entity);\n this.#refresh(object);\n return entity;\n }\n\n destroyInstance(instance: Entity): void {\n for (const record of this.#scenes.values()) record.entities.delete(instance);\n instance.destroy();\n }\n\n detachNode(scene: Entity, node: Entity): void {\n const object = node.object3D as Object3D;\n (this.#world.sceneEntity.object3D as Object3D).attach(object);\n const owned = new Set<Entity>();\n const record = this.#scenes.get(scene);\n if (record !== undefined) {\n for (const entity of record.entities) {\n if (entity === node || (entity.object3D !== undefined && isUnder(entity.object3D, object))) {\n record.entities.delete(entity);\n if (entity !== node) owned.add(entity);\n }\n }\n for (const [id, found] of record.nodes) if (found === node) record.nodes.delete(id);\n }\n this.#detached.set(node, owned);\n this.#refresh(object);\n }\n\n destroyNode(node: Entity): void {\n for (const entity of this.#detached.get(node) ?? []) entity.destroy();\n this.#detached.delete(node);\n node.destroy();\n }\n\n #setOff(object: Object3D, off: boolean): void {\n if (off) this.#off.add(object);\n else this.#off.delete(object);\n object.visible = !off;\n this.#refresh(object);\n }\n\n #refresh(object: Object3D): void {\n let inherited = false;\n for (let at = object.parent; at !== null; at = at.parent) {\n if (this.#off.has(at)) {\n inherited = true;\n break;\n }\n }\n this.#visit(object, inherited);\n }\n\n #visit(object: Object3D, inherited: boolean): void {\n const off = inherited || this.#off.has(object);\n const target = object as unknown as PointerTarget;\n const saved = this.#saved.get(object);\n if (off && saved === undefined) {\n this.#saved.set(object, { mask: object.layers.mask, pointerEvents: target.pointerEvents });\n object.layers.mask = 0;\n target.pointerEvents = \"none\";\n } else if (!off && saved !== undefined) {\n this.#saved.delete(object);\n object.layers.mask = saved.mask;\n target.pointerEvents = saved.pointerEvents;\n }\n for (const child of object.children) this.#visit(child, off);\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@realitycollective/iwsdk-environment",
|
|
3
|
-
"version": "0.1.1-preview.
|
|
3
|
+
"version": "0.1.1-preview.2",
|
|
4
4
|
"description": "Meta IWSDK adapter for the Reality Collective WebXR Environment Extensions - drives IWSDK's own DomeGradient, light components, transform entities and AudioSource machinery from the engine-free @realitycollective/webxr-environment core, with a one-call registerEnvironment(world) setup and an ECS system that ticks the directors. Re-exports the core.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"realitycollective",
|
|
@@ -35,14 +35,14 @@
|
|
|
35
35
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
36
36
|
},
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@realitycollective/webxr-environment": "^0.1.1-preview.
|
|
38
|
+
"@realitycollective/webxr-environment": "^0.1.1-preview.2"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@iwsdk/core": ">=0.
|
|
41
|
+
"@iwsdk/core": ">=1.0.0 <2.0.0",
|
|
42
42
|
"three": ">=0.170.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@iwsdk/core": "0.
|
|
45
|
+
"@iwsdk/core": "1.0.0",
|
|
46
46
|
"three": "npm:super-three@0.185.0",
|
|
47
47
|
"@types/three": "0.185.4"
|
|
48
48
|
},
|