@react-three/tsl 10.0.0-canary.f81281f → 10.0.0-canary.fbc659b
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 +333 -32
- package/dist/index.d.cts +120 -50
- package/dist/index.d.mts +120 -50
- package/dist/index.d.ts +120 -50
- package/dist/index.mjs +335 -34
- package/package.json +3 -3
- package/readme.md +1 -1
package/dist/index.cjs
CHANGED
|
@@ -177,6 +177,10 @@ function extractTSLValue(value) {
|
|
|
177
177
|
if (node.isConstNode) {
|
|
178
178
|
return node.value;
|
|
179
179
|
}
|
|
180
|
+
if (node.isVarNode && node.node) {
|
|
181
|
+
const inner = extractTSLValue(node.node);
|
|
182
|
+
return inner === node.node ? value : inner;
|
|
183
|
+
}
|
|
180
184
|
if ("value" in node) {
|
|
181
185
|
let extractedValue = node.value;
|
|
182
186
|
if (typeof node.traverse === "function") {
|
|
@@ -312,33 +316,129 @@ function ensureTSLExtension() {
|
|
|
312
316
|
});
|
|
313
317
|
}
|
|
314
318
|
|
|
319
|
+
const SCOPE = Symbol("readTracking.scope");
|
|
320
|
+
const LEAF_GUARDS = {
|
|
321
|
+
uniforms: isUniformNode,
|
|
322
|
+
nodes: isTSLNode,
|
|
323
|
+
buffers: isBufferLike,
|
|
324
|
+
gpuStorage: isStorageLike
|
|
325
|
+
};
|
|
326
|
+
function isScope(kind, value) {
|
|
327
|
+
return !!value && typeof value === "object" && !LEAF_GUARDS[kind](value);
|
|
328
|
+
}
|
|
329
|
+
function classify(kind, value) {
|
|
330
|
+
return kind !== "textures" && isScope(kind, value) ? SCOPE : value;
|
|
331
|
+
}
|
|
332
|
+
function resolveContainer(kind, root, scopePath) {
|
|
333
|
+
let container = root;
|
|
334
|
+
for (const key of scopePath) {
|
|
335
|
+
const next = container?.[key];
|
|
336
|
+
container = isScope(kind, next) ? next : void 0;
|
|
337
|
+
}
|
|
338
|
+
return container;
|
|
339
|
+
}
|
|
340
|
+
function observeNow(read, view) {
|
|
341
|
+
if (read.kind === "textures") {
|
|
342
|
+
const map = view("textures");
|
|
343
|
+
if (read.op === "keys") return [...map.keys()];
|
|
344
|
+
return read.op === "has" ? map.has(read.path[0]) : map.get(read.path[0]);
|
|
345
|
+
}
|
|
346
|
+
const root = view(read.kind);
|
|
347
|
+
if (read.op === "keys") return Object.keys(resolveContainer(read.kind, root, read.path) ?? {});
|
|
348
|
+
const container = resolveContainer(read.kind, root, read.path.slice(0, -1));
|
|
349
|
+
const key = read.path[read.path.length - 1];
|
|
350
|
+
if (read.op === "has") return !!container && key in container;
|
|
351
|
+
return classify(read.kind, container?.[key]);
|
|
352
|
+
}
|
|
353
|
+
function sameKeys(a, b) {
|
|
354
|
+
return a.length === b.length && a.every((key, i) => key === b[i]);
|
|
355
|
+
}
|
|
356
|
+
function hasChanged(read, view) {
|
|
357
|
+
const now = observeNow(read, view);
|
|
358
|
+
return read.op === "keys" ? !sameKeys(read.seen, now) : !Object.is(read.seen, now);
|
|
359
|
+
}
|
|
360
|
+
const warned = /* @__PURE__ */ new Set();
|
|
361
|
+
function warnOnce(message) {
|
|
362
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
|
|
363
|
+
if (warned.has(message)) return;
|
|
364
|
+
warned.add(message);
|
|
365
|
+
console.warn(message);
|
|
366
|
+
}
|
|
367
|
+
function describeRead(kind, path) {
|
|
368
|
+
if (kind === "textures") return `textures.get('${path[0]}')`;
|
|
369
|
+
const scopes = path.slice(0, -1).map((key) => `.scope('${key}')`);
|
|
370
|
+
return `${kind}${scopes.join("")}.${path[path.length - 1] ?? ""}`;
|
|
371
|
+
}
|
|
372
|
+
function createReadTracker(hookName) {
|
|
373
|
+
const tracker = {
|
|
374
|
+
reads: [],
|
|
375
|
+
closed: false,
|
|
376
|
+
stale: false,
|
|
377
|
+
observe: (read) => {
|
|
378
|
+
if (tracker.closed) {
|
|
379
|
+
warnOnce(
|
|
380
|
+
`[${hookName}] ${describeRead(read.kind, read.path)} was read after the creator returned, probably inside Fn(). That read is not tracked, so replacing the resource will not rebuild this graph. Read the resource in the creator and close over it in Fn.`
|
|
381
|
+
);
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
tracker.reads.push(read);
|
|
385
|
+
}
|
|
386
|
+
};
|
|
387
|
+
return tracker;
|
|
388
|
+
}
|
|
389
|
+
function isTrackerStale(tracker, view) {
|
|
390
|
+
if (!tracker.stale) tracker.stale = tracker.reads.some((read) => hasChanged(read, view));
|
|
391
|
+
return tracker.stale;
|
|
392
|
+
}
|
|
393
|
+
function warnMissingReads(hookName) {
|
|
394
|
+
return {
|
|
395
|
+
nested: false,
|
|
396
|
+
observe: (read) => {
|
|
397
|
+
if (read.op !== "get" || read.seen !== void 0) return;
|
|
398
|
+
warnOnce(
|
|
399
|
+
`[${hookName}] The creator read ${describeRead(read.kind, read.path)}, which does not exist yet. ${hookName} creators run once per generation, so this one will not re-run when it appears. Register it earlier (above this hook, or higher in the tree), or call rebuild* once it exists. To probe on purpose, use .has().`
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
}
|
|
404
|
+
|
|
315
405
|
var __defProp = Object.defineProperty;
|
|
316
406
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
317
407
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
318
|
-
var _a, _b;
|
|
408
|
+
var _a, _b, _c, _d;
|
|
319
409
|
const INTERNAL_DATA = Symbol("ScopedStore.data");
|
|
320
410
|
const INTERNAL_IS_LEAF = Symbol("ScopedStore.isLeaf");
|
|
321
|
-
|
|
411
|
+
const INTERNAL_READS = Symbol("ScopedStore.reads");
|
|
412
|
+
const INTERNAL_CHILDREN = Symbol("ScopedStore.children");
|
|
413
|
+
_d = INTERNAL_DATA, _c = INTERNAL_IS_LEAF, _b = INTERNAL_READS, _a = INTERNAL_CHILDREN;
|
|
322
414
|
const _ScopedStore = class _ScopedStore {
|
|
323
|
-
constructor(data, isLeaf) {
|
|
415
|
+
constructor(data, isLeaf, reads) {
|
|
416
|
+
/** @internal */
|
|
417
|
+
__publicField(this, _d);
|
|
418
|
+
/** @internal */
|
|
419
|
+
__publicField(this, _c);
|
|
420
|
+
/** @internal */
|
|
324
421
|
__publicField(this, _b);
|
|
422
|
+
/** @internal */
|
|
325
423
|
__publicField(this, _a);
|
|
326
424
|
this[INTERNAL_DATA] = data;
|
|
327
425
|
this[INTERNAL_IS_LEAF] = isLeaf;
|
|
426
|
+
this[INTERNAL_READS] = reads;
|
|
328
427
|
return new Proxy(this, {
|
|
329
428
|
get(target, prop, receiver) {
|
|
330
429
|
if (typeof prop === "string") {
|
|
331
430
|
if (prop === "scope" || prop === "has" || prop === "keys") {
|
|
332
431
|
return Reflect.get(target, prop, receiver);
|
|
333
432
|
}
|
|
334
|
-
return target
|
|
433
|
+
return readEntry(target, prop);
|
|
335
434
|
}
|
|
336
435
|
return Reflect.get(target, prop, receiver);
|
|
337
436
|
},
|
|
338
437
|
has(target, prop) {
|
|
339
|
-
return typeof prop === "string" ? prop
|
|
438
|
+
return typeof prop === "string" ? target.has(prop) : Reflect.has(target, prop);
|
|
340
439
|
},
|
|
341
440
|
ownKeys(target) {
|
|
441
|
+
observeKeys(target);
|
|
342
442
|
return Reflect.ownKeys(target[INTERNAL_DATA]);
|
|
343
443
|
},
|
|
344
444
|
getOwnPropertyDescriptor(target, prop) {
|
|
@@ -360,54 +460,152 @@ const _ScopedStore = class _ScopedStore {
|
|
|
360
460
|
scope(key) {
|
|
361
461
|
const value = this[INTERNAL_DATA][key];
|
|
362
462
|
const isLeaf = this[INTERNAL_IS_LEAF];
|
|
463
|
+
const reads = this[INTERNAL_READS];
|
|
363
464
|
const scope = value && typeof value === "object" && !isLeaf(value) ? value : {};
|
|
364
|
-
|
|
465
|
+
const child = reads ? { ...reads, path: [...reads.path, key] } : void 0;
|
|
466
|
+
return new _ScopedStore(scope, isLeaf, child);
|
|
365
467
|
}
|
|
366
468
|
/**
|
|
367
469
|
* Check if a key exists in the store.
|
|
368
470
|
*/
|
|
369
471
|
has(key) {
|
|
370
|
-
|
|
472
|
+
const found = key in this[INTERNAL_DATA];
|
|
473
|
+
const reads = this[INTERNAL_READS];
|
|
474
|
+
reads?.observe({ kind: reads.kind, op: "has", path: [...reads.path, key], seen: found });
|
|
475
|
+
return found;
|
|
371
476
|
}
|
|
372
477
|
/**
|
|
373
478
|
* Get all keys in the store.
|
|
374
479
|
*/
|
|
375
480
|
keys() {
|
|
481
|
+
observeKeys(this);
|
|
376
482
|
return Object.keys(this[INTERNAL_DATA]);
|
|
377
483
|
}
|
|
378
484
|
};
|
|
379
485
|
let ScopedStore = _ScopedStore;
|
|
486
|
+
function readEntry(target, key) {
|
|
487
|
+
const value = target[INTERNAL_DATA][key];
|
|
488
|
+
const reads = target[INTERNAL_READS];
|
|
489
|
+
if (!reads) return value;
|
|
490
|
+
const isLeaf = target[INTERNAL_IS_LEAF];
|
|
491
|
+
const isScope = !!value && typeof value === "object" && !isLeaf(value);
|
|
492
|
+
const path = [...reads.path, key];
|
|
493
|
+
reads.observe({ kind: reads.kind, op: "get", path, seen: isScope ? SCOPE : value });
|
|
494
|
+
if (!isScope || !reads.nested) return value;
|
|
495
|
+
const children = target[INTERNAL_CHILDREN] ?? (target[INTERNAL_CHILDREN] = /* @__PURE__ */ new Map());
|
|
496
|
+
let child = children.get(key);
|
|
497
|
+
if (!child) {
|
|
498
|
+
child = new ScopedStore(value, isLeaf, { ...reads, path });
|
|
499
|
+
children.set(key, child);
|
|
500
|
+
}
|
|
501
|
+
return child;
|
|
502
|
+
}
|
|
503
|
+
function observeKeys(target) {
|
|
504
|
+
const reads = target[INTERNAL_READS];
|
|
505
|
+
reads?.observe({ kind: reads.kind, op: "keys", path: reads.path, seen: Object.keys(target[INTERNAL_DATA]) });
|
|
506
|
+
}
|
|
380
507
|
function createScopedStore(data, isLeaf) {
|
|
381
508
|
return new ScopedStore(data, isLeaf);
|
|
382
509
|
}
|
|
383
|
-
function
|
|
510
|
+
function observeTextures(map, observe) {
|
|
511
|
+
const observeKeys2 = () => observe({ kind: "textures", op: "keys", path: [], seen: [...map.keys()] });
|
|
512
|
+
const observeAll = () => {
|
|
513
|
+
observeKeys2();
|
|
514
|
+
for (const [url, texture] of map) observe({ kind: "textures", op: "get", path: [url], seen: texture });
|
|
515
|
+
};
|
|
516
|
+
return new Proxy(map, {
|
|
517
|
+
get(target, prop) {
|
|
518
|
+
switch (prop) {
|
|
519
|
+
case "get":
|
|
520
|
+
return (url) => {
|
|
521
|
+
const texture = target.get(url);
|
|
522
|
+
observe({ kind: "textures", op: "get", path: [url], seen: texture });
|
|
523
|
+
return texture;
|
|
524
|
+
};
|
|
525
|
+
case "has":
|
|
526
|
+
return (url) => {
|
|
527
|
+
const found = target.has(url);
|
|
528
|
+
observe({ kind: "textures", op: "has", path: [url], seen: found });
|
|
529
|
+
return found;
|
|
530
|
+
};
|
|
531
|
+
case "size":
|
|
532
|
+
observeKeys2();
|
|
533
|
+
return target.size;
|
|
534
|
+
case "keys":
|
|
535
|
+
return () => {
|
|
536
|
+
observeKeys2();
|
|
537
|
+
return target.keys();
|
|
538
|
+
};
|
|
539
|
+
case "values":
|
|
540
|
+
return () => {
|
|
541
|
+
observeAll();
|
|
542
|
+
return target.values();
|
|
543
|
+
};
|
|
544
|
+
case "entries":
|
|
545
|
+
case Symbol.iterator:
|
|
546
|
+
return () => {
|
|
547
|
+
observeAll();
|
|
548
|
+
return target.entries();
|
|
549
|
+
};
|
|
550
|
+
case "forEach":
|
|
551
|
+
return (callback, thisArg) => {
|
|
552
|
+
observeAll();
|
|
553
|
+
target.forEach(callback, thisArg);
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
const value = Reflect.get(target, prop, target);
|
|
557
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
function createResourceView(primary, local = primary) {
|
|
562
|
+
return ((kind) => kind === "textures" ? extension.getTextureView(local) : withStagedOverlay(primary, kind, primary.getState()[kind]));
|
|
563
|
+
}
|
|
564
|
+
function createLazyCreatorState(state, store, options = {}) {
|
|
565
|
+
const { reads } = options;
|
|
566
|
+
const view = options.view ?? (store ? createResourceView(store) : void 0);
|
|
384
567
|
let _uniforms = null;
|
|
385
568
|
let _nodes = null;
|
|
386
569
|
let _buffers = null;
|
|
387
570
|
let _gpuStorage = null;
|
|
388
|
-
|
|
389
|
-
|
|
571
|
+
let _textures = null;
|
|
572
|
+
const read = (kind) => view ? view(kind) : state[kind];
|
|
573
|
+
const wrap = (kind, isLeaf) => new ScopedStore(
|
|
574
|
+
read(kind),
|
|
575
|
+
isLeaf,
|
|
576
|
+
reads && { ...reads, kind, path: [] }
|
|
577
|
+
);
|
|
578
|
+
const properties = {
|
|
390
579
|
uniforms: {
|
|
391
580
|
get() {
|
|
392
|
-
return _uniforms ?? (_uniforms =
|
|
581
|
+
return _uniforms ?? (_uniforms = wrap("uniforms", isUniformNode));
|
|
393
582
|
}
|
|
394
583
|
},
|
|
395
584
|
nodes: {
|
|
396
585
|
get() {
|
|
397
|
-
return _nodes ?? (_nodes =
|
|
586
|
+
return _nodes ?? (_nodes = wrap("nodes", isTSLNode));
|
|
398
587
|
}
|
|
399
588
|
},
|
|
400
589
|
buffers: {
|
|
401
590
|
get() {
|
|
402
|
-
return _buffers ?? (_buffers =
|
|
591
|
+
return _buffers ?? (_buffers = wrap("buffers", isBufferLike));
|
|
403
592
|
}
|
|
404
593
|
},
|
|
405
594
|
gpuStorage: {
|
|
406
595
|
get() {
|
|
407
|
-
return _gpuStorage ?? (_gpuStorage =
|
|
596
|
+
return _gpuStorage ?? (_gpuStorage = wrap("gpuStorage", isStorageLike));
|
|
408
597
|
}
|
|
409
598
|
}
|
|
410
|
-
}
|
|
599
|
+
};
|
|
600
|
+
if (options.view) {
|
|
601
|
+
properties.textures = {
|
|
602
|
+
get() {
|
|
603
|
+
const textures = options.view("textures");
|
|
604
|
+
return _textures ?? (_textures = reads ? observeTextures(textures, reads.observe) : textures);
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
return Object.create(state, properties);
|
|
411
609
|
}
|
|
412
610
|
|
|
413
611
|
function resolvePrimary(local) {
|
|
@@ -483,7 +681,7 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
483
681
|
const processedInput = React.useMemo(() => {
|
|
484
682
|
let raw = creatorOrScope;
|
|
485
683
|
if (typeof creatorOrScope === "function") {
|
|
486
|
-
const wrappedState = createLazyCreatorState(store.getState(), store);
|
|
684
|
+
const wrappedState = createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useUniforms") });
|
|
487
685
|
raw = creatorOrScope(wrappedState);
|
|
488
686
|
}
|
|
489
687
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
@@ -506,8 +704,9 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
506
704
|
input: memoizedInput,
|
|
507
705
|
create: () => {
|
|
508
706
|
if (isReader) return {};
|
|
509
|
-
if (typeof processedInput.candidates !== "object" || processedInput.candidates === null)
|
|
707
|
+
if (typeof processedInput.candidates !== "object" || processedInput.candidates === null) {
|
|
510
708
|
throw new Error("Invalid uniform input");
|
|
709
|
+
}
|
|
511
710
|
return processedInput.candidates;
|
|
512
711
|
},
|
|
513
712
|
prepare: (name, candidate) => createUniform(name, candidate, scope),
|
|
@@ -565,18 +764,20 @@ function isSameThreeType(a, b) {
|
|
|
565
764
|
const uniformOf = tsl.uniform;
|
|
566
765
|
function useUniform(name, value) {
|
|
567
766
|
const store = usePrimaryStore();
|
|
767
|
+
const memoizedInput = useCompareMemoize(value === void 0 ? name : { [name]: normalizeValue(value) });
|
|
568
768
|
const registered = useScopedResource({
|
|
569
769
|
store,
|
|
570
770
|
kind: "uniforms",
|
|
571
771
|
scope: void 0,
|
|
572
772
|
isLeaf: isUniformNode,
|
|
573
|
-
input:
|
|
773
|
+
input: memoizedInput,
|
|
574
774
|
create: () => value === void 0 ? {} : { [name]: value },
|
|
575
775
|
prepare: createNamedUniform,
|
|
576
776
|
reconcile: (existing2) => {
|
|
577
|
-
if (value
|
|
578
|
-
|
|
579
|
-
|
|
777
|
+
if (value === void 0 || isUniformNode(value)) return;
|
|
778
|
+
const next = normalizeValue(value);
|
|
779
|
+
if (isTSLNode(next)) return;
|
|
780
|
+
existing2.value = next;
|
|
580
781
|
}
|
|
581
782
|
});
|
|
582
783
|
if (registered[name]) return registered[name];
|
|
@@ -586,6 +787,9 @@ function useUniform(name, value) {
|
|
|
586
787
|
`[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
|
|
587
788
|
);
|
|
588
789
|
}
|
|
790
|
+
function normalizeValue(value) {
|
|
791
|
+
return typeof value === "string" ? new THREE.Color(value) : vectorize(value);
|
|
792
|
+
}
|
|
589
793
|
function createNamedUniform(name, value) {
|
|
590
794
|
let node;
|
|
591
795
|
if (isUniformNode(value)) {
|
|
@@ -626,7 +830,16 @@ function useNodes(creatorOrScope, scope) {
|
|
|
626
830
|
isLeaf: isTSLNode,
|
|
627
831
|
create: () => {
|
|
628
832
|
if (isReader) return {};
|
|
629
|
-
|
|
833
|
+
const nodes2 = creatorOrScope(
|
|
834
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useNodes") })
|
|
835
|
+
);
|
|
836
|
+
if (nodes2 == null) {
|
|
837
|
+
warnOnce(
|
|
838
|
+
"[useNodes] The creator returned nothing, so nothing was registered. useNodes registers the nodes its creator returns. To put a node onto a Three object (scene.fogNode = ...), use useLocalNodes and return a function that assigns it: it runs after commit and may return a cleanup."
|
|
839
|
+
);
|
|
840
|
+
return {};
|
|
841
|
+
}
|
|
842
|
+
return nodes2;
|
|
630
843
|
},
|
|
631
844
|
prepare: (name, node) => {
|
|
632
845
|
const setName = Reflect.get(node, "setName");
|
|
@@ -646,16 +859,100 @@ function useNodes(creatorOrScope, scope) {
|
|
|
646
859
|
function rebuildAllNodes(store, scope) {
|
|
647
860
|
rebuildResource(store, "nodes", scope);
|
|
648
861
|
}
|
|
649
|
-
function
|
|
862
|
+
function areDepsEqual(next, prev) {
|
|
863
|
+
if (next.length !== prev.length) return false;
|
|
864
|
+
for (let i = 0; i < next.length; i++) if (!Object.is(next[i], prev[i])) return false;
|
|
865
|
+
return true;
|
|
866
|
+
}
|
|
867
|
+
function useDependencyToken(deps) {
|
|
868
|
+
const previous = React.useRef(null);
|
|
869
|
+
const record = previous.current;
|
|
870
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && record) {
|
|
871
|
+
const hadDeps = record.deps !== void 0;
|
|
872
|
+
const hasDeps = deps !== void 0;
|
|
873
|
+
if (hadDeps !== hasDeps) {
|
|
874
|
+
console.warn(
|
|
875
|
+
`[useLocalNodes] The dependency array was ${hasDeps ? "added" : "omitted"} between renders. Pass an array on every render or on none; the mode must not change for a mounted component.`
|
|
876
|
+
);
|
|
877
|
+
} else if (hasDeps && record.deps.length !== deps.length) {
|
|
878
|
+
console.warn(
|
|
879
|
+
`[useLocalNodes] The dependency array length changed between renders (${record.deps.length} \u2192 ${deps.length}). Declare a fixed-length list; conditional dependencies belong inside the array as values.`
|
|
880
|
+
);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
if (deps === void 0) {
|
|
884
|
+
previous.current = { deps: void 0, token: {} };
|
|
885
|
+
} else if (!record || record.deps === void 0 || !areDepsEqual(deps, record.deps)) {
|
|
886
|
+
previous.current = { deps, token: {} };
|
|
887
|
+
} else if (record.deps !== deps) {
|
|
888
|
+
previous.current = { deps, token: record.token };
|
|
889
|
+
}
|
|
890
|
+
return previous.current.token;
|
|
891
|
+
}
|
|
892
|
+
function useLocalNodes(creator, deps) {
|
|
893
|
+
const local = extension.useStore();
|
|
650
894
|
const store = usePrimaryStore();
|
|
651
|
-
const
|
|
652
|
-
const nodes = usePrimaryThree((s) => s.nodes);
|
|
653
|
-
const textures = usePrimaryThree((s) => s.textures);
|
|
895
|
+
const view = React.useMemo(() => createResourceView(store, local), [store, local]);
|
|
654
896
|
const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
897
|
+
const depsToken = useDependencyToken(deps);
|
|
898
|
+
const committedReads = React.useRef(null);
|
|
899
|
+
const readsVersion = React.useRef(0);
|
|
900
|
+
const getReadsVersion = () => {
|
|
901
|
+
const tracker2 = committedReads.current;
|
|
902
|
+
if (tracker2 && !tracker2.stale && isTrackerStale(tracker2, view)) readsVersion.current++;
|
|
903
|
+
return readsVersion.current;
|
|
904
|
+
};
|
|
905
|
+
const subscribe = React.useCallback(
|
|
906
|
+
(onChange) => {
|
|
907
|
+
const unsubscribe = store.subscribe(onChange);
|
|
908
|
+
if (local === store) return unsubscribe;
|
|
909
|
+
const unsubscribeLocal = local.subscribe(onChange);
|
|
910
|
+
return () => {
|
|
911
|
+
unsubscribe();
|
|
912
|
+
unsubscribeLocal();
|
|
913
|
+
};
|
|
914
|
+
},
|
|
915
|
+
[store, local]
|
|
916
|
+
);
|
|
917
|
+
const resourceVersion = React.useSyncExternalStore(subscribe, getReadsVersion, getReadsVersion);
|
|
918
|
+
const evaluation = React.useMemo(() => {
|
|
919
|
+
const tracker2 = createReadTracker("useLocalNodes");
|
|
920
|
+
const reads = { observe: tracker2.observe, nested: true };
|
|
921
|
+
const value2 = creator(createLazyCreatorState(local.getState(), store, { reads, view }));
|
|
922
|
+
tracker2.closed = true;
|
|
923
|
+
return { value: value2, tracker: tracker2 };
|
|
924
|
+
}, [view, hmrVersion, depsToken, resourceVersion]);
|
|
925
|
+
const { value, tracker } = evaluation;
|
|
926
|
+
const installing = typeof value === "function";
|
|
927
|
+
useModeDiagnostics(value, installing);
|
|
928
|
+
useIsomorphicLayoutEffect(() => {
|
|
929
|
+
committedReads.current = tracker;
|
|
930
|
+
if (!installing) return;
|
|
931
|
+
tracker.closed = false;
|
|
932
|
+
let cleanup;
|
|
933
|
+
try {
|
|
934
|
+
cleanup = value();
|
|
935
|
+
} finally {
|
|
936
|
+
tracker.closed = true;
|
|
937
|
+
}
|
|
938
|
+
return typeof cleanup === "function" ? cleanup : void 0;
|
|
939
|
+
}, [evaluation]);
|
|
940
|
+
return installing ? void 0 : value;
|
|
941
|
+
}
|
|
942
|
+
function useModeDiagnostics(value, installing) {
|
|
943
|
+
const previous = React.useRef(null);
|
|
944
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
|
|
945
|
+
if (value === void 0 || value === null) {
|
|
946
|
+
warnOnce(
|
|
947
|
+
"[useLocalNodes] The creator returned nothing. To put a node onto a Three object (scene.fogNode = ...), build it in the creator and return a function that assigns it: that function runs after commit and may return a cleanup. Assigning inside the creator runs during render, which React may discard or repeat."
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
if (previous.current !== null && previous.current !== installing) {
|
|
951
|
+
warnOnce(
|
|
952
|
+
"[useLocalNodes] The creator switched between returning a record and returning an install function. Keep one form for a mounted component."
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
previous.current = installing;
|
|
659
956
|
}
|
|
660
957
|
|
|
661
958
|
const disposeBuffer = (buffer) => {
|
|
@@ -701,7 +998,9 @@ function useBuffers(creatorOrScope, scope) {
|
|
|
701
998
|
isLeaf: isBufferLike,
|
|
702
999
|
create: () => {
|
|
703
1000
|
if (isReader) return {};
|
|
704
|
-
return creatorOrScope(
|
|
1001
|
+
return creatorOrScope(
|
|
1002
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useBuffers") })
|
|
1003
|
+
);
|
|
705
1004
|
},
|
|
706
1005
|
prepare: (name, buffer) => {
|
|
707
1006
|
const setName = Reflect.get(buffer, "setName");
|
|
@@ -765,7 +1064,9 @@ function useGPUStorage(creatorOrScope, scope) {
|
|
|
765
1064
|
isLeaf: isStorageLike,
|
|
766
1065
|
create: () => {
|
|
767
1066
|
if (isReader) return {};
|
|
768
|
-
return creatorOrScope(
|
|
1067
|
+
return creatorOrScope(
|
|
1068
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useGPUStorage") })
|
|
1069
|
+
);
|
|
769
1070
|
},
|
|
770
1071
|
prepare: (name, storage) => {
|
|
771
1072
|
const label = scopedNodeName(scope, name);
|