@react-three/tsl 10.0.0-canary.87166dc → 10.0.0-canary.908b65b
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/dist/index.cjs +13 -29
- package/dist/index.d.cts +268 -53
- package/dist/index.d.mts +268 -53
- package/dist/index.d.ts +268 -53
- package/dist/index.mjs +13 -29
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -73,6 +73,13 @@ function withStagedOverlay(store, kind, committed) {
|
|
|
73
73
|
}
|
|
74
74
|
return view;
|
|
75
75
|
}
|
|
76
|
+
function readWithStaged(store, kind, committed, scope, isLeaf) {
|
|
77
|
+
if (scope === void 0) return withStagedOverlay(store, kind, committed);
|
|
78
|
+
const scopeData = committed[scope];
|
|
79
|
+
const current = scopeData && typeof scopeData === "object" && !isLeaf(scopeData) ? scopeData : {};
|
|
80
|
+
const staged = scope === ROOT_SCOPE ? void 0 : peekStaged(store, kind, scope);
|
|
81
|
+
return staged ? { ...current, ...staged } : current;
|
|
82
|
+
}
|
|
76
83
|
function flushStagedScope(store, kind, scopeKey, isLeaf) {
|
|
77
84
|
const registry = getKindRegistry(store, kind);
|
|
78
85
|
const staged = registry.staged.get(scopeKey);
|
|
@@ -704,8 +711,9 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
704
711
|
input: memoizedInput,
|
|
705
712
|
create: () => {
|
|
706
713
|
if (isReader) return {};
|
|
707
|
-
if (typeof processedInput.candidates !== "object" || processedInput.candidates === null)
|
|
714
|
+
if (typeof processedInput.candidates !== "object" || processedInput.candidates === null) {
|
|
708
715
|
throw new Error("Invalid uniform input");
|
|
716
|
+
}
|
|
709
717
|
return processedInput.candidates;
|
|
710
718
|
},
|
|
711
719
|
prepare: (name, candidate) => createUniform(name, candidate, scope),
|
|
@@ -723,13 +731,7 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
723
731
|
if (!equals) existing.value = newVal;
|
|
724
732
|
}
|
|
725
733
|
});
|
|
726
|
-
|
|
727
|
-
if (memoizedInput === void 0) {
|
|
728
|
-
uniforms = storeUniforms;
|
|
729
|
-
} else if (typeof memoizedInput === "string") {
|
|
730
|
-
const scopeData = storeUniforms[memoizedInput];
|
|
731
|
-
uniforms = scopeData && !isUniformNode(scopeData) ? scopeData : {};
|
|
732
|
-
}
|
|
734
|
+
const uniforms = isReader ? readWithStaged(store, "uniforms", storeUniforms, memoizedInput, isUniformNode) : created;
|
|
733
735
|
return { ...uniforms, removeUniforms, clearUniforms, rebuildUniforms };
|
|
734
736
|
}
|
|
735
737
|
function rebuildAllUniforms(store, scope) {
|
|
@@ -846,13 +848,7 @@ function useNodes(creatorOrScope, scope) {
|
|
|
846
848
|
return node;
|
|
847
849
|
}
|
|
848
850
|
});
|
|
849
|
-
|
|
850
|
-
if (creatorOrScope === void 0) {
|
|
851
|
-
nodes = storeNodes;
|
|
852
|
-
} else if (typeof creatorOrScope === "string") {
|
|
853
|
-
const scopeData = storeNodes[creatorOrScope];
|
|
854
|
-
nodes = scopeData && !isTSLNode(scopeData) ? scopeData : {};
|
|
855
|
-
}
|
|
851
|
+
const nodes = isReader ? readWithStaged(store, "nodes", storeNodes, creatorOrScope, isTSLNode) : created;
|
|
856
852
|
return { ...nodes, removeNodes, clearNodes, rebuildNodes };
|
|
857
853
|
}
|
|
858
854
|
function rebuildAllNodes(store, scope) {
|
|
@@ -1007,13 +1003,7 @@ function useBuffers(creatorOrScope, scope) {
|
|
|
1007
1003
|
return buffer;
|
|
1008
1004
|
}
|
|
1009
1005
|
});
|
|
1010
|
-
|
|
1011
|
-
if (creatorOrScope === void 0) {
|
|
1012
|
-
buffers = storeBuffers;
|
|
1013
|
-
} else if (typeof creatorOrScope === "string") {
|
|
1014
|
-
const scopeData = storeBuffers[creatorOrScope];
|
|
1015
|
-
buffers = scopeData && !isBufferLike(scopeData) ? scopeData : {};
|
|
1016
|
-
}
|
|
1006
|
+
const buffers = isReader ? readWithStaged(store, "buffers", storeBuffers, creatorOrScope, isBufferLike) : created;
|
|
1017
1007
|
return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
|
|
1018
1008
|
}
|
|
1019
1009
|
function rebuildAllBuffers(store, scope) {
|
|
@@ -1077,13 +1067,7 @@ function useGPUStorage(creatorOrScope, scope) {
|
|
|
1077
1067
|
return storage;
|
|
1078
1068
|
}
|
|
1079
1069
|
});
|
|
1080
|
-
|
|
1081
|
-
if (creatorOrScope === void 0) {
|
|
1082
|
-
gpuStorage = storeStorage;
|
|
1083
|
-
} else if (typeof creatorOrScope === "string") {
|
|
1084
|
-
const scopeData = storeStorage[creatorOrScope];
|
|
1085
|
-
gpuStorage = scopeData && !isStorageLike(scopeData) ? scopeData : {};
|
|
1086
|
-
}
|
|
1070
|
+
const gpuStorage = isReader ? readWithStaged(store, "gpuStorage", storeStorage, creatorOrScope, isStorageLike) : created;
|
|
1087
1071
|
return {
|
|
1088
1072
|
...gpuStorage,
|
|
1089
1073
|
removeStorage,
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { RootState, useStore } from '@react-three/fiber/extension';
|
|
2
2
|
import * as three_webgpu from 'three/webgpu';
|
|
3
3
|
import { Node, StorageTexture, Storage3DTexture, StorageArrayTexture, Data3DTexture, RenderPipeline, Vector2, Vector3, Vector4, Color, Matrix2, Matrix3, Matrix4 } from 'three/webgpu';
|
|
4
|
+
import { Fn } from 'three/tsl';
|
|
4
5
|
|
|
5
6
|
//* Buffer Types (useBuffers) ========================================
|
|
6
7
|
|
|
@@ -57,6 +58,40 @@ type NodeRecord<T extends NodeLike = NodeLike> = Record<string, T>
|
|
|
57
58
|
*/
|
|
58
59
|
type NodeStore = Record<string, NodeLike | NodeRecord>
|
|
59
60
|
|
|
61
|
+
/** What `Fn()` returns (its members are what matters here, not its signature). */
|
|
62
|
+
type FnNodeLike = ReturnType<typeof Fn>
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* What an unregistered node read is: `useNodes().wobble`, `useNodes('fx').blur`, a creator's
|
|
66
|
+
* `nodes.wobble`, `state.nodes.wobble` in `useFrame`.
|
|
67
|
+
*
|
|
68
|
+
* Why it is loose: the read goes through a string name, and TypeScript cannot know what the entry
|
|
69
|
+
* under that name holds: a float, a vec3, a texture sample, or an `Fn()` to call. three's types
|
|
70
|
+
* only give operator methods to a node whose node type is known, and only an `Fn()` can be called,
|
|
71
|
+
* so the broad `NodeLike` allowed neither and every read needed a cast.
|
|
72
|
+
*
|
|
73
|
+
* So this has every member that any of three's nodes or an `Fn()` has, each typed `any`, and can
|
|
74
|
+
* be called. `nodes.wobble.mul(2)`, `material.colorNode = nodes.wobble` and `nodes.rimLightFn()`
|
|
75
|
+
* compile, and a misspelt member is still an error. It is a deliberate simplification: results of
|
|
76
|
+
* its own methods and calls are `any`.
|
|
77
|
+
*
|
|
78
|
+
* A single `as` gives it an exact type: `nodes.wobble as Node<'vec3'>`, `nodes.fn as typeof fn`.
|
|
79
|
+
* For exact types without casts, register the nodes (`nodes` / `nodeScopes` in `Register`) or pass
|
|
80
|
+
* a schema to the reader: `useNodes<{ blur: Node<'vec4'> }>('fx')`, `nodes.scope<Schema>('fx')`.
|
|
81
|
+
*/
|
|
82
|
+
interface LooseNode extends TSLLooseMembers<Node<TSLNodeTypeName> | FnNodeLike> {
|
|
83
|
+
(...args: any[]): any
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* A root entry of an unregistered node map: a node (its members) or a scope of nodes (any other
|
|
88
|
+
* name), since the type cannot tell which a name holds.
|
|
89
|
+
*/
|
|
90
|
+
type LooseNodeEntry = LooseNode & { readonly [name: string]: LooseNode }
|
|
91
|
+
|
|
92
|
+
/** An unregistered node map read: `state.nodes`, `useNodes()`. */
|
|
93
|
+
type LooseNodeStore = Record<string, LooseNodeEntry>
|
|
94
|
+
|
|
60
95
|
//* Storage Types (useGPUStorage) ========================================
|
|
61
96
|
|
|
62
97
|
/**
|
|
@@ -93,17 +128,23 @@ type StorageStore = Record<string, StorageLike | StorageRecord>
|
|
|
93
128
|
*/
|
|
94
129
|
interface TSLRootState {
|
|
95
130
|
/**
|
|
96
|
-
* TSL uniform nodes - root-level uniforms + scoped sub-objects. Use useUniforms().
|
|
97
|
-
*
|
|
98
|
-
* needs no cast.
|
|
131
|
+
* TSL uniform nodes - root-level uniforms + scoped sub-objects. Use useUniforms(). Each read is a
|
|
132
|
+
* `LooseUniformNode`, usable without casts, or typed by name once you register them (see
|
|
133
|
+
* `Register`), so `useFrame(({ uniforms }) => uniforms.uTime.value)` needs no cast either way.
|
|
99
134
|
*/
|
|
100
135
|
uniforms: AppUniforms
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
136
|
+
/**
|
|
137
|
+
* TSL nodes - root-level nodes + scoped sub-objects. Use useNodes(). Each read is a `LooseNode`,
|
|
138
|
+
* usable without casts, or typed by name once you register your nodes (see `Register`).
|
|
139
|
+
*/
|
|
140
|
+
nodes: AppNodes
|
|
141
|
+
/** Buffers - root-level buffers + scoped sub-objects. Use useBuffers(). Typed by name once registered */
|
|
142
|
+
buffers: AppBuffers
|
|
143
|
+
/**
|
|
144
|
+
* GPU storage (textures, etc.) - root-level storage + scoped sub-objects. Use useGPUStorage().
|
|
145
|
+
* Typed by name once registered
|
|
146
|
+
*/
|
|
147
|
+
gpuStorage: AppGPUStorage
|
|
107
148
|
/** Internal: bumped by hot reloads and rebuilds to re-run creators */
|
|
108
149
|
_hmrVersion: number
|
|
109
150
|
/** The canvas's RenderPipeline, once useRenderPipeline created one. Per canvas, not shared. */
|
|
@@ -233,6 +274,69 @@ declare global {
|
|
|
233
274
|
*/
|
|
234
275
|
type UniformStore = Record<string, UniformNode | UniformRecord>
|
|
235
276
|
|
|
277
|
+
//* Loose reads ==============================
|
|
278
|
+
|
|
279
|
+
/** Every shader node type three's TSL declarations type separately. */
|
|
280
|
+
type TSLNodeTypeName =
|
|
281
|
+
| 'float'
|
|
282
|
+
| 'int'
|
|
283
|
+
| 'uint'
|
|
284
|
+
| 'bool'
|
|
285
|
+
| 'vec2'
|
|
286
|
+
| 'vec3'
|
|
287
|
+
| 'vec4'
|
|
288
|
+
| 'ivec2'
|
|
289
|
+
| 'ivec3'
|
|
290
|
+
| 'ivec4'
|
|
291
|
+
| 'uvec2'
|
|
292
|
+
| 'uvec3'
|
|
293
|
+
| 'uvec4'
|
|
294
|
+
| 'bvec2'
|
|
295
|
+
| 'bvec3'
|
|
296
|
+
| 'bvec4'
|
|
297
|
+
| 'color'
|
|
298
|
+
| 'mat2'
|
|
299
|
+
| 'mat3'
|
|
300
|
+
| 'mat4'
|
|
301
|
+
|
|
302
|
+
/** Every member of every constituent of `T`, each typed `any`. */
|
|
303
|
+
type TSLLooseMembers<T> = { [K in T extends unknown ? keyof T : never]: any }
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* What an unregistered uniform read is: `useUniforms().uTime`, `useUniforms('fx').blur`,
|
|
307
|
+
* `useUniform('uTime')`, a creator's `uniforms.uTime`, `state.uniforms.uTime` in `useFrame`.
|
|
308
|
+
*
|
|
309
|
+
* Why it is loose: the read goes through a string name, and TypeScript cannot know what a
|
|
310
|
+
* runtime entry under that name holds. three's types only give the operator methods (`.mul`,
|
|
311
|
+
* `.add`, swizzles, `.toVar()`, ...) to a node whose node type is known, so `UniformNode<unknown>`
|
|
312
|
+
* had none of them and every read needed a cast.
|
|
313
|
+
*
|
|
314
|
+
* So this has every member that any of three's uniform nodes has (float, vec3, color, mat4, ...),
|
|
315
|
+
* each typed `any`. Every operator, TSL function and material slot accepts it, and a misspelt member
|
|
316
|
+
* (`.valeu`) is still an error. It is a deliberate simplification: results of its own methods are
|
|
317
|
+
* `any`, and nothing checks that `cross()` gets a vec3.
|
|
318
|
+
*
|
|
319
|
+
* It converts to an exact type with a single `as`, since every member is `any`:
|
|
320
|
+
* `uniforms.uColor as UniformNode<'color', Color>`, or `as Node<'vec3'>`. A node typed as a float
|
|
321
|
+
* uniform would cover the operators with exact result types, but three's node types are not
|
|
322
|
+
* comparable to one another, so it could only be cast through `unknown`.
|
|
323
|
+
*
|
|
324
|
+
* Exact types without casts: register the uniforms (`Register` in `@react-three/tsl`, which also
|
|
325
|
+
* catches typos in the name), or pass a schema to the reader (`useUniforms<{ blur: number }>('fx')`,
|
|
326
|
+
* `useUniform<Color>('uColor')`, `uniforms.scope<{ uHealth: UniformNode<'float', number> }>('player')`).
|
|
327
|
+
*/
|
|
328
|
+
interface LooseUniformNode extends TSLLooseMembers<three_webgpu.UniformNode<TSLNodeTypeName, any>> {}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* A root entry of an unregistered uniform map: a uniform (its members) or a scope of uniforms (any
|
|
332
|
+
* other name), since the type cannot tell which a name holds. `uniforms.uTime.mul(2)` and
|
|
333
|
+
* `uniforms.player.uHealth` both read.
|
|
334
|
+
*/
|
|
335
|
+
type LooseUniformEntry = LooseUniformNode & { readonly [name: string]: LooseUniformNode }
|
|
336
|
+
|
|
337
|
+
/** An unregistered uniform map read: `state.uniforms`, `useUniforms()`. */
|
|
338
|
+
type LooseUniformStore = Record<string, LooseUniformEntry>
|
|
339
|
+
|
|
236
340
|
/**
|
|
237
341
|
* Helper to safely access a uniform node from the store.
|
|
238
342
|
* Use this when accessing state.uniforms to get proper typing.
|
|
@@ -395,13 +499,14 @@ type ResourceLeafGuard<T> = (value: unknown) => value is T;
|
|
|
395
499
|
*
|
|
396
500
|
* @example
|
|
397
501
|
* ```tsx
|
|
398
|
-
* // With uniforms registered (see `Register` and the Typed
|
|
502
|
+
* // With uniforms registered (see `Register` and the Typed Resources guide), reads are typed by name
|
|
399
503
|
* useLocalNodes(({ uniforms }) => ({
|
|
400
504
|
* wobble: sin(uniforms.uTime.mul(2)),
|
|
401
505
|
* playerHealth: uniforms.scope('player').uHealth, // or uniforms.player.uHealth
|
|
402
506
|
* }), [])
|
|
403
507
|
*
|
|
404
|
-
* // Without registration
|
|
508
|
+
* // Without registration a read is loose: TSL takes it as is. A scope schema makes it exact.
|
|
509
|
+
* useLocalNodes(({ uniforms }) => ({ wobble: sin(uniforms.uTime.mul(2)) }), [])
|
|
405
510
|
* useLocalNodes(({ uniforms }) => {
|
|
406
511
|
* const player = uniforms.scope<{ uHealth: UniformNode<'float', number> }>('player')
|
|
407
512
|
* return { damage: player.uHealth.mul(2) }
|
|
@@ -414,16 +519,19 @@ type ResourceLeafGuard<T> = (value: unknown) => value is T;
|
|
|
414
519
|
* Property access returns `T` (assumes leaf node).
|
|
415
520
|
* Use `.scope(key)` for nested object access.
|
|
416
521
|
*
|
|
522
|
+
* `TLeaf` is what a scope schema must hold (`scope<{ uHealth: UniformNode<'float', number> }>()`),
|
|
523
|
+
* `TRead` what a leaf reads as without one. They differ for uniforms and nodes, whose unregistered
|
|
524
|
+
* reads are the loose `LooseUniformNode` / `LooseNode` (see those types for why).
|
|
417
525
|
*/
|
|
418
|
-
type ScopedStoreMethods<TLeaf> = {
|
|
526
|
+
type ScopedStoreMethods<TLeaf, TRead extends TLeaf = TLeaf> = {
|
|
419
527
|
/** Access a nested scope by key. Returns empty wrapper if scope doesn't exist. */
|
|
420
|
-
scope<TScope extends Record<string, TLeaf> = Record<string,
|
|
528
|
+
scope<TScope extends Record<string, TLeaf> = Record<string, TRead>>(key: string): ScopedStoreType<TLeaf, TScope, TRead>;
|
|
421
529
|
/** Check if a key exists in the store */
|
|
422
530
|
has(key: string): boolean;
|
|
423
531
|
/** Get all keys in the store */
|
|
424
532
|
keys(): string[];
|
|
425
533
|
};
|
|
426
|
-
type ScopedStoreType<TLeaf, TEntries extends Record<string, TLeaf> = Record<string, TLeaf
|
|
534
|
+
type ScopedStoreType<TLeaf, TEntries extends Record<string, TLeaf> = Record<string, TLeaf>, TRead extends TLeaf = TLeaf> = Readonly<TEntries> & ScopedStoreMethods<TLeaf, TRead>;
|
|
427
535
|
interface ScopedStoreData<TLeaf> {
|
|
428
536
|
[key: string]: TLeaf | ScopedStoreData<TLeaf>;
|
|
429
537
|
}
|
|
@@ -440,18 +548,18 @@ declare function createScopedStore<TLeaf>(data: ScopedStoreData<TLeaf>, isLeaf:
|
|
|
440
548
|
type CreatorState = Omit<RootState, 'uniforms' | 'nodes' | 'buffers' | 'gpuStorage'> & {
|
|
441
549
|
/** Type-safe uniform access - property access returns UniformNode, typed by name when registered */
|
|
442
550
|
uniforms: CreatorUniforms;
|
|
443
|
-
/**
|
|
444
|
-
nodes:
|
|
445
|
-
/**
|
|
446
|
-
buffers:
|
|
447
|
-
/**
|
|
448
|
-
gpuStorage:
|
|
551
|
+
/** Node access: each read a `LooseNode` (callable, every operator), typed by name when registered */
|
|
552
|
+
nodes: CreatorNodes;
|
|
553
|
+
/** Buffer access: typed by name when registered, BufferLike (typed arrays, attributes, nodes) otherwise */
|
|
554
|
+
buffers: CreatorBuffers;
|
|
555
|
+
/** GPU storage access: typed by name when registered, StorageLike (storage textures, nodes) otherwise */
|
|
556
|
+
gpuStorage: CreatorGPUStorage;
|
|
449
557
|
};
|
|
450
558
|
|
|
451
559
|
/**
|
|
452
|
-
* @fileoverview Typed
|
|
560
|
+
* @fileoverview Typed TSL resources by registration (the TanStack Router `Register` pattern).
|
|
453
561
|
*
|
|
454
|
-
* Nothing registered: every type below falls back to
|
|
562
|
+
* Nothing registered: every type below falls back to loose reads (`LooseUniformNode`, `LooseNode`).
|
|
455
563
|
* Registered once, anywhere in the app:
|
|
456
564
|
*
|
|
457
565
|
* ```ts
|
|
@@ -471,14 +579,25 @@ type CreatorState = Omit<RootState, 'uniforms' | 'nodes' | 'buffers' | 'gpuStora
|
|
|
471
579
|
*
|
|
472
580
|
* then `useUniforms().uTime`, `useUniforms('player').uHealth`, `useUniform('uTime')`,
|
|
473
581
|
* `useFrame(({ uniforms }) => uniforms.uTime)`, `useThree((s) => s.uniforms.uTime)` and a creator's
|
|
474
|
-
* `({ uniforms }) => uniforms.uTime` are typed by name, with no generics.
|
|
582
|
+
* `({ uniforms }) => uniforms.uTime` are typed by name, with no generics. Nodes register the same
|
|
583
|
+
* way, by their node types: `nodes: { wobble: Node<'float'>; rimLight: typeof rimLightFn }` and
|
|
584
|
+
* `nodeScopes: { fx: { blur: Node<'vec4'> } }`, and so do buffers (`buffers` / `bufferScopes`) and
|
|
585
|
+
* GPU storage (`gpuStorage` / `gpuStorageScopes`). Docs: docs/webgpu/typed-resources.mdx.
|
|
586
|
+
*
|
|
587
|
+
* Unregistered reads are loose: `LooseUniformNode` / `LooseNode`, which every operator accepts and
|
|
588
|
+
* a single `as` makes exact (see those types for why).
|
|
475
589
|
*/
|
|
476
590
|
|
|
477
591
|
/**
|
|
478
|
-
* Augment this to type
|
|
592
|
+
* Augment this to type TSL resources by name. Recognised keys:
|
|
479
593
|
* - `uniforms`: the root-level uniform inputs (e.g. `typeof globalUniforms`)
|
|
480
|
-
* - `scopes`: named scopes and their inputs (e.g. `{ player: typeof playerUniforms }`)
|
|
481
|
-
* - `
|
|
594
|
+
* - `scopes`: named uniform scopes and their inputs (e.g. `{ player: typeof playerUniforms }`)
|
|
595
|
+
* - `nodes`: the root-level nodes by node type (e.g. `{ wobble: Node<'float'>; rim: typeof rimFn }`)
|
|
596
|
+
* - `nodeScopes`: named node scopes and their nodes (e.g. `{ fx: { blur: Node<'vec4'> } }`)
|
|
597
|
+
* - `buffers` / `bufferScopes`: buffers by name (e.g. `{ positions: StorageBufferNode<'vec3'> }`)
|
|
598
|
+
* - `gpuStorage` / `gpuStorageScopes`: GPU storage by name (e.g. `{ heightMap: StorageTexture }`)
|
|
599
|
+
* - `strict`: `false` makes unregistered root keys loose instead of errors (default: strict), for
|
|
600
|
+
* every resource family alike
|
|
482
601
|
*/
|
|
483
602
|
interface Register {
|
|
484
603
|
}
|
|
@@ -503,9 +622,11 @@ type TypedUniforms = UniformNodesFor<RegisteredUniforms> & {
|
|
|
503
622
|
};
|
|
504
623
|
/**
|
|
505
624
|
* What `state.uniforms` and `useUniforms()` are: the registered shape (strict by default, so a typo
|
|
506
|
-
* is an error), or
|
|
625
|
+
* is an error), or the loose `LooseUniformStore` when nothing is registered. With `strict: false`
|
|
626
|
+
* an unregistered root key reads loose too. A loose read is a `LooseUniformNode`: every operator and
|
|
627
|
+
* material slot accepts it, and a single `as` gives it an exact type (see that type for why).
|
|
507
628
|
*/
|
|
508
|
-
type AppUniforms = IsRegistered extends true ? IsStrict extends true ? TypedUniforms : TypedUniforms &
|
|
629
|
+
type AppUniforms = IsRegistered extends true ? IsStrict extends true ? TypedUniforms : TypedUniforms & LooseUniformStore : LooseUniformStore;
|
|
509
630
|
/** Creator input at root level: new keys are free, registered keys must keep their registered type. */
|
|
510
631
|
type RootUniformInput = Partial<RegisteredUniforms> & UniformInputRecord;
|
|
511
632
|
/** Creator input for scope `S`: checked against the registered scope, free for any other scope. */
|
|
@@ -515,13 +636,101 @@ type RegisteredScopeUniforms<S extends keyof RegisteredScopes> = UniformNodesFor
|
|
|
515
636
|
/** A registered root uniform's node. */
|
|
516
637
|
type RegisteredUniform<K extends keyof RegisteredUniforms> = UniformNodesFor<RegisteredUniforms>[K];
|
|
517
638
|
/**
|
|
518
|
-
* `uniforms` in a creator (`useNodes(({ uniforms }) => ...)`). Unregistered: any name
|
|
519
|
-
* `
|
|
639
|
+
* `uniforms` in a creator (`useNodes(({ uniforms }) => ...)`). Unregistered: any name reads as a
|
|
640
|
+
* `LooseUniformNode` (or, by dot access, a scope of them), and `.scope(name)` as a record of them
|
|
641
|
+
* unless given a schema. Registered: typed by name like `state.uniforms`, and `.scope(name)` is
|
|
520
642
|
* typed for registered scopes.
|
|
521
643
|
*/
|
|
522
644
|
type CreatorUniforms = IsRegistered extends true ? {
|
|
523
|
-
scope<S extends keyof RegisteredScopes & string>(key: S): ScopedStoreType<UniformNode, RegisteredScopeUniforms<S
|
|
524
|
-
} & Readonly<AppUniforms> & ScopedStoreMethods<UniformNode> : ScopedStoreType<UniformNode>;
|
|
645
|
+
scope<S extends keyof RegisteredScopes & string>(key: S): ScopedStoreType<UniformNode, RegisteredScopeUniforms<S>, LooseUniformNode>;
|
|
646
|
+
} & Readonly<AppUniforms> & ScopedStoreMethods<UniformNode, LooseUniformNode> : ScopedStoreType<UniformNode, LooseUniformStore, LooseUniformNode>;
|
|
647
|
+
/** The registered root-level nodes, or `{}` when none are registered. */
|
|
648
|
+
type RegisteredNodes = Register extends {
|
|
649
|
+
nodes: infer N extends NodeRecord;
|
|
650
|
+
} ? N : {};
|
|
651
|
+
/** The registered node scopes and their nodes, or `{}` when none are registered. */
|
|
652
|
+
type RegisteredNodeScopes = Register extends {
|
|
653
|
+
nodeScopes: infer S extends Record<string, NodeRecord>;
|
|
654
|
+
} ? S : {};
|
|
655
|
+
type IsNodesRegistered = Register extends {
|
|
656
|
+
nodes: any;
|
|
657
|
+
} | {
|
|
658
|
+
nodeScopes: any;
|
|
659
|
+
} ? true : false;
|
|
660
|
+
type TypedNodes = RegisteredNodes & RegisteredNodeScopes;
|
|
661
|
+
/**
|
|
662
|
+
* What `state.nodes` and `useNodes()` are: the registered shape (strict by default, so a typo is an
|
|
663
|
+
* error), or the loose `LooseNodeStore` when no nodes are registered. With `strict: false` an
|
|
664
|
+
* unregistered root key reads loose too.
|
|
665
|
+
*/
|
|
666
|
+
type AppNodes = IsNodesRegistered extends true ? IsStrict extends true ? TypedNodes : TypedNodes & LooseNodeStore : LooseNodeStore;
|
|
667
|
+
/** Creator output at root level: new keys are free, registered keys must keep their registered type. */
|
|
668
|
+
type RootNodeInput = Partial<RegisteredNodes> & NodeRecord;
|
|
669
|
+
/** Creator output for scope `S`: checked against the registered scope, free for any other scope. */
|
|
670
|
+
type ScopeNodeInput<S extends string> = S extends keyof RegisteredNodeScopes ? Partial<RegisteredNodeScopes[S]> : unknown;
|
|
671
|
+
/**
|
|
672
|
+
* `nodes` in a creator. Unregistered: any name reads as a `LooseNode` (or, by dot access, a scope of
|
|
673
|
+
* them). Registered: typed by name like `state.nodes`, and `.scope(name)` is typed for registered
|
|
674
|
+
* scopes.
|
|
675
|
+
*/
|
|
676
|
+
type CreatorNodes = IsNodesRegistered extends true ? {
|
|
677
|
+
scope<S extends keyof RegisteredNodeScopes & string>(key: S): ScopedStoreType<NodeLike, RegisteredNodeScopes[S], LooseNode>;
|
|
678
|
+
} & Readonly<AppNodes> & ScopedStoreMethods<NodeLike, LooseNode> : ScopedStoreType<NodeLike, LooseNodeStore, LooseNode>;
|
|
679
|
+
/** A node reader's result for an explicit schema `T`, or loose nodes when none was given. */
|
|
680
|
+
type ReadNodes<T extends NodeRecord> = NodeRecord extends T ? Record<string, LooseNode> : T;
|
|
681
|
+
/** The registered root-level buffers, or `{}` when none are registered. */
|
|
682
|
+
type RegisteredBuffers = Register extends {
|
|
683
|
+
buffers: infer B extends BufferRecord;
|
|
684
|
+
} ? B : {};
|
|
685
|
+
/** The registered buffer scopes and their buffers, or `{}` when none are registered. */
|
|
686
|
+
type RegisteredBufferScopes = Register extends {
|
|
687
|
+
bufferScopes: infer S extends Record<string, BufferRecord>;
|
|
688
|
+
} ? S : {};
|
|
689
|
+
type IsBuffersRegistered = Register extends {
|
|
690
|
+
buffers: any;
|
|
691
|
+
} | {
|
|
692
|
+
bufferScopes: any;
|
|
693
|
+
} ? true : false;
|
|
694
|
+
type TypedBuffers = RegisteredBuffers & RegisteredBufferScopes;
|
|
695
|
+
/** What `state.buffers` and `useBuffers()` are: the registered shape, or `BufferStore` when none are. */
|
|
696
|
+
type AppBuffers = IsBuffersRegistered extends true ? IsStrict extends true ? TypedBuffers : TypedBuffers & BufferStore : BufferStore;
|
|
697
|
+
/** Creator output at root level: new keys are free, registered keys must keep their registered type. */
|
|
698
|
+
type RootBufferInput = Partial<RegisteredBuffers> & BufferRecord;
|
|
699
|
+
/** Creator output for scope `S`: checked against the registered scope, free for any other scope. */
|
|
700
|
+
type ScopeBufferInput<S extends string> = S extends keyof RegisteredBufferScopes ? Partial<RegisteredBufferScopes[S]> : unknown;
|
|
701
|
+
/** `buffers` in a creator: typed by name when registered, `BufferLike` reads otherwise. */
|
|
702
|
+
type CreatorBuffers = IsBuffersRegistered extends true ? {
|
|
703
|
+
scope<S extends keyof RegisteredBufferScopes & string>(key: S): ScopedStoreType<BufferLike, RegisteredBufferScopes[S]>;
|
|
704
|
+
} & Readonly<AppBuffers> & ScopedStoreMethods<BufferLike> : ScopedStoreType<BufferLike>;
|
|
705
|
+
/** The registered root-level GPU storage, or `{}` when none is registered. */
|
|
706
|
+
type RegisteredGPUStorage = Register extends {
|
|
707
|
+
gpuStorage: infer G extends StorageRecord;
|
|
708
|
+
} ? G : {};
|
|
709
|
+
/** The registered GPU storage scopes and their entries, or `{}` when none are registered. */
|
|
710
|
+
type RegisteredGPUStorageScopes = Register extends {
|
|
711
|
+
gpuStorageScopes: infer S extends Record<string, StorageRecord>;
|
|
712
|
+
} ? S : {};
|
|
713
|
+
type IsGPUStorageRegistered = Register extends {
|
|
714
|
+
gpuStorage: any;
|
|
715
|
+
} | {
|
|
716
|
+
gpuStorageScopes: any;
|
|
717
|
+
} ? true : false;
|
|
718
|
+
type TypedGPUStorage = RegisteredGPUStorage & RegisteredGPUStorageScopes;
|
|
719
|
+
/** What `state.gpuStorage` and `useGPUStorage()` are: the registered shape, or `StorageStore`. */
|
|
720
|
+
type AppGPUStorage = IsGPUStorageRegistered extends true ? IsStrict extends true ? TypedGPUStorage : TypedGPUStorage & StorageStore : StorageStore;
|
|
721
|
+
/** Creator output at root level: new keys are free, registered keys must keep their registered type. */
|
|
722
|
+
type RootGPUStorageInput = Partial<RegisteredGPUStorage> & StorageRecord;
|
|
723
|
+
/** Creator output for scope `S`: checked against the registered scope, free for any other scope. */
|
|
724
|
+
type ScopeGPUStorageInput<S extends string> = S extends keyof RegisteredGPUStorageScopes ? Partial<RegisteredGPUStorageScopes[S]> : unknown;
|
|
725
|
+
/** `gpuStorage` in a creator: typed by name when registered, `StorageLike` reads otherwise. */
|
|
726
|
+
type CreatorGPUStorage = IsGPUStorageRegistered extends true ? {
|
|
727
|
+
scope<S extends keyof RegisteredGPUStorageScopes & string>(key: S): ScopedStoreType<StorageLike, RegisteredGPUStorageScopes[S]>;
|
|
728
|
+
} & Readonly<AppGPUStorage> & ScopedStoreMethods<StorageLike> : ScopedStoreType<StorageLike>;
|
|
729
|
+
/**
|
|
730
|
+
* A reader's result for an explicit schema `T`, or loose uniforms when none was given (`T` is then
|
|
731
|
+
* its constraint, `UniformInputRecord`): `useUniforms<{ blur: number }>('fx')` vs `useUniforms('fx')`.
|
|
732
|
+
*/
|
|
733
|
+
type ReadUniforms<T extends UniformInputRecord> = UniformInputRecord extends T ? Record<string, LooseUniformNode> : UniformNodesFor<T>;
|
|
525
734
|
|
|
526
735
|
/** Uniforms that exist on every primary canvas from the start. See configureTSL. */
|
|
527
736
|
interface TSLConfig {
|
|
@@ -565,7 +774,7 @@ declare function useUniforms<T extends RootUniformInput>(uniforms: T): UniformsW
|
|
|
565
774
|
declare function useUniforms<T extends UniformInputRecord, S extends string>(uniforms: T & ScopeUniformInput<S>, scope: S): UniformsWithUtils<UniformNodesFor<T>>;
|
|
566
775
|
declare function useUniforms(): UniformsWithUtils<AppUniforms>;
|
|
567
776
|
declare function useUniforms<S extends keyof RegisteredScopes & string>(scope: S): UniformsWithUtils<RegisteredScopeUniforms<S>>;
|
|
568
|
-
declare function useUniforms<T extends UniformInputRecord>(scope?: string): UniformsWithUtils<
|
|
777
|
+
declare function useUniforms<T extends UniformInputRecord>(scope?: string): UniformsWithUtils<ReadUniforms<T>>;
|
|
569
778
|
/**
|
|
570
779
|
* Global rebuildUniforms function for HMR integration.
|
|
571
780
|
* Invalidates cached uniforms and increments _hmrVersion to trigger re-creation.
|
|
@@ -587,8 +796,10 @@ declare function rebuildAllUniforms(store: ReturnType<typeof useStore>, scope?:
|
|
|
587
796
|
* - UniformNode: existing uniforms (reused as-is)
|
|
588
797
|
*/
|
|
589
798
|
type UniformValue = number | boolean | string | Vector2 | Vector3 | Vector4 | Color | Matrix2 | Matrix3 | Matrix4 | Node | UniformNode;
|
|
799
|
+
/** `useUniform<T>(name)`'s result: exact for an explicit `T`, a `LooseUniformNode` without one. */
|
|
800
|
+
type ReadUniform<T extends UniformValue> = UniformValue extends T ? LooseUniformNode : UniformNodeFor<T>;
|
|
590
801
|
declare function useUniform<K extends keyof RegisteredUniforms & string>(name: K): RegisteredUniform<K>;
|
|
591
|
-
declare function useUniform<T extends UniformValue = UniformValue>(name: string):
|
|
802
|
+
declare function useUniform<T extends UniformValue = UniformValue>(name: string): ReadUniform<T>;
|
|
592
803
|
declare function useUniform<T extends UniformValue>(name: string, value: T): UniformNodeFor<T>;
|
|
593
804
|
|
|
594
805
|
/**
|
|
@@ -618,12 +829,12 @@ type NodesWithUtils<T extends Record<string, unknown> = NodeRecord> = T & {
|
|
|
618
829
|
clearNodes: ClearNodesFn;
|
|
619
830
|
rebuildNodes: RebuildNodesFn;
|
|
620
831
|
};
|
|
621
|
-
declare function useNodes(): NodesWithUtils<
|
|
622
|
-
declare function useNodes(scope:
|
|
623
|
-
declare function useNodes<T extends NodeRecord>(scope?: string): NodesWithUtils<T
|
|
624
|
-
declare function useNodes<T extends
|
|
625
|
-
declare function useNodes<T extends NodeRecord>(creator:
|
|
626
|
-
declare function useNodes(creatorOrScope
|
|
832
|
+
declare function useNodes(): NodesWithUtils<AppNodes>;
|
|
833
|
+
declare function useNodes<S extends keyof RegisteredNodeScopes & string>(scope: S): NodesWithUtils<RegisteredNodeScopes[S]>;
|
|
834
|
+
declare function useNodes<T extends NodeRecord>(scope?: string): NodesWithUtils<ReadNodes<T>>;
|
|
835
|
+
declare function useNodes<T extends RootNodeInput>(creator: NodeCreator<T>): NodesWithUtils<T>;
|
|
836
|
+
declare function useNodes<T extends NodeRecord, S extends string>(creator: (state: CreatorState) => T & ScopeNodeInput<S>, scope: S): NodesWithUtils<T>;
|
|
837
|
+
declare function useNodes<S extends string | undefined>(creatorOrScope: NodeCreator<NodeRecord> | string | undefined, scope: S & (string extends S ? unknown : never)): NodesWithUtils<Record<string, unknown>>;
|
|
627
838
|
/**
|
|
628
839
|
* Global rebuildNodes function for HMR integration.
|
|
629
840
|
* Invalidates cached nodes and increments _hmrVersion to trigger re-creation.
|
|
@@ -694,8 +905,8 @@ type LocalNodeInstaller = (state: CreatorState) => LocalNodeInstall;
|
|
|
694
905
|
*
|
|
695
906
|
* @example
|
|
696
907
|
* ```tsx
|
|
697
|
-
* // Resource-driven composition: no surrounding JS inputs. `uniforms.uTime` is typed when
|
|
698
|
-
* // app registers its uniforms (see `Register`)
|
|
908
|
+
* // Resource-driven composition: no surrounding JS inputs. `uniforms.uTime` is typed by name when
|
|
909
|
+
* // the app registers its uniforms (see `Register`), and a loose node TSL accepts as is otherwise.
|
|
699
910
|
* const { wobble, uTime } = useLocalNodes(({ uniforms, nodes }) => ({
|
|
700
911
|
* wobble: sin(uniforms.uTime.mul(2)),
|
|
701
912
|
* uTime: uniforms.uTime, // can return uniforms too
|
|
@@ -706,10 +917,10 @@ type LocalNodeInstaller = (state: CreatorState) => LocalNodeInstall;
|
|
|
706
917
|
* result: pattern === 'noise' ? nodes.noise : nodes.stripes,
|
|
707
918
|
* }), [pattern])
|
|
708
919
|
*
|
|
709
|
-
* // An unregistered uniform
|
|
710
|
-
* const { colorNode } = useLocalNodes(({ uniforms }) => {
|
|
711
|
-
* const
|
|
712
|
-
* return { colorNode: mix(colorA, colorB,
|
|
920
|
+
* // An unregistered uniform works as is; one `as` gives its exact type where that matters
|
|
921
|
+
* const { colorNode, facing } = useLocalNodes(({ uniforms }) => {
|
|
922
|
+
* const uDir = uniforms.uDir as UniformNode<'vec3', Vector3> // cross() wants a vec3
|
|
923
|
+
* return { colorNode: mix(colorA, colorB, uniforms.myUniform), facing: cross(uDir, normalView) }
|
|
713
924
|
* }, [])
|
|
714
925
|
* ```
|
|
715
926
|
*/
|
|
@@ -740,11 +951,13 @@ type BuffersWithUtils<T extends BufferRecordType | BufferStore = BufferRecordTyp
|
|
|
740
951
|
rebuildBuffers: RebuildBuffersFn;
|
|
741
952
|
disposeBuffers: DisposeBuffersFn;
|
|
742
953
|
};
|
|
743
|
-
declare function useBuffers(): BuffersWithUtils<
|
|
954
|
+
declare function useBuffers(): BuffersWithUtils<AppBuffers>;
|
|
955
|
+
declare function useBuffers<S extends keyof RegisteredBufferScopes & string>(scope: S): BuffersWithUtils<RegisteredBufferScopes[S]>;
|
|
744
956
|
declare function useBuffers(scope: string): BuffersWithUtils<Record<string, BufferLike>>;
|
|
745
957
|
declare function useBuffers<T extends Record<string, BufferLike>>(scope?: string): BuffersWithUtils<T>;
|
|
746
|
-
declare function useBuffers<T extends
|
|
747
|
-
declare function useBuffers<T extends Record<string, BufferLike
|
|
958
|
+
declare function useBuffers<T extends RootBufferInput>(creator: BufferCreator<T>): BuffersWithUtils<T>;
|
|
959
|
+
declare function useBuffers<T extends Record<string, BufferLike>, S extends string>(creator: (state: CreatorState) => T & ScopeBufferInput<S>, scope: S): BuffersWithUtils<T>;
|
|
960
|
+
declare function useBuffers<S extends string | undefined>(creator: BufferCreator<BufferRecord>, scope: S & (string extends S ? unknown : never)): BuffersWithUtils<BufferRecord>;
|
|
748
961
|
/**
|
|
749
962
|
* Global rebuildBuffers function for HMR integration.
|
|
750
963
|
* Invalidates cached buffers and increments _hmrVersion to trigger re-creation.
|
|
@@ -782,11 +995,13 @@ type StorageWithUtils<T extends StorageRecordType | StorageStore = StorageRecord
|
|
|
782
995
|
rebuildStorage: RebuildStorageFn;
|
|
783
996
|
disposeStorage: DisposeStorageFn;
|
|
784
997
|
};
|
|
785
|
-
declare function useGPUStorage(): StorageWithUtils<
|
|
998
|
+
declare function useGPUStorage(): StorageWithUtils<AppGPUStorage>;
|
|
999
|
+
declare function useGPUStorage<S extends keyof RegisteredGPUStorageScopes & string>(scope: S): StorageWithUtils<RegisteredGPUStorageScopes[S]>;
|
|
786
1000
|
declare function useGPUStorage(scope: string): StorageWithUtils<Record<string, StorageLike>>;
|
|
787
1001
|
declare function useGPUStorage<T extends Record<string, StorageLike>>(scope?: string): StorageWithUtils<T>;
|
|
788
|
-
declare function useGPUStorage<T extends
|
|
789
|
-
declare function useGPUStorage<T extends Record<string, StorageLike
|
|
1002
|
+
declare function useGPUStorage<T extends RootGPUStorageInput>(creator: StorageCreator<T>): StorageWithUtils<T>;
|
|
1003
|
+
declare function useGPUStorage<T extends Record<string, StorageLike>, S extends string>(creator: (state: CreatorState) => T & ScopeGPUStorageInput<S>, scope: S): StorageWithUtils<T>;
|
|
1004
|
+
declare function useGPUStorage<S extends string | undefined>(creator: StorageCreator<StorageRecord>, scope: S & (string extends S ? unknown : never)): StorageWithUtils<StorageRecord>;
|
|
790
1005
|
/**
|
|
791
1006
|
* Global rebuildStorage function for HMR integration.
|
|
792
1007
|
* Invalidates cached storage and increments _hmrVersion to trigger re-creation.
|
|
@@ -844,4 +1059,4 @@ declare function rebuildAllStorage(store: ReturnType<typeof useStore>, scope?: s
|
|
|
844
1059
|
declare function useRenderPipeline(mainCB?: RenderPipelineMainCallback, setupCB?: RenderPipelineSetupCallback): UseRenderPipelineReturn;
|
|
845
1060
|
|
|
846
1061
|
export { configureTSL, createScopedStore, rebuildAllBuffers, rebuildAllNodes, rebuildAllStorage, rebuildAllUniforms, useBuffers, useGPUStorage, useLocalNodes, useNodes, useRenderPipeline, useUniform, useUniforms };
|
|
847
|
-
export type { AppUniforms, BufferCreator, BufferLike, BufferRecord, BufferStore, BuffersWithUtils, ClearBuffersFn, ClearNodesFn, ClearStorageFn, ClearUniformsFn, CreatorState, DisposeBuffersFn, DisposeStorageFn, LocalNodeCreator, LocalNodeInstall, LocalNodeInstaller, NodeCreator, NodeLike, NodeRecord, NodeStore, NodesWithUtils, RebuildBuffersFn, RebuildNodesFn, RebuildStorageFn, RebuildUniformsFn, Register, RegisteredScopeUniforms, RegisteredScopes, RegisteredUniform, RegisteredUniforms, RemoveBuffersFn, RemoveNodesFn, RemoveStorageFn, RemoveUniformsFn, RootUniformInput, ScopeUniformInput, ScopedStoreType, StorageCreator, StorageLike, StorageRecord, StorageStore, StorageWithUtils, TSLConfig, TSLNode, TSLNodeLike, TSLRootState, UniformCreator, UniformValue, UniformsWithUtils };
|
|
1062
|
+
export type { AppBuffers, AppGPUStorage, AppNodes, AppUniforms, BufferCreator, BufferLike, BufferRecord, BufferStore, BuffersWithUtils, ClearBuffersFn, ClearNodesFn, ClearStorageFn, ClearUniformsFn, CreatorState, DisposeBuffersFn, DisposeStorageFn, FnNodeLike, LocalNodeCreator, LocalNodeInstall, LocalNodeInstaller, LooseNode, LooseNodeEntry, LooseNodeStore, NodeCreator, NodeLike, NodeRecord, NodeStore, NodesWithUtils, RebuildBuffersFn, RebuildNodesFn, RebuildStorageFn, RebuildUniformsFn, Register, RegisteredBufferScopes, RegisteredBuffers, RegisteredGPUStorage, RegisteredGPUStorageScopes, RegisteredNodeScopes, RegisteredNodes, RegisteredScopeUniforms, RegisteredScopes, RegisteredUniform, RegisteredUniforms, RemoveBuffersFn, RemoveNodesFn, RemoveStorageFn, RemoveUniformsFn, RootBufferInput, RootGPUStorageInput, RootNodeInput, RootUniformInput, ScopeBufferInput, ScopeGPUStorageInput, ScopeNodeInput, ScopeUniformInput, ScopedStoreType, StorageCreator, StorageLike, StorageRecord, StorageStore, StorageWithUtils, TSLConfig, TSLNode, TSLNodeLike, TSLRootState, UniformCreator, UniformValue, UniformsWithUtils };
|