@displayxr/inline3d 1.38.0 → 1.40.0
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 +162 -0
- package/call.d.ts +9 -1
- package/camera.d.ts +17 -0
- package/dist/call.js +4 -4
- package/index.d.ts +155 -18
- package/js/call/element.js +2 -0
- package/js/call/options.js +4 -0
- package/js/camera/pair-cursor.js +166 -0
- package/js/inline3d-call.js +28 -4
- package/js/inline3d-camera.js +30 -4
- package/js/inline3d-cursor-option.js +18 -0
- package/js/inline3d-model-entry.js +7 -0
- package/js/inline3d-model-playcanvas.js +5 -0
- package/js/inline3d-model.js +12 -0
- package/js/inline3d-splat-pc-entry.js +7 -1
- package/js/inline3d-splat-playcanvas.js +105 -4
- package/js/inline3d-splat-shared.js +17 -0
- package/js/inline3d-splat.js +12 -0
- package/js/inline3d-viewer.js +35 -5
- package/js/inline3d.js +667 -17
- package/model.d.ts +8 -1
- package/package.json +1 -1
- package/splat.d.ts +8 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,168 @@ entry points (`.`, `./three`) are frozen for 1.x, while the **scene subpaths** (
|
|
|
5
5
|
`./splat`, `./model`) are a preview tier whose options may change in any release. Entries below say
|
|
6
6
|
which tier they touch, because that is what tells you whether an upgrade can move your pixels.
|
|
7
7
|
|
|
8
|
+
## 1.40.0 — 2026-10-10
|
|
9
|
+
|
|
10
|
+
Touches the **preview tier** (`./call`, `./camera`) only: one new option, off by default. New type
|
|
11
|
+
`PairCursorOptions`; nothing removed, no default changes.
|
|
12
|
+
|
|
13
|
+
### `./call` + `./camera` — a depth cursor on stereo tiles (`cursor: 'depth'`)
|
|
14
|
+
|
|
15
|
+
- The scene cursor of 1.36.0 (runtime ADR-046) comes to the call and the camera view. Over a 3D
|
|
16
|
+
tile — a stereo peer, or your own stereo self view — a crosshair just in front of the person
|
|
17
|
+
under the pointer replaces the CSS cursor. Before, the flat cursor sat on the glass, drawn over a
|
|
18
|
+
face whose disparity says it is in front: the depth violation ADR-046 is about.
|
|
19
|
+
- A pair has no scene and no depth buffer, so the depth is **measured**: the cursor's footprint is
|
|
20
|
+
block-matched between the two halves of the tile's own frames (the same matcher auto-convergence
|
|
21
|
+
uses), the nearest confident match wins, and the crosshair is drawn into both halves with that
|
|
22
|
+
disparity plus a small margin. Nothing matched → the display plane. Same fast-rise / slow-sink
|
|
23
|
+
filter as the scene cursor. A mirrored self view flips the pointer, never the disparity.
|
|
24
|
+
- Zero cost when off: the option is validated at the call, and the cursor module is a dynamic
|
|
25
|
+
import made only on opt-in. When on, it costs nothing until the pointer is over a woven tile,
|
|
26
|
+
then a ~15 Hz grayscale copy of that tile's source (it shares the auto-convergence copy) and a
|
|
27
|
+
9-block match when the pointer or the copy changes.
|
|
28
|
+
- `addCall(…, { cursor: 'depth' })`, `<dxr-call cursor="depth">`, `addCameraView(…, { cursor:
|
|
29
|
+
'depth' })`; `{ height, pointerScope }` tune it (`margin` / `anchor` are scene-only and refused).
|
|
30
|
+
Lifted (2D→3D) and flat tiles keep the normal cursor. `diagnostics().peers[i].cursor` reads the
|
|
31
|
+
last placement. The `call`, `call-embed` and `camera` samples turn it on (`?cursor=0` turns it off).
|
|
32
|
+
|
|
33
|
+
## 1.39.0 — 2026-10-09
|
|
34
|
+
|
|
35
|
+
Core (`.`) changes are **additive**, with every existing default unchanged: `firstWoven` and
|
|
36
|
+
`rewoven()` settle on the browser's `wovenState` report where it exists (the hold path elsewhere,
|
|
37
|
+
byte for byte), the optional `onResize` on `addScene`, a warning on a double attach (the call
|
|
38
|
+
proceeds as before), and the display reads (`getDisplayInfo()` / `getRenderingModes()` /
|
|
39
|
+
`requestRenderingMode()`) waiting for a layer's first frame instead of answering `null` / `[]`
|
|
40
|
+
early. One **preview-tier default** changes: `rectCover` is `'auto'` on `./splat` and `./model`
|
|
41
|
+
(`'off'` in the core). New types only (`WovenState`, `WovenWithheldReason`, `RectCoverOption`);
|
|
42
|
+
nothing removed.
|
|
43
|
+
|
|
44
|
+
### Core: `firstWoven` / `rewoven()` settle on the browser's join report (`.`, additive)
|
|
45
|
+
|
|
46
|
+
- **`XRDisplayLayer.wovenState`** (`'pending' | 'woven' | 'withheld'`, plus `withheldReason`) is
|
|
47
|
+
the browser's per-frame report of whether a layer's rect went into the weave (DisplayXR Browser
|
|
48
|
+
draft PR #258, Windows, feature-flagged). Detected with `'wovenState' in
|
|
49
|
+
XRDisplayLayer.prototype`. Where it is absent (mac, Linux, Android, Windows builds without it)
|
|
50
|
+
nothing changes: the hold, `confirmed: false`, byte for byte.
|
|
51
|
+
- Where it is present:
|
|
52
|
+
- **`firstWoven`** settles `{ woven: true, confirmed: true, reason: 'woven' }` on the first
|
|
53
|
+
`'woven'` read once a stereo frame is drawn, with no hold.
|
|
54
|
+
- **`rewoven()`** settles on a `'woven'` read that follows a `'withheld'`/`'pending'` read seen
|
|
55
|
+
after the call, or three session frames after the last box change seen after the call, or,
|
|
56
|
+
when no box change is seen after the call, three session frames after the call. The report
|
|
57
|
+
is a live per-frame level trailing the join by 1–3 frames, so a read three frames on reflects
|
|
58
|
+
the rect as it was at the call; a `'woven'` from the old rect, or one read on the frame of the
|
|
59
|
+
call, never settles it. A stereo frame since the call is still required, and the cap stays.
|
|
60
|
+
- The no-change case is the common one: a page that moves the canvas, then calls
|
|
61
|
+
`rewoven()`. It used to wait a full hold of steady `'woven'` reads (a kiosk panel run:
|
|
62
|
+
46/46 calls confirmed, median 1206 ms, the same as the timer path). It now settles about
|
|
63
|
+
three frames after the call (~50 ms). The timer path (no `wovenState`) is unchanged.
|
|
64
|
+
- **The cap stays, as a safety.** A layer that never leaves `'withheld'` (a CSS effect on an
|
|
65
|
+
ancestor) settles `{ woven: false, confirmed: true, reason: <withheldReason> }` (or
|
|
66
|
+
`'pending'`) four holds after the call, never sooner than 4.8 s. That is a level, not a loss:
|
|
67
|
+
the canvas is not taken flat and `rewoven()` keeps working.
|
|
68
|
+
- **New read-only `handle.wovenState` / `handle.withheldReason`** on the `TileHandle`, read live
|
|
69
|
+
(`null` where the browser does not report it, `'pending'` while the window has no layer). Types:
|
|
70
|
+
`WovenState`, `WovenWithheldReason`; `FirstWovenResult.reason` gains `'woven'`, `'pending'` and
|
|
71
|
+
the withheld tokens.
|
|
72
|
+
- "Woven" means submitted to the weave. A GPU-stage failure after submit is not reflected.
|
|
73
|
+
|
|
74
|
+
### Cover across a rect change, owned by the SDK (`rectCover`; core primitive, subpath default)
|
|
75
|
+
|
|
76
|
+
- **New `rectCover` option on every `add*()`**: `'auto' | 'off' | { color?, snapshot? }`. Once a
|
|
77
|
+
window has woven, a change of its canvas's CSS size, dpr or page position raises a cover on the
|
|
78
|
+
frame the change is seen, and cuts it when `rewoven()` settles. A further change while it is up
|
|
79
|
+
restarts the wait. `remove()` and session end take it out of the DOM.
|
|
80
|
+
- **What the cover is:** a 2D canvas placed as the canvas's next sibling, never a second woven
|
|
81
|
+
canvas. It has `pointer-events: none` and `data-inline3d-cover`, and it is filled with `color`
|
|
82
|
+
(default `#000`).
|
|
83
|
+
- **The snapshot:** with `snapshot` (the default), the cover also shows the last frame: the left
|
|
84
|
+
eye, cover-fit to the new box. It is taken once per change, with one `drawImage` into a canvas
|
|
85
|
+
the size of the CSS box. Never `toDataURL`, never per frame.
|
|
86
|
+
- **Not before the first join:** cover a fresh canvas yourself until `firstWoven` (rule 5).
|
|
87
|
+
- **Defaults.** `'off'` on the core `addScene` / `addImage` / `addVideo` (frozen tier, unchanged).
|
|
88
|
+
**`'auto'` on `./splat` and `./model`, all engines.** That is a default change in the preview
|
|
89
|
+
tier: a page that already runs its own cover passes `rectCover: 'off'`.
|
|
90
|
+
- **`rewoven()` now also restarts on a pure move** (core, timing only). The box it watches gained
|
|
91
|
+
the canvas's page position (viewport rect plus document scroll, whole CSS px). A canvas moved
|
|
92
|
+
to another place at the same size goes through the same identity gap. Scrolling the document is
|
|
93
|
+
not a move. A scroll inside a nested scroller does read as one.
|
|
94
|
+
- From a kiosk demo that keeps one woven canvas and moves it between screens: a black box at the
|
|
95
|
+
new rect for 0.4–1.2 s, covered by hand on timers until now.
|
|
96
|
+
|
|
97
|
+
### Resize and draw in the same task (`./splat`, `./model`; core `onResize`, additive)
|
|
98
|
+
|
|
99
|
+
- **The PlayCanvas viewer** (`./splat` `engine: 'playcanvas'`, `./model`'s default engine) now
|
|
100
|
+
resizes its backing store and replays the last frame inside the ResizeObserver callback, which
|
|
101
|
+
runs before the paint. Before, it waited for the next animation frame, and for one paint the
|
|
102
|
+
old store was shown stretched onto the new box.
|
|
103
|
+
- **The replay keeps its proportions.** The cached frustum's x row is scaled by the aspect
|
|
104
|
+
change, until the runtime's next views carry the true one.
|
|
105
|
+
- **Guards.** A callback that arrives while the viewer is drawing, or re-entrantly while it is
|
|
106
|
+
resizing, takes the old one-frame debounce instead.
|
|
107
|
+
- **Not covered:** `./splat` on Spark and `./model` `engine: 'three'` (`SceneViewer`) keep the
|
|
108
|
+
one-frame debounce for now.
|
|
109
|
+
- **Core `addScene(…, { onResize(box) })`**, optional. It is called in the same ResizeObserver
|
|
110
|
+
callback with `{ width, height, dpr }`, so a page-drawn window can resize and draw before the
|
|
111
|
+
paint too. It runs only on a real change, and a throw is caught. Without it, nothing changes:
|
|
112
|
+
no observer is attached to a scene canvas.
|
|
113
|
+
|
|
114
|
+
### Display reads wait for the layer's first frame (core `.`, additive, no new API)
|
|
115
|
+
|
|
116
|
+
- **`getDisplayInfo()` / `getRenderingModes()` / `requestRenderingMode()`**, on a tile handle or
|
|
117
|
+
the wall, called before the window's layer has delivered its first frame now **wait for it** and
|
|
118
|
+
ask the layer then. Before, a page calling them right after `add*()` got `null` / `[]` (and a
|
|
119
|
+
request answered "not forwarded"): the browser only answers once the layer has a weave session,
|
|
120
|
+
and `null` is documented as "no glasses-free display". Found by the samples panel test (#151),
|
|
121
|
+
which worked around it page-side with `await handle.firstWoven`; that is no longer needed.
|
|
122
|
+
- **Released by** the first `'woven'` read where the browser reports `wovenState`, or the
|
|
123
|
+
layer's first stereo frame elsewhere. No first-woven hold. A `'withheld'` read does not
|
|
124
|
+
release: the layer reads it right after creation, before the weave session can answer
|
|
125
|
+
(measured on a Chromium 156 panel build: released on it, `getDisplayInfo()` answered `null`
|
|
126
|
+
at 1.55 s while the layer only read `'woven'` at ~3.9 s).
|
|
127
|
+
- **A `null` / `[]` answer before the cap is not returned.** The read is asked again on each
|
|
128
|
+
later release signal (the next `'woven'` read, or the next session frame once a stereo frame
|
|
129
|
+
has been drawn) and resolves with the first non-empty answer, on both paths.
|
|
130
|
+
`requestRenderingMode()` is forwarded once on the release, not retried.
|
|
131
|
+
- **Capped** at the first-woven cap: four holds from the layer's construction, never under
|
|
132
|
+
4.8 s. Only past it is the layer's `null` / `[]` returned, as a real absence.
|
|
133
|
+
- **Unchanged:** once a read on the layer has answered for real, later calls go straight
|
|
134
|
+
through; no session, no live layer, a failed layer, or a window removed / session ended
|
|
135
|
+
mid-wait settles at once, as before.
|
|
136
|
+
|
|
137
|
+
### Double-attach warning (core, and the `./splat` / `./model` entries)
|
|
138
|
+
|
|
139
|
+
- `addScene` / `addImage` / `addVideo` / `addSplat` / `addModel` on a canvas that is still
|
|
140
|
+
registered on the same wall (not yet `remove()`d) now **warns**, once per canvas. The warning
|
|
141
|
+
names the method and the live registration, and says the call is rebuilding the layer (a fresh
|
|
142
|
+
0.4–1.2 s identity gap) or putting a second renderer on the context.
|
|
143
|
+
- **It is a warning only, so no page changes behavior.** The call proceeds exactly as before: the
|
|
144
|
+
core closes and rebuilds the layer, the subpaths attach a new renderer, and the call returns its
|
|
145
|
+
own new handle. A page that re-adds to swap pictures keeps working.
|
|
146
|
+
- To swap content, redraw a source canvas or use `setSource`. To re-register on purpose,
|
|
147
|
+
`remove()` first.
|
|
148
|
+
- `./splat` and `./model` check at their entry, not only in `addScene`: their renderer exists
|
|
149
|
+
before their core window, so a second call during the first one's load is caught too.
|
|
150
|
+
- From a kiosk demo that attached its stage twice per screen visit.
|
|
151
|
+
|
|
152
|
+
### Every session frame presents (`./splat`, `./model`, `./viewer`)
|
|
153
|
+
|
|
154
|
+
- The viewers behind the scene subpaths now draw on **every** session frame they are handed. This
|
|
155
|
+
covers the PlayCanvas viewer (`./splat` `engine: 'playcanvas'`, `./model`'s default) and
|
|
156
|
+
`SceneViewer` (`./splat` on Spark, `./model` `engine: 'three'`, `./viewer`).
|
|
157
|
+
- **Before the first good frame,** a frame they cannot draw (a short view list, a missing or
|
|
158
|
+
degenerate viewport) used to draw nothing. It now draws the mono camera into both halves: the
|
|
159
|
+
subject flat if it is loaded, a cleared frame if not. Afterwards they replay the last good
|
|
160
|
+
frame, as before.
|
|
161
|
+
- **No source, or nothing visible,** still draws a frame.
|
|
162
|
+
- **Before the engine has created its GL context,** nothing can be presented, and creating one
|
|
163
|
+
early would give it the wrong attributes.
|
|
164
|
+
- The dark-blink rule holds: no clear without a draw.
|
|
165
|
+
- **Note for `addScene` pages:** the SDK cannot enforce this for a window your page draws. Draw on
|
|
166
|
+
every `onFrame` ([`woven-canvas-rules.md`](docs/woven-canvas-rules.md), "redraw every frame").
|
|
167
|
+
A fully transparent woven frame weaves **black**: a browser bug (browser-pvt #255), listed there
|
|
168
|
+
as a known limitation.
|
|
169
|
+
|
|
8
170
|
## 1.38.0 — 2026-10-09
|
|
9
171
|
|
|
10
172
|
Touches the **preview tier** (`./call`, `./splat`) only: one default and two additive handle
|
package/call.d.ts
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// until the snapshot is updated in the same change (`node tools/api-surface.mjs --update`).
|
|
18
18
|
|
|
19
19
|
import type { Inline3D, Inline3DUnsupported } from './index.js';
|
|
20
|
-
import type { StereoCamera } from './camera.js';
|
|
20
|
+
import type { StereoCamera, PairCursorOptions } from './camera.js';
|
|
21
21
|
|
|
22
22
|
/** What a participant sends: a side-by-side pair (left eye left) or one flat picture. */
|
|
23
23
|
export type CallFormat = 'sbs' | 'mono';
|
|
@@ -215,6 +215,14 @@ export interface CallOptions {
|
|
|
215
215
|
* at the display plane. No calibration needed. The depth slider stays an offset on top.
|
|
216
216
|
*/
|
|
217
217
|
autoConverge?: boolean;
|
|
218
|
+
/**
|
|
219
|
+
* `'depth'`: over a 3D (stereo) tile — a peer's or your self view — a crosshair just in front of
|
|
220
|
+
* the person under the pointer replaces the CSS cursor, instead of the flat cursor drawn on the
|
|
221
|
+
* glass behind them (runtime ADR-046; the depth is measured off each tile's two halves). Lifted
|
|
222
|
+
* (2D→3D) and flat tiles keep the normal cursor. Off by default (zero cost).
|
|
223
|
+
* `<dxr-call cursor="depth">`.
|
|
224
|
+
*/
|
|
225
|
+
cursor?: 'depth' | PairCursorOptions;
|
|
218
226
|
/** Participants INCLUDING you. Default 4, clamped to 2..4 (full mesh). */
|
|
219
227
|
maxPeers?: number;
|
|
220
228
|
/**
|
package/camera.d.ts
CHANGED
|
@@ -184,6 +184,17 @@ export function openCamera(opts?: CameraOptions | CameraPreference): Promise<Ste
|
|
|
184
184
|
/** Is `x` a {@link StereoCamera}? Duck-typed, so two copies of the SDK interoperate. */
|
|
185
185
|
export function isStereoCamera(x: unknown): x is StereoCamera;
|
|
186
186
|
|
|
187
|
+
/**
|
|
188
|
+
* `cursor: { … }` on a stereo pair (an object also means `'depth'`). A pair has no scene: the
|
|
189
|
+
* depth under the pointer is MEASURED off its two halves, so there is no anchor mode or margin.
|
|
190
|
+
*/
|
|
191
|
+
export interface PairCursorOptions {
|
|
192
|
+
/** Sprite height as a fraction of the tile height (default 0.06). */
|
|
193
|
+
height?: number;
|
|
194
|
+
/** `'window'` (default): the cursor stays while the pointer is over DOM on the tile (a badge). `'canvas'`: only over the canvas itself. */
|
|
195
|
+
pointerScope?: 'canvas' | 'window';
|
|
196
|
+
}
|
|
197
|
+
|
|
187
198
|
export interface CameraViewOptions {
|
|
188
199
|
/** Selfie mirroring, done right for a pair (each half mirrored AND swapped). Default true. */
|
|
189
200
|
mirror?: boolean;
|
|
@@ -193,6 +204,12 @@ export interface CameraViewOptions {
|
|
|
193
204
|
depth?: number;
|
|
194
205
|
/** The tile's aspect (w/h) = the woven buffer's per-eye aspect. Default 16/9. */
|
|
195
206
|
aspect?: number;
|
|
207
|
+
/**
|
|
208
|
+
* `'depth'`: while the pointer is over the woven view, a crosshair just in front of whatever is
|
|
209
|
+
* under it (runtime ADR-046, measured off the pair's two halves) replaces the CSS cursor, so it
|
|
210
|
+
* is never drawn on the glass behind a face that sits in front of it. Off by default (zero cost).
|
|
211
|
+
*/
|
|
212
|
+
cursor?: 'depth' | PairCursorOptions;
|
|
196
213
|
/**
|
|
197
214
|
* Called when the view's route changes — including when the wall says the tile will not weave
|
|
198
215
|
* and the view drops to its flat left eye by itself — and when its `firstWoven` settles. Keep a
|