@react-three/tsl 10.0.0-canary.777b94d → 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 +344 -60
- package/dist/index.d.cts +385 -100
- package/dist/index.d.mts +385 -100
- package/dist/index.d.ts +385 -100
- package/dist/index.mjs +346 -62
- package/package.json +3 -3
- package/readme.md +4 -4
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);
|
|
@@ -177,6 +184,10 @@ function extractTSLValue(value) {
|
|
|
177
184
|
if (node.isConstNode) {
|
|
178
185
|
return node.value;
|
|
179
186
|
}
|
|
187
|
+
if (node.isVarNode && node.node) {
|
|
188
|
+
const inner = extractTSLValue(node.node);
|
|
189
|
+
return inner === node.node ? value : inner;
|
|
190
|
+
}
|
|
180
191
|
if ("value" in node) {
|
|
181
192
|
let extractedValue = node.value;
|
|
182
193
|
if (typeof node.traverse === "function") {
|
|
@@ -312,33 +323,129 @@ function ensureTSLExtension() {
|
|
|
312
323
|
});
|
|
313
324
|
}
|
|
314
325
|
|
|
326
|
+
const SCOPE = Symbol("readTracking.scope");
|
|
327
|
+
const LEAF_GUARDS = {
|
|
328
|
+
uniforms: isUniformNode,
|
|
329
|
+
nodes: isTSLNode,
|
|
330
|
+
buffers: isBufferLike,
|
|
331
|
+
gpuStorage: isStorageLike
|
|
332
|
+
};
|
|
333
|
+
function isScope(kind, value) {
|
|
334
|
+
return !!value && typeof value === "object" && !LEAF_GUARDS[kind](value);
|
|
335
|
+
}
|
|
336
|
+
function classify(kind, value) {
|
|
337
|
+
return kind !== "textures" && isScope(kind, value) ? SCOPE : value;
|
|
338
|
+
}
|
|
339
|
+
function resolveContainer(kind, root, scopePath) {
|
|
340
|
+
let container = root;
|
|
341
|
+
for (const key of scopePath) {
|
|
342
|
+
const next = container?.[key];
|
|
343
|
+
container = isScope(kind, next) ? next : void 0;
|
|
344
|
+
}
|
|
345
|
+
return container;
|
|
346
|
+
}
|
|
347
|
+
function observeNow(read, view) {
|
|
348
|
+
if (read.kind === "textures") {
|
|
349
|
+
const map = view("textures");
|
|
350
|
+
if (read.op === "keys") return [...map.keys()];
|
|
351
|
+
return read.op === "has" ? map.has(read.path[0]) : map.get(read.path[0]);
|
|
352
|
+
}
|
|
353
|
+
const root = view(read.kind);
|
|
354
|
+
if (read.op === "keys") return Object.keys(resolveContainer(read.kind, root, read.path) ?? {});
|
|
355
|
+
const container = resolveContainer(read.kind, root, read.path.slice(0, -1));
|
|
356
|
+
const key = read.path[read.path.length - 1];
|
|
357
|
+
if (read.op === "has") return !!container && key in container;
|
|
358
|
+
return classify(read.kind, container?.[key]);
|
|
359
|
+
}
|
|
360
|
+
function sameKeys(a, b) {
|
|
361
|
+
return a.length === b.length && a.every((key, i) => key === b[i]);
|
|
362
|
+
}
|
|
363
|
+
function hasChanged(read, view) {
|
|
364
|
+
const now = observeNow(read, view);
|
|
365
|
+
return read.op === "keys" ? !sameKeys(read.seen, now) : !Object.is(read.seen, now);
|
|
366
|
+
}
|
|
367
|
+
const warned = /* @__PURE__ */ new Set();
|
|
368
|
+
function warnOnce(message) {
|
|
369
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
|
|
370
|
+
if (warned.has(message)) return;
|
|
371
|
+
warned.add(message);
|
|
372
|
+
console.warn(message);
|
|
373
|
+
}
|
|
374
|
+
function describeRead(kind, path) {
|
|
375
|
+
if (kind === "textures") return `textures.get('${path[0]}')`;
|
|
376
|
+
const scopes = path.slice(0, -1).map((key) => `.scope('${key}')`);
|
|
377
|
+
return `${kind}${scopes.join("")}.${path[path.length - 1] ?? ""}`;
|
|
378
|
+
}
|
|
379
|
+
function createReadTracker(hookName) {
|
|
380
|
+
const tracker = {
|
|
381
|
+
reads: [],
|
|
382
|
+
closed: false,
|
|
383
|
+
stale: false,
|
|
384
|
+
observe: (read) => {
|
|
385
|
+
if (tracker.closed) {
|
|
386
|
+
warnOnce(
|
|
387
|
+
`[${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.`
|
|
388
|
+
);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
tracker.reads.push(read);
|
|
392
|
+
}
|
|
393
|
+
};
|
|
394
|
+
return tracker;
|
|
395
|
+
}
|
|
396
|
+
function isTrackerStale(tracker, view) {
|
|
397
|
+
if (!tracker.stale) tracker.stale = tracker.reads.some((read) => hasChanged(read, view));
|
|
398
|
+
return tracker.stale;
|
|
399
|
+
}
|
|
400
|
+
function warnMissingReads(hookName) {
|
|
401
|
+
return {
|
|
402
|
+
nested: false,
|
|
403
|
+
observe: (read) => {
|
|
404
|
+
if (read.op !== "get" || read.seen !== void 0) return;
|
|
405
|
+
warnOnce(
|
|
406
|
+
`[${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().`
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
|
|
315
412
|
var __defProp = Object.defineProperty;
|
|
316
413
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
317
414
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
318
|
-
var _a, _b;
|
|
415
|
+
var _a, _b, _c, _d;
|
|
319
416
|
const INTERNAL_DATA = Symbol("ScopedStore.data");
|
|
320
417
|
const INTERNAL_IS_LEAF = Symbol("ScopedStore.isLeaf");
|
|
321
|
-
|
|
418
|
+
const INTERNAL_READS = Symbol("ScopedStore.reads");
|
|
419
|
+
const INTERNAL_CHILDREN = Symbol("ScopedStore.children");
|
|
420
|
+
_d = INTERNAL_DATA, _c = INTERNAL_IS_LEAF, _b = INTERNAL_READS, _a = INTERNAL_CHILDREN;
|
|
322
421
|
const _ScopedStore = class _ScopedStore {
|
|
323
|
-
constructor(data, isLeaf) {
|
|
422
|
+
constructor(data, isLeaf, reads) {
|
|
423
|
+
/** @internal */
|
|
424
|
+
__publicField(this, _d);
|
|
425
|
+
/** @internal */
|
|
426
|
+
__publicField(this, _c);
|
|
427
|
+
/** @internal */
|
|
324
428
|
__publicField(this, _b);
|
|
429
|
+
/** @internal */
|
|
325
430
|
__publicField(this, _a);
|
|
326
431
|
this[INTERNAL_DATA] = data;
|
|
327
432
|
this[INTERNAL_IS_LEAF] = isLeaf;
|
|
433
|
+
this[INTERNAL_READS] = reads;
|
|
328
434
|
return new Proxy(this, {
|
|
329
435
|
get(target, prop, receiver) {
|
|
330
436
|
if (typeof prop === "string") {
|
|
331
437
|
if (prop === "scope" || prop === "has" || prop === "keys") {
|
|
332
438
|
return Reflect.get(target, prop, receiver);
|
|
333
439
|
}
|
|
334
|
-
return target
|
|
440
|
+
return readEntry(target, prop);
|
|
335
441
|
}
|
|
336
442
|
return Reflect.get(target, prop, receiver);
|
|
337
443
|
},
|
|
338
444
|
has(target, prop) {
|
|
339
|
-
return typeof prop === "string" ? prop
|
|
445
|
+
return typeof prop === "string" ? target.has(prop) : Reflect.has(target, prop);
|
|
340
446
|
},
|
|
341
447
|
ownKeys(target) {
|
|
448
|
+
observeKeys(target);
|
|
342
449
|
return Reflect.ownKeys(target[INTERNAL_DATA]);
|
|
343
450
|
},
|
|
344
451
|
getOwnPropertyDescriptor(target, prop) {
|
|
@@ -360,54 +467,152 @@ const _ScopedStore = class _ScopedStore {
|
|
|
360
467
|
scope(key) {
|
|
361
468
|
const value = this[INTERNAL_DATA][key];
|
|
362
469
|
const isLeaf = this[INTERNAL_IS_LEAF];
|
|
470
|
+
const reads = this[INTERNAL_READS];
|
|
363
471
|
const scope = value && typeof value === "object" && !isLeaf(value) ? value : {};
|
|
364
|
-
|
|
472
|
+
const child = reads ? { ...reads, path: [...reads.path, key] } : void 0;
|
|
473
|
+
return new _ScopedStore(scope, isLeaf, child);
|
|
365
474
|
}
|
|
366
475
|
/**
|
|
367
476
|
* Check if a key exists in the store.
|
|
368
477
|
*/
|
|
369
478
|
has(key) {
|
|
370
|
-
|
|
479
|
+
const found = key in this[INTERNAL_DATA];
|
|
480
|
+
const reads = this[INTERNAL_READS];
|
|
481
|
+
reads?.observe({ kind: reads.kind, op: "has", path: [...reads.path, key], seen: found });
|
|
482
|
+
return found;
|
|
371
483
|
}
|
|
372
484
|
/**
|
|
373
485
|
* Get all keys in the store.
|
|
374
486
|
*/
|
|
375
487
|
keys() {
|
|
488
|
+
observeKeys(this);
|
|
376
489
|
return Object.keys(this[INTERNAL_DATA]);
|
|
377
490
|
}
|
|
378
491
|
};
|
|
379
492
|
let ScopedStore = _ScopedStore;
|
|
493
|
+
function readEntry(target, key) {
|
|
494
|
+
const value = target[INTERNAL_DATA][key];
|
|
495
|
+
const reads = target[INTERNAL_READS];
|
|
496
|
+
if (!reads) return value;
|
|
497
|
+
const isLeaf = target[INTERNAL_IS_LEAF];
|
|
498
|
+
const isScope = !!value && typeof value === "object" && !isLeaf(value);
|
|
499
|
+
const path = [...reads.path, key];
|
|
500
|
+
reads.observe({ kind: reads.kind, op: "get", path, seen: isScope ? SCOPE : value });
|
|
501
|
+
if (!isScope || !reads.nested) return value;
|
|
502
|
+
const children = target[INTERNAL_CHILDREN] ?? (target[INTERNAL_CHILDREN] = /* @__PURE__ */ new Map());
|
|
503
|
+
let child = children.get(key);
|
|
504
|
+
if (!child) {
|
|
505
|
+
child = new ScopedStore(value, isLeaf, { ...reads, path });
|
|
506
|
+
children.set(key, child);
|
|
507
|
+
}
|
|
508
|
+
return child;
|
|
509
|
+
}
|
|
510
|
+
function observeKeys(target) {
|
|
511
|
+
const reads = target[INTERNAL_READS];
|
|
512
|
+
reads?.observe({ kind: reads.kind, op: "keys", path: reads.path, seen: Object.keys(target[INTERNAL_DATA]) });
|
|
513
|
+
}
|
|
380
514
|
function createScopedStore(data, isLeaf) {
|
|
381
515
|
return new ScopedStore(data, isLeaf);
|
|
382
516
|
}
|
|
383
|
-
function
|
|
517
|
+
function observeTextures(map, observe) {
|
|
518
|
+
const observeKeys2 = () => observe({ kind: "textures", op: "keys", path: [], seen: [...map.keys()] });
|
|
519
|
+
const observeAll = () => {
|
|
520
|
+
observeKeys2();
|
|
521
|
+
for (const [url, texture] of map) observe({ kind: "textures", op: "get", path: [url], seen: texture });
|
|
522
|
+
};
|
|
523
|
+
return new Proxy(map, {
|
|
524
|
+
get(target, prop) {
|
|
525
|
+
switch (prop) {
|
|
526
|
+
case "get":
|
|
527
|
+
return (url) => {
|
|
528
|
+
const texture = target.get(url);
|
|
529
|
+
observe({ kind: "textures", op: "get", path: [url], seen: texture });
|
|
530
|
+
return texture;
|
|
531
|
+
};
|
|
532
|
+
case "has":
|
|
533
|
+
return (url) => {
|
|
534
|
+
const found = target.has(url);
|
|
535
|
+
observe({ kind: "textures", op: "has", path: [url], seen: found });
|
|
536
|
+
return found;
|
|
537
|
+
};
|
|
538
|
+
case "size":
|
|
539
|
+
observeKeys2();
|
|
540
|
+
return target.size;
|
|
541
|
+
case "keys":
|
|
542
|
+
return () => {
|
|
543
|
+
observeKeys2();
|
|
544
|
+
return target.keys();
|
|
545
|
+
};
|
|
546
|
+
case "values":
|
|
547
|
+
return () => {
|
|
548
|
+
observeAll();
|
|
549
|
+
return target.values();
|
|
550
|
+
};
|
|
551
|
+
case "entries":
|
|
552
|
+
case Symbol.iterator:
|
|
553
|
+
return () => {
|
|
554
|
+
observeAll();
|
|
555
|
+
return target.entries();
|
|
556
|
+
};
|
|
557
|
+
case "forEach":
|
|
558
|
+
return (callback, thisArg) => {
|
|
559
|
+
observeAll();
|
|
560
|
+
target.forEach(callback, thisArg);
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
const value = Reflect.get(target, prop, target);
|
|
564
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
function createResourceView(primary, local = primary) {
|
|
569
|
+
return ((kind) => kind === "textures" ? extension.getTextureView(local) : withStagedOverlay(primary, kind, primary.getState()[kind]));
|
|
570
|
+
}
|
|
571
|
+
function createLazyCreatorState(state, store, options = {}) {
|
|
572
|
+
const { reads } = options;
|
|
573
|
+
const view = options.view ?? (store ? createResourceView(store) : void 0);
|
|
384
574
|
let _uniforms = null;
|
|
385
575
|
let _nodes = null;
|
|
386
576
|
let _buffers = null;
|
|
387
577
|
let _gpuStorage = null;
|
|
388
|
-
|
|
389
|
-
|
|
578
|
+
let _textures = null;
|
|
579
|
+
const read = (kind) => view ? view(kind) : state[kind];
|
|
580
|
+
const wrap = (kind, isLeaf) => new ScopedStore(
|
|
581
|
+
read(kind),
|
|
582
|
+
isLeaf,
|
|
583
|
+
reads && { ...reads, kind, path: [] }
|
|
584
|
+
);
|
|
585
|
+
const properties = {
|
|
390
586
|
uniforms: {
|
|
391
587
|
get() {
|
|
392
|
-
return _uniforms ?? (_uniforms =
|
|
588
|
+
return _uniforms ?? (_uniforms = wrap("uniforms", isUniformNode));
|
|
393
589
|
}
|
|
394
590
|
},
|
|
395
591
|
nodes: {
|
|
396
592
|
get() {
|
|
397
|
-
return _nodes ?? (_nodes =
|
|
593
|
+
return _nodes ?? (_nodes = wrap("nodes", isTSLNode));
|
|
398
594
|
}
|
|
399
595
|
},
|
|
400
596
|
buffers: {
|
|
401
597
|
get() {
|
|
402
|
-
return _buffers ?? (_buffers =
|
|
598
|
+
return _buffers ?? (_buffers = wrap("buffers", isBufferLike));
|
|
403
599
|
}
|
|
404
600
|
},
|
|
405
601
|
gpuStorage: {
|
|
406
602
|
get() {
|
|
407
|
-
return _gpuStorage ?? (_gpuStorage =
|
|
603
|
+
return _gpuStorage ?? (_gpuStorage = wrap("gpuStorage", isStorageLike));
|
|
408
604
|
}
|
|
409
605
|
}
|
|
410
|
-
}
|
|
606
|
+
};
|
|
607
|
+
if (options.view) {
|
|
608
|
+
properties.textures = {
|
|
609
|
+
get() {
|
|
610
|
+
const textures = options.view("textures");
|
|
611
|
+
return _textures ?? (_textures = reads ? observeTextures(textures, reads.observe) : textures);
|
|
612
|
+
}
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
return Object.create(state, properties);
|
|
411
616
|
}
|
|
412
617
|
|
|
413
618
|
function resolvePrimary(local) {
|
|
@@ -483,7 +688,7 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
483
688
|
const processedInput = React.useMemo(() => {
|
|
484
689
|
let raw = creatorOrScope;
|
|
485
690
|
if (typeof creatorOrScope === "function") {
|
|
486
|
-
const wrappedState = createLazyCreatorState(store.getState(), store);
|
|
691
|
+
const wrappedState = createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useUniforms") });
|
|
487
692
|
raw = creatorOrScope(wrappedState);
|
|
488
693
|
}
|
|
489
694
|
if (raw && typeof raw === "object" && !Array.isArray(raw)) {
|
|
@@ -506,8 +711,9 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
506
711
|
input: memoizedInput,
|
|
507
712
|
create: () => {
|
|
508
713
|
if (isReader) return {};
|
|
509
|
-
if (typeof processedInput.candidates !== "object" || processedInput.candidates === null)
|
|
714
|
+
if (typeof processedInput.candidates !== "object" || processedInput.candidates === null) {
|
|
510
715
|
throw new Error("Invalid uniform input");
|
|
716
|
+
}
|
|
511
717
|
return processedInput.candidates;
|
|
512
718
|
},
|
|
513
719
|
prepare: (name, candidate) => createUniform(name, candidate, scope),
|
|
@@ -525,13 +731,7 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
525
731
|
if (!equals) existing.value = newVal;
|
|
526
732
|
}
|
|
527
733
|
});
|
|
528
|
-
|
|
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
|
-
}
|
|
734
|
+
const uniforms = isReader ? readWithStaged(store, "uniforms", storeUniforms, memoizedInput, isUniformNode) : created;
|
|
535
735
|
return { ...uniforms, removeUniforms, clearUniforms, rebuildUniforms };
|
|
536
736
|
}
|
|
537
737
|
function rebuildAllUniforms(store, scope) {
|
|
@@ -565,18 +765,20 @@ function isSameThreeType(a, b) {
|
|
|
565
765
|
const uniformOf = tsl.uniform;
|
|
566
766
|
function useUniform(name, value) {
|
|
567
767
|
const store = usePrimaryStore();
|
|
768
|
+
const memoizedInput = useCompareMemoize(value === void 0 ? name : { [name]: normalizeValue(value) });
|
|
568
769
|
const registered = useScopedResource({
|
|
569
770
|
store,
|
|
570
771
|
kind: "uniforms",
|
|
571
772
|
scope: void 0,
|
|
572
773
|
isLeaf: isUniformNode,
|
|
573
|
-
input:
|
|
774
|
+
input: memoizedInput,
|
|
574
775
|
create: () => value === void 0 ? {} : { [name]: value },
|
|
575
776
|
prepare: createNamedUniform,
|
|
576
777
|
reconcile: (existing2) => {
|
|
577
|
-
if (value
|
|
578
|
-
|
|
579
|
-
|
|
778
|
+
if (value === void 0 || isUniformNode(value)) return;
|
|
779
|
+
const next = normalizeValue(value);
|
|
780
|
+
if (isTSLNode(next)) return;
|
|
781
|
+
existing2.value = next;
|
|
580
782
|
}
|
|
581
783
|
});
|
|
582
784
|
if (registered[name]) return registered[name];
|
|
@@ -586,6 +788,9 @@ function useUniform(name, value) {
|
|
|
586
788
|
`[useUniform] Uniform "${name}" not found. Create it first with: useUniform('${name}', initialValue)`
|
|
587
789
|
);
|
|
588
790
|
}
|
|
791
|
+
function normalizeValue(value) {
|
|
792
|
+
return typeof value === "string" ? new THREE.Color(value) : vectorize(value);
|
|
793
|
+
}
|
|
589
794
|
function createNamedUniform(name, value) {
|
|
590
795
|
let node;
|
|
591
796
|
if (isUniformNode(value)) {
|
|
@@ -626,7 +831,16 @@ function useNodes(creatorOrScope, scope) {
|
|
|
626
831
|
isLeaf: isTSLNode,
|
|
627
832
|
create: () => {
|
|
628
833
|
if (isReader) return {};
|
|
629
|
-
|
|
834
|
+
const nodes2 = creatorOrScope(
|
|
835
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useNodes") })
|
|
836
|
+
);
|
|
837
|
+
if (nodes2 == null) {
|
|
838
|
+
warnOnce(
|
|
839
|
+
"[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."
|
|
840
|
+
);
|
|
841
|
+
return {};
|
|
842
|
+
}
|
|
843
|
+
return nodes2;
|
|
630
844
|
},
|
|
631
845
|
prepare: (name, node) => {
|
|
632
846
|
const setName = Reflect.get(node, "setName");
|
|
@@ -634,28 +848,106 @@ function useNodes(creatorOrScope, scope) {
|
|
|
634
848
|
return node;
|
|
635
849
|
}
|
|
636
850
|
});
|
|
637
|
-
|
|
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
|
-
}
|
|
851
|
+
const nodes = isReader ? readWithStaged(store, "nodes", storeNodes, creatorOrScope, isTSLNode) : created;
|
|
644
852
|
return { ...nodes, removeNodes, clearNodes, rebuildNodes };
|
|
645
853
|
}
|
|
646
854
|
function rebuildAllNodes(store, scope) {
|
|
647
855
|
rebuildResource(store, "nodes", scope);
|
|
648
856
|
}
|
|
649
|
-
function
|
|
857
|
+
function areDepsEqual(next, prev) {
|
|
858
|
+
if (next.length !== prev.length) return false;
|
|
859
|
+
for (let i = 0; i < next.length; i++) if (!Object.is(next[i], prev[i])) return false;
|
|
860
|
+
return true;
|
|
861
|
+
}
|
|
862
|
+
function useDependencyToken(deps) {
|
|
863
|
+
const previous = React.useRef(null);
|
|
864
|
+
const record = previous.current;
|
|
865
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV !== "production" && record) {
|
|
866
|
+
const hadDeps = record.deps !== void 0;
|
|
867
|
+
const hasDeps = deps !== void 0;
|
|
868
|
+
if (hadDeps !== hasDeps) {
|
|
869
|
+
console.warn(
|
|
870
|
+
`[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.`
|
|
871
|
+
);
|
|
872
|
+
} else if (hasDeps && record.deps.length !== deps.length) {
|
|
873
|
+
console.warn(
|
|
874
|
+
`[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.`
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
if (deps === void 0) {
|
|
879
|
+
previous.current = { deps: void 0, token: {} };
|
|
880
|
+
} else if (!record || record.deps === void 0 || !areDepsEqual(deps, record.deps)) {
|
|
881
|
+
previous.current = { deps, token: {} };
|
|
882
|
+
} else if (record.deps !== deps) {
|
|
883
|
+
previous.current = { deps, token: record.token };
|
|
884
|
+
}
|
|
885
|
+
return previous.current.token;
|
|
886
|
+
}
|
|
887
|
+
function useLocalNodes(creator, deps) {
|
|
888
|
+
const local = extension.useStore();
|
|
650
889
|
const store = usePrimaryStore();
|
|
651
|
-
const
|
|
652
|
-
const nodes = usePrimaryThree((s) => s.nodes);
|
|
653
|
-
const textures = usePrimaryThree((s) => s.textures);
|
|
890
|
+
const view = React.useMemo(() => createResourceView(store, local), [store, local]);
|
|
654
891
|
const hmrVersion = usePrimaryThree((s) => s._hmrVersion);
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
892
|
+
const depsToken = useDependencyToken(deps);
|
|
893
|
+
const committedReads = React.useRef(null);
|
|
894
|
+
const readsVersion = React.useRef(0);
|
|
895
|
+
const getReadsVersion = () => {
|
|
896
|
+
const tracker2 = committedReads.current;
|
|
897
|
+
if (tracker2 && !tracker2.stale && isTrackerStale(tracker2, view)) readsVersion.current++;
|
|
898
|
+
return readsVersion.current;
|
|
899
|
+
};
|
|
900
|
+
const subscribe = React.useCallback(
|
|
901
|
+
(onChange) => {
|
|
902
|
+
const unsubscribe = store.subscribe(onChange);
|
|
903
|
+
if (local === store) return unsubscribe;
|
|
904
|
+
const unsubscribeLocal = local.subscribe(onChange);
|
|
905
|
+
return () => {
|
|
906
|
+
unsubscribe();
|
|
907
|
+
unsubscribeLocal();
|
|
908
|
+
};
|
|
909
|
+
},
|
|
910
|
+
[store, local]
|
|
911
|
+
);
|
|
912
|
+
const resourceVersion = React.useSyncExternalStore(subscribe, getReadsVersion, getReadsVersion);
|
|
913
|
+
const evaluation = React.useMemo(() => {
|
|
914
|
+
const tracker2 = createReadTracker("useLocalNodes");
|
|
915
|
+
const reads = { observe: tracker2.observe, nested: true };
|
|
916
|
+
const value2 = creator(createLazyCreatorState(local.getState(), store, { reads, view }));
|
|
917
|
+
tracker2.closed = true;
|
|
918
|
+
return { value: value2, tracker: tracker2 };
|
|
919
|
+
}, [view, hmrVersion, depsToken, resourceVersion]);
|
|
920
|
+
const { value, tracker } = evaluation;
|
|
921
|
+
const installing = typeof value === "function";
|
|
922
|
+
useModeDiagnostics(value, installing);
|
|
923
|
+
useIsomorphicLayoutEffect(() => {
|
|
924
|
+
committedReads.current = tracker;
|
|
925
|
+
if (!installing) return;
|
|
926
|
+
tracker.closed = false;
|
|
927
|
+
let cleanup;
|
|
928
|
+
try {
|
|
929
|
+
cleanup = value();
|
|
930
|
+
} finally {
|
|
931
|
+
tracker.closed = true;
|
|
932
|
+
}
|
|
933
|
+
return typeof cleanup === "function" ? cleanup : void 0;
|
|
934
|
+
}, [evaluation]);
|
|
935
|
+
return installing ? void 0 : value;
|
|
936
|
+
}
|
|
937
|
+
function useModeDiagnostics(value, installing) {
|
|
938
|
+
const previous = React.useRef(null);
|
|
939
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "production") return;
|
|
940
|
+
if (value === void 0 || value === null) {
|
|
941
|
+
warnOnce(
|
|
942
|
+
"[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."
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
if (previous.current !== null && previous.current !== installing) {
|
|
946
|
+
warnOnce(
|
|
947
|
+
"[useLocalNodes] The creator switched between returning a record and returning an install function. Keep one form for a mounted component."
|
|
948
|
+
);
|
|
949
|
+
}
|
|
950
|
+
previous.current = installing;
|
|
659
951
|
}
|
|
660
952
|
|
|
661
953
|
const disposeBuffer = (buffer) => {
|
|
@@ -701,7 +993,9 @@ function useBuffers(creatorOrScope, scope) {
|
|
|
701
993
|
isLeaf: isBufferLike,
|
|
702
994
|
create: () => {
|
|
703
995
|
if (isReader) return {};
|
|
704
|
-
return creatorOrScope(
|
|
996
|
+
return creatorOrScope(
|
|
997
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useBuffers") })
|
|
998
|
+
);
|
|
705
999
|
},
|
|
706
1000
|
prepare: (name, buffer) => {
|
|
707
1001
|
const setName = Reflect.get(buffer, "setName");
|
|
@@ -709,13 +1003,7 @@ function useBuffers(creatorOrScope, scope) {
|
|
|
709
1003
|
return buffer;
|
|
710
1004
|
}
|
|
711
1005
|
});
|
|
712
|
-
|
|
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
|
-
}
|
|
1006
|
+
const buffers = isReader ? readWithStaged(store, "buffers", storeBuffers, creatorOrScope, isBufferLike) : created;
|
|
719
1007
|
return { ...buffers, removeBuffers, clearBuffers, rebuildBuffers, disposeBuffers };
|
|
720
1008
|
}
|
|
721
1009
|
function rebuildAllBuffers(store, scope) {
|
|
@@ -765,7 +1053,9 @@ function useGPUStorage(creatorOrScope, scope) {
|
|
|
765
1053
|
isLeaf: isStorageLike,
|
|
766
1054
|
create: () => {
|
|
767
1055
|
if (isReader) return {};
|
|
768
|
-
return creatorOrScope(
|
|
1056
|
+
return creatorOrScope(
|
|
1057
|
+
createLazyCreatorState(store.getState(), store, { reads: warnMissingReads("useGPUStorage") })
|
|
1058
|
+
);
|
|
769
1059
|
},
|
|
770
1060
|
prepare: (name, storage) => {
|
|
771
1061
|
const label = scopedNodeName(scope, name);
|
|
@@ -777,13 +1067,7 @@ function useGPUStorage(creatorOrScope, scope) {
|
|
|
777
1067
|
return storage;
|
|
778
1068
|
}
|
|
779
1069
|
});
|
|
780
|
-
|
|
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
|
-
}
|
|
1070
|
+
const gpuStorage = isReader ? readWithStaged(store, "gpuStorage", storeStorage, creatorOrScope, isStorageLike) : created;
|
|
787
1071
|
return {
|
|
788
1072
|
...gpuStorage,
|
|
789
1073
|
removeStorage,
|