@solidrt/flux-types 0.0.54 → 0.0.56
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/gui/gpu.d.ts +312 -32
- package/gui/rendertree.d.ts +20 -4
- package/gui/spatial.d.ts +196 -15
- package/index.d.ts +1 -0
- package/package.json +1 -1
- package/standards/clipboard.d.ts +25 -0
package/gui/gpu.d.ts
CHANGED
|
@@ -107,9 +107,12 @@ declare module "flux:gpu" {
|
|
|
107
107
|
* A uniform that is declared but optimized out by the compiler is accepted
|
|
108
108
|
* with a warning and the write is skipped, so one param object can drive
|
|
109
109
|
* shader variants that do not all use every uniform.
|
|
110
|
-
* An `undefined` value is skipped, so conditional spreads stay
|
|
110
|
+
* An `undefined` (or `null`) value is skipped, so conditional spreads stay
|
|
111
|
+
* usable. A `Float32Array`/`Float64Array` is accepted as a flat array. The
|
|
112
|
+
* same decoder serves the `<texture params>`, view and window shader props,
|
|
113
|
+
* so the prop and the imperative path accept and reject identically.
|
|
111
114
|
*/
|
|
112
|
-
export type ShaderParams = Record<string, number | number[]>
|
|
115
|
+
export type ShaderParams = Record<string, number | number[] | Float32Array | Float64Array>
|
|
113
116
|
/** Magnification/minification filter; "linear" (default) or hard-pixel "nearest". */
|
|
114
117
|
export type FilterMode = "linear" | "nearest"
|
|
115
118
|
/** Sampling outside 0..1: "clamp" (default, extend edge pixels) or "repeat" (tile). */
|
|
@@ -129,8 +132,19 @@ declare module "flux:gpu" {
|
|
|
129
132
|
* shader sampling minifies through the chain; the `<texture>` display
|
|
130
133
|
* draw samples the full-size level. Regeneration is one GPU pass per
|
|
131
134
|
* upload or render, so a per-frame texture pays it per frame.
|
|
135
|
+
*
|
|
136
|
+
* `anisotropy` (default 1 = off) is the anisotropic filtering level, Three's
|
|
137
|
+
* `texture.anisotropy`: the most taps a shader sample may take along the
|
|
138
|
+
* axis a surface is foreshortened on, so a tiled ground plane seen at a
|
|
139
|
+
* grazing angle stays sharp to the horizon instead of smearing into the
|
|
140
|
+
* mip level its long axis picks. Any number >= 1; rounded down to a power
|
|
141
|
+
* of two, capped at 16 and then at the device's `limits.maxAnisotropy`
|
|
142
|
+
* (1 on a device without the extension - the texture then samples as
|
|
143
|
+
* today, no error). Only meaningful with `mipmap: true` (it filters
|
|
144
|
+
* through the chain), ignored by the `<texture>` display draw like the
|
|
145
|
+
* chain itself. Below 1 or non-finite throws.
|
|
132
146
|
*/
|
|
133
|
-
export type SamplerOptions = { filter?: FilterMode; wrap?: WrapMode; mipmap?: boolean }
|
|
147
|
+
export type SamplerOptions = { filter?: FilterMode; wrap?: WrapMode; mipmap?: boolean; anisotropy?: number }
|
|
134
148
|
/**
|
|
135
149
|
* One `textures` binding value: a texture id, sampled with the texture's
|
|
136
150
|
* own declared state, or `{ id, filter?, wrap? }` to sample that id with a
|
|
@@ -141,7 +155,14 @@ declare module "flux:gpu" {
|
|
|
141
155
|
* not overridable - the chain either exists on the id or it does not.
|
|
142
156
|
*/
|
|
143
157
|
export type TextureBinding = TextureId | { id: TextureId; filter?: FilterMode; wrap?: WrapMode }
|
|
144
|
-
/**
|
|
158
|
+
/**
|
|
159
|
+
* Sampler inputs by uniform name; see {@link TextureBinding}. The uniform's
|
|
160
|
+
* declared type picks what may bind: a `sampler2D` takes any 2D texture,
|
|
161
|
+
* a `samplerCube` a cube map ({@link createCubeTexture}), a
|
|
162
|
+
* `sampler2DShadow` a draw target's depth texture; a mismatch throws. An
|
|
163
|
+
* `undefined` (or `null`) binding is skipped, like a param; an id must be
|
|
164
|
+
* a non-negative integer (a negative or fractional value throws).
|
|
165
|
+
*/
|
|
145
166
|
export type TextureBindings = Record<string, TextureBinding>
|
|
146
167
|
/**
|
|
147
168
|
* A free-form debug name every create accepts (WebGPU's label): surfaced by
|
|
@@ -163,8 +184,45 @@ declare module "flux:gpu" {
|
|
|
163
184
|
* texture as `(v, 0, 0, 1)` - read `.r`; displaying one via `<texture src>`
|
|
164
185
|
* shows that same red-channel reading. Shader/pipeline targets and
|
|
165
186
|
* readbacks stay RGBA8.
|
|
187
|
+
*
|
|
188
|
+
* "r32f" and "rgba32f" are the float data-texture formats, for float
|
|
189
|
+
* payloads a shader fetches - per-sector heights read in a vertex stage,
|
|
190
|
+
* bone matrices at scale, lookup tables wider than 8 bits - without
|
|
191
|
+
* fixed-point encoding into RGBA8 channels. One f32 per channel: data is a
|
|
192
|
+
* Float32Array of width*height (r32f, sampled as `(v, 0, 0, 1)`) or
|
|
193
|
+
* width*height*4 (rgba32f, a vec4 per texel). Upload-and-sample only, and
|
|
194
|
+
* nearest-only - `filter` defaults to "nearest" and "linear", `mipmap` and
|
|
195
|
+
* `anisotropy` throw (float linear filtering is not in core GLES 3.0;
|
|
196
|
+
* fetch exact texels with `texelFetch`). No readback or copy path
|
|
197
|
+
* (readTexture and copyTexture throw: float is not color-renderable in
|
|
198
|
+
* core), and displaying one via `<texture src>` is out of contract - these
|
|
199
|
+
* are shader data, not image content.
|
|
200
|
+
*
|
|
201
|
+
* "rgba16f" is the HDR image format (environment maps, panoramas): the same
|
|
202
|
+
* Float32Array payload (width*height*4), packed to half float on upload,
|
|
203
|
+
* and filterable like a byte format (RGBA16F is texture-filterable in core
|
|
204
|
+
* GLES 3.0), so `filter` defaults to "linear" and `anisotropy` applies.
|
|
205
|
+
* `mipmap: true` needs half float to be color-renderable on the device
|
|
206
|
+
* (`limits.halfFloatRenderable`; an extension almost every device has) and
|
|
207
|
+
* throws where it is not. Sample-only like the 32-bit formats (no
|
|
208
|
+
* readback, copy or display); also a draw target format where half float
|
|
209
|
+
* is renderable ({@link createDrawTarget}, {@link createCubeDrawTarget}:
|
|
210
|
+
* HDR views, probes and bakes).
|
|
211
|
+
*
|
|
212
|
+
* "rgba8-srgb" is "rgba8" whose bytes are sRGB-encoded: a shader sampling
|
|
213
|
+
* it reads linear light (the decode is done in hardware, before filtering,
|
|
214
|
+
* so `filter`, `mipmap` and `anisotropy` are all correct); alpha is not
|
|
215
|
+
* decoded. The color-map format of linear-space lighting - base color and
|
|
216
|
+
* emissive maps - while data maps (normal, roughness, metallic) stay
|
|
217
|
+
* "rgba8". Sample-only: readTexture and copyTexture throw (a readback
|
|
218
|
+
* would decode rather than return the stored bytes) and displaying one
|
|
219
|
+
* via `<texture src>` is out of contract; also a draw target format (the
|
|
220
|
+
* target encodes what a pass writes).
|
|
221
|
+
*
|
|
222
|
+
* Reserved future value of this same vocabulary: "etc2-rgba8" (compressed
|
|
223
|
+
* uploads).
|
|
166
224
|
*/
|
|
167
|
-
export type TextureFormat = "rgba8" | "r8"
|
|
225
|
+
export type TextureFormat = "rgba8" | "rgba8-srgb" | "r8" | "r32f" | "rgba32f" | "rgba16f"
|
|
168
226
|
export type TextureFormatOption = { format?: TextureFormat }
|
|
169
227
|
/**
|
|
170
228
|
* This device's hard ceilings, queried once at startup: process constants.
|
|
@@ -178,6 +236,8 @@ declare module "flux:gpu" {
|
|
|
178
236
|
export let limits: {
|
|
179
237
|
/** Largest width/height of any texture or render target, in pixels (>= 2048). */
|
|
180
238
|
maxTextureSize: number
|
|
239
|
+
/** Largest face edge of a cube map, in pixels (>= 2048); its own GL ceiling. */
|
|
240
|
+
maxCubeMapSize: number
|
|
181
241
|
/**
|
|
182
242
|
* Sampler inputs one pass may bind (>= 16): a target's `textures`
|
|
183
243
|
* entries; on a window shader the runtime-filled `uSource` (and
|
|
@@ -186,26 +246,73 @@ declare module "flux:gpu" {
|
|
|
186
246
|
maxTextureUnits: number
|
|
187
247
|
/** Vertex attributes one pipeline may declare (>= 16). */
|
|
188
248
|
maxVertexAttribs: number
|
|
249
|
+
/**
|
|
250
|
+
* Highest `anisotropy` level the device samples at (a power of two, 1
|
|
251
|
+
* when the device has no anisotropic filtering). A report, not a
|
|
252
|
+
* ceiling: a higher requested level is clamped to it silently, so
|
|
253
|
+
* `anisotropy: limits.maxAnisotropy` is the "as sharp as this device
|
|
254
|
+
* goes" spelling.
|
|
255
|
+
*/
|
|
256
|
+
maxAnisotropy: number
|
|
257
|
+
/**
|
|
258
|
+
* vec4 uniform slots a vertex stage may declare (>= 256): a mat4 costs
|
|
259
|
+
* 4, a `mat4 u[N]` array 4N. What bounds a bone-palette array - a
|
|
260
|
+
* dynamically indexed array keeps every declared element active, so
|
|
261
|
+
* the declaration itself must fit or the program fails to link.
|
|
262
|
+
*/
|
|
263
|
+
maxVertexUniformVectors: number
|
|
264
|
+
/**
|
|
265
|
+
* Whether half float is color-renderable on this device (the
|
|
266
|
+
* EXT_color_buffer_half_float extension, which almost every device
|
|
267
|
+
* has): what a generated mip chain needs, so `mipmap: true` on an
|
|
268
|
+
* "rgba16f" texture throws where this is false.
|
|
269
|
+
*/
|
|
270
|
+
halfFloatRenderable: boolean
|
|
189
271
|
}
|
|
190
272
|
/**
|
|
191
|
-
* Create an immutable texture from a pixel buffer
|
|
192
|
-
*
|
|
193
|
-
*
|
|
273
|
+
* Create an immutable texture from a pixel buffer holding exactly one
|
|
274
|
+
* frame at the declared format's size. The view type must match the
|
|
275
|
+
* format: byte formats ("rgba8", "rgba8-srgb", "r8") take a Uint8Array,
|
|
276
|
+
* float formats ("r32f", "rgba32f", "rgba16f") a Float32Array. Returns
|
|
277
|
+
* the texture id.
|
|
278
|
+
*/
|
|
279
|
+
export function createTexture(data: Uint8Array | Float32Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
280
|
+
/**
|
|
281
|
+
* Create a cube map from six square faces in GL order (+X, -X, +Y, -Y,
|
|
282
|
+
* +Z, -Z), each exactly one `size` x `size` frame at the declared format
|
|
283
|
+
* (view type per format as in {@link createTexture}) - or from an explicit
|
|
284
|
+
* mip chain: an array of such six-face arrays, level 0 first, sizes
|
|
285
|
+
* halving down to the 1x1 level (the FULL chain; a shorter one throws),
|
|
286
|
+
* uploaded as given instead of generated, so it carries prefiltered
|
|
287
|
+
* levels and needs no color-renderable format (an "rgba16f" chain works
|
|
288
|
+
* on every device). Explicit levels imply `mipmap: true` (`mipmap: false`
|
|
289
|
+
* with them throws). Returns an ordinary texture id that only a `uniform
|
|
290
|
+
* samplerCube` binding consumes - sampled by direction (`texture(uEnv,
|
|
291
|
+
* dir)`, `textureLod` for a level when the chain exists). Sampler-only
|
|
292
|
+
* and create-once: `<texture src>` cannot display it, and uploadTexture,
|
|
293
|
+
* resizeTexture, readTexture and copyTexture throw for it. `wrap` is
|
|
294
|
+
* accepted and has no effect (cube filtering is seamless). A cube map on a
|
|
295
|
+
* `sampler2D`, or a 2D texture on a `samplerCube`, throws at the bind.
|
|
296
|
+
* GL's convention holds: each face is what a lookup in that direction
|
|
297
|
+
* returns (the cube seen from outside), so a face set authored as seen
|
|
298
|
+
* from inside (Three's) is mirrored per image before upload.
|
|
194
299
|
*/
|
|
195
|
-
export function
|
|
300
|
+
export function createCubeTexture(faces: (Uint8Array | Float32Array)[] | (Uint8Array | Float32Array)[][], size: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
196
301
|
/**
|
|
197
302
|
* Create a texture intended to be updated later via {@link uploadTexture}. The
|
|
198
|
-
* seed buffer must hold at least one frame
|
|
199
|
-
*
|
|
200
|
-
*
|
|
303
|
+
* seed buffer must hold at least one frame at the declared format's size
|
|
304
|
+
* and may hold more (uploadTexture selects a frame by offset); like
|
|
305
|
+
* {@link createTexture}, the view type must match the format (Uint8Array
|
|
306
|
+
* for byte formats, Float32Array for float formats).
|
|
201
307
|
*/
|
|
202
|
-
export function createMutableTexture(data: Uint8Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
308
|
+
export function createMutableTexture(data: Uint8Array | Float32Array, width: number, height: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
203
309
|
/**
|
|
204
|
-
* Replace a mutable texture's pixels; the frame size
|
|
205
|
-
*
|
|
206
|
-
*
|
|
310
|
+
* Replace a mutable texture's pixels; the frame size and required view
|
|
311
|
+
* type (Uint8Array for byte formats, Float32Array for float formats)
|
|
312
|
+
* follow the format the id was created with. `data` may hold several
|
|
313
|
+
* frames; `offset` (default 0) selects which frame to upload.
|
|
207
314
|
*/
|
|
208
|
-
export function uploadTexture(id: TextureId, data: Uint8Array, offset?: number): void
|
|
315
|
+
export function uploadTexture(id: TextureId, data: Uint8Array | Float32Array, offset?: number): void
|
|
209
316
|
/**
|
|
210
317
|
* Replace a texture's storage with a new size at the same id (an id-stable
|
|
211
318
|
* resize): `<texture src>` references and shader sampler bindings keep
|
|
@@ -215,7 +322,7 @@ declare module "flux:gpu" {
|
|
|
215
322
|
* state). Render target ids are rejected - resize those with
|
|
216
323
|
* {@link setTargetSize}.
|
|
217
324
|
*/
|
|
218
|
-
export function resizeTexture(id: TextureId, data: Uint8Array, width: number, height: number): void
|
|
325
|
+
export function resizeTexture(id: TextureId, data: Uint8Array | Float32Array, width: number, height: number): void
|
|
219
326
|
/**
|
|
220
327
|
* Destroy a texture (immutable, mutable, or shader). Frame-safe: the id is
|
|
221
328
|
* reclaimed by the runtime once the render tree no longer references it, so
|
|
@@ -277,7 +384,14 @@ declare module "flux:gpu" {
|
|
|
277
384
|
* 300 es`, `precision highp float;`, `uniform vec2 iResolution;`, plus
|
|
278
385
|
* `out vec4 fragColor;` for a fragment stage (the same text
|
|
279
386
|
* {@link createPipelineTexture} injects). Do not combine `header` with your
|
|
280
|
-
* own `#version` line.
|
|
387
|
+
* own `#version` line. The header declares those names rather than only
|
|
388
|
+
* filling them, so a source compiled with `header: true` must not declare
|
|
389
|
+
* them again: `uniform vec3 iResolution;` carried over from a ported shader
|
|
390
|
+
* is the usual mistake and fails as a redefinition (under the header
|
|
391
|
+
* iResolution is vec2; only a complete source may declare it vec3 and get
|
|
392
|
+
* `(w, h, 1.0)`). Everything else - samplers, app-driven uniforms,
|
|
393
|
+
* varyings (no `vUV` here) - is the source's own declaration.
|
|
394
|
+
* Returns a shader (stage) id in its own id space;
|
|
281
395
|
* compile errors throw here, synchronously, at a call site the app chose.
|
|
282
396
|
* Free with {@link destroyShader}.
|
|
283
397
|
*
|
|
@@ -419,6 +533,8 @@ declare module "flux:gpu" {
|
|
|
419
533
|
/** One buffer per instance slot of the pipeline (index = the
|
|
420
534
|
* attributes' `slot`); pass this OR `instanceBuffer`, not both. */
|
|
421
535
|
instanceBuffers?: BufferId[]
|
|
536
|
+
/** Draw the instance records in key order (see {@link InstanceOrder}). */
|
|
537
|
+
instanceOrder?: InstanceOrder
|
|
422
538
|
clearColor?: [number, number, number, number]
|
|
423
539
|
render?: "auto" | "manual"
|
|
424
540
|
loadOp?: "clear" | "load"
|
|
@@ -577,6 +693,90 @@ declare module "flux:gpu" {
|
|
|
577
693
|
instanceBuffer?: BufferId
|
|
578
694
|
instanceBuffers?: BufferId[]
|
|
579
695
|
} & ({} | IndexBinding)
|
|
696
|
+
/**
|
|
697
|
+
* A draw entry's instance ORDER, declared at creation via the
|
|
698
|
+
* `instanceOrder` option: the entry draws the records of its instance
|
|
699
|
+
* buffer in KEY order instead of buffer order, while the writer keeps
|
|
700
|
+
* addressing stable slots - raise and y-sort without record churn, and
|
|
701
|
+
* back-to-front over a large population without per-record JS. (This
|
|
702
|
+
* orders one entry's RECORDS; {@link setDrawOrder} orders a target's
|
|
703
|
+
* ENTRIES - different granularity, unrelated verbs.)
|
|
704
|
+
*
|
|
705
|
+
* The key, one of two modes fixed at creation, both offsets counted in
|
|
706
|
+
* FLOATS into one instance record: `field` reads the single f32 at that
|
|
707
|
+
* offset (a y coordinate, an age, an explicit sort field); `position`
|
|
708
|
+
* reads the vec3 at that offset and keys on dot(position, `direction`) -
|
|
709
|
+
* view depth along a direction you update per camera move with
|
|
710
|
+
* `orderDirection` (see {@link OrderUpdate}). Ascending by default
|
|
711
|
+
* (smallest key draws first); `descending: true` for back-to-front when
|
|
712
|
+
* larger keys are nearer. The sort is stable - equal keys keep slot
|
|
713
|
+
* order, so untouched populations draw exactly as without an order - and
|
|
714
|
+
* NaN keys sort last (ascending).
|
|
715
|
+
*
|
|
716
|
+
* The order materializes when the buffer is PUBLISHED: each
|
|
717
|
+
* {@link beginBufferWrite}/{@link endBufferWrite} publish gathers the
|
|
718
|
+
* records into key order on the way to the GPU (one extra copy of the
|
|
719
|
+
* published bytes; a single-buffer entry retains nothing). A population
|
|
720
|
+
* that republishes each frame re-orders each frame for free; a direction
|
|
721
|
+
* change alone takes effect at the next publish - unless the entry opts
|
|
722
|
+
* into `retain`, where it re-orders immediately from core's retained
|
|
723
|
+
* copy. Because the GPU-side
|
|
724
|
+
* contents are the gathered records, {@link writeBuffer} on an ordered
|
|
725
|
+
* buffer throws (a byte-offset write would land on the wrong records -
|
|
726
|
+
* publish whole record sets through the lease), {@link readBuffer} reads
|
|
727
|
+
* back gathered order, a publish must be a whole number of records, and
|
|
728
|
+
* `instanceCount` below the published record count draws the first N in
|
|
729
|
+
* key order.
|
|
730
|
+
*
|
|
731
|
+
* An entry with SEVERAL instance buffers orders them all under ONE
|
|
732
|
+
* permutation: the key reads from slot 0's records, and every other
|
|
733
|
+
* slot's publishes gather to match, so split records (a core-written
|
|
734
|
+
* pose buffer plus an app-written style buffer) always describe the same
|
|
735
|
+
* draw order. This retains a copy of each slot's records: when the key
|
|
736
|
+
* order changes - including through spatial record sinks, with no
|
|
737
|
+
* publish from the app anywhere - the sibling buffers republish
|
|
738
|
+
* themselves in the same frame. The entry's instance buffers must be
|
|
739
|
+
* pairwise distinct, each ordered by no other entry; a buffer swap
|
|
740
|
+
* ({@link BufferUpdate}) carries the order to the new buffers, every
|
|
741
|
+
* swapped slot at once - republish app-written slots after a swap, the
|
|
742
|
+
* new buffer starts empty.
|
|
743
|
+
*/
|
|
744
|
+
export type InstanceOrder = ({ field: number } | { position: number; direction: [number, number, number] }) & {
|
|
745
|
+
descending?: boolean
|
|
746
|
+
/**
|
|
747
|
+
* Which instance slot's records hold the key (default 0). On a
|
|
748
|
+
* multi-buffer entry the key may live in any one slot - a core-written
|
|
749
|
+
* pose slot keyed by position, or an app-written style slot keyed by
|
|
750
|
+
* an explicit sort field - and every other slot gathers under its
|
|
751
|
+
* permutation.
|
|
752
|
+
*/
|
|
753
|
+
slot?: number
|
|
754
|
+
/**
|
|
755
|
+
* The retained-copy strategy, for write-once populations (a splat
|
|
756
|
+
* cloud: records written once, only the ORDER changes per camera
|
|
757
|
+
* move). Core keeps a CPU copy of each slot's published records, and
|
|
758
|
+
* an `orderDirection` update re-sorts that copy and republishes the
|
|
759
|
+
* entry's buffers core-side, in the same call, with no publish from
|
|
760
|
+
* the app; when the re-sort leaves the order unchanged nothing
|
|
761
|
+
* uploads, so a parked camera costs nothing. The copy is the
|
|
762
|
+
* deliberate cost (the records' size in CPU memory); publishes still
|
|
763
|
+
* go through the lease and update the copy. Position keys only - a
|
|
764
|
+
* field key re-orders when its records republish, so retaining buys
|
|
765
|
+
* nothing there and throws.
|
|
766
|
+
*/
|
|
767
|
+
retain?: boolean
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* The order half of a draw-entry update: `orderDirection` replaces the
|
|
771
|
+
* projected key's direction ({@link InstanceOrder}'s `position` mode) -
|
|
772
|
+
* the per-camera-move update. On a gather entry it stages sort state for
|
|
773
|
+
* the entry's NEXT publish and renders nothing by itself; on a retained
|
|
774
|
+
* entry (`retain: true`) it re-sorts core's copy right away and, when
|
|
775
|
+
* the order changed, republishes the entry's buffers - no publish from
|
|
776
|
+
* the app. On an entry without a position-keyed instance order it
|
|
777
|
+
* throws.
|
|
778
|
+
*/
|
|
779
|
+
export type OrderUpdate = { orderDirection?: [number, number, number] }
|
|
580
780
|
|
|
581
781
|
/**
|
|
582
782
|
* Compile a GLSL ES vertex+fragment pipeline into an offscreen texture of
|
|
@@ -625,6 +825,8 @@ declare module "flux:gpu" {
|
|
|
625
825
|
/** One buffer per instance slot (index = the attributes' `slot`);
|
|
626
826
|
* pass this OR `instanceBuffer`, not both. */
|
|
627
827
|
instanceBuffers?: BufferId[]
|
|
828
|
+
/** Draw the instance records in key order (see {@link InstanceOrder}). */
|
|
829
|
+
instanceOrder?: InstanceOrder
|
|
628
830
|
topology?: Topology
|
|
629
831
|
depth?: boolean
|
|
630
832
|
depthWrite?: boolean
|
|
@@ -667,12 +869,18 @@ declare module "flux:gpu" {
|
|
|
667
869
|
* closes and nothing is published. Always closes the lease, error or not;
|
|
668
870
|
* throws when no write is open or `byteLength` exceeds the buffer size.
|
|
669
871
|
* Pipelines drawing from the buffer re-render, like {@link writeBuffer}.
|
|
872
|
+
* On an ordered instance buffer ({@link InstanceOrder}) the publish is
|
|
873
|
+
* where the order materializes: the records reach the GPU gathered into
|
|
874
|
+
* key order, and `byteLength` must be a whole number of records.
|
|
670
875
|
*/
|
|
671
876
|
export function endBufferWrite(id: BufferId, byteLength?: number): void
|
|
672
877
|
/**
|
|
673
878
|
* Overwrite part of a vertex buffer at `byteOffset` (default 0), within the
|
|
674
879
|
* size it was created with. Pipelines drawing from the buffer re-render
|
|
675
|
-
* with their last-applied params.
|
|
880
|
+
* with their last-applied params. Throws on an ordered instance buffer
|
|
881
|
+
* ({@link InstanceOrder}): its GPU contents are in key order, so a
|
|
882
|
+
* byte-offset write would land on the wrong records - publish whole
|
|
883
|
+
* record sets through {@link beginBufferWrite}/{@link endBufferWrite}.
|
|
676
884
|
*/
|
|
677
885
|
export function writeBuffer(id: BufferId, data: Uint8Array, byteOffset?: number): void
|
|
678
886
|
/**
|
|
@@ -700,7 +908,7 @@ declare module "flux:gpu" {
|
|
|
700
908
|
* buffer and extends the range into it, and a call that throws changes
|
|
701
909
|
* nothing (range and buffers commit together or not at all).
|
|
702
910
|
*/
|
|
703
|
-
export function setDraw(id: TextureId, draw: (DrawRange | IndexRange) & BufferUpdate): void
|
|
911
|
+
export function setDraw(id: TextureId, draw: (DrawRange | IndexRange) & BufferUpdate & OrderUpdate): void
|
|
704
912
|
/**
|
|
705
913
|
* Create a draw target: a render target whose contents are an ordered,
|
|
706
914
|
* mutable LIST of draws - one render clears once, then executes every
|
|
@@ -741,6 +949,17 @@ declare module "flux:gpu" {
|
|
|
741
949
|
* Returns a texture id (display, resize, destroy like any target; entries
|
|
742
950
|
* die with it).
|
|
743
951
|
*
|
|
952
|
+
* `format` is the color storage: "rgba8" (default), "rgba8-srgb" (the
|
|
953
|
+
* target encodes what a pass writes and decodes on sample, like an
|
|
954
|
+
* uploaded sRGB texture) or "rgba16f" (half float: the range a pass
|
|
955
|
+
* writes survives - an HDR view or bake; renderable where
|
|
956
|
+
* `limits.halfFloatRenderable`, throws elsewhere). Other formats throw
|
|
957
|
+
* (not color-renderable). A target of either non-default format is
|
|
958
|
+
* SAMPLER-ONLY: bind it to a `sampler2D`; a `<texture>` leaf cannot
|
|
959
|
+
* display it, {@link readTexture} and {@link copyTexture} refuse it -
|
|
960
|
+
* render it through a pass into an rgba8 target for those. Multisampling
|
|
961
|
+
* and resize work at the format.
|
|
962
|
+
*
|
|
744
963
|
* `into` makes a SUB-TARGET: a draw target that renders into the `width`
|
|
745
964
|
* x `height` rectangle at `x`/`y` (top-left origin, the texture leaf's
|
|
746
965
|
* `srcX`/`srcY` space; default 0) of draw target `into`'s storage instead
|
|
@@ -752,8 +971,8 @@ declare module "flux:gpu" {
|
|
|
752
971
|
* cost one pass instead of N. The returned id is not a texture: sample,
|
|
753
972
|
* display (`<d-texture src={parent} srcX srcY srcW srcH>`), read back and
|
|
754
973
|
* copy the PARENT; `depthTexture(parent)` is the tile's depth too. Depth,
|
|
755
|
-
* `samples`, `render` and `loadOp` are the parent's (passing
|
|
756
|
-
* throws), `clearColor` is the tile's own. A rectangle partly outside the
|
|
974
|
+
* `samples`, `format`, `render` and `loadOp` are the parent's (passing
|
|
975
|
+
* them throws), `clearColor` is the tile's own. A rectangle partly outside the
|
|
757
976
|
* parent is clipped; {@link setTargetRect} moves it. Tiles do not nest.
|
|
758
977
|
* Destroying the parent destroys its tiles.
|
|
759
978
|
*/
|
|
@@ -763,6 +982,7 @@ declare module "flux:gpu" {
|
|
|
763
982
|
params?: ShaderParams | null,
|
|
764
983
|
opts?: {
|
|
765
984
|
depth?: boolean | "texture"
|
|
985
|
+
format?: "rgba8" | "rgba8-srgb" | "rgba16f"
|
|
766
986
|
textures?: TextureBindings
|
|
767
987
|
clearColor?: [number, number, number, number]
|
|
768
988
|
render?: "auto" | "manual"
|
|
@@ -774,6 +994,46 @@ declare module "flux:gpu" {
|
|
|
774
994
|
} & SamplerOptions &
|
|
775
995
|
LabelOption,
|
|
776
996
|
): TextureId
|
|
997
|
+
/**
|
|
998
|
+
* Create a cube draw target: a draw target whose output is a `size` x
|
|
999
|
+
* `size` CUBE MAP, rendered one face at a time with `renderTarget(id,
|
|
1000
|
+
* face)` (GL order: 0 +X, 1 -X, 2 +Y, 3 -Y, 4 +Z, 5 -Z) - the
|
|
1001
|
+
* reflection-probe and sky-bake primitive. One entry list, shared params
|
|
1002
|
+
* and bindings as on {@link createDrawTarget}; the app writes the face's
|
|
1003
|
+
* camera into the shared params and renders, six times. Manual by
|
|
1004
|
+
* contract (`render` defaults to "manual"; "auto" throws), one private
|
|
1005
|
+
* depth renderbuffer for all faces (`depth: true`; "texture" throws), no
|
|
1006
|
+
* `samples`, no `into`; `format` rgba8, rgba8-srgb or rgba16f. `mipmap:
|
|
1007
|
+
* true` allocates the whole chain: a face
|
|
1008
|
+
* render without a level regenerates it from level 0 (as every content
|
|
1009
|
+
* write on a mipmapped target), a render into an explicit `level`
|
|
1010
|
+
* writes that level alone at its own edge - how a prefiltered
|
|
1011
|
+
* environment chain is rendered level by level. The id is a cube map to
|
|
1012
|
+
* every consumer: bind it to a `samplerCube`, never display, read back,
|
|
1013
|
+
* copy or resize it. Each face pass inverts the front-face rule: a GL cube
|
|
1014
|
+
* face is seen from outside, the x mirror of a 2D target's image, so
|
|
1015
|
+
* render every face through an x-mirrored projection and the pipelines'
|
|
1016
|
+
* cull modes keep their meaning (derivative-built tangent frames flip
|
|
1017
|
+
* with the mirror - a known artifact of mirrored views).
|
|
1018
|
+
*/
|
|
1019
|
+
export function createCubeDrawTarget(
|
|
1020
|
+
size: number,
|
|
1021
|
+
params?: ShaderParams | null,
|
|
1022
|
+
opts?: {
|
|
1023
|
+
depth?: boolean
|
|
1024
|
+
/** rgba8 (default); rgba8-srgb: the cube encodes what a pass writes
|
|
1025
|
+
* and decodes on sample, like an uploaded sRGB cube; rgba16f: half
|
|
1026
|
+
* float, the range a pass writes survives (HDR probes), renderable
|
|
1027
|
+
* where `limits.halfFloatRenderable` (throws elsewhere - fall back to
|
|
1028
|
+
* rgba8). Other formats throw: not color-renderable. */
|
|
1029
|
+
format?: "rgba8" | "rgba8-srgb" | "rgba16f"
|
|
1030
|
+
textures?: TextureBindings
|
|
1031
|
+
clearColor?: [number, number, number, number]
|
|
1032
|
+
render?: "manual"
|
|
1033
|
+
loadOp?: "clear" | "load"
|
|
1034
|
+
} & SamplerOptions &
|
|
1035
|
+
LabelOption,
|
|
1036
|
+
): TextureId
|
|
777
1037
|
/**
|
|
778
1038
|
* The depth texture of a draw target created with `depth: "texture"`: a
|
|
779
1039
|
* texture id of its own, stable for the target's life (a
|
|
@@ -786,9 +1046,15 @@ declare module "flux:gpu" {
|
|
|
786
1046
|
* depth throws. SAMPLER-ONLY: it is not an upload texture and not a
|
|
787
1047
|
* readback source (`readTexture`/`copyTexture` throw - render it through a
|
|
788
1048
|
* pass to read it), its sampling is fixed at `nearest`/`clamp` (a depth
|
|
789
|
-
* texture is only complete at nearest without a comparison mode
|
|
790
|
-
*
|
|
791
|
-
*
|
|
1049
|
+
* texture is only complete at nearest without a comparison mode), and it
|
|
1050
|
+
* dies with its target (`destroyTexture` on it throws). A pipeline
|
|
1051
|
+
* program may instead declare the uniform as `sampler2DShadow`: the
|
|
1052
|
+
* engine then binds a COMPARISON sampler for it - `texture(map,
|
|
1053
|
+
* vec3(uv, ref))` returns the hardware LEQUAL compare of `ref` against
|
|
1054
|
+
* the map, LINEAR-weighted over the 2x2 footprint (one-tap PCF). Only a
|
|
1055
|
+
* depth texture may back a sampler2DShadow uniform (anything else is
|
|
1056
|
+
* refused at bind); a plain `sampler2D` keeps the raw nearest reads.
|
|
1057
|
+
* Displaying it via `<texture src>`
|
|
792
1058
|
* shows the depth in the red channel. Throws for a target without texture
|
|
793
1059
|
* depth.
|
|
794
1060
|
*/
|
|
@@ -833,6 +1099,8 @@ declare module "flux:gpu" {
|
|
|
833
1099
|
/** One buffer per instance slot of the pipeline (index = the
|
|
834
1100
|
* attributes' `slot`); pass this OR `instanceBuffer`, not both. */
|
|
835
1101
|
instanceBuffers?: BufferId[]
|
|
1102
|
+
/** Draw the instance records in key order (see {@link InstanceOrder}). */
|
|
1103
|
+
instanceOrder?: InstanceOrder
|
|
836
1104
|
before?: DrawId
|
|
837
1105
|
} & (DrawRange | (IndexBinding & IndexRange)),
|
|
838
1106
|
): DrawId
|
|
@@ -929,7 +1197,7 @@ declare module "flux:gpu" {
|
|
|
929
1197
|
* single entry, same partial merge, bounds validation, and vocabulary
|
|
930
1198
|
* rule (an indexed entry speaks {@link IndexRange}).
|
|
931
1199
|
*/
|
|
932
|
-
export function setDrawRange(target: TextureId, draw: DrawId, update: DrawRange | IndexRange): void
|
|
1200
|
+
export function setDrawRange(target: TextureId, draw: DrawId, update: (DrawRange | IndexRange) & OrderUpdate): void
|
|
933
1201
|
/**
|
|
934
1202
|
* Swap one draw entry's buffers: the {@link BufferUpdate} half of
|
|
935
1203
|
* {@link setDraw} addressed to a single entry, same replace-only rule and
|
|
@@ -945,7 +1213,9 @@ declare module "flux:gpu" {
|
|
|
945
1213
|
* back-to-front, and re-issue the order when the camera moves. Entry
|
|
946
1214
|
* state (params, textures, ranges) rides along untouched; ids are
|
|
947
1215
|
* unaffected. Like every draw-list write it re-renders an auto target
|
|
948
|
-
* once at the next flush, and folds silently on a manual one.
|
|
1216
|
+
* once at the next flush, and folds silently on a manual one. (This
|
|
1217
|
+
* orders a target's ENTRIES; ordering the instance RECORDS within one
|
|
1218
|
+
* entry is {@link InstanceOrder}.)
|
|
949
1219
|
*/
|
|
950
1220
|
export function setDrawOrder(target: TextureId, order: DrawId[]): void
|
|
951
1221
|
/**
|
|
@@ -960,9 +1230,16 @@ declare module "flux:gpu" {
|
|
|
960
1230
|
* the one counting. Ping-pong feedback is two manual targets sampling
|
|
961
1231
|
* each other, stepped alternately from `onFrame`; binding a target to
|
|
962
1232
|
* ITSELF still throws (same-pass GL feedback, undefined pixels regardless
|
|
963
|
-
* of who schedules it).
|
|
1233
|
+
* of who schedules it). `face` (0..5) names the face of a cube draw
|
|
1234
|
+
* target ({@link createCubeDrawTarget}) and is required there; a 2D
|
|
1235
|
+
* target takes none. `level` names the mip level of that face to render
|
|
1236
|
+
* into, on a `mipmap: true` cube target (0 up to the chain's last; the
|
|
1237
|
+
* pass runs at the level's edge and regenerates nothing); without it the
|
|
1238
|
+
* face's level 0 renders and the chain regenerates. Throws for a level
|
|
1239
|
+
* on a 2D target or beyond the chain (a cube target without `mipmap`
|
|
1240
|
+
* has level 0 only).
|
|
964
1241
|
*/
|
|
965
|
-
export function renderTarget(id: TextureId): void
|
|
1242
|
+
export function renderTarget(id: TextureId, face?: number, level?: number): void
|
|
966
1243
|
/**
|
|
967
1244
|
* Overwrite a `render: "manual"` target with another texture's current
|
|
968
1245
|
* pixels, GPU-side: the seed/history analog of {@link uploadTexture}
|
|
@@ -974,7 +1251,9 @@ declare module "flux:gpu" {
|
|
|
974
1251
|
* copy sees the copy, and targets sampling `dst` update afterwards.
|
|
975
1252
|
* Throws if either id is unknown, `dst` is not a manual target (the
|
|
976
1253
|
* runtime owns those contents), or `src === dst`. `src` may be any
|
|
977
|
-
* texture: uploaded, mutable, a camera frame, or another target's output
|
|
1254
|
+
* texture: uploaded, mutable, a camera frame, or another target's output -
|
|
1255
|
+
* except a float-format texture (the copy would quantize its values to the
|
|
1256
|
+
* target's rgba8; float textures are sample-only, so it throws).
|
|
978
1257
|
*/
|
|
979
1258
|
export function copyTexture(src: TextureId, dst: TextureId): void
|
|
980
1259
|
/**
|
|
@@ -1013,7 +1292,8 @@ declare module "flux:gpu" {
|
|
|
1013
1292
|
* Read back a registered texture's current pixels as RGBA8 (tightly packed,
|
|
1014
1293
|
* top-to-bottom rows), for any texture id whatever created it (createTexture,
|
|
1015
1294
|
* createShaderTexture, a render target). Synchronous. Throws if the id is
|
|
1016
|
-
* unknown
|
|
1295
|
+
* unknown, or names a float-format texture (upload-and-sample only: float
|
|
1296
|
+
* is not color-renderable in core GLES 3.0, so no readback path exists).
|
|
1017
1297
|
*/
|
|
1018
1298
|
export function readTexture(id: TextureId): { width: number; height: number; data: Uint8Array }
|
|
1019
1299
|
}
|
package/gui/rendertree.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ declare module "flux:rendertree" {
|
|
|
15
15
|
lineHeight?: number
|
|
16
16
|
/** measureText only. */
|
|
17
17
|
maxLines?: number
|
|
18
|
-
/** prepareText only: also report each unit's {@link TextUnit.carets}. */
|
|
18
|
+
/** prepareText only: also report each unit's {@link TextUnit.carets} (kerned per-glyph positions). */
|
|
19
19
|
carets?: boolean
|
|
20
20
|
/**
|
|
21
21
|
* prepareText only: styled ranges over the text, in JS string offsets,
|
|
@@ -65,7 +65,11 @@ declare module "flux:rendertree" {
|
|
|
65
65
|
* With `carets`: the caret positions inside the unit, one per grapheme
|
|
66
66
|
* cluster boundary from its start (`offset` = start, x 0) to the end of
|
|
67
67
|
* its shaped text (before any break characters), in order. `offset` is
|
|
68
|
-
* into the prepared text, `x` from the unit's pen position.
|
|
68
|
+
* into the prepared text, `x` from the unit's pen position. These are
|
|
69
|
+
* the per-glyph x positions of the shaping that is drawn, kerning
|
|
70
|
+
* included: for placing or animating single glyphs, use them rather
|
|
71
|
+
* than measuring characters one at a time with measureText, which sees
|
|
72
|
+
* no neighbors and drifts on every kerning pair.
|
|
69
73
|
*/
|
|
70
74
|
carets?: { offset: number, x: number }[]
|
|
71
75
|
}
|
|
@@ -165,8 +169,11 @@ declare module "flux:rendertree" {
|
|
|
165
169
|
export function measureText(text: string, options?: MeasureTextOptions): { width: number, height: number }
|
|
166
170
|
/**
|
|
167
171
|
* Segment `text` into wrap units and shape each in the given font (through
|
|
168
|
-
* the shared word cache
|
|
169
|
-
*
|
|
172
|
+
* the shared word cache, so a text already drawn or prepared costs no
|
|
173
|
+
* shaping), for app-side line breaking - text into a shape, around an
|
|
174
|
+
* obstacle, across columns, fitted by size, per glyph; see layoutNextLine
|
|
175
|
+
* in @solidrt/core. `runs` restyle ranges, `carets` adds glyph positions;
|
|
176
|
+
* `maxLines` is ignored.
|
|
170
177
|
*/
|
|
171
178
|
export function prepareText(text: string, options?: MeasureTextOptions): PreparedText
|
|
172
179
|
/**
|
|
@@ -182,6 +189,15 @@ declare module "flux:rendertree" {
|
|
|
182
189
|
* semantics), for comparing against pointer event coordinates.
|
|
183
190
|
*/
|
|
184
191
|
export function getBoundingBoxViewport(id: number): { x: number, y: number, width: number, height: number } | null
|
|
192
|
+
/**
|
|
193
|
+
* The node's laid-out box in its parent's frame: layout output before any
|
|
194
|
+
* transform composes (DOM offsetLeft/offsetTop/offsetWidth/offsetHeight
|
|
195
|
+
* semantics). The untransformed companion to getBoundingBox: pointer events
|
|
196
|
+
* report local coordinates in this box's units, whatever designSize fits or
|
|
197
|
+
* transforms sit on the ancestor chain. Null for detached nodes and before
|
|
198
|
+
* the first layout.
|
|
199
|
+
*/
|
|
200
|
+
export function getLayoutBox(id: number): { x: number, y: number, width: number, height: number } | null
|
|
185
201
|
/**
|
|
186
202
|
* The texture id of a snapshot repaint boundary's retained rasterization
|
|
187
203
|
* (its subtree's pixels at display scale, premultiplied, top-left origin,
|
package/gui/spatial.d.ts
CHANGED
|
@@ -97,16 +97,17 @@ declare module "flux:spatial" {
|
|
|
97
97
|
|
|
98
98
|
export type ShapeId = number & { readonly __spatialShape: unique symbol }
|
|
99
99
|
|
|
100
|
-
/** One hit of raycast(), nearest first. `face`/`uv
|
|
101
|
-
*
|
|
102
|
-
* bounds but no shape
|
|
100
|
+
/** One hit of raycast(), nearest first. `face`/`uv` are present for
|
|
101
|
+
* nodes with a shape (uv only when the shape has UVs); a node with
|
|
102
|
+
* bounds but no shape is its local box's twelve triangles, so its hit
|
|
103
|
+
* carries the struck face's normal and no face/uv. */
|
|
103
104
|
export type Hit = {
|
|
104
105
|
node: NodeId
|
|
105
106
|
/** World units along the normalized ray. */
|
|
106
107
|
distance: number
|
|
107
108
|
point: [number, number, number]
|
|
108
109
|
/** World-space geometric normal, facing the ray. */
|
|
109
|
-
normal
|
|
110
|
+
normal: [number, number, number]
|
|
110
111
|
/** Triangle index into the shape's index list. */
|
|
111
112
|
face?: number
|
|
112
113
|
uv?: [number, number]
|
|
@@ -116,6 +117,26 @@ declare module "flux:spatial" {
|
|
|
116
117
|
* maxY, maxZ]. With one the node is in the picking index: its world box
|
|
117
118
|
* follows the flush; hidden nodes stay in and are skipped at query time. */
|
|
118
119
|
export function setBounds(node: NodeId, bounds: Float32Array | null): void
|
|
120
|
+
/**
|
|
121
|
+
* The clip volume gating every draw sink on `target`: its view-projection
|
|
122
|
+
* (Float32Array of 16, column-major), or null to lift it. An entry whose
|
|
123
|
+
* node box (grown by its cull margin) falls wholly outside reads
|
|
124
|
+
* instanceCount 0 like a hidden node, and comes back with a fresh params
|
|
125
|
+
* write. Nodes without a box, or with culling off, are never gated. Read
|
|
126
|
+
* at flush: set it before the flush that should see it.
|
|
127
|
+
*/
|
|
128
|
+
export function setFrustum(target: TextureId, viewProj: Float32Array | null): void
|
|
129
|
+
/** Whether frustums gate the node's draw sinks (default true), and the
|
|
130
|
+
* world-unit margin the test grows its box by (default 0). */
|
|
131
|
+
export function setCull(node: NodeId, enabled: boolean, margin: number): void
|
|
132
|
+
/** A local box for culling ONLY - the node stays out of the picking
|
|
133
|
+
* index (setBounds is the indexed one). null falls back to its bounds. */
|
|
134
|
+
export function setCullBounds(node: NodeId, bounds: Float32Array | null): void
|
|
135
|
+
/** Cull the node by the union of these nodes' world boxes instead of
|
|
136
|
+
* its own (a skinned mesh by its joints): an empty list restores its
|
|
137
|
+
* own. Members without a box contribute nothing; with none at all the
|
|
138
|
+
* node is not culled. */
|
|
139
|
+
export function setCullGroup(node: NodeId, members: NodeId[]): void
|
|
119
140
|
/**
|
|
120
141
|
* Triangle data for the picking narrowphase, one copy shared by every
|
|
121
142
|
* node that references it: positions read from an interleaved vertex
|
|
@@ -131,15 +152,48 @@ declare module "flux:spatial" {
|
|
|
131
152
|
* direction need not be normalized; distances are world units. Reads
|
|
132
153
|
* the index as of the last flush. */
|
|
133
154
|
export function raycast(origin: Float32Array, direction: Float32Array): Hit[]
|
|
155
|
+
/** A query volume's layout for overlap/sweep: "capsule" is
|
|
156
|
+
* 7 floats (a, b, radius; a sphere when a == b), "box" is 10 (center,
|
|
157
|
+
* half extents, unit quaternion xyzw). */
|
|
158
|
+
export type VolumeKind = "capsule" | "box"
|
|
159
|
+
/** One node overlap() touches: its deepest contact - the point on
|
|
160
|
+
* the node's surface, the unit direction out of it, and the depth along
|
|
161
|
+
* that direction that clears the contact. */
|
|
162
|
+
export type Overlap = {
|
|
163
|
+
node: NodeId
|
|
164
|
+
point: [number, number, number]
|
|
165
|
+
normal: [number, number, number]
|
|
166
|
+
depth: number
|
|
167
|
+
}
|
|
168
|
+
/** One node sweep() touches: `time` is the fraction of the motion
|
|
169
|
+
* at first touch, `point` the touch point on the node's surface,
|
|
170
|
+
* `normal` the unit normal there facing the volume. */
|
|
171
|
+
export type Impact = {
|
|
172
|
+
node: NodeId
|
|
173
|
+
time: number
|
|
174
|
+
point: [number, number, number]
|
|
175
|
+
normal: [number, number, number]
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Every shown node with bounds the volume touches, each with its
|
|
179
|
+
* deepest contact. A node with a shape is tested per triangle in world
|
|
180
|
+
* space, so it holds under any transform; a node without one is its
|
|
181
|
+
* local box's twelve triangles (a flat rect is four of them, so a
|
|
182
|
+
* rotated sprite tests exactly - the 2d marquee's box query is a "box"
|
|
183
|
+
* volume). Surfaces, not solids: a volume wholly inside a closed mesh
|
|
184
|
+
* with no triangle in reach touches nothing. Unordered; reads the index
|
|
185
|
+
* as of the last flush, like raycast.
|
|
186
|
+
*/
|
|
187
|
+
export function overlap(kind: VolumeKind, volume: Float32Array): Overlap[]
|
|
134
188
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
189
|
+
* The volume moved by `motion` (a Float32Array of 3): every shown node
|
|
190
|
+
* with bounds it touches on the way, at its first touch, earliest
|
|
191
|
+
* first. A node already in contact reports time 0 while the motion
|
|
192
|
+
* closes in, and nothing while it leaves or slides along the contact;
|
|
193
|
+
* a zero motion touches nothing. Same testing and index contract as
|
|
194
|
+
* overlap.
|
|
141
195
|
*/
|
|
142
|
-
export function
|
|
196
|
+
export function sweep(kind: VolumeKind, volume: Float32Array, motion: Float32Array): Impact[]
|
|
143
197
|
|
|
144
198
|
/**
|
|
145
199
|
* Route the world DIRECTION of the node's local `vector` (a
|
|
@@ -150,14 +204,46 @@ declare module "flux:spatial" {
|
|
|
150
204
|
* zeros. Every sink naming the same param shares one array (`len` must
|
|
151
205
|
* agree); what the slots mean - light directions, an emitter axis - is
|
|
152
206
|
* the caller's business, packed alongside its own non-spatial params.
|
|
153
|
-
* One slot sink per target,
|
|
154
|
-
* replaces that sink (the abandoned slot zeroes)
|
|
207
|
+
* One slot sink per (target, name): rebinding the same param on the
|
|
208
|
+
* same target replaces that sink (the abandoned slot zeroes); another
|
|
209
|
+
* param or another target adds one, so a node may feed several arrays
|
|
210
|
+
* of one target (a spot light: its direction and its position).
|
|
155
211
|
*/
|
|
156
212
|
export function bindDirectionSlot(node: NodeId, target: TextureId, name: string, len: number, index: number, vector: Float32Array): void
|
|
157
|
-
/**
|
|
213
|
+
/**
|
|
214
|
+
* Route the node's world POSITION into vec3 slot `index` of the
|
|
215
|
+
* `len`-float shared array param `name` on a draw target -
|
|
216
|
+
* bindDirectionSlot's translation sibling, with the same sharing,
|
|
217
|
+
* zeroing and per-(target, name) replacement rules. What a positional
|
|
218
|
+
* light's slot follows.
|
|
219
|
+
*/
|
|
220
|
+
export function bindPositionSlot(node: NodeId, target: TextureId, name: string, len: number, index: number): void
|
|
221
|
+
/** Remove the node's slot sinks on `target`, or every slot sink without
|
|
158
222
|
* one (the abandoned slots zero at the next flush). */
|
|
159
223
|
export function unbindSlot(node: NodeId, target?: TextureId): void
|
|
160
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Route the node's world matrix, post-multiplied by the constant `post`
|
|
227
|
+
* (a Float32Array of 16, column-major), into row `row` of a float
|
|
228
|
+
* texture: the flush writes the 16 floats as the row's four rgba32f
|
|
229
|
+
* texels - the matrix-palette channel a vertex shader `texelFetch`es
|
|
230
|
+
* (a skin binds each joint node with `post` its inverse bind). Writes
|
|
231
|
+
* batch: however many bound nodes moved, each flush uploads at most one
|
|
232
|
+
* whole palette per texture. With `anchor` - one node shared by every
|
|
233
|
+
* bind on the texture, and an ANCESTOR of every bound node - rows are
|
|
234
|
+
* anchor-local (`inverse(anchorWorld) * world * post`), so a model
|
|
235
|
+
* root's placement stays out of its own palette. Rows keep updating
|
|
236
|
+
* while nodes are hidden (visibility is the drawing entry's business),
|
|
237
|
+
* and an unbound or destroyed node's row keeps its last value. Validated
|
|
238
|
+
* at bind time: the texture must be an uploadable rgba32f texture 4
|
|
239
|
+
* texels wide with `row` inside it. Rebinding the same texture replaces
|
|
240
|
+
* the node's slot there; another texture adds one.
|
|
241
|
+
*/
|
|
242
|
+
export function bindTextureSlot(node: NodeId, texture: TextureId, row: number, post: Float32Array, anchor?: NodeId): void
|
|
243
|
+
/** Remove the node's texture slot on `texture`, or every texture slot
|
|
244
|
+
* without one (abandoned rows keep their last value). */
|
|
245
|
+
export function unbindTextureSlot(node: NodeId, texture?: TextureId): void
|
|
246
|
+
|
|
161
247
|
/**
|
|
162
248
|
* Route the node's world pose to record slot `index` of vertex buffer
|
|
163
249
|
* `buffer` used as an instance buffer: the flush writes the 5 floats
|
|
@@ -172,7 +258,23 @@ declare module "flux:spatial" {
|
|
|
172
258
|
* Rebinding replaces the node's record sink; the abandoned slot zeroes.
|
|
173
259
|
*/
|
|
174
260
|
export function bindPoseRecord(node: NodeId, buffer: BufferId, index: number): void
|
|
175
|
-
/**
|
|
261
|
+
/**
|
|
262
|
+
* Route the node's world matrix to record slot `index` of vertex buffer
|
|
263
|
+
* `buffer` as 16 floats (one column-major mat4) at float offset
|
|
264
|
+
* index * 16: the per-instance model matrix a vertex stage reads as
|
|
265
|
+
* four vec4 attributes. With `anchor` (an ANCESTOR of the node, one per
|
|
266
|
+
* buffer - every bind on the buffer names the same one) the record is
|
|
267
|
+
* the matrix RELATIVE to it, `inverse(anchorWorld) * world`, so a mesh's
|
|
268
|
+
* instances stay in the mesh's own space and its uModel still places
|
|
269
|
+
* the whole population (`uModel * instanceMatrix`); without one it is
|
|
270
|
+
* the plain world matrix. Batching, growth (retargetRecords) and
|
|
271
|
+
* rebinding as bindPoseRecord; a hidden, unbound or destroyed node's
|
|
272
|
+
* slot becomes a zero-scale matrix (w stays 1), so the instance
|
|
273
|
+
* collapses to a point and draws nothing. Every sink on one buffer
|
|
274
|
+
* shares one projection.
|
|
275
|
+
*/
|
|
276
|
+
export function bindMatrixRecord(node: NodeId, buffer: BufferId, index: number, anchor?: NodeId): void
|
|
277
|
+
/** Remove the node's record sink (its slot hides at the next flush). */
|
|
176
278
|
export function unbindRecord(node: NodeId): void
|
|
177
279
|
/**
|
|
178
280
|
* Move every record sink on buffer `old` to buffer `new`, slot indices
|
|
@@ -185,4 +287,83 @@ declare module "flux:spatial" {
|
|
|
185
287
|
* when `new` already carries record sinks.
|
|
186
288
|
*/
|
|
187
289
|
export function retargetRecords(old: BufferId, next: BufferId): void
|
|
290
|
+
|
|
291
|
+
/** A registered clip's handle (createClip). */
|
|
292
|
+
export type ClipId = number
|
|
293
|
+
/** A playing clip instance's handle (createPlayer). */
|
|
294
|
+
export type PlayerId = number
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Register a baked animation clip: `duration` in seconds, `meta` four
|
|
298
|
+
* words per channel - [targetSlot, path (0 position, 1 rotation,
|
|
299
|
+
* 2 scale), interpolation (0 step, 1 linear, 2 cubic), keyCount] - and
|
|
300
|
+
* `times`/`values` every channel's key arrays concatenated in meta
|
|
301
|
+
* order (3 floats per key, 4 for rotation; cubic stores three elements
|
|
302
|
+
* per key: in-tangent, value, out-tangent, tangents per second - the
|
|
303
|
+
* glTF CUBICSPLINE layout). A clip is shared data: `targetSlot`
|
|
304
|
+
* indexes the target table each PLAYER supplies, so one clip drives
|
|
305
|
+
* any number of instances, and retargeting is a different table.
|
|
306
|
+
*/
|
|
307
|
+
export function createClip(duration: number, meta: Uint32Array, times: Float32Array, values: Float32Array): ClipId
|
|
308
|
+
/** Free a clip. Players still on it drop at their next advance (a
|
|
309
|
+
* "dropped" spatialClipEnd). */
|
|
310
|
+
export function destroyClip(clip: ClipId): void
|
|
311
|
+
/**
|
|
312
|
+
* Start playing `clip`: `targets[slot]` is the node each channel
|
|
313
|
+
* animates - every target must be a live scene node (throws
|
|
314
|
+
* otherwise). Players advance on the frame clock BEFORE each frame's
|
|
315
|
+
* JS, sample and weight-blend every active player per (node, path) -
|
|
316
|
+
* two players on one node crossfade - and write the blended TRS into
|
|
317
|
+
* the arena, so frame handlers read and may overwrite freshly posed
|
|
318
|
+
* nodes (last write wins) and the frame's flush publishes the result.
|
|
319
|
+
* `speed` scales clip time (1 = as authored); `loop` wraps, else the
|
|
320
|
+
* player holds its final pose and reports once; `weight`/`fade` start
|
|
321
|
+
* the crossfade state (weight 0..1, fade = weight change per second -
|
|
322
|
+
* positive fades in, negative out; past 0 the player is removed).
|
|
323
|
+
* When a player finishes or is removed without finishing (faded out,
|
|
324
|
+
* clip or target destroyed), the "spatialClipEnd" engine event
|
|
325
|
+
* (srt:events) fires with payload { player, reason: "finished" |
|
|
326
|
+
* "dropped" }, before the same frame's handlers.
|
|
327
|
+
*/
|
|
328
|
+
export function createPlayer(clip: ClipId, targets: NodeId[], speed: number, loop: boolean, weight: number, fade: number): PlayerId
|
|
329
|
+
/** Write the given fields of a player - the O(changes) crossfade
|
|
330
|
+
* channel. Setting `time` (seconds) also re-arms a finished player's
|
|
331
|
+
* end report. Throws on an id that already dropped. */
|
|
332
|
+
export function setPlayer(player: PlayerId, update: { weight?: number; fade?: number; speed?: number; time?: number }): void
|
|
333
|
+
/**
|
|
334
|
+
* Bind root motion to a player: `channel` of `clip` (a position channel
|
|
335
|
+
* - normally the ROOT track of the authored clip, while the player
|
|
336
|
+
* plays an in-place variant that holds the root still) is sampled at
|
|
337
|
+
* the player's time each advance, and its travel since the previous
|
|
338
|
+
* advance (continuous across a loop wrap, weighted by the player's
|
|
339
|
+
* weight) is reported as one "spatialRootMotion" engine event
|
|
340
|
+
* (srt:events) per advance, payload { player, x, y, z, yaw }, before
|
|
341
|
+
* the frame's handlers. The translation is given in the root's CURRENT
|
|
342
|
+
* facing (the clip's own turn so far undone), i.e. the character's
|
|
343
|
+
* local frame. `rotation`, a rotation channel of the same clip (the
|
|
344
|
+
* root's), adds the yaw half: the twist about `up`, in radians (zero
|
|
345
|
+
* without it). With an `anchor` the translation, rotated by the
|
|
346
|
+
* anchor's own rotation, is added to the anchor's position and the yaw
|
|
347
|
+
* turns the anchor about `up` in its own frame - Unity's
|
|
348
|
+
* applyRootMotion; without one it is Godot's root_motion_track, the
|
|
349
|
+
* app's to apply. `opts.up` is the root's parent-space up axis (default
|
|
350
|
+
* [0, 1, 0]); `opts.vertical: false` drops the component along it from
|
|
351
|
+
* the delta (the height stays in the pose). Binding primes at the
|
|
352
|
+
* player's current time, so the next advance already delivers. A
|
|
353
|
+
* second call rebinds. Throws on a dropped player, a wrong channel
|
|
354
|
+
* kind, a zero up axis, or a dead anchor.
|
|
355
|
+
*/
|
|
356
|
+
export function bindRootMotion(player: PlayerId, clip: ClipId, channel: number, rotation?: number, anchor?: NodeId, opts?: { up?: [number, number, number]; vertical?: boolean }): void
|
|
357
|
+
/** Remove a player at once, holding whatever pose it last wrote (no
|
|
358
|
+
* event; stop-with-fade is a setPlayer fade write instead). A dropped
|
|
359
|
+
* id is fine. */
|
|
360
|
+
export function destroyPlayer(player: PlayerId): void
|
|
361
|
+
/**
|
|
362
|
+
* Fill `out` (a Float32Array of 10) with the node's CURRENT local
|
|
363
|
+
* transform - position xyz, quaternion xyzw, scale xyz - as the arena
|
|
364
|
+
* holds it, players' writes included. The pose read for root-motion
|
|
365
|
+
* strips and skeleton copies: JS-side mirrors of a node animated by a
|
|
366
|
+
* player go stale, this does not.
|
|
367
|
+
*/
|
|
368
|
+
export function readTransform(node: NodeId, out: Float32Array): void
|
|
188
369
|
}
|
package/index.d.ts
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
/// <reference path="./standards/websocket.d.ts" />
|
|
26
26
|
/// <reference path="./standards/abort.d.ts" />
|
|
27
27
|
/// <reference path="./standards/crypto.d.ts" />
|
|
28
|
+
/// <reference path="./standards/clipboard.d.ts" />
|
|
28
29
|
|
|
29
30
|
// GUI capabilities (present only on a gui-enabled runtime). rendertree/camera/
|
|
30
31
|
// microphone/gpu are flux:* modules like the rest; requestAnimationFrame stays a
|
package/package.json
CHANGED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// navigator. The one navigator surface flux provides: the clipboard, text
|
|
2
|
+
// only. Solidrt semantics on the standard shape: no permissions model and no
|
|
3
|
+
// clipboard events - a single known app does not ask itself for permission.
|
|
4
|
+
// GUI builds only (the clipboard belongs to the windowing platform); on a
|
|
5
|
+
// headless flux there is no `navigator` at all, so
|
|
6
|
+
// `typeof navigator !== "undefined"` is the availability check.
|
|
7
|
+
|
|
8
|
+
interface Clipboard {
|
|
9
|
+
/**
|
|
10
|
+
* Resolves to the OS clipboard's text, "" when the clipboard is empty or
|
|
11
|
+
* holds no text. Rejects on platform failure.
|
|
12
|
+
*/
|
|
13
|
+
readText(): Promise<string>
|
|
14
|
+
/**
|
|
15
|
+
* Replaces the OS clipboard's contents with `text`. Rejects on platform
|
|
16
|
+
* failure or a non-string argument.
|
|
17
|
+
*/
|
|
18
|
+
writeText(text: string): Promise<void>
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Navigator {
|
|
22
|
+
readonly clipboard: Clipboard
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
declare var navigator: Navigator
|