@displayxr/inline3d 1.44.0 → 1.45.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/camera.d.ts +5 -1
- package/cursor-depth.d.ts +151 -0
- package/dist/call.js +18 -4
- package/js/camera/pair-cursor.js +66 -32
- package/js/inline3d-cursor-depth-playcanvas.js +369 -69
- package/js/inline3d-cursor-depth.js +118 -20
- package/js/inline3d-cursor-option.js +24 -5
- package/js/inline3d-cursor-shape.js +962 -0
- package/js/inline3d-playcanvas-engine.js +1 -0
- package/js/inline3d-splat-layer-rig.js +3 -0
- package/js/inline3d-three.js +117 -6
- package/model.d.ts +1 -1
- package/package.json +2 -1
- package/splat.d.ts +1 -1
- package/three.d.ts +11 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,86 @@ 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.45.1 — 2026-10-13
|
|
9
|
+
|
|
10
|
+
### Fixed — `cursor: 'depth'` showed up under a finger (touch)
|
|
11
|
+
|
|
12
|
+
- On a touch device, dragging a finger over a scene drew the 3D cursor, though the browser shows no
|
|
13
|
+
cursor there. Since 1.44.0 the pointer took every `pointermove` / `pointerrawupdate` whatever
|
|
14
|
+
its `pointerType`. Now only a **mouse** or a **hovering pen** places the cursor. A touch or a pen
|
|
15
|
+
in contact clears it, including a tap that never moves (so no stale mouse hover stays up), and
|
|
16
|
+
the CSS cursor is never hidden for them. This covers every backend (three.js `./model`,
|
|
17
|
+
PlayCanvas `./model` / `./splat`, the `./call` / `./camera` pair cursor). They all share
|
|
18
|
+
`CursorPointer`.
|
|
19
|
+
- New `cursorPointerHovers(e)` in `./cursor-depth`. An event with no `pointerType` (synthetic)
|
|
20
|
+
counts as a mouse, as before.
|
|
21
|
+
|
|
22
|
+
## 1.45.0 — 2026-10-13
|
|
23
|
+
|
|
24
|
+
### Packaging
|
|
25
|
+
|
|
26
|
+
- `js/inline3d-cursor-shape.js` (new in this release, imported by `./cursor-depth` and so by
|
|
27
|
+
`./three`) is now in the published file set. A new unit test fails whenever a published module
|
|
28
|
+
imports a file the package does not ship.
|
|
29
|
+
|
|
30
|
+
### Depth cursor: the sprite is the page's CSS cursor — any keyword, and `url()` images (ADR-046 §C, page side)
|
|
31
|
+
|
|
32
|
+
Touches `./cursor-depth`, `./three` (`DepthCursor`) and the preview tier (`./model`, `./splat`,
|
|
33
|
+
`./call`, `./camera`), all only under `cursor: 'depth'`.
|
|
34
|
+
|
|
35
|
+
- The lifted cursor was always the arrow, so over a model whose page cursor is `grab` it showed an
|
|
36
|
+
arrow. It now draws what the browser would show: the page's computed CSS cursor under the pointer.
|
|
37
|
+
- **Every standard keyword** has a procedural sprite in the arrow's style (white fill, dark
|
|
38
|
+
outline, the OS cursor's 19 CSS px, its hotspot at its natural point): `pointer`, `grab`,
|
|
39
|
+
`grabbing`, `move` / `all-scroll`, `text` / `vertical-text`, `crosshair`, `not-allowed` /
|
|
40
|
+
`no-drop`, `help`, `wait`, `progress`, `copy`, `alias`, `context-menu`, `cell`, every resize
|
|
41
|
+
cursor (`n/s/e/w/ne/nw/se/sw-resize`, `ns/ew/nesw/nwse-resize`, `col-resize`, `row-resize`),
|
|
42
|
+
`zoom-in`, `zoom-out`; `auto` / `default` and anything unknown → the arrow.
|
|
43
|
+
- **`none`** draws nothing and keeps the OS cursor hidden: a page that hides its cursor on purpose
|
|
44
|
+
stays that way.
|
|
45
|
+
- **`url(a) x y, url(b), fallback`**: the first url that loads as an image (crossOrigin
|
|
46
|
+
'anonymous' — same-origin and `data:` always do; cross-origin without CORS falls to the next
|
|
47
|
+
item), at its natural CSS size capped at 128 px; hotspot = the explicit `x y`, else a `.cur`
|
|
48
|
+
file's own, else 0 0. Loaded async and cached per URL; the keyword fallback is drawn until it is
|
|
49
|
+
ready.
|
|
50
|
+
- All of it softened like the arrow, in the cursor's own material (keyword sprites and images are
|
|
51
|
+
textures on one soft quad: one shader program, the same 13-tap blur and opacity). No CSS filter,
|
|
52
|
+
no CSS opacity.
|
|
53
|
+
- **`cursor: { shape }`**: `'auto'` (default, mirror the CSS), `'none'`, or a sprite name
|
|
54
|
+
(`CURSOR_SHAPES`) to fix it. **`cursor: { shapeFor(hit) }`**: decide per drawn frame
|
|
55
|
+
(`{ u, v, point, css, cssValue }` → a name, or nothing for `shape`'s answer).
|
|
56
|
+
- **Behaviour change — the CSS hide.** The cursor no longer writes `canvas.style.cursor = 'none'`
|
|
57
|
+
while it draws: it sets a `data-inline3d-cursor-hidden` attribute (on the canvas, or on `<html>`
|
|
58
|
+
for `pointerScope: 'window'`, replacing the `inline3d-cursor-hidden` class) plus one injected
|
|
59
|
+
`cursor: none !important` rule. The page's own cursor value is never touched, so it can be read
|
|
60
|
+
back (the attribute is lifted for the read and put back in the same task, so nothing paints).
|
|
61
|
+
Reads happen only on frames that draw the sprite, after pointer moves (at most every 100 ms) and
|
|
62
|
+
at once on pointerdown / pointerup. A page that checked `style.cursor === 'none'` should check
|
|
63
|
+
the attribute (or `getComputedStyle(canvas).cursor`).
|
|
64
|
+
- `tools/cursor-shape-capture/run.mjs`: headless per-sprite captures over the lifted model sample
|
|
65
|
+
(keywords, a `data:` url() image, `none`), hotspot stability and the Shop grab → grabbing → grab
|
|
66
|
+
sequence, failing on any GL error; the samples take `&shape=`.
|
|
67
|
+
|
|
68
|
+
### Fixed — `cursor: 'depth'` (PlayCanvas) over a layer on the display rig (#185)
|
|
69
|
+
|
|
70
|
+
- A layer moved with `handle.setLayerRig(name, 'display')` is drawn by the layer rig's cameras, not
|
|
71
|
+
the eye camera, so the pick never saw it and the cursor stayed on the glass over it (a round
|
|
72
|
+
stage, a map tile under a camera-rig photo). The layer rig's post run, and its display run when
|
|
73
|
+
it is not rounded, now join the eye pick. A ROUNDED display run (a camera rig, gain ≠ 1) gets a
|
|
74
|
+
second low-res pick pass through the same round views, and each hit becomes the point the photo
|
|
75
|
+
views show at the same pixels in both eyes, so the placement (unchanged) puts the arrow in front
|
|
76
|
+
of the stage as the panel shows it. The two passes merge by the nearest point. Same cadence and
|
|
77
|
+
readback as before (every other frame, asynchronous); the second pass exists only while a layer
|
|
78
|
+
is on the display rig, and nothing at all without `cursor: 'depth'`.
|
|
79
|
+
|
|
80
|
+
### Added — a `pc.ShaderMaterial` can take part in the depth pick
|
|
81
|
+
|
|
82
|
+
- `CURSOR_DEPTH_PICK_GLSL` and `CURSOR_DEPTH_PICK_WGSL` (`./cursor-depth`): `{ declarations, output }`
|
|
83
|
+
snippets that write the pick id and, under `DEPTH_PICK_PASS`, the pick depth with the engine's own
|
|
84
|
+
`pickPS` chunk. Since 1.44.0 the pick pass leaves out a ShaderMaterial that does not write the
|
|
85
|
+
depth target; with these it is hit like any engine material. docs/authoring-inline-3d.md §A cursor
|
|
86
|
+
that respects depth.
|
|
87
|
+
|
|
8
88
|
## 1.44.0 — 2026-10-13
|
|
9
89
|
|
|
10
90
|
Touches the **core** `./three` (`DepthCursor`), `./cursor-depth`, and the **preview tier** (`./model`,
|
package/camera.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// until the snapshot is updated in the same change (`node tools/api-surface.mjs --update`).
|
|
13
13
|
|
|
14
14
|
import type { Inline3D, Inline3DUnsupported, TileHandle } from './index.js';
|
|
15
|
-
import type { CursorSoftenOption } from './cursor-depth.js';
|
|
15
|
+
import type { CursorShape, CursorShapeHit, CursorShapeOption, CursorSoftenOption } from './cursor-depth.js';
|
|
16
16
|
|
|
17
17
|
/** What the camera delivers: a side-by-side pair (left eye left) or one flat picture. */
|
|
18
18
|
export type CameraFormat = 'sbs' | 'mono';
|
|
@@ -196,6 +196,10 @@ export interface PairCursorOptions {
|
|
|
196
196
|
pointerScope?: 'canvas' | 'window';
|
|
197
197
|
/** Crosstalk softening of the lifted arrow (runtime ADR-046 Amendment 2 §B.7), drawn into the woven pixels. Default `'both'`. */
|
|
198
198
|
soften?: CursorSoftenOption;
|
|
199
|
+
/** The sprite. Default `'auto'`: the page's CSS cursor over the tile (any keyword, url() images, `none`). */
|
|
200
|
+
shape?: CursorShapeOption;
|
|
201
|
+
/** Per-frame override while the sprite draws (`point` is null on a pair): a shape name, or nothing for `shape`'s. */
|
|
202
|
+
shapeFor?: (hit: CursorShapeHit) => CursorShape | 'none' | 'auto' | null | undefined;
|
|
199
203
|
}
|
|
200
204
|
|
|
201
205
|
export interface CameraViewOptions {
|
package/cursor-depth.d.ts
CHANGED
|
@@ -96,6 +96,10 @@ export interface CursorOptions {
|
|
|
96
96
|
debug?: boolean | 'both';
|
|
97
97
|
/** Crosstalk softening of the lifted arrow (runtime ADR-046 Amendment 2 §B.7). Default `'both'`. */
|
|
98
98
|
soften?: CursorSoftenOption;
|
|
99
|
+
/** The sprite. Default `'auto'`: mirror the page's CSS cursor under the pointer (`grab` → the open hand, …). */
|
|
100
|
+
shape?: CursorShapeOption;
|
|
101
|
+
/** Per-frame override while the sprite draws: a shape name, or nothing for `shape`'s answer. */
|
|
102
|
+
shapeFor?: (hit: CursorShapeHit) => CursorShape | 'none' | 'auto' | null | undefined;
|
|
99
103
|
}
|
|
100
104
|
export const CURSOR_OPTION_KEYS: readonly (keyof CursorOptions)[];
|
|
101
105
|
/** `cursor` as ./model and ./splat accept it: null = off; throws on a bad value. */
|
|
@@ -164,6 +168,140 @@ export class CursorDepthPlacer {
|
|
|
164
168
|
): CursorPlacement;
|
|
165
169
|
}
|
|
166
170
|
|
|
171
|
+
// ── The sprite: the page's CSS cursor, mirrored (ADR-046 Amendment 2 §C, page side) ─────────
|
|
172
|
+
|
|
173
|
+
/** The procedural sprites: one per standard CSS cursor keyword (aliases share one). */
|
|
174
|
+
export type CursorShape =
|
|
175
|
+
| 'arrow' | 'pointer' | 'grab' | 'grabbing' | 'move' | 'text' | 'vertical-text' | 'crosshair'
|
|
176
|
+
| 'not-allowed' | 'help' | 'wait' | 'progress' | 'copy' | 'alias' | 'context-menu' | 'cell'
|
|
177
|
+
| 'ns-resize' | 'ew-resize' | 'nesw-resize' | 'nwse-resize' | 'col-resize' | 'row-resize'
|
|
178
|
+
| 'zoom-in' | 'zoom-out';
|
|
179
|
+
/** `cursor.shape`: `'auto'` (default — the page's computed CSS cursor, url() images included), `'none'`, or a sprite. */
|
|
180
|
+
export type CursorShapeOption = 'auto' | 'none' | CursorShape;
|
|
181
|
+
/** What `shapeFor` is handed each drawn frame. */
|
|
182
|
+
export interface CursorShapeHit {
|
|
183
|
+
/** Canvas-normalised pointer, v down. */
|
|
184
|
+
u: number;
|
|
185
|
+
v: number;
|
|
186
|
+
/** The nearest content point under the footprint (the views' / world space), null on a pair or a miss. */
|
|
187
|
+
point: number[] | null;
|
|
188
|
+
/** What `'auto'` would draw: a sprite name, `'none'` or `'image'` (null unless `shape: 'auto'`). */
|
|
189
|
+
css: CursorShape | 'none' | 'image' | null;
|
|
190
|
+
/** The page's computed CSS `cursor` value (null unless `shape: 'auto'`). */
|
|
191
|
+
cssValue: string | null;
|
|
192
|
+
}
|
|
193
|
+
/** What a cursor draws: nothing, a keyword sprite, or a loaded url() image. */
|
|
194
|
+
export type CursorSprite =
|
|
195
|
+
| { kind: 'none' }
|
|
196
|
+
| { kind: 'shape'; name: CursorShape; key: string }
|
|
197
|
+
| { kind: 'image'; key: string; entry: CursorImageEntry; width: number; height: number; hotspot: [number, number] };
|
|
198
|
+
export interface CursorImageEntry {
|
|
199
|
+
url: string;
|
|
200
|
+
state: 'loading' | 'ready' | 'failed';
|
|
201
|
+
image: HTMLImageElement | null;
|
|
202
|
+
/** Natural size, CSS px. */
|
|
203
|
+
width: number;
|
|
204
|
+
height: number;
|
|
205
|
+
/** A .cur file's own hotspot, once read. */
|
|
206
|
+
curHotspot: [number, number] | null;
|
|
207
|
+
}
|
|
208
|
+
export const CURSOR_SHAPES: readonly CursorShape[];
|
|
209
|
+
export const CURSOR_SHAPE_OPTIONS: readonly CursorShapeOption[];
|
|
210
|
+
/** Every CSS cursor keyword the map knows. */
|
|
211
|
+
export const CURSOR_CSS_KEYWORDS: readonly string[];
|
|
212
|
+
/** The data attribute that hides the CSS cursor while the sprite draws (on the canvas; on <html> for pointerScope 'window'). */
|
|
213
|
+
export const CURSOR_HIDDEN_ATTR: string;
|
|
214
|
+
/** The one rule injected for it: `cursor: none !important` on the attribute's element and its descendants. */
|
|
215
|
+
export const CURSOR_HIDDEN_RULE: string;
|
|
216
|
+
/** Pointer moves re-read the CSS cursor at most this often, ms (100); press/release re-read at once. */
|
|
217
|
+
export const CURSOR_CSS_READ_MS: number;
|
|
218
|
+
/** Image cursors larger than this (CSS px) are scaled down to it (128). */
|
|
219
|
+
export const CURSOR_IMAGE_MAX_PX: number;
|
|
220
|
+
/** Texels per sprite unit of the keyword textures (64). */
|
|
221
|
+
export const CURSOR_SHAPE_TEXTURE_PX: number;
|
|
222
|
+
/** One keyword → its sprite, `'none'`, or the arrow for anything unknown. */
|
|
223
|
+
export function cssKeywordToShape(keyword: string): CursorShape | 'none';
|
|
224
|
+
/** A computed CSS `cursor` value's keyword fallback → its sprite (images ignored). */
|
|
225
|
+
export function cssCursorToShape(css: string | null | undefined): CursorShape | 'none';
|
|
226
|
+
/** A computed CSS `cursor` value: its url() images in order (explicit hotspot or null) and the keyword fallback. */
|
|
227
|
+
export function parseCssCursor(css: string | null | undefined): { images: ReadonlyArray<{ url: string; hotspot: [number, number] | null }>; keyword: string };
|
|
228
|
+
/** `shapeFor`'s answer if it names a sprite (or 'none'), else the fixed `shape`, else the CSS one, else the arrow. */
|
|
229
|
+
export function cursorPickShape(shape: CursorShapeOption | undefined, fromShapeFor: unknown, css: string | null | undefined): CursorShape | 'none';
|
|
230
|
+
/** A .cur file's hotspot (the entry `w` px wide, else the first), or null when not a cursor file. */
|
|
231
|
+
export function curFileHotspot(buf: ArrayBuffer | null, w?: number): [number, number] | null;
|
|
232
|
+
/** Cursor images by URL (crossOrigin 'anonymous'), loaded once. */
|
|
233
|
+
export class CursorImageCache {
|
|
234
|
+
constructor(opts?: { ImageCtor?: unknown; fetchFn?: unknown });
|
|
235
|
+
get(url: string): CursorImageEntry;
|
|
236
|
+
}
|
|
237
|
+
/** The page-wide cache the cursors share. */
|
|
238
|
+
export const cursorImageCache: CursorImageCache;
|
|
239
|
+
/** What to draw for a computed CSS cursor value: the first READY image (failed → next, loading → the fallback), else the keyword. */
|
|
240
|
+
export function resolveCursorSprite(css: string, images?: Pick<CursorImageCache, 'get'>): CursorSprite;
|
|
241
|
+
export function spriteForName(name: CursorShape | 'none' | string): CursorSprite;
|
|
242
|
+
/** The sprite's area in sprite units (arrow heights; y down, hotspot at the origin). */
|
|
243
|
+
export function cursorSpriteRect(sprite: CursorSprite): [number, number, number, number];
|
|
244
|
+
/** That area padded by `radius` (+ `aa`). */
|
|
245
|
+
export function cursorSpriteBox(sprite: CursorSprite, radius: number, aa?: number): [number, number, number, number];
|
|
246
|
+
/** A sprite's geometry (sprite units, y down, hotspot at the origin). */
|
|
247
|
+
export interface CursorShapeGeometry {
|
|
248
|
+
readonly name: CursorShape;
|
|
249
|
+
/** The dark outline: a union of polygons. */
|
|
250
|
+
readonly parts: ReadonlyArray<ReadonlyArray<readonly [number, number]>>;
|
|
251
|
+
/** Each part inset by 1/19: the white fill. */
|
|
252
|
+
readonly fill: ReadonlyArray<ReadonlyArray<readonly [number, number]>>;
|
|
253
|
+
/** Dark marks over the fill (the ? of help, the + of copy). */
|
|
254
|
+
readonly detail: ReadonlyArray<ReadonlyArray<readonly [number, number]>>;
|
|
255
|
+
readonly box: readonly [number, number, number, number];
|
|
256
|
+
}
|
|
257
|
+
export function cursorShape(name: CursorShape | string): CursorShapeGeometry;
|
|
258
|
+
type CursorPolys = { outline: ReadonlyArray<ReadonlyArray<readonly [number, number]>>; fill: ReadonlyArray<ReadonlyArray<readonly [number, number]>>; detail?: ReadonlyArray<ReadonlyArray<readonly [number, number]>> };
|
|
259
|
+
/** The outline, the fill inset by `d` sprite units (default 1/19), the detail. */
|
|
260
|
+
export function cursorShapePolys(name: CursorShape | string, d?: number): CursorPolys;
|
|
261
|
+
/** The shape's box padded by `radius` (+ `aa`). */
|
|
262
|
+
export function cursorShapeBox(name: CursorShape | string, radius: number, aa?: number): [number, number, number, number];
|
|
263
|
+
/** The softened shape at (x, y) of sprite units (straight RGB + alpha); for the arrow, exactly cursorSoftArrowSample. */
|
|
264
|
+
export function cursorShapeSample(name: CursorShape | string, x: number, y: number, radius: number, aa: number, fill?: readonly number[], outline?: readonly number[], polys?: CursorPolys): [number, number, number, number];
|
|
265
|
+
/** The softened shape rasterised for a 2D canvas, the hotspot at (cx, cy), one sprite unit = `sizePx`. */
|
|
266
|
+
export function cursorShapeRaster(name: CursorShape | string, cx: number, cy: number, sizePx: number, radius: number, alpha: number, fill?: readonly number[], outline?: readonly number[], polys?: CursorPolys): { x0: number; y0: number; width: number; height: number; data: Uint8ClampedArray };
|
|
267
|
+
/** A keyword sprite as a crisp straight-alpha RGBA texture (row 0 = the top) and the sprite-unit rect it covers. */
|
|
268
|
+
export function cursorShapeTexture(name: CursorShape | string): { width: number; height: number; data: Uint8ClampedArray; rect: readonly [number, number, number, number] };
|
|
269
|
+
/** RGBA pixels softened with the same 13-tap kernel and opacity, padded by the radius. */
|
|
270
|
+
export function cursorSoftenPixels(src: ArrayLike<number>, w: number, h: number, radiusPx: number, alpha: number): { pad: number; width: number; height: number; data: Uint8ClampedArray };
|
|
271
|
+
/** The crisp shape drawn with a 2D context (outline dark, fill white, detail dark), the hotspot at (cx, cy). */
|
|
272
|
+
export function drawCursorShape2D(g: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, name: CursorShape | string, cx: number, cy: number, sizePx: number, polys?: CursorPolys, fillStyle?: string, outlineStyle?: string): void;
|
|
273
|
+
/** GLSL defining `vec4 inline3dSoftSprite(sampler2D tex, vec2 p, vec4 rect, float radius)` — every non-arrow sprite, one program. */
|
|
274
|
+
export function cursorSpriteGLSL(): string;
|
|
275
|
+
/** Inject CURSOR_HIDDEN_RULE once; false without a document. */
|
|
276
|
+
export function ensureCursorHiddenRule(doc?: Document): boolean;
|
|
277
|
+
/** The page's computed cursor at `el` with the depth cursor's hide lifted (synchronously) off `holders`; null if unreadable. */
|
|
278
|
+
export function readPageCursor(el: Element, holders: ReadonlyArray<Element | null | undefined>): string | null;
|
|
279
|
+
/** Tracks the page's CSS cursor under the pointer: listeners mark it stale; `value()` re-reads (moves throttled, press/release at once). */
|
|
280
|
+
export class CursorCssReader {
|
|
281
|
+
constructor(pointer: { canvas: Element; scope: 'canvas' | 'window' }, opts?: { throttleMs?: number });
|
|
282
|
+
/** The last computed CSS cursor read, or null. */
|
|
283
|
+
readonly css: string | null;
|
|
284
|
+
/** Reads performed (diagnostics). */
|
|
285
|
+
readonly reads: number;
|
|
286
|
+
throttleMs: number;
|
|
287
|
+
value(nowMs?: number): string;
|
|
288
|
+
shape(nowMs?: number): CursorShape | 'none';
|
|
289
|
+
read(nowMs?: number): string;
|
|
290
|
+
dispose(): void;
|
|
291
|
+
}
|
|
292
|
+
/** Per-cursor sprite state: the option, `shapeFor`, and a reader only for `'auto'`. */
|
|
293
|
+
export class CursorShapeSelector {
|
|
294
|
+
constructor(pointer: { canvas: Element; scope: 'canvas' | 'window' }, opts?: { shape?: CursorShapeOption; shapeFor?: ((hit: CursorShapeHit) => unknown) | null; images?: Pick<CursorImageCache, 'get'> });
|
|
295
|
+
readonly option: CursorShapeOption;
|
|
296
|
+
readonly reader: CursorCssReader | null;
|
|
297
|
+
/** The sprite picked last. */
|
|
298
|
+
readonly sprite: CursorSprite;
|
|
299
|
+
/** …as a label: a sprite name, 'none' or 'image'. */
|
|
300
|
+
readonly current: CursorShape | 'none' | 'image';
|
|
301
|
+
pick(hit: { u: number; v: number; point: number[] | null }, nowMs?: number): CursorSprite;
|
|
302
|
+
dispose(): void;
|
|
303
|
+
}
|
|
304
|
+
|
|
167
305
|
// ── Crosstalk softening of the lifted cursor (runtime ADR-046 Amendment 2 §B.7) ─────────────
|
|
168
306
|
|
|
169
307
|
/**
|
|
@@ -275,3 +413,16 @@ export function cursorSoftArrowRaster(
|
|
|
275
413
|
outline?: readonly number[],
|
|
276
414
|
polys?: ReadonlyArray<ReadonlyArray<readonly [number, number]>>,
|
|
277
415
|
): { x0: number; y0: number; width: number; height: number; data: Uint8ClampedArray };
|
|
416
|
+
|
|
417
|
+
/** A shader snippet pair for a PlayCanvas `pc.ShaderMaterial`: `declarations` above `main`, `output` inside `#ifdef PICK_PASS`. */
|
|
418
|
+
export interface CursorDepthPickSnippet {
|
|
419
|
+
readonly declarations: string;
|
|
420
|
+
readonly output: string;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* PlayCanvas ShaderMaterial opt-in to `cursor: 'depth'`'s pick (GLSL): writes the pick id to
|
|
424
|
+
* `pcFragColor0` and, under `DEPTH_PICK_PASS`, the pick depth to `pcFragColor1` (the engine's `pickPS`).
|
|
425
|
+
*/
|
|
426
|
+
export const CURSOR_DEPTH_PICK_GLSL: CursorDepthPickSnippet;
|
|
427
|
+
/** The WGSL twin of CURSOR_DEPTH_PICK_GLSL: writes `output.color` / `output.color1`. */
|
|
428
|
+
export const CURSOR_DEPTH_PICK_WGSL: CursorDepthPickSnippet;
|