@react-three/tsl 10.0.0-canary.490e4992

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 ADDED
@@ -0,0 +1,935 @@
1
+ 'use strict';
2
+
3
+ const extension = require('@react-three/fiber/extension');
4
+ const tsl = require('three/tsl');
5
+ const THREE = require('three/webgpu');
6
+ const React = require('react');
7
+ const lite = require('dequal/lite');
8
+ require('zustand/shallow');
9
+
10
+ function _interopNamespaceCompat(e) {
11
+ if (e && typeof e === 'object' && 'default' in e) return e;
12
+ const n = Object.create(null);
13
+ if (e) {
14
+ for (const k in e) {
15
+ n[k] = e[k];
16
+ }
17
+ }
18
+ n.default = e;
19
+ return n;
20
+ }
21
+
22
+ const THREE__namespace = /*#__PURE__*/_interopNamespaceCompat(THREE);
23
+ const React__namespace = /*#__PURE__*/_interopNamespaceCompat(React);
24
+
25
+ const RESOURCE_KINDS = ["nodes", "uniforms", "buffers", "gpuStorage"];
26
+ const ROOT_SCOPE = "";
27
+ const registries = /* @__PURE__ */ new WeakMap();
28
+ function getKindRegistry(store, kind) {
29
+ let registry = registries.get(store);
30
+ if (!registry) {
31
+ registry = {
32
+ nodes: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
33
+ uniforms: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
34
+ buffers: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() },
35
+ gpuStorage: { valid: /* @__PURE__ */ new Set(), staged: /* @__PURE__ */ new Map() }
36
+ };
37
+ registries.set(store, registry);
38
+ }
39
+ return registry[kind];
40
+ }
41
+ function resolvePrimary$1(store) {
42
+ return store.getState().primaryStore ?? store;
43
+ }
44
+ function isScopeValid(store, kind, scopeKey) {
45
+ return getKindRegistry(store, kind).valid.has(scopeKey);
46
+ }
47
+ function peekStaged(store, kind, scopeKey) {
48
+ return getKindRegistry(store, kind).staged.get(scopeKey);
49
+ }
50
+ function stageEntries(store, kind, scopeKey, entries) {
51
+ const { staged } = getKindRegistry(store, kind);
52
+ const current = staged.get(scopeKey);
53
+ if (current) Object.assign(current, entries);
54
+ else staged.set(scopeKey, { ...entries });
55
+ }
56
+ function readCommittedScope(map, scopeKey, isLeaf) {
57
+ if (scopeKey === ROOT_SCOPE) {
58
+ const leaves = {};
59
+ for (const [key, value] of Object.entries(map)) if (isLeaf(value)) leaves[key] = value;
60
+ return leaves;
61
+ }
62
+ const scopeData = map[scopeKey];
63
+ if (scopeData && typeof scopeData === "object" && !isLeaf(scopeData)) return scopeData;
64
+ return {};
65
+ }
66
+ function withStagedOverlay(store, kind, committed) {
67
+ const { staged } = getKindRegistry(store, kind);
68
+ if (staged.size === 0) return committed;
69
+ const view = { ...committed };
70
+ for (const [scopeKey, entries] of staged) {
71
+ if (scopeKey === ROOT_SCOPE) Object.assign(view, entries);
72
+ else view[scopeKey] = { ...view[scopeKey], ...entries };
73
+ }
74
+ return view;
75
+ }
76
+ function flushStagedScope(store, kind, scopeKey, isLeaf) {
77
+ const registry = getKindRegistry(store, kind);
78
+ const staged = registry.staged.get(scopeKey);
79
+ if (!staged) return;
80
+ registry.staged.delete(scopeKey);
81
+ const replace = !registry.valid.has(scopeKey);
82
+ store.setState((state) => {
83
+ const map = state[kind];
84
+ if (scopeKey === ROOT_SCOPE) {
85
+ if (!replace) return { [kind]: { ...map, ...staged } };
86
+ const next = {};
87
+ for (const [key, value] of Object.entries(map)) if (!isLeaf(value)) next[key] = value;
88
+ return { [kind]: { ...next, ...staged } };
89
+ }
90
+ const current = replace ? {} : map[scopeKey] ?? {};
91
+ return { [kind]: { ...map, [scopeKey]: { ...current, ...staged } } };
92
+ });
93
+ registry.valid.add(scopeKey);
94
+ }
95
+ function invalidateResource(store, kind, scopeKey) {
96
+ const registry = getKindRegistry(store, kind);
97
+ if (scopeKey === void 0) {
98
+ registry.valid.clear();
99
+ registry.staged.clear();
100
+ } else {
101
+ registry.valid.delete(scopeKey);
102
+ registry.staged.delete(scopeKey);
103
+ }
104
+ }
105
+ function rebuildResource(store, kind, userScope) {
106
+ const primary = resolvePrimary$1(store);
107
+ invalidateResource(primary, kind, userScope === "root" ? ROOT_SCOPE : userScope);
108
+ primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
109
+ }
110
+ function invalidateAllResources(store) {
111
+ for (const kind of RESOURCE_KINDS) invalidateResource(store, kind);
112
+ }
113
+ function removeResourceEntries(store, kind, names, scope, isLeaf) {
114
+ const staged = peekStaged(store, kind, scope ?? ROOT_SCOPE);
115
+ if (staged) for (const name of names) delete staged[name];
116
+ store.setState((state) => {
117
+ const map = state[kind];
118
+ if (scope) {
119
+ const currentScope = { ...map[scope] };
120
+ for (const name of names) delete currentScope[name];
121
+ return { [kind]: { ...map, [scope]: currentScope } };
122
+ }
123
+ const next = { ...map };
124
+ for (const name of names) if (isLeaf(next[name])) delete next[name];
125
+ return { [kind]: next };
126
+ });
127
+ }
128
+ function clearResourceEntries(store, kind, scope, isLeaf) {
129
+ const registry = getKindRegistry(store, kind);
130
+ if (scope && scope !== "root") registry.staged.delete(scope);
131
+ else if (scope === "root") registry.staged.delete(ROOT_SCOPE);
132
+ else registry.staged.clear();
133
+ store.setState((state) => {
134
+ const map = state[kind];
135
+ if (scope && scope !== "root") {
136
+ const { [scope]: _, ...rest } = map;
137
+ return { [kind]: rest };
138
+ }
139
+ if (scope === "root") {
140
+ const next = {};
141
+ for (const [key, value] of Object.entries(map)) if (!isLeaf(value)) next[key] = value;
142
+ return { [kind]: next };
143
+ }
144
+ return { [kind]: {} };
145
+ });
146
+ }
147
+
148
+ function clearHmrCaches(store) {
149
+ const primary = store.getState().primaryStore ?? store;
150
+ invalidateAllResources(primary);
151
+ primary.setState((state) => ({ _hmrVersion: state._hmrVersion + 1 }));
152
+ }
153
+
154
+ const isObjectLike = (value) => value !== null && (typeof value === "object" || typeof value === "function");
155
+ const hasValidLegacyNodeMarkers = (value) => {
156
+ const hasUuid = "uuid" in value;
157
+ const hasNodeType = "nodeType" in value;
158
+ const uuid = Reflect.get(value, "uuid");
159
+ const nodeType = Reflect.get(value, "nodeType");
160
+ return (hasUuid || hasNodeType) && (!hasUuid || uuid === void 0 || typeof uuid === "string") && (!hasNodeType || nodeType === void 0 || nodeType === null || typeof nodeType === "string");
161
+ };
162
+ const isUniformNode = (value) => isObjectLike(value) && "isUniformNode" in value && value.isUniformNode === true;
163
+ const isTSLNode = (value) => isObjectLike(value) && (Reflect.get(value, "isNode") === true || Reflect.get(value, "shaderNode") !== void 0 && typeof Reflect.get(value, "id") === "number" || hasValidLegacyNodeMarkers(value));
164
+ const isBufferLike = (value) => ArrayBuffer.isView(value) && !(value instanceof DataView) || isObjectLike(value) && ("isBufferAttribute" in value || "isInterleavedBufferAttribute" in value || isTSLNode(value));
165
+ const isStorageLike = (value) => isObjectLike(value) && ("isTexture" in value || "isData3DTexture" in value || isTSLNode(value));
166
+
167
+ function scopedNodeName(scope, name) {
168
+ const raw = scope ? `${scope}_${name}` : name;
169
+ const safe = raw.replace(/[^A-Za-z0-9_]/g, "_");
170
+ return /^[A-Za-z_]/.test(safe) ? safe : `_${safe}`;
171
+ }
172
+ function extractTSLValue(value) {
173
+ if (value === null || value === void 0) return value;
174
+ if (typeof value !== "object") return value;
175
+ const node = value;
176
+ if (!node.isNode) return value;
177
+ if (node.isConstNode) {
178
+ return node.value;
179
+ }
180
+ if ("value" in node) {
181
+ let extractedValue = node.value;
182
+ if (typeof node.traverse === "function") {
183
+ node.traverse((n) => {
184
+ if (n.isConstNode) {
185
+ extractedValue = n.value;
186
+ }
187
+ });
188
+ }
189
+ return extractedValue;
190
+ }
191
+ return value;
192
+ }
193
+ function vectorize(inObject) {
194
+ if (inObject === null || inObject === void 0) return inObject;
195
+ if (typeof inObject === "string") {
196
+ const isHexColor = /^(#|0x)[0-9a-f]{3,8}$/i.test(inObject);
197
+ const isNamedColor = /^[a-z]+$/i.test(inObject);
198
+ if (isHexColor || isNamedColor) {
199
+ return new THREE__namespace.Color(inObject);
200
+ }
201
+ return inObject;
202
+ }
203
+ if (typeof inObject !== "object") return inObject;
204
+ const obj = inObject;
205
+ if (obj.isNode) {
206
+ return extractTSLValue(inObject);
207
+ }
208
+ if (obj.isVector2 || obj.isVector3 || obj.isVector4) return inObject;
209
+ if (obj.isMatrix2 || obj.isMatrix3 || obj.isMatrix4) return inObject;
210
+ if (obj.isColor || obj.isEuler || obj.isQuaternion || obj.isSpherical) return inObject;
211
+ if ("r" in obj && "g" in obj && "b" in obj && typeof obj.r === "number" && typeof obj.g === "number" && typeof obj.b === "number") {
212
+ const scale = obj.r > 1 || obj.g > 1 || obj.b > 1 ? 1 / 255 : 1;
213
+ return new THREE__namespace.Color(obj.r * scale, obj.g * scale, obj.b * scale);
214
+ }
215
+ if ("x" in obj && "y" in obj && typeof obj.x === "number" && typeof obj.y === "number") {
216
+ if ("w" in obj && typeof obj.w === "number" && "z" in obj && typeof obj.z === "number") {
217
+ return new THREE__namespace.Vector4(obj.x, obj.y, obj.z, obj.w);
218
+ }
219
+ if ("z" in obj && typeof obj.z === "number") {
220
+ return new THREE__namespace.Vector3(obj.x, obj.y, obj.z);
221
+ }
222
+ return new THREE__namespace.Vector2(obj.x, obj.y);
223
+ }
224
+ return inObject;
225
+ }
226
+
227
+ function createUniform(inName, node, scope) {
228
+ if (node.type === "UniformNode") return node;
229
+ const inValue = vectorize(node);
230
+ const newUniform = tsl.uniform(inValue);
231
+ if (typeof newUniform.setName === "function") {
232
+ newUniform.setName(scopedNodeName(scope, inName));
233
+ }
234
+ return newUniform;
235
+ }
236
+
237
+ const primaries = /* @__PURE__ */ new Set();
238
+ let config = null;
239
+ function trackPrimary(store) {
240
+ primaries.add(store);
241
+ if (config) applyConfig(store, config);
242
+ }
243
+ function untrackPrimary(store) {
244
+ primaries.delete(store);
245
+ }
246
+ function applyConfig(store, config2) {
247
+ const add = (scope, inputs) => {
248
+ if (!inputs) return;
249
+ const scopeKey = scope ?? ROOT_SCOPE;
250
+ const existing = readCommittedScope(store.getState().uniforms, scopeKey, isUniformNode);
251
+ const fresh = {};
252
+ for (const [name, value] of Object.entries(inputs)) {
253
+ if (!(name in existing)) fresh[name] = createUniform(name, value, scope);
254
+ }
255
+ if (!Object.keys(fresh).length) return;
256
+ stageEntries(store, "uniforms", scopeKey, fresh);
257
+ flushStagedScope(store, "uniforms", scopeKey, isUniformNode);
258
+ };
259
+ add(void 0, config2.uniforms);
260
+ for (const [scope, inputs] of Object.entries(config2.scopes ?? {})) add(scope, inputs);
261
+ }
262
+ function configureTSL(next) {
263
+ config = {
264
+ uniforms: { ...config?.uniforms, ...next.uniforms },
265
+ scopes: { ...config?.scopes, ...next.scopes }
266
+ };
267
+ for (const store of primaries) applyConfig(store, config);
268
+ }
269
+
270
+ const SHARED_KEYS = ["uniforms", "nodes", "buffers", "gpuStorage", "_hmrVersion"];
271
+ const following = /* @__PURE__ */ new WeakMap();
272
+ function createResourceState() {
273
+ return { uniforms: {}, nodes: {}, buffers: {}, gpuStorage: {}, _hmrVersion: 0 };
274
+ }
275
+ function setup(store) {
276
+ const primary = store.getState().primaryStore;
277
+ if (!primary || primary === store) {
278
+ store.setState(createResourceState());
279
+ trackPrimary(store);
280
+ return;
281
+ }
282
+ followPrimary(store, primary);
283
+ }
284
+ function followPrimary(store, primary) {
285
+ const sync = () => {
286
+ const source = primary.getState();
287
+ const target = store.getState();
288
+ let patch = null;
289
+ for (const key of SHARED_KEYS) {
290
+ if (target[key] !== source[key]) (patch ?? (patch = {}))[key] = source[key];
291
+ }
292
+ if (patch) store.setState(patch);
293
+ };
294
+ sync();
295
+ following.set(store, primary.subscribe(sync));
296
+ }
297
+ function dispose(store) {
298
+ untrackPrimary(store);
299
+ following.get(store)?.();
300
+ following.delete(store);
301
+ }
302
+ let registered = false;
303
+ function ensureTSLExtension() {
304
+ if (registered) return;
305
+ registered = true;
306
+ extension.registerRootExtension({
307
+ name: "@react-three/tsl",
308
+ setup,
309
+ dispose,
310
+ // Resolves primaryStore itself, so every canvas sharing a renderer refreshes the same resources.
311
+ hmr: clearHmrCaches
312
+ });
313
+ }
314
+
315
+ var __defProp = Object.defineProperty;
316
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
317
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
318
+ var _a, _b;
319
+ const INTERNAL_DATA = Symbol("ScopedStore.data");
320
+ const INTERNAL_IS_LEAF = Symbol("ScopedStore.isLeaf");
321
+ _b = INTERNAL_DATA, _a = INTERNAL_IS_LEAF;
322
+ const _ScopedStore = class _ScopedStore {
323
+ constructor(data, isLeaf) {
324
+ __publicField(this, _b);
325
+ __publicField(this, _a);
326
+ this[INTERNAL_DATA] = data;
327
+ this[INTERNAL_IS_LEAF] = isLeaf;
328
+ return new Proxy(this, {
329
+ get(target, prop, receiver) {
330
+ if (typeof prop === "string") {
331
+ if (prop === "scope" || prop === "has" || prop === "keys") {
332
+ return Reflect.get(target, prop, receiver);
333
+ }
334
+ return target[INTERNAL_DATA][prop];
335
+ }
336
+ return Reflect.get(target, prop, receiver);
337
+ },
338
+ has(target, prop) {
339
+ return typeof prop === "string" ? prop in target[INTERNAL_DATA] : Reflect.has(target, prop);
340
+ },
341
+ ownKeys(target) {
342
+ return Reflect.ownKeys(target[INTERNAL_DATA]);
343
+ },
344
+ getOwnPropertyDescriptor(target, prop) {
345
+ if (typeof prop === "string" && prop in target[INTERNAL_DATA]) {
346
+ return {
347
+ configurable: true,
348
+ enumerable: true,
349
+ value: target[INTERNAL_DATA][prop]
350
+ };
351
+ }
352
+ return void 0;
353
+ }
354
+ });
355
+ }
356
+ /**
357
+ * Access a nested scope by key.
358
+ * If the key doesn't exist or isn't a scope object, returns an empty ScopedStore.
359
+ */
360
+ scope(key) {
361
+ const value = this[INTERNAL_DATA][key];
362
+ const isLeaf = this[INTERNAL_IS_LEAF];
363
+ const scope = value && typeof value === "object" && !isLeaf(value) ? value : {};
364
+ return new _ScopedStore(scope, isLeaf);
365
+ }
366
+ /**
367
+ * Check if a key exists in the store.
368
+ */
369
+ has(key) {
370
+ return key in this[INTERNAL_DATA];
371
+ }
372
+ /**
373
+ * Get all keys in the store.
374
+ */
375
+ keys() {
376
+ return Object.keys(this[INTERNAL_DATA]);
377
+ }
378
+ };
379
+ let ScopedStore = _ScopedStore;
380
+ function createScopedStore(data, isLeaf) {
381
+ return new ScopedStore(data, isLeaf);
382
+ }
383
+ function createLazyCreatorState(state, store) {
384
+ let _uniforms = null;
385
+ let _nodes = null;
386
+ let _buffers = null;
387
+ let _gpuStorage = null;
388
+ const view = (kind) => store ? withStagedOverlay(store, kind, state[kind]) : state[kind];
389
+ return Object.create(state, {
390
+ uniforms: {
391
+ get() {
392
+ return _uniforms ?? (_uniforms = createScopedStore(view("uniforms"), isUniformNode));
393
+ }
394
+ },
395
+ nodes: {
396
+ get() {
397
+ return _nodes ?? (_nodes = createScopedStore(view("nodes"), isTSLNode));
398
+ }
399
+ },
400
+ buffers: {
401
+ get() {
402
+ return _buffers ?? (_buffers = createScopedStore(view("buffers"), isBufferLike));
403
+ }
404
+ },
405
+ gpuStorage: {
406
+ get() {
407
+ return _gpuStorage ?? (_gpuStorage = createScopedStore(view("gpuStorage"), isStorageLike));
408
+ }
409
+ }
410
+ });
411
+ }
412
+
413
+ function resolvePrimary(local) {
414
+ return local.getState().primaryStore ?? local;
415
+ }
416
+ function usePrimaryStore() {
417
+ const local = extension.useStore();
418
+ const primary = local.getState().primaryStore;
419
+ return React.useMemo(() => primary ?? local, [primary, local]);
420
+ }
421
+ function usePrimaryThree(selector = (state) => state, equalityFn) {
422
+ const local = extension.useStore();
423
+ const primary = resolvePrimary(local);
424
+ return primary(selector, equalityFn);
425
+ }
426
+
427
+ function useCompareMemoize(value, deep) {
428
+ const ref = React.useRef(value);
429
+ const compare = lite.dequal ;
430
+ if (!compare(value, ref.current)) {
431
+ ref.current = value;
432
+ }
433
+ return ref.current;
434
+ }
435
+
436
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" && (window.document?.createElement || window.navigator?.product === "ReactNative") ? React__namespace.useLayoutEffect : React__namespace.useEffect;
437
+
438
+ function useScopedResource(options) {
439
+ const { store, kind, scope, isLeaf, create, prepare, reconcile, input } = options;
440
+ const scopeKey = scope ?? ROOT_SCOPE;
441
+ const version = store((s) => s._hmrVersion);
442
+ const entries = React.useMemo(() => {
443
+ const staged = peekStaged(store, kind, scopeKey);
444
+ const reusable = isScopeValid(store, kind, scopeKey);
445
+ const map = store.getState()[kind];
446
+ const committed = reusable ? readCommittedScope(map, scopeKey, isLeaf) : void 0;
447
+ const result = {};
448
+ let fresh = null;
449
+ for (const [name, candidate] of Object.entries(create())) {
450
+ const existing = staged?.[name] ?? committed?.[name];
451
+ if (existing !== void 0) {
452
+ result[name] = existing;
453
+ reconcile?.(existing, candidate);
454
+ } else {
455
+ const value = prepare ? prepare(name, candidate) : candidate;
456
+ result[name] = value;
457
+ (fresh ?? (fresh = {}))[name] = value;
458
+ }
459
+ }
460
+ if (fresh) stageEntries(store, kind, scopeKey, fresh);
461
+ return result;
462
+ }, [store, kind, scopeKey, version, input]);
463
+ useIsomorphicLayoutEffect(() => {
464
+ flushStagedScope(store, kind, scopeKey, isLeaf);
465
+ });
466
+ return entries;
467
+ }
468
+
469
+ function useUniforms(creatorOrScope, scope) {
470
+ const store = usePrimaryStore();
471
+ const removeUniforms = React.useCallback(
472
+ (names, targetScope) => removeResourceEntries(store, "uniforms", Array.isArray(names) ? names : [names], targetScope, isUniformNode),
473
+ [store]
474
+ );
475
+ const clearUniforms = React.useCallback(
476
+ (targetScope) => clearResourceEntries(store, "uniforms", targetScope, isUniformNode),
477
+ [store]
478
+ );
479
+ const rebuildUniforms = React.useCallback(
480
+ (targetScope) => rebuildResource(store, "uniforms", targetScope),
481
+ [store]
482
+ );
483
+ const processedInput = React.useMemo(() => {
484
+ let raw = creatorOrScope;
485
+ if (typeof creatorOrScope === "function") {
486
+ const wrappedState = createLazyCreatorState(store.getState(), store);
487
+ raw = creatorOrScope(wrappedState);
488
+ }
489
+ if (raw && typeof raw === "object" && !Array.isArray(raw)) {
490
+ const normalized = {};
491
+ for (const [key, value] of Object.entries(raw)) {
492
+ normalized[key] = vectorize(value);
493
+ }
494
+ return { candidates: raw, normalized };
495
+ }
496
+ return { candidates: raw, normalized: raw };
497
+ }, [creatorOrScope, store]);
498
+ const memoizedInput = useCompareMemoize(processedInput.normalized);
499
+ const isReader = memoizedInput === void 0 || typeof memoizedInput === "string";
500
+ const storeUniforms = usePrimaryThree((s) => s.uniforms);
501
+ const created = useScopedResource({
502
+ store,
503
+ kind: "uniforms",
504
+ scope: isReader ? void 0 : scope,
505
+ isLeaf: isUniformNode,
506
+ input: memoizedInput,
507
+ create: () => {
508
+ if (isReader) return {};
509
+ if (typeof processedInput.candidates !== "object" || processedInput.candidates === null)
510
+ throw new Error("Invalid uniform input");
511
+ return processedInput.candidates;
512
+ },
513
+ prepare: (name, candidate) => createUniform(name, candidate, scope),
514
+ reconcile: (existing, candidate) => {
515
+ const existingVal = existing.value;
516
+ const newVal = vectorize(candidate);
517
+ let equals = newVal === existingVal;
518
+ if (!equals && hasEqualsMethod(existingVal) && hasEqualsMethod(newVal)) {
519
+ if (isThreeVector(existingVal) && isThreeVector(newVal)) {
520
+ equals = vectorEquals(existingVal, newVal);
521
+ } else if (isSameThreeType(existingVal, newVal)) {
522
+ equals = existingVal.equals(newVal);
523
+ }
524
+ }
525
+ if (!equals) existing.value = newVal;
526
+ }
527
+ });
528
+ let uniforms = created;
529
+ if (memoizedInput === void 0) {
530
+ uniforms = storeUniforms;
531
+ } else if (typeof memoizedInput === "string") {
532
+ const scopeData = storeUniforms[memoizedInput];
533
+ uniforms = scopeData && !isUniformNode(scopeData) ? scopeData : {};
534
+ }
535
+ return { ...uniforms, removeUniforms, clearUniforms, rebuildUniforms };
536
+ }
537
+ function rebuildAllUniforms(store, scope) {
538
+ rebuildResource(store, "uniforms", scope);
539
+ }
540
+ function isThreeVector(inVector) {
541
+ if (!inVector) return false;
542
+ return inVector.isVector2 || inVector.isVector3 || inVector.isVector4;
543
+ }
544
+ function vectorEquals(a, b) {
545
+ if (a.isVector2 && b.isVector2) {
546
+ return a.equals(b);
547
+ }
548
+ if (a.isVector3 && b.isVector3) {
549
+ return a.equals(b);
550
+ }
551
+ if (a.isVector4 && b.isVector4) {
552
+ return a.equals(b);
553
+ }
554
+ return false;
555
+ }
556
+ function hasEqualsMethod(value) {
557
+ return value !== null && typeof value === "object" && "equals" in value && typeof value.equals === "function";
558
+ }
559
+ function isSameThreeType(a, b) {
560
+ const obj_a = a;
561
+ const obj_b = b;
562
+ return obj_a.isColor && obj_b.isColor || obj_a.isMatrix2 && obj_b.isMatrix2 || obj_a.isMatrix3 && obj_b.isMatrix3 || obj_a.isMatrix4 && obj_b.isMatrix4 || obj_a.isEuler && obj_b.isEuler || obj_a.isQuaternion && obj_b.isQuaternion;
563
+ }
564
+
565
+ const uniformOf = tsl.uniform;
566
+ function useUniform(name, value) {
567
+ const store = usePrimaryStore();
568
+ const registered = useScopedResource({
569
+ store,
570
+ kind: "uniforms",
571
+ scope: void 0,
572
+ isLeaf: isUniformNode,
573
+ input: name,
574
+ create: () => value === void 0 ? {} : { [name]: value },
575
+ prepare: createNamedUniform,
576
+ reconcile: (existing2) => {
577
+ if (value !== void 0 && !isTSLNode(value) && !isUniformNode(value)) {
578
+ existing2.value = typeof value === "string" ? new THREE.Color(value) : value;
579
+ }
580
+ }
581
+ });
582
+ if (registered[name]) return registered[name];
583
+ const existing = peekStaged(store, "uniforms", ROOT_SCOPE)?.[name] ?? store.getState().uniforms[name];
584
+ if (existing && isUniformNode(existing)) return existing;
585
+ throw new Error(
586
+ `[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
587
+ );
588
+ }
589
+ function createNamedUniform(name, value) {
590
+ let node;
591
+ if (isUniformNode(value)) {
592
+ node = value;
593
+ } else if (isTSLNode(value)) {
594
+ node = uniformOf(value);
595
+ } else if (typeof value === "string") {
596
+ node = tsl.uniform(new THREE.Color(value));
597
+ } else {
598
+ node = uniformOf(value);
599
+ }
600
+ if (typeof node.setName === "function") {
601
+ node.setName(name);
602
+ }
603
+ return node;
604
+ }
605
+
606
+ function useNodes(creatorOrScope, scope) {
607
+ const store = usePrimaryStore();
608
+ const removeNodes = React.useCallback(
609
+ (names, targetScope) => removeResourceEntries(store, "nodes", Array.isArray(names) ? names : [names], targetScope, isTSLNode),
610
+ [store]
611
+ );
612
+ const clearNodes = React.useCallback(
613
+ (targetScope) => clearResourceEntries(store, "nodes", targetScope, isTSLNode),
614
+ [store]
615
+ );
616
+ const rebuildNodes = React.useCallback(
617
+ (targetScope) => rebuildResource(store, "nodes", targetScope),
618
+ [store]
619
+ );
620
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
621
+ const storeNodes = usePrimaryThree((s) => s.nodes);
622
+ const created = useScopedResource({
623
+ store,
624
+ kind: "nodes",
625
+ scope: isReader ? void 0 : scope,
626
+ isLeaf: isTSLNode,
627
+ create: () => {
628
+ if (isReader) return {};
629
+ return creatorOrScope(createLazyCreatorState(store.getState(), store));
630
+ },
631
+ prepare: (name, node) => {
632
+ const setName = Reflect.get(node, "setName");
633
+ if (typeof setName === "function") setName.call(node, scopedNodeName(scope, name));
634
+ return node;
635
+ }
636
+ });
637
+ let nodes = created;
638
+ if (creatorOrScope === void 0) {
639
+ nodes = storeNodes;
640
+ } else if (typeof creatorOrScope === "string") {
641
+ const scopeData = storeNodes[creatorOrScope];
642
+ nodes = scopeData && !isTSLNode(scopeData) ? scopeData : {};
643
+ }
644
+ return { ...nodes, removeNodes, clearNodes, rebuildNodes };
645
+ }
646
+ function rebuildAllNodes(store, scope) {
647
+ rebuildResource(store, "nodes", scope);
648
+ }
649
+ function useLocalNodes(creator) {
650
+ const store = usePrimaryStore();
651
+ const uniforms = usePrimaryThree((s) => s.uniforms);
652
+ const nodes = usePrimaryThree((s) => s.nodes);
653
+ const textures = usePrimaryThree((s) => s.textures);
654
+ const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
655
+ return React.useMemo(() => {
656
+ const wrappedState = createLazyCreatorState(store.getState(), store);
657
+ return creator(wrappedState);
658
+ }, [store, creator, uniforms, nodes, textures, hmrVersion]);
659
+ }
660
+
661
+ const disposeBuffer = (buffer) => {
662
+ if (buffer === null || typeof buffer !== "object") return;
663
+ if ("dispose" in buffer && typeof buffer.dispose === "function") {
664
+ buffer.dispose();
665
+ }
666
+ };
667
+ function useBuffers(creatorOrScope, scope) {
668
+ const store = usePrimaryStore();
669
+ const removeBuffers = React.useCallback(
670
+ (names, targetScope) => removeResourceEntries(store, "buffers", Array.isArray(names) ? names : [names], targetScope, isBufferLike),
671
+ [store]
672
+ );
673
+ const clearBuffers = React.useCallback(
674
+ (targetScope) => clearResourceEntries(store, "buffers", targetScope, isBufferLike),
675
+ [store]
676
+ );
677
+ const rebuildBuffers = React.useCallback(
678
+ (targetScope) => rebuildResource(store, "buffers", targetScope),
679
+ [store]
680
+ );
681
+ const disposeBuffers = React.useCallback(
682
+ (names, targetScope) => {
683
+ const nameArray = Array.isArray(names) ? names : [names];
684
+ const state = store.getState();
685
+ for (const name of nameArray) {
686
+ const buffer = targetScope ? state.buffers[targetScope]?.[name] : state.buffers[name];
687
+ if (buffer && isBufferLike(buffer)) {
688
+ disposeBuffer(buffer);
689
+ }
690
+ }
691
+ removeBuffers(names, targetScope);
692
+ },
693
+ [store, removeBuffers]
694
+ );
695
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
696
+ const storeBuffers = usePrimaryThree((s) => s.buffers);
697
+ const created = useScopedResource({
698
+ store,
699
+ kind: "buffers",
700
+ scope: isReader ? void 0 : scope,
701
+ isLeaf: isBufferLike,
702
+ create: () => {
703
+ if (isReader) return {};
704
+ return creatorOrScope(createLazyCreatorState(store.getState(), store));
705
+ },
706
+ prepare: (name, buffer) => {
707
+ const setName = Reflect.get(buffer, "setName");
708
+ if (typeof setName === "function") setName.call(buffer, scopedNodeName(scope, name));
709
+ return buffer;
710
+ }
711
+ });
712
+ let buffers = created;
713
+ if (creatorOrScope === void 0) {
714
+ buffers = storeBuffers;
715
+ } else if (typeof creatorOrScope === "string") {
716
+ const scopeData = storeBuffers[creatorOrScope];
717
+ buffers = scopeData && !isBufferLike(scopeData) ? scopeData : {};
718
+ }
719
+ return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
720
+ }
721
+ function rebuildAllBuffers(store, scope) {
722
+ rebuildResource(store, "buffers", scope);
723
+ }
724
+
725
+ const disposeStorage = (storage) => {
726
+ if (storage === null || typeof storage !== "object") return;
727
+ if ("dispose" in storage && typeof storage.dispose === "function") {
728
+ storage.dispose();
729
+ }
730
+ };
731
+ function useGPUStorage(creatorOrScope, scope) {
732
+ const store = usePrimaryStore();
733
+ const removeStorage = React.useCallback(
734
+ (names, targetScope) => removeResourceEntries(store, "gpuStorage", Array.isArray(names) ? names : [names], targetScope, isStorageLike),
735
+ [store]
736
+ );
737
+ const clearStorage = React.useCallback(
738
+ (targetScope) => clearResourceEntries(store, "gpuStorage", targetScope, isStorageLike),
739
+ [store]
740
+ );
741
+ const rebuildStorage = React.useCallback(
742
+ (targetScope) => rebuildResource(store, "gpuStorage", targetScope),
743
+ [store]
744
+ );
745
+ const disposeStorageFn = React.useCallback(
746
+ (names, targetScope) => {
747
+ const nameArray = Array.isArray(names) ? names : [names];
748
+ const state = store.getState();
749
+ for (const name of nameArray) {
750
+ const storage = targetScope ? state.gpuStorage[targetScope]?.[name] : state.gpuStorage[name];
751
+ if (storage && isStorageLike(storage)) {
752
+ disposeStorage(storage);
753
+ }
754
+ }
755
+ removeStorage(names, targetScope);
756
+ },
757
+ [store, removeStorage]
758
+ );
759
+ const isReader = creatorOrScope === void 0 || typeof creatorOrScope === "string";
760
+ const storeStorage = usePrimaryThree((s) => s.gpuStorage);
761
+ const created = useScopedResource({
762
+ store,
763
+ kind: "gpuStorage",
764
+ scope: isReader ? void 0 : scope,
765
+ isLeaf: isStorageLike,
766
+ create: () => {
767
+ if (isReader) return {};
768
+ return creatorOrScope(createLazyCreatorState(store.getState(), store));
769
+ },
770
+ prepare: (name, storage) => {
771
+ const label = scopedNodeName(scope, name);
772
+ const setName = Reflect.get(storage, "setName");
773
+ if (typeof setName === "function") setName.call(storage, label);
774
+ if ("name" in storage && typeof storage.name === "string") {
775
+ storage.name = label;
776
+ }
777
+ return storage;
778
+ }
779
+ });
780
+ let gpuStorage = created;
781
+ if (creatorOrScope === void 0) {
782
+ gpuStorage = storeStorage;
783
+ } else if (typeof creatorOrScope === "string") {
784
+ const scopeData = storeStorage[creatorOrScope];
785
+ gpuStorage = scopeData && !isStorageLike(scopeData) ? scopeData : {};
786
+ }
787
+ return {
788
+ ...gpuStorage,
789
+ removeStorage,
790
+ clearStorage,
791
+ rebuildStorage,
792
+ disposeStorage: disposeStorageFn
793
+ };
794
+ }
795
+ function rebuildAllStorage(store, scope) {
796
+ rebuildResource(store, "gpuStorage", scope);
797
+ }
798
+
799
+ const NO_PASSES = Object.freeze({});
800
+ function renderThroughPipeline(store) {
801
+ return () => {
802
+ const pipeline = store.getState().renderPipeline;
803
+ if (pipeline?.render) pipeline.render();
804
+ };
805
+ }
806
+ function useRenderPipeline(mainCB, setupCB) {
807
+ const store = extension.useStore();
808
+ const { scene, camera, renderer, isLegacy } = extension.useThree();
809
+ const callbacksRanRef = React.useRef(false);
810
+ const scenePassCacheRef = React.useRef(null);
811
+ const forceScenePassRebuildRef = React.useRef(false);
812
+ const mainCBRef = React.useRef(mainCB);
813
+ const setupCBRef = React.useRef(setupCB);
814
+ mainCBRef.current = mainCB;
815
+ setupCBRef.current = setupCB;
816
+ const [rebuildVersion, setRebuildVersion] = React.useState(0);
817
+ React.useEffect(() => {
818
+ callbacksRanRef.current = false;
819
+ forceScenePassRebuildRef.current = true;
820
+ }, []);
821
+ const clearPasses = React.useCallback(() => {
822
+ store.setState({ passes: {} });
823
+ }, [store]);
824
+ const reset = React.useCallback(() => {
825
+ store.setState({
826
+ renderPipeline: null,
827
+ passes: {}
828
+ });
829
+ extension.setRenderOverride(store, null);
830
+ scenePassCacheRef.current?.scenePass.dispose();
831
+ callbacksRanRef.current = false;
832
+ scenePassCacheRef.current = null;
833
+ forceScenePassRebuildRef.current = false;
834
+ }, [store]);
835
+ const rebuild = React.useCallback(() => {
836
+ callbacksRanRef.current = false;
837
+ forceScenePassRebuildRef.current = true;
838
+ setRebuildVersion((v) => v + 1);
839
+ }, []);
840
+ React.useLayoutEffect(() => {
841
+ if (isLegacy) {
842
+ throw new Error("useRenderPipeline is only available with WebGPU renderer. Set renderer prop on Canvas.");
843
+ }
844
+ if (!renderer || !scene || !camera) return;
845
+ if (!("isWebGPURenderer" in renderer)) {
846
+ throw new Error("useRenderPipeline: renderer is not a WebGPURenderer but isLegacy is false.");
847
+ }
848
+ const state = store.getState();
849
+ const set = store.setState;
850
+ try {
851
+ let pipeline = state.renderPipeline ?? null;
852
+ let currentPasses = { ...state.passes };
853
+ let justCreatedPipeline = false;
854
+ if (!pipeline) {
855
+ pipeline = new THREE__namespace.RenderPipeline(renderer);
856
+ justCreatedPipeline = true;
857
+ }
858
+ const cacheValid = !forceScenePassRebuildRef.current && scenePassCacheRef.current && scenePassCacheRef.current.sceneUuid === scene.uuid && scenePassCacheRef.current.cameraUuid === camera.uuid;
859
+ let outgoingScenePass = null;
860
+ let scenePass;
861
+ if (cacheValid) {
862
+ scenePass = scenePassCacheRef.current.scenePass;
863
+ } else {
864
+ outgoingScenePass = scenePassCacheRef.current?.scenePass ?? null;
865
+ scenePass = tsl.pass(scene, camera);
866
+ scenePassCacheRef.current = { sceneUuid: scene.uuid, cameraUuid: camera.uuid, scenePass };
867
+ forceScenePassRebuildRef.current = false;
868
+ }
869
+ currentPasses.scenePass = scenePass;
870
+ let outputNodeChanged = false;
871
+ if (!pipeline.outputNode || justCreatedPipeline || pipeline.outputNode === outgoingScenePass) {
872
+ pipeline.outputNode = scenePass;
873
+ outputNodeChanged = true;
874
+ }
875
+ set({ renderPipeline: pipeline, passes: currentPasses });
876
+ extension.setRenderOverride(store, renderThroughPipeline(store));
877
+ const shouldRunCallbacks = justCreatedPipeline || !callbacksRanRef.current || !cacheValid;
878
+ if (shouldRunCallbacks) {
879
+ if (setupCBRef.current) {
880
+ const freshState = Object.assign({}, store.getState(), {
881
+ renderPipeline: pipeline,
882
+ passes: { ...currentPasses, scenePass },
883
+ renderer,
884
+ isLegacy: false
885
+ });
886
+ const setupResult = setupCBRef.current(freshState);
887
+ if (setupResult && typeof setupResult === "object") {
888
+ currentPasses = { ...currentPasses, ...setupResult };
889
+ set({ passes: currentPasses });
890
+ }
891
+ }
892
+ if (mainCBRef.current) {
893
+ const freshState = Object.assign({}, store.getState(), {
894
+ renderPipeline: pipeline,
895
+ passes: { ...currentPasses, scenePass },
896
+ renderer,
897
+ isLegacy: false
898
+ });
899
+ const mainResult = mainCBRef.current(freshState);
900
+ if (mainResult && typeof mainResult === "object") {
901
+ currentPasses = { ...currentPasses, ...mainResult };
902
+ set({ passes: currentPasses });
903
+ }
904
+ }
905
+ outputNodeChanged = true;
906
+ callbacksRanRef.current = true;
907
+ }
908
+ if (outputNodeChanged) pipeline.needsUpdate = true;
909
+ if (outgoingScenePass && outgoingScenePass !== scenePass) outgoingScenePass.dispose();
910
+ } catch (error) {
911
+ console.warn("[useRenderPipeline] Setup failed; the render pipeline was not configured:", error);
912
+ }
913
+ }, [store, renderer, scene, camera, isLegacy, rebuildVersion]);
914
+ const passes = extension.useThree((s) => s.passes) ?? NO_PASSES;
915
+ const renderPipeline = extension.useThree((s) => s.renderPipeline) ?? null;
916
+ const actions = { passes, clearPasses, reset, rebuild };
917
+ if (renderPipeline) return { ...actions, isReady: true, renderPipeline };
918
+ return { ...actions, isReady: false, renderPipeline: null };
919
+ }
920
+
921
+ ensureTSLExtension();
922
+
923
+ exports.configureTSL = configureTSL;
924
+ exports.createScopedStore = createScopedStore;
925
+ exports.rebuildAllBuffers = rebuildAllBuffers;
926
+ exports.rebuildAllNodes = rebuildAllNodes;
927
+ exports.rebuildAllStorage = rebuildAllStorage;
928
+ exports.rebuildAllUniforms = rebuildAllUniforms;
929
+ exports.useBuffers = useBuffers;
930
+ exports.useGPUStorage = useGPUStorage;
931
+ exports.useLocalNodes = useLocalNodes;
932
+ exports.useNodes = useNodes;
933
+ exports.useRenderPipeline = useRenderPipeline;
934
+ exports.useUniform = useUniform;
935
+ exports.useUniforms = useUniforms;