@solidrt/flux-types 0.0.55 → 0.0.57
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 +284 -30
- package/gui/rendertree.d.ts +9 -0
- package/gui/spatial.d.ts +245 -16
- 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). */
|
|
@@ -152,7 +155,14 @@ declare module "flux:gpu" {
|
|
|
152
155
|
* not overridable - the chain either exists on the id or it does not.
|
|
153
156
|
*/
|
|
154
157
|
export type TextureBinding = TextureId | { id: TextureId; filter?: FilterMode; wrap?: WrapMode }
|
|
155
|
-
/**
|
|
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
|
+
*/
|
|
156
166
|
export type TextureBindings = Record<string, TextureBinding>
|
|
157
167
|
/**
|
|
158
168
|
* A free-form debug name every create accepts (WebGPU's label): surfaced by
|
|
@@ -174,8 +184,45 @@ declare module "flux:gpu" {
|
|
|
174
184
|
* texture as `(v, 0, 0, 1)` - read `.r`; displaying one via `<texture src>`
|
|
175
185
|
* shows that same red-channel reading. Shader/pipeline targets and
|
|
176
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).
|
|
177
224
|
*/
|
|
178
|
-
export type TextureFormat = "rgba8" | "r8"
|
|
225
|
+
export type TextureFormat = "rgba8" | "rgba8-srgb" | "r8" | "r32f" | "rgba32f" | "rgba16f"
|
|
179
226
|
export type TextureFormatOption = { format?: TextureFormat }
|
|
180
227
|
/**
|
|
181
228
|
* This device's hard ceilings, queried once at startup: process constants.
|
|
@@ -189,6 +236,8 @@ declare module "flux:gpu" {
|
|
|
189
236
|
export let limits: {
|
|
190
237
|
/** Largest width/height of any texture or render target, in pixels (>= 2048). */
|
|
191
238
|
maxTextureSize: number
|
|
239
|
+
/** Largest face edge of a cube map, in pixels (>= 2048); its own GL ceiling. */
|
|
240
|
+
maxCubeMapSize: number
|
|
192
241
|
/**
|
|
193
242
|
* Sampler inputs one pass may bind (>= 16): a target's `textures`
|
|
194
243
|
* entries; on a window shader the runtime-filled `uSource` (and
|
|
@@ -205,26 +254,65 @@ declare module "flux:gpu" {
|
|
|
205
254
|
* goes" spelling.
|
|
206
255
|
*/
|
|
207
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
|
|
208
271
|
}
|
|
209
272
|
/**
|
|
210
|
-
* Create an immutable texture from a pixel buffer
|
|
211
|
-
*
|
|
212
|
-
*
|
|
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.
|
|
213
299
|
*/
|
|
214
|
-
export function
|
|
300
|
+
export function createCubeTexture(faces: (Uint8Array | Float32Array)[] | (Uint8Array | Float32Array)[][], size: number, opts?: SamplerOptions & TextureFormatOption & LabelOption): TextureId
|
|
215
301
|
/**
|
|
216
302
|
* Create a texture intended to be updated later via {@link uploadTexture}. The
|
|
217
|
-
* seed buffer must hold at least one frame
|
|
218
|
-
*
|
|
219
|
-
*
|
|
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).
|
|
220
307
|
*/
|
|
221
|
-
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
|
|
222
309
|
/**
|
|
223
|
-
* Replace a mutable texture's pixels; the frame size
|
|
224
|
-
*
|
|
225
|
-
*
|
|
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.
|
|
226
314
|
*/
|
|
227
|
-
export function uploadTexture(id: TextureId, data: Uint8Array, offset?: number): void
|
|
315
|
+
export function uploadTexture(id: TextureId, data: Uint8Array | Float32Array, offset?: number): void
|
|
228
316
|
/**
|
|
229
317
|
* Replace a texture's storage with a new size at the same id (an id-stable
|
|
230
318
|
* resize): `<texture src>` references and shader sampler bindings keep
|
|
@@ -234,7 +322,7 @@ declare module "flux:gpu" {
|
|
|
234
322
|
* state). Render target ids are rejected - resize those with
|
|
235
323
|
* {@link setTargetSize}.
|
|
236
324
|
*/
|
|
237
|
-
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
|
|
238
326
|
/**
|
|
239
327
|
* Destroy a texture (immutable, mutable, or shader). Frame-safe: the id is
|
|
240
328
|
* reclaimed by the runtime once the render tree no longer references it, so
|
|
@@ -445,6 +533,8 @@ declare module "flux:gpu" {
|
|
|
445
533
|
/** One buffer per instance slot of the pipeline (index = the
|
|
446
534
|
* attributes' `slot`); pass this OR `instanceBuffer`, not both. */
|
|
447
535
|
instanceBuffers?: BufferId[]
|
|
536
|
+
/** Draw the instance records in key order (see {@link InstanceOrder}). */
|
|
537
|
+
instanceOrder?: InstanceOrder
|
|
448
538
|
clearColor?: [number, number, number, number]
|
|
449
539
|
render?: "auto" | "manual"
|
|
450
540
|
loadOp?: "clear" | "load"
|
|
@@ -603,6 +693,90 @@ declare module "flux:gpu" {
|
|
|
603
693
|
instanceBuffer?: BufferId
|
|
604
694
|
instanceBuffers?: BufferId[]
|
|
605
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] }
|
|
606
780
|
|
|
607
781
|
/**
|
|
608
782
|
* Compile a GLSL ES vertex+fragment pipeline into an offscreen texture of
|
|
@@ -651,6 +825,8 @@ declare module "flux:gpu" {
|
|
|
651
825
|
/** One buffer per instance slot (index = the attributes' `slot`);
|
|
652
826
|
* pass this OR `instanceBuffer`, not both. */
|
|
653
827
|
instanceBuffers?: BufferId[]
|
|
828
|
+
/** Draw the instance records in key order (see {@link InstanceOrder}). */
|
|
829
|
+
instanceOrder?: InstanceOrder
|
|
654
830
|
topology?: Topology
|
|
655
831
|
depth?: boolean
|
|
656
832
|
depthWrite?: boolean
|
|
@@ -693,12 +869,18 @@ declare module "flux:gpu" {
|
|
|
693
869
|
* closes and nothing is published. Always closes the lease, error or not;
|
|
694
870
|
* throws when no write is open or `byteLength` exceeds the buffer size.
|
|
695
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.
|
|
696
875
|
*/
|
|
697
876
|
export function endBufferWrite(id: BufferId, byteLength?: number): void
|
|
698
877
|
/**
|
|
699
878
|
* Overwrite part of a vertex buffer at `byteOffset` (default 0), within the
|
|
700
879
|
* size it was created with. Pipelines drawing from the buffer re-render
|
|
701
|
-
* 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}.
|
|
702
884
|
*/
|
|
703
885
|
export function writeBuffer(id: BufferId, data: Uint8Array, byteOffset?: number): void
|
|
704
886
|
/**
|
|
@@ -726,7 +908,7 @@ declare module "flux:gpu" {
|
|
|
726
908
|
* buffer and extends the range into it, and a call that throws changes
|
|
727
909
|
* nothing (range and buffers commit together or not at all).
|
|
728
910
|
*/
|
|
729
|
-
export function setDraw(id: TextureId, draw: (DrawRange | IndexRange) & BufferUpdate): void
|
|
911
|
+
export function setDraw(id: TextureId, draw: (DrawRange | IndexRange) & BufferUpdate & OrderUpdate): void
|
|
730
912
|
/**
|
|
731
913
|
* Create a draw target: a render target whose contents are an ordered,
|
|
732
914
|
* mutable LIST of draws - one render clears once, then executes every
|
|
@@ -767,6 +949,17 @@ declare module "flux:gpu" {
|
|
|
767
949
|
* Returns a texture id (display, resize, destroy like any target; entries
|
|
768
950
|
* die with it).
|
|
769
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
|
+
*
|
|
770
963
|
* `into` makes a SUB-TARGET: a draw target that renders into the `width`
|
|
771
964
|
* x `height` rectangle at `x`/`y` (top-left origin, the texture leaf's
|
|
772
965
|
* `srcX`/`srcY` space; default 0) of draw target `into`'s storage instead
|
|
@@ -778,8 +971,8 @@ declare module "flux:gpu" {
|
|
|
778
971
|
* cost one pass instead of N. The returned id is not a texture: sample,
|
|
779
972
|
* display (`<d-texture src={parent} srcX srcY srcW srcH>`), read back and
|
|
780
973
|
* copy the PARENT; `depthTexture(parent)` is the tile's depth too. Depth,
|
|
781
|
-
* `samples`, `render` and `loadOp` are the parent's (passing
|
|
782
|
-
* 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
|
|
783
976
|
* parent is clipped; {@link setTargetRect} moves it. Tiles do not nest.
|
|
784
977
|
* Destroying the parent destroys its tiles.
|
|
785
978
|
*/
|
|
@@ -789,6 +982,7 @@ declare module "flux:gpu" {
|
|
|
789
982
|
params?: ShaderParams | null,
|
|
790
983
|
opts?: {
|
|
791
984
|
depth?: boolean | "texture"
|
|
985
|
+
format?: "rgba8" | "rgba8-srgb" | "rgba16f"
|
|
792
986
|
textures?: TextureBindings
|
|
793
987
|
clearColor?: [number, number, number, number]
|
|
794
988
|
render?: "auto" | "manual"
|
|
@@ -800,6 +994,46 @@ declare module "flux:gpu" {
|
|
|
800
994
|
} & SamplerOptions &
|
|
801
995
|
LabelOption,
|
|
802
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
|
|
803
1037
|
/**
|
|
804
1038
|
* The depth texture of a draw target created with `depth: "texture"`: a
|
|
805
1039
|
* texture id of its own, stable for the target's life (a
|
|
@@ -812,9 +1046,15 @@ declare module "flux:gpu" {
|
|
|
812
1046
|
* depth throws. SAMPLER-ONLY: it is not an upload texture and not a
|
|
813
1047
|
* readback source (`readTexture`/`copyTexture` throw - render it through a
|
|
814
1048
|
* pass to read it), its sampling is fixed at `nearest`/`clamp` (a depth
|
|
815
|
-
* texture is only complete at nearest without a comparison mode
|
|
816
|
-
*
|
|
817
|
-
*
|
|
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>`
|
|
818
1058
|
* shows the depth in the red channel. Throws for a target without texture
|
|
819
1059
|
* depth.
|
|
820
1060
|
*/
|
|
@@ -859,6 +1099,8 @@ declare module "flux:gpu" {
|
|
|
859
1099
|
/** One buffer per instance slot of the pipeline (index = the
|
|
860
1100
|
* attributes' `slot`); pass this OR `instanceBuffer`, not both. */
|
|
861
1101
|
instanceBuffers?: BufferId[]
|
|
1102
|
+
/** Draw the instance records in key order (see {@link InstanceOrder}). */
|
|
1103
|
+
instanceOrder?: InstanceOrder
|
|
862
1104
|
before?: DrawId
|
|
863
1105
|
} & (DrawRange | (IndexBinding & IndexRange)),
|
|
864
1106
|
): DrawId
|
|
@@ -955,7 +1197,7 @@ declare module "flux:gpu" {
|
|
|
955
1197
|
* single entry, same partial merge, bounds validation, and vocabulary
|
|
956
1198
|
* rule (an indexed entry speaks {@link IndexRange}).
|
|
957
1199
|
*/
|
|
958
|
-
export function setDrawRange(target: TextureId, draw: DrawId, update: DrawRange | IndexRange): void
|
|
1200
|
+
export function setDrawRange(target: TextureId, draw: DrawId, update: (DrawRange | IndexRange) & OrderUpdate): void
|
|
959
1201
|
/**
|
|
960
1202
|
* Swap one draw entry's buffers: the {@link BufferUpdate} half of
|
|
961
1203
|
* {@link setDraw} addressed to a single entry, same replace-only rule and
|
|
@@ -971,7 +1213,9 @@ declare module "flux:gpu" {
|
|
|
971
1213
|
* back-to-front, and re-issue the order when the camera moves. Entry
|
|
972
1214
|
* state (params, textures, ranges) rides along untouched; ids are
|
|
973
1215
|
* unaffected. Like every draw-list write it re-renders an auto target
|
|
974
|
-
* 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}.)
|
|
975
1219
|
*/
|
|
976
1220
|
export function setDrawOrder(target: TextureId, order: DrawId[]): void
|
|
977
1221
|
/**
|
|
@@ -986,9 +1230,16 @@ declare module "flux:gpu" {
|
|
|
986
1230
|
* the one counting. Ping-pong feedback is two manual targets sampling
|
|
987
1231
|
* each other, stepped alternately from `onFrame`; binding a target to
|
|
988
1232
|
* ITSELF still throws (same-pass GL feedback, undefined pixels regardless
|
|
989
|
-
* 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).
|
|
990
1241
|
*/
|
|
991
|
-
export function renderTarget(id: TextureId): void
|
|
1242
|
+
export function renderTarget(id: TextureId, face?: number, level?: number): void
|
|
992
1243
|
/**
|
|
993
1244
|
* Overwrite a `render: "manual"` target with another texture's current
|
|
994
1245
|
* pixels, GPU-side: the seed/history analog of {@link uploadTexture}
|
|
@@ -1000,7 +1251,9 @@ declare module "flux:gpu" {
|
|
|
1000
1251
|
* copy sees the copy, and targets sampling `dst` update afterwards.
|
|
1001
1252
|
* Throws if either id is unknown, `dst` is not a manual target (the
|
|
1002
1253
|
* runtime owns those contents), or `src === dst`. `src` may be any
|
|
1003
|
-
* 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).
|
|
1004
1257
|
*/
|
|
1005
1258
|
export function copyTexture(src: TextureId, dst: TextureId): void
|
|
1006
1259
|
/**
|
|
@@ -1039,7 +1292,8 @@ declare module "flux:gpu" {
|
|
|
1039
1292
|
* Read back a registered texture's current pixels as RGBA8 (tightly packed,
|
|
1040
1293
|
* top-to-bottom rows), for any texture id whatever created it (createTexture,
|
|
1041
1294
|
* createShaderTexture, a render target). Synchronous. Throws if the id is
|
|
1042
|
-
* 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).
|
|
1043
1297
|
*/
|
|
1044
1298
|
export function readTexture(id: TextureId): { width: number; height: number; data: Uint8Array }
|
|
1045
1299
|
}
|
package/gui/rendertree.d.ts
CHANGED
|
@@ -189,6 +189,15 @@ declare module "flux:rendertree" {
|
|
|
189
189
|
* semantics), for comparing against pointer event coordinates.
|
|
190
190
|
*/
|
|
191
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
|
|
192
201
|
/**
|
|
193
202
|
* The texture id of a snapshot repaint boundary's retained rasterization
|
|
194
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
|
|
@@ -127,19 +148,100 @@ declare module "flux:spatial" {
|
|
|
127
148
|
/** Free a shape; nodes still referencing it fall back to their box. */
|
|
128
149
|
export function destroyShape(shape: ShapeId): void
|
|
129
150
|
export function setShape(node: NodeId, shape: ShapeId | null): void
|
|
151
|
+
/** The node's layer membership bitmask (default 1), what a query's
|
|
152
|
+
* `layers` mask is tested against. Query-only: needs no flush. */
|
|
153
|
+
export function setLayers(node: NodeId, layers: number): void
|
|
154
|
+
/**
|
|
155
|
+
* The filter every query takes as its trailing argument, each field
|
|
156
|
+
* optional: `root` admits only the nodes under it (the caller's own
|
|
157
|
+
* subtree in the arena every scene and layer shares), `layers` only
|
|
158
|
+
* nodes whose mask intersects it (a node's mask of 0 then never
|
|
159
|
+
* reports), `nodes` only the listed ones. A dead root throws.
|
|
160
|
+
*/
|
|
161
|
+
export type QueryFilter = {
|
|
162
|
+
root?: NodeId
|
|
163
|
+
layers?: number
|
|
164
|
+
nodes?: NodeId[]
|
|
165
|
+
}
|
|
130
166
|
/** Every shown node with bounds the ray strikes, nearest first. The
|
|
131
167
|
* direction need not be normalized; distances are world units. Reads
|
|
132
168
|
* the index as of the last flush. */
|
|
133
|
-
export function raycast(origin: Float32Array, direction: Float32Array): Hit[]
|
|
169
|
+
export function raycast(origin: Float32Array, direction: Float32Array, filter?: QueryFilter): Hit[]
|
|
170
|
+
/** A query volume's layout for overlap/sweep: "capsule" is
|
|
171
|
+
* 7 floats (a, b, radius; a sphere when a == b), "box" is 10 (center,
|
|
172
|
+
* half extents, unit quaternion xyzw). */
|
|
173
|
+
export type VolumeKind = "capsule" | "box"
|
|
174
|
+
/** One node overlap() touches: its deepest contact - the point on
|
|
175
|
+
* the node's surface, the unit direction out of it, and the depth along
|
|
176
|
+
* that direction that clears the contact. */
|
|
177
|
+
export type Overlap = {
|
|
178
|
+
node: NodeId
|
|
179
|
+
point: [number, number, number]
|
|
180
|
+
normal: [number, number, number]
|
|
181
|
+
depth: number
|
|
182
|
+
}
|
|
183
|
+
/** One node sweep() touches: `time` is the fraction of the motion
|
|
184
|
+
* at first touch, `point` the touch point on the node's surface,
|
|
185
|
+
* `normal` the unit normal there facing the volume. */
|
|
186
|
+
export type Impact = {
|
|
187
|
+
node: NodeId
|
|
188
|
+
time: number
|
|
189
|
+
point: [number, number, number]
|
|
190
|
+
normal: [number, number, number]
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Every shown node with bounds the volume touches, each with its
|
|
194
|
+
* deepest contact. A node with a shape is tested per triangle in world
|
|
195
|
+
* space, so it holds under any transform; a node without one is its
|
|
196
|
+
* local box's twelve triangles (a flat rect is four of them, so a
|
|
197
|
+
* rotated sprite tests exactly - the 2d marquee's box query is a "box"
|
|
198
|
+
* volume). Surfaces, not solids: a volume wholly inside a closed mesh
|
|
199
|
+
* with no triangle in reach touches nothing. Unordered; reads the index
|
|
200
|
+
* as of the last flush, like raycast.
|
|
201
|
+
*/
|
|
202
|
+
export function overlap(kind: VolumeKind, volume: Float32Array, filter?: QueryFilter): Overlap[]
|
|
203
|
+
/**
|
|
204
|
+
* The volume moved by `motion` (a Float32Array of 3): every shown node
|
|
205
|
+
* with bounds it touches on the way, at its first touch, earliest
|
|
206
|
+
* first. A node already in contact reports time 0 while the motion
|
|
207
|
+
* closes in, and nothing while it leaves or slides along the contact;
|
|
208
|
+
* a zero motion touches nothing. Same testing and index contract as
|
|
209
|
+
* overlap.
|
|
210
|
+
*/
|
|
211
|
+
export function sweep(kind: VolumeKind, volume: Float32Array, motion: Float32Array, filter?: QueryFilter): Impact[]
|
|
212
|
+
/** The mover's tuning, every field optional over the core's defaults:
|
|
213
|
+
* `up` (the direction floors face, [0, 1, 0]), `floorMaxAngle` (radians
|
|
214
|
+
* from `up` a contact still counts as floor, 45 degrees), `maxSlides`
|
|
215
|
+
* (6), `skin` (the gap kept from every surface, 0.01) and `floorSnap`
|
|
216
|
+
* (how far below a floor is pulled to when the motion does not rise,
|
|
217
|
+
* 0.1; 0 disables). */
|
|
218
|
+
export type MoveOptions = {
|
|
219
|
+
up?: [number, number, number]
|
|
220
|
+
floorMaxAngle?: number
|
|
221
|
+
maxSlides?: number
|
|
222
|
+
skin?: number
|
|
223
|
+
floorSnap?: number
|
|
224
|
+
}
|
|
225
|
+
/** What moveAndSlide() returns: the displacement the body gets, the
|
|
226
|
+
* unit normal of the floor it ends on (null when airborne or on a slope
|
|
227
|
+
* too steep to stand on), whether a wall or a ceiling was met, and
|
|
228
|
+
* every impact in order, the floor snap's last. */
|
|
229
|
+
export type MoveResult = {
|
|
230
|
+
motion: [number, number, number]
|
|
231
|
+
floor: [number, number, number] | null
|
|
232
|
+
wall: boolean
|
|
233
|
+
ceiling: boolean
|
|
234
|
+
hits: Impact[]
|
|
235
|
+
}
|
|
134
236
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
237
|
+
* Move the volume by `motion` through the admitted nodes, sliding along
|
|
238
|
+
* what it hits (Godot's move_and_slide, Unity's CharacterController.Move
|
|
239
|
+
* as one pure call): a push out of anything it starts inside, then
|
|
240
|
+
* sweep, stop a skin short, slide the rest along the contact, up to
|
|
241
|
+
* `maxSlides` times, then unless the motion rises a snap down onto a
|
|
242
|
+
* floor within `floorSnap`. Same index and filter contract as sweep.
|
|
141
243
|
*/
|
|
142
|
-
export function
|
|
244
|
+
export function moveAndSlide(kind: VolumeKind, volume: Float32Array, motion: Float32Array, opts?: MoveOptions, filter?: QueryFilter): MoveResult
|
|
143
245
|
|
|
144
246
|
/**
|
|
145
247
|
* Route the world DIRECTION of the node's local `vector` (a
|
|
@@ -150,14 +252,46 @@ declare module "flux:spatial" {
|
|
|
150
252
|
* zeros. Every sink naming the same param shares one array (`len` must
|
|
151
253
|
* agree); what the slots mean - light directions, an emitter axis - is
|
|
152
254
|
* 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)
|
|
255
|
+
* One slot sink per (target, name): rebinding the same param on the
|
|
256
|
+
* same target replaces that sink (the abandoned slot zeroes); another
|
|
257
|
+
* param or another target adds one, so a node may feed several arrays
|
|
258
|
+
* of one target (a spot light: its direction and its position).
|
|
155
259
|
*/
|
|
156
260
|
export function bindDirectionSlot(node: NodeId, target: TextureId, name: string, len: number, index: number, vector: Float32Array): void
|
|
157
|
-
/**
|
|
261
|
+
/**
|
|
262
|
+
* Route the node's world POSITION into vec3 slot `index` of the
|
|
263
|
+
* `len`-float shared array param `name` on a draw target -
|
|
264
|
+
* bindDirectionSlot's translation sibling, with the same sharing,
|
|
265
|
+
* zeroing and per-(target, name) replacement rules. What a positional
|
|
266
|
+
* light's slot follows.
|
|
267
|
+
*/
|
|
268
|
+
export function bindPositionSlot(node: NodeId, target: TextureId, name: string, len: number, index: number): void
|
|
269
|
+
/** Remove the node's slot sinks on `target`, or every slot sink without
|
|
158
270
|
* one (the abandoned slots zero at the next flush). */
|
|
159
271
|
export function unbindSlot(node: NodeId, target?: TextureId): void
|
|
160
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Route the node's world matrix, post-multiplied by the constant `post`
|
|
275
|
+
* (a Float32Array of 16, column-major), into row `row` of a float
|
|
276
|
+
* texture: the flush writes the 16 floats as the row's four rgba32f
|
|
277
|
+
* texels - the matrix-palette channel a vertex shader `texelFetch`es
|
|
278
|
+
* (a skin binds each joint node with `post` its inverse bind). Writes
|
|
279
|
+
* batch: however many bound nodes moved, each flush uploads at most one
|
|
280
|
+
* whole palette per texture. With `anchor` - one node shared by every
|
|
281
|
+
* bind on the texture, and an ANCESTOR of every bound node - rows are
|
|
282
|
+
* anchor-local (`inverse(anchorWorld) * world * post`), so a model
|
|
283
|
+
* root's placement stays out of its own palette. Rows keep updating
|
|
284
|
+
* while nodes are hidden (visibility is the drawing entry's business),
|
|
285
|
+
* and an unbound or destroyed node's row keeps its last value. Validated
|
|
286
|
+
* at bind time: the texture must be an uploadable rgba32f texture 4
|
|
287
|
+
* texels wide with `row` inside it. Rebinding the same texture replaces
|
|
288
|
+
* the node's slot there; another texture adds one.
|
|
289
|
+
*/
|
|
290
|
+
export function bindTextureSlot(node: NodeId, texture: TextureId, row: number, post: Float32Array, anchor?: NodeId): void
|
|
291
|
+
/** Remove the node's texture slot on `texture`, or every texture slot
|
|
292
|
+
* without one (abandoned rows keep their last value). */
|
|
293
|
+
export function unbindTextureSlot(node: NodeId, texture?: TextureId): void
|
|
294
|
+
|
|
161
295
|
/**
|
|
162
296
|
* Route the node's world pose to record slot `index` of vertex buffer
|
|
163
297
|
* `buffer` used as an instance buffer: the flush writes the 5 floats
|
|
@@ -172,7 +306,23 @@ declare module "flux:spatial" {
|
|
|
172
306
|
* Rebinding replaces the node's record sink; the abandoned slot zeroes.
|
|
173
307
|
*/
|
|
174
308
|
export function bindPoseRecord(node: NodeId, buffer: BufferId, index: number): void
|
|
175
|
-
/**
|
|
309
|
+
/**
|
|
310
|
+
* Route the node's world matrix to record slot `index` of vertex buffer
|
|
311
|
+
* `buffer` as 16 floats (one column-major mat4) at float offset
|
|
312
|
+
* index * 16: the per-instance model matrix a vertex stage reads as
|
|
313
|
+
* four vec4 attributes. With `anchor` (an ANCESTOR of the node, one per
|
|
314
|
+
* buffer - every bind on the buffer names the same one) the record is
|
|
315
|
+
* the matrix RELATIVE to it, `inverse(anchorWorld) * world`, so a mesh's
|
|
316
|
+
* instances stay in the mesh's own space and its uModel still places
|
|
317
|
+
* the whole population (`uModel * instanceMatrix`); without one it is
|
|
318
|
+
* the plain world matrix. Batching, growth (retargetRecords) and
|
|
319
|
+
* rebinding as bindPoseRecord; a hidden, unbound or destroyed node's
|
|
320
|
+
* slot becomes a zero-scale matrix (w stays 1), so the instance
|
|
321
|
+
* collapses to a point and draws nothing. Every sink on one buffer
|
|
322
|
+
* shares one projection.
|
|
323
|
+
*/
|
|
324
|
+
export function bindMatrixRecord(node: NodeId, buffer: BufferId, index: number, anchor?: NodeId): void
|
|
325
|
+
/** Remove the node's record sink (its slot hides at the next flush). */
|
|
176
326
|
export function unbindRecord(node: NodeId): void
|
|
177
327
|
/**
|
|
178
328
|
* Move every record sink on buffer `old` to buffer `new`, slot indices
|
|
@@ -185,4 +335,83 @@ declare module "flux:spatial" {
|
|
|
185
335
|
* when `new` already carries record sinks.
|
|
186
336
|
*/
|
|
187
337
|
export function retargetRecords(old: BufferId, next: BufferId): void
|
|
338
|
+
|
|
339
|
+
/** A registered clip's handle (createClip). */
|
|
340
|
+
export type ClipId = number
|
|
341
|
+
/** A playing clip instance's handle (createPlayer). */
|
|
342
|
+
export type PlayerId = number
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Register a baked animation clip: `duration` in seconds, `meta` four
|
|
346
|
+
* words per channel - [targetSlot, path (0 position, 1 rotation,
|
|
347
|
+
* 2 scale), interpolation (0 step, 1 linear, 2 cubic), keyCount] - and
|
|
348
|
+
* `times`/`values` every channel's key arrays concatenated in meta
|
|
349
|
+
* order (3 floats per key, 4 for rotation; cubic stores three elements
|
|
350
|
+
* per key: in-tangent, value, out-tangent, tangents per second - the
|
|
351
|
+
* glTF CUBICSPLINE layout). A clip is shared data: `targetSlot`
|
|
352
|
+
* indexes the target table each PLAYER supplies, so one clip drives
|
|
353
|
+
* any number of instances, and retargeting is a different table.
|
|
354
|
+
*/
|
|
355
|
+
export function createClip(duration: number, meta: Uint32Array, times: Float32Array, values: Float32Array): ClipId
|
|
356
|
+
/** Free a clip. Players still on it drop at their next advance (a
|
|
357
|
+
* "dropped" spatialClipEnd). */
|
|
358
|
+
export function destroyClip(clip: ClipId): void
|
|
359
|
+
/**
|
|
360
|
+
* Start playing `clip`: `targets[slot]` is the node each channel
|
|
361
|
+
* animates - every target must be a live scene node (throws
|
|
362
|
+
* otherwise). Players advance on the frame clock BEFORE each frame's
|
|
363
|
+
* JS, sample and weight-blend every active player per (node, path) -
|
|
364
|
+
* two players on one node crossfade - and write the blended TRS into
|
|
365
|
+
* the arena, so frame handlers read and may overwrite freshly posed
|
|
366
|
+
* nodes (last write wins) and the frame's flush publishes the result.
|
|
367
|
+
* `speed` scales clip time (1 = as authored); `loop` wraps, else the
|
|
368
|
+
* player holds its final pose and reports once; `weight`/`fade` start
|
|
369
|
+
* the crossfade state (weight 0..1, fade = weight change per second -
|
|
370
|
+
* positive fades in, negative out; past 0 the player is removed).
|
|
371
|
+
* When a player finishes or is removed without finishing (faded out,
|
|
372
|
+
* clip or target destroyed), the "spatialClipEnd" engine event
|
|
373
|
+
* (srt:events) fires with payload { player, reason: "finished" |
|
|
374
|
+
* "dropped" }, before the same frame's handlers.
|
|
375
|
+
*/
|
|
376
|
+
export function createPlayer(clip: ClipId, targets: NodeId[], speed: number, loop: boolean, weight: number, fade: number): PlayerId
|
|
377
|
+
/** Write the given fields of a player - the O(changes) crossfade
|
|
378
|
+
* channel. Setting `time` (seconds) also re-arms a finished player's
|
|
379
|
+
* end report. Throws on an id that already dropped. */
|
|
380
|
+
export function setPlayer(player: PlayerId, update: { weight?: number; fade?: number; speed?: number; time?: number }): void
|
|
381
|
+
/**
|
|
382
|
+
* Bind root motion to a player: `channel` of `clip` (a position channel
|
|
383
|
+
* - normally the ROOT track of the authored clip, while the player
|
|
384
|
+
* plays an in-place variant that holds the root still) is sampled at
|
|
385
|
+
* the player's time each advance, and its travel since the previous
|
|
386
|
+
* advance (continuous across a loop wrap, weighted by the player's
|
|
387
|
+
* weight) is reported as one "spatialRootMotion" engine event
|
|
388
|
+
* (srt:events) per advance, payload { player, x, y, z, yaw }, before
|
|
389
|
+
* the frame's handlers. The translation is given in the root's CURRENT
|
|
390
|
+
* facing (the clip's own turn so far undone), i.e. the character's
|
|
391
|
+
* local frame. `rotation`, a rotation channel of the same clip (the
|
|
392
|
+
* root's), adds the yaw half: the twist about `up`, in radians (zero
|
|
393
|
+
* without it). With an `anchor` the translation, rotated by the
|
|
394
|
+
* anchor's own rotation, is added to the anchor's position and the yaw
|
|
395
|
+
* turns the anchor about `up` in its own frame - Unity's
|
|
396
|
+
* applyRootMotion; without one it is Godot's root_motion_track, the
|
|
397
|
+
* app's to apply. `opts.up` is the root's parent-space up axis (default
|
|
398
|
+
* [0, 1, 0]); `opts.vertical: false` drops the component along it from
|
|
399
|
+
* the delta (the height stays in the pose). Binding primes at the
|
|
400
|
+
* player's current time, so the next advance already delivers. A
|
|
401
|
+
* second call rebinds. Throws on a dropped player, a wrong channel
|
|
402
|
+
* kind, a zero up axis, or a dead anchor.
|
|
403
|
+
*/
|
|
404
|
+
export function bindRootMotion(player: PlayerId, clip: ClipId, channel: number, rotation?: number, anchor?: NodeId, opts?: { up?: [number, number, number]; vertical?: boolean }): void
|
|
405
|
+
/** Remove a player at once, holding whatever pose it last wrote (no
|
|
406
|
+
* event; stop-with-fade is a setPlayer fade write instead). A dropped
|
|
407
|
+
* id is fine. */
|
|
408
|
+
export function destroyPlayer(player: PlayerId): void
|
|
409
|
+
/**
|
|
410
|
+
* Fill `out` (a Float32Array of 10) with the node's CURRENT local
|
|
411
|
+
* transform - position xyz, quaternion xyzw, scale xyz - as the arena
|
|
412
|
+
* holds it, players' writes included. The pose read for root-motion
|
|
413
|
+
* strips and skeleton copies: JS-side mirrors of a node animated by a
|
|
414
|
+
* player go stale, this does not.
|
|
415
|
+
*/
|
|
416
|
+
export function readTransform(node: NodeId, out: Float32Array): void
|
|
188
417
|
}
|
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
|