@react-three/fiber 10.0.0-alpha.0 → 10.0.0-alpha.1
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 +498 -80
- package/dist/index.d.cts +199 -23
- package/dist/index.d.mts +199 -23
- package/dist/index.d.ts +199 -23
- package/dist/index.mjs +499 -83
- package/dist/legacy.cjs +494 -79
- package/dist/legacy.d.cts +200 -22
- package/dist/legacy.d.mts +200 -22
- package/dist/legacy.d.ts +200 -22
- package/dist/legacy.mjs +494 -81
- package/dist/webgpu/index.cjs +586 -145
- package/dist/webgpu/index.d.cts +244 -36
- package/dist/webgpu/index.d.mts +244 -36
- package/dist/webgpu/index.d.ts +244 -36
- package/dist/webgpu/index.mjs +586 -147
- package/package.json +1 -1
package/dist/webgpu/index.cjs
CHANGED
|
@@ -54,12 +54,15 @@ const WebGLRenderer = class WebGLRenderer2 {
|
|
|
54
54
|
);
|
|
55
55
|
}
|
|
56
56
|
};
|
|
57
|
+
const WebGLRenderTarget = null;
|
|
57
58
|
|
|
58
59
|
const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
59
60
|
__proto__: null,
|
|
60
61
|
Inspector: Inspector_js.Inspector,
|
|
61
62
|
R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
|
|
62
63
|
R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
|
|
64
|
+
RenderTargetCompat: webgpu.RenderTarget,
|
|
65
|
+
WebGLRenderTarget: WebGLRenderTarget,
|
|
63
66
|
WebGLRenderer: WebGLRenderer
|
|
64
67
|
}, [webgpu__namespace]);
|
|
65
68
|
|
|
@@ -168,6 +171,13 @@ function updateCamera(camera, size) {
|
|
|
168
171
|
}
|
|
169
172
|
camera.updateProjectionMatrix();
|
|
170
173
|
}
|
|
174
|
+
const frustumMatrix = new webgpu.Matrix4();
|
|
175
|
+
function updateFrustum(camera, frustum) {
|
|
176
|
+
const target = frustum ?? new webgpu.Frustum();
|
|
177
|
+
frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
178
|
+
target.setFromProjectionMatrix(frustumMatrix);
|
|
179
|
+
return target;
|
|
180
|
+
}
|
|
171
181
|
|
|
172
182
|
const REACT_INTERNAL_PROPS = ["children", "key", "ref"];
|
|
173
183
|
function findInitialRoot(instance) {
|
|
@@ -248,6 +258,205 @@ function invalidateInstance(instance) {
|
|
|
248
258
|
if (state && state.internal.frames === 0) state.invalidate();
|
|
249
259
|
}
|
|
250
260
|
|
|
261
|
+
const tempFrustum = new webgpu.Frustum();
|
|
262
|
+
let hasWarnedWebGL = false;
|
|
263
|
+
let tslModule = null;
|
|
264
|
+
async function loadTSL() {
|
|
265
|
+
if (tslModule) return tslModule;
|
|
266
|
+
try {
|
|
267
|
+
const tsl = await import('three/tsl');
|
|
268
|
+
tslModule = { uniform: tsl.uniform, nodeObject: tsl.nodeObject };
|
|
269
|
+
return tslModule;
|
|
270
|
+
} catch {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function createOcclusionObserverNode(store, uniform) {
|
|
275
|
+
const node = new webgpu.Node("float");
|
|
276
|
+
node.updateType = webgpu.NodeUpdateType.OBJECT;
|
|
277
|
+
node.update = function(frame) {
|
|
278
|
+
const { internal } = store.getState();
|
|
279
|
+
const registry = internal.visibilityRegistry;
|
|
280
|
+
const cache = internal.occlusionCache;
|
|
281
|
+
for (const entry of registry.values()) {
|
|
282
|
+
const { object, handlers } = entry;
|
|
283
|
+
if (handlers.onOccluded || handlers.onVisible) {
|
|
284
|
+
const isOccluded = frame.renderer.isOccluded(object);
|
|
285
|
+
cache.set(object, isOccluded);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
node.setup = function() {
|
|
290
|
+
return uniform(0);
|
|
291
|
+
};
|
|
292
|
+
return node;
|
|
293
|
+
}
|
|
294
|
+
let occlusionSetupPromise = null;
|
|
295
|
+
function enableOcclusion(store) {
|
|
296
|
+
const state = store.getState();
|
|
297
|
+
const { internal, renderer, rootScene } = state;
|
|
298
|
+
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
299
|
+
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
300
|
+
if (!hasOcclusionSupport) {
|
|
301
|
+
if (!hasWarnedWebGL) {
|
|
302
|
+
console.warn(
|
|
303
|
+
"[R3F] Warning: onOccluded/onVisible occlusion queries require WebGPU renderer. Occlusion events will not fire on WebGL."
|
|
304
|
+
);
|
|
305
|
+
hasWarnedWebGL = true;
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
occlusionSetupPromise = setupOcclusion(store);
|
|
310
|
+
}
|
|
311
|
+
async function setupOcclusion(store) {
|
|
312
|
+
const state = store.getState();
|
|
313
|
+
const { internal, rootScene, set } = state;
|
|
314
|
+
const tsl = await loadTSL();
|
|
315
|
+
if (!tsl) {
|
|
316
|
+
console.warn("[R3F] Warning: TSL module not available. Occlusion queries disabled.");
|
|
317
|
+
occlusionSetupPromise = null;
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
const { uniform, nodeObject } = tsl;
|
|
321
|
+
let helperGroup = internal.helperGroup;
|
|
322
|
+
if (!helperGroup) {
|
|
323
|
+
helperGroup = new webgpu.Group();
|
|
324
|
+
helperGroup.name = "__r3fInternal";
|
|
325
|
+
helperGroup.__r3fInternal = true;
|
|
326
|
+
rootScene.add(helperGroup);
|
|
327
|
+
}
|
|
328
|
+
const geometry = new webgpu.BoxGeometry(1, 1, 1);
|
|
329
|
+
const material = new webgpu.MeshBasicNodeMaterial({
|
|
330
|
+
transparent: true,
|
|
331
|
+
opacity: 0
|
|
332
|
+
});
|
|
333
|
+
const observerNode = nodeObject(createOcclusionObserverNode(store, uniform));
|
|
334
|
+
material.colorNode = observerNode;
|
|
335
|
+
material.needsUpdate = true;
|
|
336
|
+
const mesh = new webgpu.Mesh(geometry, material);
|
|
337
|
+
mesh.name = "__r3fOcclusionObserver";
|
|
338
|
+
mesh.scale.setScalar(1e-4);
|
|
339
|
+
mesh.frustumCulled = false;
|
|
340
|
+
mesh.__r3fInternal = true;
|
|
341
|
+
helperGroup.add(mesh);
|
|
342
|
+
set((state2) => ({
|
|
343
|
+
internal: {
|
|
344
|
+
...state2.internal,
|
|
345
|
+
helperGroup,
|
|
346
|
+
occlusionObserver: mesh,
|
|
347
|
+
occlusionEnabled: true
|
|
348
|
+
}
|
|
349
|
+
}));
|
|
350
|
+
occlusionSetupPromise = null;
|
|
351
|
+
}
|
|
352
|
+
function disableOcclusion(store) {
|
|
353
|
+
const { internal, set } = store.getState();
|
|
354
|
+
if (!internal.occlusionEnabled) return;
|
|
355
|
+
if (internal.occlusionObserver) {
|
|
356
|
+
internal.occlusionObserver.removeFromParent();
|
|
357
|
+
internal.occlusionObserver.geometry.dispose();
|
|
358
|
+
internal.occlusionObserver.material.dispose();
|
|
359
|
+
}
|
|
360
|
+
internal.occlusionCache.clear();
|
|
361
|
+
set((state) => ({
|
|
362
|
+
internal: {
|
|
363
|
+
...state.internal,
|
|
364
|
+
occlusionObserver: null,
|
|
365
|
+
occlusionEnabled: false
|
|
366
|
+
}
|
|
367
|
+
}));
|
|
368
|
+
}
|
|
369
|
+
function cleanupHelperGroup(store) {
|
|
370
|
+
const { internal, set } = store.getState();
|
|
371
|
+
disableOcclusion(store);
|
|
372
|
+
if (internal.helperGroup) {
|
|
373
|
+
internal.helperGroup.removeFromParent();
|
|
374
|
+
set((state) => ({
|
|
375
|
+
internal: {
|
|
376
|
+
...state.internal,
|
|
377
|
+
helperGroup: null
|
|
378
|
+
}
|
|
379
|
+
}));
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
function registerVisibility(store, object, handlers) {
|
|
383
|
+
const { internal } = store.getState();
|
|
384
|
+
const registry = internal.visibilityRegistry;
|
|
385
|
+
const entry = {
|
|
386
|
+
object,
|
|
387
|
+
handlers,
|
|
388
|
+
lastFramedState: null,
|
|
389
|
+
lastOccludedState: null,
|
|
390
|
+
lastVisibleState: null
|
|
391
|
+
};
|
|
392
|
+
registry.set(object.uuid, entry);
|
|
393
|
+
if (handlers.onOccluded || handlers.onVisible) {
|
|
394
|
+
object.occlusionTest = true;
|
|
395
|
+
if (!internal.occlusionEnabled) {
|
|
396
|
+
enableOcclusion(store);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
function unregisterVisibility(store, object) {
|
|
401
|
+
const { internal } = store.getState();
|
|
402
|
+
internal.visibilityRegistry.delete(object.uuid);
|
|
403
|
+
internal.occlusionCache.delete(object);
|
|
404
|
+
}
|
|
405
|
+
function checkVisibility(state) {
|
|
406
|
+
const { internal, camera } = state;
|
|
407
|
+
const registry = internal.visibilityRegistry;
|
|
408
|
+
if (registry.size === 0) return;
|
|
409
|
+
updateFrustum(camera, tempFrustum);
|
|
410
|
+
for (const entry of registry.values()) {
|
|
411
|
+
const { object, handlers, lastFramedState, lastOccludedState, lastVisibleState } = entry;
|
|
412
|
+
let inFrustum = null;
|
|
413
|
+
const computeFrustum = () => {
|
|
414
|
+
if (inFrustum === null) {
|
|
415
|
+
if (object.geometry?.boundingSphere === null) {
|
|
416
|
+
object.geometry?.computeBoundingSphere();
|
|
417
|
+
}
|
|
418
|
+
inFrustum = tempFrustum.intersectsObject(object);
|
|
419
|
+
}
|
|
420
|
+
return inFrustum;
|
|
421
|
+
};
|
|
422
|
+
if (handlers.onFramed) {
|
|
423
|
+
const currentInFrustum = computeFrustum();
|
|
424
|
+
if (currentInFrustum !== lastFramedState) {
|
|
425
|
+
entry.lastFramedState = currentInFrustum;
|
|
426
|
+
handlers.onFramed(currentInFrustum);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
let currentOcclusion = null;
|
|
430
|
+
if (handlers.onOccluded && internal.occlusionEnabled) {
|
|
431
|
+
currentOcclusion = internal.occlusionCache.get(object) ?? null;
|
|
432
|
+
if (currentOcclusion !== null && currentOcclusion !== lastOccludedState) {
|
|
433
|
+
entry.lastOccludedState = currentOcclusion;
|
|
434
|
+
handlers.onOccluded(currentOcclusion);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (handlers.onVisible) {
|
|
438
|
+
const currentInFrustum = computeFrustum();
|
|
439
|
+
if (!handlers.onFramed && currentInFrustum !== lastFramedState) {
|
|
440
|
+
entry.lastFramedState = currentInFrustum;
|
|
441
|
+
}
|
|
442
|
+
let isOccluded = currentOcclusion;
|
|
443
|
+
if (isOccluded === null && internal.occlusionEnabled) {
|
|
444
|
+
isOccluded = internal.occlusionCache.get(object) ?? null;
|
|
445
|
+
}
|
|
446
|
+
if (isOccluded === null) isOccluded = false;
|
|
447
|
+
const isVisible = currentInFrustum && !isOccluded && object.visible;
|
|
448
|
+
if (isVisible !== lastVisibleState) {
|
|
449
|
+
entry.lastVisibleState = isVisible;
|
|
450
|
+
handlers.onVisible(isVisible);
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function hasVisibilityHandlers(handlers) {
|
|
456
|
+
if (!handlers) return false;
|
|
457
|
+
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
458
|
+
}
|
|
459
|
+
|
|
251
460
|
const RESERVED_PROPS = [
|
|
252
461
|
"children",
|
|
253
462
|
"key",
|
|
@@ -262,6 +471,7 @@ const RESERVED_PROPS = [
|
|
|
262
471
|
"dispose"
|
|
263
472
|
];
|
|
264
473
|
const EVENT_REGEX = /^on(Pointer|Drag|Drop|Click|DoubleClick|ContextMenu|Wheel)/;
|
|
474
|
+
const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
|
|
265
475
|
const INDEX_REGEX = /-\d+$/;
|
|
266
476
|
const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
|
|
267
477
|
const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
|
|
@@ -356,6 +566,12 @@ function applyProps(object, props) {
|
|
|
356
566
|
instance.eventCount = Object.keys(instance.handlers).length;
|
|
357
567
|
continue;
|
|
358
568
|
}
|
|
569
|
+
if (instance && VISIBILITY_EVENT_REGEX.test(prop)) {
|
|
570
|
+
if (typeof value === "function") instance.handlers[prop] = value;
|
|
571
|
+
else delete instance.handlers[prop];
|
|
572
|
+
instance.eventCount = Object.keys(instance.handlers).length;
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
359
575
|
if (value === void 0) continue;
|
|
360
576
|
let { root, key, target } = resolve(object, prop);
|
|
361
577
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
@@ -392,6 +608,17 @@ function applyProps(object, props) {
|
|
|
392
608
|
if (instance.eventCount && object2.raycast !== null) {
|
|
393
609
|
rootState.internal.interaction.push(object2);
|
|
394
610
|
}
|
|
611
|
+
const root = findInitialRoot(instance);
|
|
612
|
+
const visibilityHandlers = {
|
|
613
|
+
onFramed: instance.handlers.onFramed,
|
|
614
|
+
onOccluded: instance.handlers.onOccluded,
|
|
615
|
+
onVisible: instance.handlers.onVisible
|
|
616
|
+
};
|
|
617
|
+
if (hasVisibilityHandlers(visibilityHandlers)) {
|
|
618
|
+
registerVisibility(root, object2, visibilityHandlers);
|
|
619
|
+
} else {
|
|
620
|
+
unregisterVisibility(root, object2);
|
|
621
|
+
}
|
|
395
622
|
}
|
|
396
623
|
if (instance && instance.props.attach === void 0) {
|
|
397
624
|
if (instance.object.isBufferGeometry) instance.props.attach = "geometry";
|
|
@@ -426,6 +653,7 @@ function removeInteractivity(store, object) {
|
|
|
426
653
|
internal.capturedMap.forEach((captures, pointerId) => {
|
|
427
654
|
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
428
655
|
});
|
|
656
|
+
unregisterVisibility(store, object);
|
|
429
657
|
}
|
|
430
658
|
function createEvents(store) {
|
|
431
659
|
function calculateDistance(event) {
|
|
@@ -808,8 +1036,21 @@ function formatLocation(url, line) {
|
|
|
808
1036
|
const file = clean.split("/").pop() ?? clean;
|
|
809
1037
|
return `${file}:${line}`;
|
|
810
1038
|
}
|
|
1039
|
+
function notifyAlpha({ message, link }) {
|
|
1040
|
+
if (typeof process !== "undefined" && (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) && process.env.R3F_SHOW_ALPHA_WARNINGS !== "true") {
|
|
1041
|
+
return;
|
|
1042
|
+
}
|
|
1043
|
+
if (shownNotices.has(message)) return;
|
|
1044
|
+
shownNotices.add(message);
|
|
1045
|
+
const boxStyle = "background: #6366f1; color: #ffffff; padding: 6px 10px; border-radius: 4px; font-weight: 500;";
|
|
1046
|
+
console.log(`%c\u{1F52C} ${message}`, boxStyle);
|
|
1047
|
+
{
|
|
1048
|
+
console.log(`%cMore info: ${link}`, "color: #6366f1; font-weight: normal;");
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
811
1051
|
|
|
812
|
-
const
|
|
1052
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
1053
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
|
|
813
1054
|
const createStore = (invalidate, advance) => {
|
|
814
1055
|
const rootStore = traditional.createWithEqualityFn((set, get) => {
|
|
815
1056
|
const position = new webgpu.Vector3();
|
|
@@ -840,6 +1081,8 @@ const createStore = (invalidate, advance) => {
|
|
|
840
1081
|
gl: null,
|
|
841
1082
|
renderer: null,
|
|
842
1083
|
camera: null,
|
|
1084
|
+
frustum: new webgpu.Frustum(),
|
|
1085
|
+
autoUpdateFrustum: true,
|
|
843
1086
|
raycaster: null,
|
|
844
1087
|
events: { priority: 1, enabled: true, connected: false },
|
|
845
1088
|
scene: null,
|
|
@@ -921,6 +1164,13 @@ const createStore = (invalidate, advance) => {
|
|
|
921
1164
|
initialHits: [],
|
|
922
1165
|
capturedMap: /* @__PURE__ */ new Map(),
|
|
923
1166
|
lastEvent: React__namespace.createRef(),
|
|
1167
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1168
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1169
|
+
// Occlusion system (WebGPU only)
|
|
1170
|
+
occlusionEnabled: false,
|
|
1171
|
+
occlusionObserver: null,
|
|
1172
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1173
|
+
helperGroup: null,
|
|
924
1174
|
// Updates
|
|
925
1175
|
active: false,
|
|
926
1176
|
frames: 0,
|
|
@@ -1006,7 +1256,15 @@ const createStore = (invalidate, advance) => {
|
|
|
1006
1256
|
}
|
|
1007
1257
|
if (camera !== oldCamera) {
|
|
1008
1258
|
oldCamera = camera;
|
|
1259
|
+
const { rootScene } = rootStore.getState();
|
|
1260
|
+
if (camera && rootScene && !camera.parent) {
|
|
1261
|
+
rootScene.add(camera);
|
|
1262
|
+
}
|
|
1009
1263
|
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1264
|
+
const currentState = rootStore.getState();
|
|
1265
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
1266
|
+
updateFrustum(camera, currentState.frustum);
|
|
1267
|
+
}
|
|
1010
1268
|
}
|
|
1011
1269
|
});
|
|
1012
1270
|
rootStore.subscribe((state2) => invalidate(state2));
|
|
@@ -1370,6 +1628,10 @@ const _Scheduler = class _Scheduler {
|
|
|
1370
1628
|
__publicField(this, "jobStateListeners", /* @__PURE__ */ new Map());
|
|
1371
1629
|
__publicField(this, "pendingFrames", 0);
|
|
1372
1630
|
__publicField(this, "_frameloop", "always");
|
|
1631
|
+
//* Independent Mode & Error Handling State ================================
|
|
1632
|
+
__publicField(this, "_independent", false);
|
|
1633
|
+
__publicField(this, "errorHandler", null);
|
|
1634
|
+
__publicField(this, "rootReadyCallbacks", /* @__PURE__ */ new Set());
|
|
1373
1635
|
//* Core Loop Execution Methods ================================
|
|
1374
1636
|
/**
|
|
1375
1637
|
* Main RAF loop callback.
|
|
@@ -1392,6 +1654,12 @@ const _Scheduler = class _Scheduler {
|
|
|
1392
1654
|
});
|
|
1393
1655
|
this.phaseGraph = new PhaseGraph();
|
|
1394
1656
|
}
|
|
1657
|
+
static get instance() {
|
|
1658
|
+
return globalThis[_Scheduler.INSTANCE_KEY] ?? null;
|
|
1659
|
+
}
|
|
1660
|
+
static set instance(value) {
|
|
1661
|
+
globalThis[_Scheduler.INSTANCE_KEY] = value;
|
|
1662
|
+
}
|
|
1395
1663
|
/**
|
|
1396
1664
|
* Get the global scheduler instance (creates if doesn't exist).
|
|
1397
1665
|
* Uses HMR data to preserve instance across hot reloads.
|
|
@@ -1440,29 +1708,43 @@ const _Scheduler = class _Scheduler {
|
|
|
1440
1708
|
get isRunning() {
|
|
1441
1709
|
return this.loopState.running;
|
|
1442
1710
|
}
|
|
1711
|
+
get isReady() {
|
|
1712
|
+
return this.roots.size > 0;
|
|
1713
|
+
}
|
|
1714
|
+
get independent() {
|
|
1715
|
+
return this._independent;
|
|
1716
|
+
}
|
|
1717
|
+
set independent(value) {
|
|
1718
|
+
this._independent = value;
|
|
1719
|
+
if (value) this.ensureDefaultRoot();
|
|
1720
|
+
}
|
|
1443
1721
|
//* Root Management Methods ================================
|
|
1444
1722
|
/**
|
|
1445
1723
|
* Register a root (Canvas) with the scheduler.
|
|
1446
1724
|
* The first root to register starts the RAF loop (if frameloop='always').
|
|
1447
1725
|
* @param {string} id - Unique identifier for this root
|
|
1448
|
-
* @param {
|
|
1726
|
+
* @param {RootOptions} [options] - Optional configuration with getState and onError callbacks
|
|
1449
1727
|
* @returns {() => void} Unsubscribe function to remove this root
|
|
1450
1728
|
*/
|
|
1451
|
-
registerRoot(id,
|
|
1729
|
+
registerRoot(id, options = {}) {
|
|
1452
1730
|
if (this.roots.has(id)) {
|
|
1453
1731
|
console.warn(`[Scheduler] Root "${id}" already registered`);
|
|
1454
1732
|
return () => this.unregisterRoot(id);
|
|
1455
1733
|
}
|
|
1456
1734
|
const entry = {
|
|
1457
1735
|
id,
|
|
1458
|
-
getState,
|
|
1736
|
+
getState: options.getState ?? (() => ({})),
|
|
1459
1737
|
jobs: /* @__PURE__ */ new Map(),
|
|
1460
1738
|
sortedJobs: [],
|
|
1461
1739
|
needsRebuild: false
|
|
1462
1740
|
};
|
|
1741
|
+
if (options.onError) {
|
|
1742
|
+
this.errorHandler = options.onError;
|
|
1743
|
+
}
|
|
1463
1744
|
this.roots.set(id, entry);
|
|
1464
|
-
if (this.roots.size === 1
|
|
1465
|
-
this.
|
|
1745
|
+
if (this.roots.size === 1) {
|
|
1746
|
+
this.notifyRootReady();
|
|
1747
|
+
if (this._frameloop === "always") this.start();
|
|
1466
1748
|
}
|
|
1467
1749
|
return () => this.unregisterRoot(id);
|
|
1468
1750
|
}
|
|
@@ -1482,7 +1764,60 @@ const _Scheduler = class _Scheduler {
|
|
|
1482
1764
|
this.roots.delete(id);
|
|
1483
1765
|
if (this.roots.size === 0) {
|
|
1484
1766
|
this.stop();
|
|
1767
|
+
this.errorHandler = null;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
/**
|
|
1771
|
+
* Subscribe to be notified when a root becomes available.
|
|
1772
|
+
* Fires immediately if a root already exists.
|
|
1773
|
+
* @param {() => void} callback - Function called when first root registers
|
|
1774
|
+
* @returns {() => void} Unsubscribe function
|
|
1775
|
+
*/
|
|
1776
|
+
onRootReady(callback) {
|
|
1777
|
+
if (this.roots.size > 0) {
|
|
1778
|
+
callback();
|
|
1779
|
+
return () => {
|
|
1780
|
+
};
|
|
1485
1781
|
}
|
|
1782
|
+
this.rootReadyCallbacks.add(callback);
|
|
1783
|
+
return () => this.rootReadyCallbacks.delete(callback);
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Notify all registered root-ready callbacks.
|
|
1787
|
+
* Called when the first root registers.
|
|
1788
|
+
* @returns {void}
|
|
1789
|
+
* @private
|
|
1790
|
+
*/
|
|
1791
|
+
notifyRootReady() {
|
|
1792
|
+
for (const cb of this.rootReadyCallbacks) {
|
|
1793
|
+
try {
|
|
1794
|
+
cb();
|
|
1795
|
+
} catch (error) {
|
|
1796
|
+
console.error("[Scheduler] Error in root-ready callback:", error);
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
this.rootReadyCallbacks.clear();
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Ensure a default root exists for independent mode.
|
|
1803
|
+
* Creates a minimal root with no state provider.
|
|
1804
|
+
* @returns {void}
|
|
1805
|
+
* @private
|
|
1806
|
+
*/
|
|
1807
|
+
ensureDefaultRoot() {
|
|
1808
|
+
if (!this.roots.has("__default__")) {
|
|
1809
|
+
this.registerRoot("__default__");
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
/**
|
|
1813
|
+
* Trigger error handling for job errors.
|
|
1814
|
+
* Uses the bound error handler if available, otherwise logs to console.
|
|
1815
|
+
* @param {Error} error - The error to handle
|
|
1816
|
+
* @returns {void}
|
|
1817
|
+
*/
|
|
1818
|
+
triggerError(error) {
|
|
1819
|
+
if (this.errorHandler) this.errorHandler(error);
|
|
1820
|
+
else console.error("[Scheduler]", error);
|
|
1486
1821
|
}
|
|
1487
1822
|
//* Phase Management Methods ================================
|
|
1488
1823
|
/**
|
|
@@ -1842,9 +2177,9 @@ const _Scheduler = class _Scheduler {
|
|
|
1842
2177
|
const deltaMs = this.loopState.lastTime !== null ? now - this.loopState.lastTime : 0;
|
|
1843
2178
|
const delta = deltaMs / 1e3;
|
|
1844
2179
|
const elapsed = now - this.loopState.createdAt;
|
|
1845
|
-
const
|
|
2180
|
+
const providedState = root.getState?.() ?? {};
|
|
1846
2181
|
const frameState = {
|
|
1847
|
-
...
|
|
2182
|
+
...providedState,
|
|
1848
2183
|
time: now,
|
|
1849
2184
|
delta,
|
|
1850
2185
|
elapsed,
|
|
@@ -1854,6 +2189,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1854
2189
|
job.callback(frameState, delta);
|
|
1855
2190
|
} catch (error) {
|
|
1856
2191
|
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2192
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
1857
2193
|
}
|
|
1858
2194
|
}
|
|
1859
2195
|
/**
|
|
@@ -1895,7 +2231,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1895
2231
|
/**
|
|
1896
2232
|
* Execute all jobs for a single root in sorted order.
|
|
1897
2233
|
* Rebuilds sorted job list if needed, then dispatches each job.
|
|
1898
|
-
* Errors are caught and propagated
|
|
2234
|
+
* Errors are caught and propagated via triggerError.
|
|
1899
2235
|
* @param {RootEntry} root - The root entry to tick
|
|
1900
2236
|
* @param {number} timestamp - RAF timestamp in milliseconds
|
|
1901
2237
|
* @param {number} delta - Time since last frame in seconds
|
|
@@ -1907,10 +2243,9 @@ const _Scheduler = class _Scheduler {
|
|
|
1907
2243
|
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
1908
2244
|
root.needsRebuild = false;
|
|
1909
2245
|
}
|
|
1910
|
-
const
|
|
1911
|
-
if (!rootState) return;
|
|
2246
|
+
const providedState = root.getState?.() ?? {};
|
|
1912
2247
|
const frameState = {
|
|
1913
|
-
...
|
|
2248
|
+
...providedState,
|
|
1914
2249
|
time: timestamp,
|
|
1915
2250
|
delta,
|
|
1916
2251
|
elapsed: this.loopState.elapsedTime / 1e3,
|
|
@@ -1923,7 +2258,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1923
2258
|
job.callback(frameState, delta);
|
|
1924
2259
|
} catch (error) {
|
|
1925
2260
|
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
1926
|
-
|
|
2261
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
1927
2262
|
}
|
|
1928
2263
|
}
|
|
1929
2264
|
}
|
|
@@ -2008,8 +2343,11 @@ const _Scheduler = class _Scheduler {
|
|
|
2008
2343
|
return /* @__PURE__ */ new Set([value]);
|
|
2009
2344
|
}
|
|
2010
2345
|
};
|
|
2011
|
-
//* Static State & Methods (
|
|
2012
|
-
|
|
2346
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2347
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2348
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2349
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2350
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2013
2351
|
let Scheduler = _Scheduler;
|
|
2014
2352
|
const getScheduler = () => Scheduler.get();
|
|
2015
2353
|
if (hmrData) {
|
|
@@ -2017,11 +2355,9 @@ if (hmrData) {
|
|
|
2017
2355
|
}
|
|
2018
2356
|
|
|
2019
2357
|
function useFrame(callback, priorityOrOptions) {
|
|
2020
|
-
const store =
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2023
|
-
return state.internal.rootId;
|
|
2024
|
-
}, [store]);
|
|
2358
|
+
const store = React__namespace.useContext(context);
|
|
2359
|
+
const isInsideCanvas = store !== null;
|
|
2360
|
+
const scheduler = getScheduler();
|
|
2025
2361
|
const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
|
|
2026
2362
|
id: priorityOrOptions.id,
|
|
2027
2363
|
phase: priorityOrOptions.phase,
|
|
@@ -2041,55 +2377,71 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2041
2377
|
const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
|
|
2042
2378
|
useIsomorphicLayoutEffect(() => {
|
|
2043
2379
|
if (!callback) return;
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
if (isLegacyPriority) {
|
|
2048
|
-
state.internal.priority++;
|
|
2049
|
-
let parentRoot = state.previousRoot;
|
|
2050
|
-
while (parentRoot) {
|
|
2051
|
-
const parentState = parentRoot.getState();
|
|
2052
|
-
if (parentState?.internal) parentState.internal.priority++;
|
|
2053
|
-
parentRoot = parentState?.previousRoot;
|
|
2054
|
-
}
|
|
2055
|
-
notifyDepreciated({
|
|
2056
|
-
heading: "useFrame with numeric priority is deprecated",
|
|
2057
|
-
body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
|
|
2058
|
-
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
2059
|
-
});
|
|
2060
|
-
}
|
|
2061
|
-
const wrappedCallback = (frameState, delta) => {
|
|
2062
|
-
const localState = store.getState();
|
|
2063
|
-
const mergedState = {
|
|
2064
|
-
...localState,
|
|
2065
|
-
time: frameState.time,
|
|
2066
|
-
delta: frameState.delta,
|
|
2067
|
-
elapsed: frameState.elapsed,
|
|
2068
|
-
frame: frameState.frame
|
|
2069
|
-
};
|
|
2070
|
-
callbackRef.current?.(mergedState, delta);
|
|
2071
|
-
};
|
|
2072
|
-
const unregister = scheduler.register(wrappedCallback, {
|
|
2073
|
-
id,
|
|
2074
|
-
rootId,
|
|
2075
|
-
...options
|
|
2076
|
-
});
|
|
2077
|
-
return () => {
|
|
2078
|
-
unregister();
|
|
2380
|
+
if (isInsideCanvas) {
|
|
2381
|
+
const state = store.getState();
|
|
2382
|
+
const rootId = state.internal.rootId;
|
|
2079
2383
|
if (isLegacyPriority) {
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2384
|
+
state.internal.priority++;
|
|
2385
|
+
let parentRoot = state.previousRoot;
|
|
2386
|
+
while (parentRoot) {
|
|
2387
|
+
const parentState = parentRoot.getState();
|
|
2388
|
+
if (parentState?.internal) parentState.internal.priority++;
|
|
2389
|
+
parentRoot = parentState?.previousRoot;
|
|
2390
|
+
}
|
|
2391
|
+
notifyDepreciated({
|
|
2392
|
+
heading: "useFrame with numeric priority is deprecated",
|
|
2393
|
+
body: 'Using useFrame(callback, number) to control render order is deprecated.\n\nFor custom rendering, use: useFrame(callback, { phase: "render" })\nFor execution order within update phase, use: useFrame(callback, { priority: number })',
|
|
2394
|
+
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
2395
|
+
});
|
|
2396
|
+
}
|
|
2397
|
+
const wrappedCallback = (frameState, delta) => {
|
|
2398
|
+
const localState = store.getState();
|
|
2399
|
+
const mergedState = {
|
|
2400
|
+
...localState,
|
|
2401
|
+
time: frameState.time,
|
|
2402
|
+
delta: frameState.delta,
|
|
2403
|
+
elapsed: frameState.elapsed,
|
|
2404
|
+
frame: frameState.frame
|
|
2405
|
+
};
|
|
2406
|
+
callbackRef.current?.(mergedState, delta);
|
|
2407
|
+
};
|
|
2408
|
+
const unregister = scheduler.register(wrappedCallback, {
|
|
2409
|
+
id,
|
|
2410
|
+
rootId,
|
|
2411
|
+
...options
|
|
2412
|
+
});
|
|
2413
|
+
return () => {
|
|
2414
|
+
unregister();
|
|
2415
|
+
if (isLegacyPriority) {
|
|
2416
|
+
const currentState = store.getState();
|
|
2417
|
+
if (currentState.internal) {
|
|
2418
|
+
currentState.internal.priority--;
|
|
2419
|
+
let parentRoot = currentState.previousRoot;
|
|
2420
|
+
while (parentRoot) {
|
|
2421
|
+
const parentState = parentRoot.getState();
|
|
2422
|
+
if (parentState?.internal) parentState.internal.priority--;
|
|
2423
|
+
parentRoot = parentState?.previousRoot;
|
|
2424
|
+
}
|
|
2088
2425
|
}
|
|
2089
2426
|
}
|
|
2427
|
+
};
|
|
2428
|
+
} else {
|
|
2429
|
+
const registerOutside = () => {
|
|
2430
|
+
return scheduler.register((state, delta) => callbackRef.current?.(state, delta), { id, ...options });
|
|
2431
|
+
};
|
|
2432
|
+
if (scheduler.independent || scheduler.isReady) {
|
|
2433
|
+
return registerOutside();
|
|
2090
2434
|
}
|
|
2091
|
-
|
|
2092
|
-
|
|
2435
|
+
let unregisterJob = null;
|
|
2436
|
+
const unsubReady = scheduler.onRootReady(() => {
|
|
2437
|
+
unregisterJob = registerOutside();
|
|
2438
|
+
});
|
|
2439
|
+
return () => {
|
|
2440
|
+
unsubReady();
|
|
2441
|
+
unregisterJob?.();
|
|
2442
|
+
};
|
|
2443
|
+
}
|
|
2444
|
+
}, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
|
|
2093
2445
|
const isPaused = React__namespace.useSyncExternalStore(
|
|
2094
2446
|
// Subscribe function
|
|
2095
2447
|
React__namespace.useCallback(
|
|
@@ -2104,7 +2456,7 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2104
2456
|
React__namespace.useCallback(() => false, [])
|
|
2105
2457
|
);
|
|
2106
2458
|
const controls = React__namespace.useMemo(() => {
|
|
2107
|
-
const
|
|
2459
|
+
const scheduler2 = getScheduler();
|
|
2108
2460
|
return {
|
|
2109
2461
|
/** The job's unique ID */
|
|
2110
2462
|
id,
|
|
@@ -2112,7 +2464,7 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2112
2464
|
* Access to the global scheduler for frame loop control.
|
|
2113
2465
|
* Use for controlling the entire frame loop, adding phases, etc.
|
|
2114
2466
|
*/
|
|
2115
|
-
scheduler,
|
|
2467
|
+
scheduler: scheduler2,
|
|
2116
2468
|
/**
|
|
2117
2469
|
* Manually step this job only.
|
|
2118
2470
|
* Bypasses FPS limiting - always runs.
|
|
@@ -2360,6 +2712,16 @@ function useTextures() {
|
|
|
2360
2712
|
}, [store]);
|
|
2361
2713
|
}
|
|
2362
2714
|
|
|
2715
|
+
function useRenderTarget(width, height, options) {
|
|
2716
|
+
const isLegacy = useThree((s) => s.isLegacy);
|
|
2717
|
+
const size = useThree((s) => s.size);
|
|
2718
|
+
return React.useMemo(() => {
|
|
2719
|
+
const w = width ?? size.width;
|
|
2720
|
+
const h = height ?? size.height;
|
|
2721
|
+
return new webgpu.RenderTarget(w, h, options);
|
|
2722
|
+
}, [width, height, size.width, size.height, options, isLegacy]);
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2363
2725
|
function useStore() {
|
|
2364
2726
|
const store = React.useContext(context);
|
|
2365
2727
|
if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
|
|
@@ -2411,7 +2773,7 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
|
|
|
2411
2773
|
getScheduler().step(timestamp);
|
|
2412
2774
|
}
|
|
2413
2775
|
|
|
2414
|
-
const version = "10.0.0-alpha.
|
|
2776
|
+
const version = "10.0.0-alpha.1";
|
|
2415
2777
|
const packageData = {
|
|
2416
2778
|
version: version};
|
|
2417
2779
|
|
|
@@ -13712,7 +14074,8 @@ function createReconciler(config) {
|
|
|
13712
14074
|
return reconciler2;
|
|
13713
14075
|
}
|
|
13714
14076
|
const NoEventPriority = 0;
|
|
13715
|
-
const
|
|
14077
|
+
const R3F_CATALOGUE = Symbol.for("@react-three/fiber.catalogue");
|
|
14078
|
+
const catalogue = globalThis[R3F_CATALOGUE] ?? (globalThis[R3F_CATALOGUE] = {});
|
|
13716
14079
|
const PREFIX_REGEX = /^three(?=[A-Z])/;
|
|
13717
14080
|
const toPascalCase = (type) => `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
13718
14081
|
let i = 0;
|
|
@@ -14210,7 +14573,9 @@ function createRoot(canvas) {
|
|
|
14210
14573
|
camera: cameraOptions,
|
|
14211
14574
|
onPointerMissed,
|
|
14212
14575
|
onDragOverMissed,
|
|
14213
|
-
onDropMissed
|
|
14576
|
+
onDropMissed,
|
|
14577
|
+
autoUpdateFrustum = true,
|
|
14578
|
+
occlusion = false
|
|
14214
14579
|
} = props;
|
|
14215
14580
|
let state = store.getState();
|
|
14216
14581
|
const defaultGPUProps = {
|
|
@@ -14227,7 +14592,9 @@ function createRoot(canvas) {
|
|
|
14227
14592
|
let renderer = state.internal.actualRenderer;
|
|
14228
14593
|
if (!state.internal.actualRenderer) {
|
|
14229
14594
|
renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
|
|
14230
|
-
|
|
14595
|
+
if (!renderer.hasInitialized?.()) {
|
|
14596
|
+
await renderer.init();
|
|
14597
|
+
}
|
|
14231
14598
|
const backend = renderer.backend;
|
|
14232
14599
|
const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
|
|
14233
14600
|
state.internal.actualRenderer = renderer;
|
|
@@ -14275,6 +14642,8 @@ function createRoot(canvas) {
|
|
|
14275
14642
|
rootScene: scene,
|
|
14276
14643
|
internal: { ...prev.internal, container: scene }
|
|
14277
14644
|
}));
|
|
14645
|
+
const camera = state.camera;
|
|
14646
|
+
if (camera && !camera.parent) scene.add(camera);
|
|
14278
14647
|
}
|
|
14279
14648
|
if (events && !state.events.handlers) {
|
|
14280
14649
|
state.set({ events: events(store) });
|
|
@@ -14302,6 +14671,10 @@ function createRoot(canvas) {
|
|
|
14302
14671
|
if (!state.onPointerMissed) state.set({ onPointerMissed });
|
|
14303
14672
|
if (!state.onDragOverMissed) state.set({ onDragOverMissed });
|
|
14304
14673
|
if (!state.onDropMissed) state.set({ onDropMissed });
|
|
14674
|
+
if (state.autoUpdateFrustum !== autoUpdateFrustum) state.set({ autoUpdateFrustum });
|
|
14675
|
+
if (occlusion && !state.internal.occlusionEnabled) {
|
|
14676
|
+
enableOcclusion(store);
|
|
14677
|
+
}
|
|
14305
14678
|
if (performance && !is.equ(performance, lastConfiguredProps.performance, shallowLoose)) {
|
|
14306
14679
|
state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
|
|
14307
14680
|
lastConfiguredProps.performance = performance;
|
|
@@ -14374,7 +14747,37 @@ function createRoot(canvas) {
|
|
|
14374
14747
|
const rootId = state.internal.rootId;
|
|
14375
14748
|
if (!rootId) {
|
|
14376
14749
|
const newRootId = scheduler.generateRootId();
|
|
14377
|
-
const unregisterRoot = scheduler.registerRoot(newRootId,
|
|
14750
|
+
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14751
|
+
getState: () => store.getState(),
|
|
14752
|
+
onError: (err) => store.getState().setError(err)
|
|
14753
|
+
});
|
|
14754
|
+
const unregisterFrustum = scheduler.register(
|
|
14755
|
+
() => {
|
|
14756
|
+
const state2 = store.getState();
|
|
14757
|
+
if (state2.autoUpdateFrustum && state2.camera) {
|
|
14758
|
+
updateFrustum(state2.camera, state2.frustum);
|
|
14759
|
+
}
|
|
14760
|
+
},
|
|
14761
|
+
{
|
|
14762
|
+
id: `${newRootId}_frustum`,
|
|
14763
|
+
rootId: newRootId,
|
|
14764
|
+
phase: "preRender",
|
|
14765
|
+
system: true
|
|
14766
|
+
}
|
|
14767
|
+
);
|
|
14768
|
+
const unregisterVisibility = scheduler.register(
|
|
14769
|
+
() => {
|
|
14770
|
+
const state2 = store.getState();
|
|
14771
|
+
checkVisibility(state2);
|
|
14772
|
+
},
|
|
14773
|
+
{
|
|
14774
|
+
id: `${newRootId}_visibility`,
|
|
14775
|
+
rootId: newRootId,
|
|
14776
|
+
phase: "preRender",
|
|
14777
|
+
system: true,
|
|
14778
|
+
after: `${newRootId}_frustum`
|
|
14779
|
+
}
|
|
14780
|
+
);
|
|
14378
14781
|
const unregisterRender = scheduler.register(
|
|
14379
14782
|
() => {
|
|
14380
14783
|
const state2 = store.getState();
|
|
@@ -14402,6 +14805,8 @@ function createRoot(canvas) {
|
|
|
14402
14805
|
rootId: newRootId,
|
|
14403
14806
|
unregisterRoot: () => {
|
|
14404
14807
|
unregisterRoot();
|
|
14808
|
+
unregisterFrustum();
|
|
14809
|
+
unregisterVisibility();
|
|
14405
14810
|
unregisterRender();
|
|
14406
14811
|
},
|
|
14407
14812
|
scheduler
|
|
@@ -14459,6 +14864,7 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14459
14864
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14460
14865
|
if (unregisterRoot) unregisterRoot();
|
|
14461
14866
|
state.events.disconnect?.();
|
|
14867
|
+
cleanupHelperGroup(root.store);
|
|
14462
14868
|
renderer?.renderLists?.dispose?.();
|
|
14463
14869
|
renderer?.forceContextLoss?.();
|
|
14464
14870
|
if (renderer?.xr) state.xr.disconnect();
|
|
@@ -14638,7 +15044,22 @@ function CanvasImpl({
|
|
|
14638
15044
|
effectActiveRef.current = true;
|
|
14639
15045
|
const canvas = canvasRef.current;
|
|
14640
15046
|
if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
|
|
14641
|
-
if (!root.current)
|
|
15047
|
+
if (!root.current) {
|
|
15048
|
+
root.current = createRoot(canvas);
|
|
15049
|
+
notifyAlpha({
|
|
15050
|
+
message: "React Three Fiber v10 is in ALPHA - expect breaking changes",
|
|
15051
|
+
link: "https://github.com/pmndrs/react-three-fiber/discussions"
|
|
15052
|
+
});
|
|
15053
|
+
const rootEntry = _roots.get(canvas);
|
|
15054
|
+
if (rootEntry?.store) {
|
|
15055
|
+
if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
|
|
15056
|
+
unsubscribeErrorRef.current = rootEntry.store.subscribe((state) => {
|
|
15057
|
+
if (state.error && effectActiveRef.current) {
|
|
15058
|
+
setError(state.error);
|
|
15059
|
+
}
|
|
15060
|
+
});
|
|
15061
|
+
}
|
|
15062
|
+
}
|
|
14642
15063
|
async function run() {
|
|
14643
15064
|
if (!effectActiveRef.current || !root.current) return;
|
|
14644
15065
|
await root.current.configure({
|
|
@@ -14679,15 +15100,9 @@ function CanvasImpl({
|
|
|
14679
15100
|
}
|
|
14680
15101
|
});
|
|
14681
15102
|
if (!effectActiveRef.current || !root.current) return;
|
|
14682
|
-
|
|
15103
|
+
root.current.render(
|
|
14683
15104
|
/* @__PURE__ */ jsxRuntime.jsx(Bridge, { children: /* @__PURE__ */ jsxRuntime.jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsxRuntime.jsx(React__namespace.Suspense, { fallback: /* @__PURE__ */ jsxRuntime.jsx(Block, { set: setBlock }), children: children ?? null }) }) })
|
|
14684
15105
|
);
|
|
14685
|
-
if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
|
|
14686
|
-
unsubscribeErrorRef.current = store.subscribe((state) => {
|
|
14687
|
-
if (state.error && effectActiveRef.current) {
|
|
14688
|
-
setError(state.error);
|
|
14689
|
-
}
|
|
14690
|
-
});
|
|
14691
15106
|
}
|
|
14692
15107
|
run();
|
|
14693
15108
|
}
|
|
@@ -14808,11 +15223,46 @@ function useCompareMemoize(value, deep) {
|
|
|
14808
15223
|
const isUniformNode$1 = (value) => value !== null && typeof value === "object" && "value" in value && "uuid" in value;
|
|
14809
15224
|
function useUniforms(creatorOrScope, scope) {
|
|
14810
15225
|
const store = useStore();
|
|
15226
|
+
const removeUniforms2 = React.useCallback(
|
|
15227
|
+
(names, targetScope) => {
|
|
15228
|
+
const nameArray = Array.isArray(names) ? names : [names];
|
|
15229
|
+
store.setState((state) => {
|
|
15230
|
+
if (targetScope) {
|
|
15231
|
+
const currentScope = { ...state.uniforms[targetScope] };
|
|
15232
|
+
for (const name of nameArray) delete currentScope[name];
|
|
15233
|
+
return { uniforms: { ...state.uniforms, [targetScope]: currentScope } };
|
|
15234
|
+
}
|
|
15235
|
+
const uniforms2 = { ...state.uniforms };
|
|
15236
|
+
for (const name of nameArray) if (isUniformNode$1(uniforms2[name])) delete uniforms2[name];
|
|
15237
|
+
return { uniforms: uniforms2 };
|
|
15238
|
+
});
|
|
15239
|
+
},
|
|
15240
|
+
[store]
|
|
15241
|
+
);
|
|
15242
|
+
const clearUniforms = React.useCallback(
|
|
15243
|
+
(targetScope) => {
|
|
15244
|
+
store.setState((state) => {
|
|
15245
|
+
if (targetScope && targetScope !== "root") {
|
|
15246
|
+
const { [targetScope]: _, ...rest } = state.uniforms;
|
|
15247
|
+
return { uniforms: rest };
|
|
15248
|
+
}
|
|
15249
|
+
if (targetScope === "root") {
|
|
15250
|
+
const uniforms2 = {};
|
|
15251
|
+
for (const [key, value] of Object.entries(state.uniforms)) {
|
|
15252
|
+
if (!isUniformNode$1(value)) uniforms2[key] = value;
|
|
15253
|
+
}
|
|
15254
|
+
return { uniforms: uniforms2 };
|
|
15255
|
+
}
|
|
15256
|
+
return { uniforms: {} };
|
|
15257
|
+
});
|
|
15258
|
+
},
|
|
15259
|
+
[store]
|
|
15260
|
+
);
|
|
14811
15261
|
const inputForMemoization = React.useMemo(() => {
|
|
14812
15262
|
return is.fun(creatorOrScope) ? creatorOrScope(store.getState()) : creatorOrScope;
|
|
14813
15263
|
}, [creatorOrScope, store]);
|
|
14814
15264
|
const memoizedInput = useCompareMemoize(inputForMemoization);
|
|
14815
|
-
|
|
15265
|
+
const uniforms = React.useMemo(() => {
|
|
14816
15266
|
const state = store.getState();
|
|
14817
15267
|
const set = store.setState;
|
|
14818
15268
|
if (memoizedInput === void 0) {
|
|
@@ -14858,44 +15308,26 @@ function useUniforms(creatorOrScope, scope) {
|
|
|
14858
15308
|
set((s) => ({
|
|
14859
15309
|
uniforms: {
|
|
14860
15310
|
...s.uniforms,
|
|
14861
|
-
[scope]: {
|
|
14862
|
-
...s.uniforms[scope],
|
|
14863
|
-
...result
|
|
14864
|
-
}
|
|
15311
|
+
[scope]: { ...s.uniforms[scope], ...result }
|
|
14865
15312
|
}
|
|
14866
15313
|
}));
|
|
14867
15314
|
} else {
|
|
14868
|
-
set((s) => ({
|
|
14869
|
-
uniforms: {
|
|
14870
|
-
...s.uniforms,
|
|
14871
|
-
...result
|
|
14872
|
-
}
|
|
14873
|
-
}));
|
|
15315
|
+
set((s) => ({ uniforms: { ...s.uniforms, ...result } }));
|
|
14874
15316
|
}
|
|
14875
15317
|
}
|
|
14876
15318
|
return result;
|
|
14877
15319
|
}, [store, memoizedInput, scope]);
|
|
15320
|
+
return { ...uniforms, removeUniforms: removeUniforms2, clearUniforms };
|
|
14878
15321
|
}
|
|
14879
15322
|
function removeUniforms(set, names, scope) {
|
|
14880
15323
|
set((state) => {
|
|
14881
15324
|
if (scope) {
|
|
14882
15325
|
const currentScope = { ...state.uniforms[scope] };
|
|
14883
|
-
for (const name of names)
|
|
14884
|
-
|
|
14885
|
-
}
|
|
14886
|
-
return {
|
|
14887
|
-
uniforms: {
|
|
14888
|
-
...state.uniforms,
|
|
14889
|
-
[scope]: currentScope
|
|
14890
|
-
}
|
|
14891
|
-
};
|
|
15326
|
+
for (const name of names) delete currentScope[name];
|
|
15327
|
+
return { uniforms: { ...state.uniforms, [scope]: currentScope } };
|
|
14892
15328
|
}
|
|
14893
15329
|
const uniforms = { ...state.uniforms };
|
|
14894
|
-
for (const name of names)
|
|
14895
|
-
if (isUniformNode$1(uniforms[name])) {
|
|
14896
|
-
delete uniforms[name];
|
|
14897
|
-
}
|
|
14898
|
-
}
|
|
15330
|
+
for (const name of names) if (isUniformNode$1(uniforms[name])) delete uniforms[name];
|
|
14899
15331
|
return { uniforms };
|
|
14900
15332
|
});
|
|
14901
15333
|
}
|
|
@@ -14909,9 +15341,7 @@ function clearRootUniforms(set) {
|
|
|
14909
15341
|
set((state) => {
|
|
14910
15342
|
const uniforms = {};
|
|
14911
15343
|
for (const [key, value] of Object.entries(state.uniforms)) {
|
|
14912
|
-
if (!isUniformNode$1(value))
|
|
14913
|
-
uniforms[key] = value;
|
|
14914
|
-
}
|
|
15344
|
+
if (!isUniformNode$1(value)) uniforms[key] = value;
|
|
14915
15345
|
}
|
|
14916
15346
|
return { uniforms };
|
|
14917
15347
|
});
|
|
@@ -14986,7 +15416,42 @@ function useUniform(name, value) {
|
|
|
14986
15416
|
const isTSLNode = (value) => value !== null && typeof value === "object" && ("uuid" in value || "nodeType" in value);
|
|
14987
15417
|
function useNodes(creatorOrScope, scope) {
|
|
14988
15418
|
const store = useStore();
|
|
14989
|
-
|
|
15419
|
+
const removeNodes2 = React.useCallback(
|
|
15420
|
+
(names, targetScope) => {
|
|
15421
|
+
const nameArray = Array.isArray(names) ? names : [names];
|
|
15422
|
+
store.setState((state) => {
|
|
15423
|
+
if (targetScope) {
|
|
15424
|
+
const currentScope = { ...state.nodes[targetScope] };
|
|
15425
|
+
for (const name of nameArray) delete currentScope[name];
|
|
15426
|
+
return { nodes: { ...state.nodes, [targetScope]: currentScope } };
|
|
15427
|
+
}
|
|
15428
|
+
const nodes2 = { ...state.nodes };
|
|
15429
|
+
for (const name of nameArray) if (isTSLNode(nodes2[name])) delete nodes2[name];
|
|
15430
|
+
return { nodes: nodes2 };
|
|
15431
|
+
});
|
|
15432
|
+
},
|
|
15433
|
+
[store]
|
|
15434
|
+
);
|
|
15435
|
+
const clearNodes = React.useCallback(
|
|
15436
|
+
(targetScope) => {
|
|
15437
|
+
store.setState((state) => {
|
|
15438
|
+
if (targetScope && targetScope !== "root") {
|
|
15439
|
+
const { [targetScope]: _, ...rest } = state.nodes;
|
|
15440
|
+
return { nodes: rest };
|
|
15441
|
+
}
|
|
15442
|
+
if (targetScope === "root") {
|
|
15443
|
+
const nodes2 = {};
|
|
15444
|
+
for (const [key, value] of Object.entries(state.nodes)) {
|
|
15445
|
+
if (!isTSLNode(value)) nodes2[key] = value;
|
|
15446
|
+
}
|
|
15447
|
+
return { nodes: nodes2 };
|
|
15448
|
+
}
|
|
15449
|
+
return { nodes: {} };
|
|
15450
|
+
});
|
|
15451
|
+
},
|
|
15452
|
+
[store]
|
|
15453
|
+
);
|
|
15454
|
+
const nodes = React.useMemo(() => {
|
|
14990
15455
|
const state = store.getState();
|
|
14991
15456
|
const set = store.setState;
|
|
14992
15457
|
if (creatorOrScope === void 0) {
|
|
@@ -14994,9 +15459,7 @@ function useNodes(creatorOrScope, scope) {
|
|
|
14994
15459
|
}
|
|
14995
15460
|
if (typeof creatorOrScope === "string") {
|
|
14996
15461
|
const scopeData = state.nodes[creatorOrScope];
|
|
14997
|
-
if (scopeData && !isTSLNode(scopeData))
|
|
14998
|
-
return scopeData;
|
|
14999
|
-
}
|
|
15462
|
+
if (scopeData && !isTSLNode(scopeData)) return scopeData;
|
|
15000
15463
|
return {};
|
|
15001
15464
|
}
|
|
15002
15465
|
const creator = creatorOrScope;
|
|
@@ -15009,9 +15472,7 @@ function useNodes(creatorOrScope, scope) {
|
|
|
15009
15472
|
if (currentScope[name]) {
|
|
15010
15473
|
result[name] = currentScope[name];
|
|
15011
15474
|
} else {
|
|
15012
|
-
if (typeof node.label === "function") {
|
|
15013
|
-
node.setName(`${scope}.${name}`);
|
|
15014
|
-
}
|
|
15475
|
+
if (typeof node.label === "function") node.setName(`${scope}.${name}`);
|
|
15015
15476
|
result[name] = node;
|
|
15016
15477
|
hasNewNodes = true;
|
|
15017
15478
|
}
|
|
@@ -15020,10 +15481,7 @@ function useNodes(creatorOrScope, scope) {
|
|
|
15020
15481
|
set((s) => ({
|
|
15021
15482
|
nodes: {
|
|
15022
15483
|
...s.nodes,
|
|
15023
|
-
[scope]: {
|
|
15024
|
-
...s.nodes[scope],
|
|
15025
|
-
...result
|
|
15026
|
-
}
|
|
15484
|
+
[scope]: { ...s.nodes[scope], ...result }
|
|
15027
15485
|
}
|
|
15028
15486
|
}));
|
|
15029
15487
|
}
|
|
@@ -15034,44 +15492,27 @@ function useNodes(creatorOrScope, scope) {
|
|
|
15034
15492
|
if (existing && isTSLNode(existing)) {
|
|
15035
15493
|
result[name] = existing;
|
|
15036
15494
|
} else {
|
|
15037
|
-
if (typeof node.label === "function")
|
|
15038
|
-
node.setName(name);
|
|
15039
|
-
}
|
|
15495
|
+
if (typeof node.label === "function") node.setName(name);
|
|
15040
15496
|
result[name] = node;
|
|
15041
15497
|
hasNewNodes = true;
|
|
15042
15498
|
}
|
|
15043
15499
|
}
|
|
15044
15500
|
if (hasNewNodes) {
|
|
15045
|
-
set((s) => ({
|
|
15046
|
-
nodes: {
|
|
15047
|
-
...s.nodes,
|
|
15048
|
-
...result
|
|
15049
|
-
}
|
|
15050
|
-
}));
|
|
15501
|
+
set((s) => ({ nodes: { ...s.nodes, ...result } }));
|
|
15051
15502
|
}
|
|
15052
15503
|
return result;
|
|
15053
15504
|
}, [store, typeof creatorOrScope === "string" ? creatorOrScope : scope]);
|
|
15505
|
+
return { ...nodes, removeNodes: removeNodes2, clearNodes };
|
|
15054
15506
|
}
|
|
15055
15507
|
function removeNodes(set, names, scope) {
|
|
15056
15508
|
set((state) => {
|
|
15057
15509
|
if (scope) {
|
|
15058
15510
|
const currentScope = { ...state.nodes[scope] };
|
|
15059
|
-
for (const name of names)
|
|
15060
|
-
|
|
15061
|
-
}
|
|
15062
|
-
return {
|
|
15063
|
-
nodes: {
|
|
15064
|
-
...state.nodes,
|
|
15065
|
-
[scope]: currentScope
|
|
15066
|
-
}
|
|
15067
|
-
};
|
|
15511
|
+
for (const name of names) delete currentScope[name];
|
|
15512
|
+
return { nodes: { ...state.nodes, [scope]: currentScope } };
|
|
15068
15513
|
}
|
|
15069
15514
|
const nodes = { ...state.nodes };
|
|
15070
|
-
for (const name of names)
|
|
15071
|
-
if (isTSLNode(nodes[name])) {
|
|
15072
|
-
delete nodes[name];
|
|
15073
|
-
}
|
|
15074
|
-
}
|
|
15515
|
+
for (const name of names) if (isTSLNode(nodes[name])) delete nodes[name];
|
|
15075
15516
|
return { nodes };
|
|
15076
15517
|
});
|
|
15077
15518
|
}
|
|
@@ -15085,9 +15526,7 @@ function clearRootNodes(set) {
|
|
|
15085
15526
|
set((state) => {
|
|
15086
15527
|
const nodes = {};
|
|
15087
15528
|
for (const [key, value] of Object.entries(state.nodes)) {
|
|
15088
|
-
if (!isTSLNode(value))
|
|
15089
|
-
nodes[key] = value;
|
|
15090
|
-
}
|
|
15529
|
+
if (!isTSLNode(value)) nodes[key] = value;
|
|
15091
15530
|
}
|
|
15092
15531
|
return { nodes };
|
|
15093
15532
|
});
|
|
@@ -15256,6 +15695,7 @@ exports.removeUniforms = removeUniforms;
|
|
|
15256
15695
|
exports.resolve = resolve;
|
|
15257
15696
|
exports.unmountComponentAtNode = unmountComponentAtNode;
|
|
15258
15697
|
exports.updateCamera = updateCamera;
|
|
15698
|
+
exports.updateFrustum = updateFrustum;
|
|
15259
15699
|
exports.useBridge = useBridge;
|
|
15260
15700
|
exports.useFrame = useFrame;
|
|
15261
15701
|
exports.useGraph = useGraph;
|
|
@@ -15266,6 +15706,7 @@ exports.useLocalNodes = useLocalNodes;
|
|
|
15266
15706
|
exports.useMutableCallback = useMutableCallback;
|
|
15267
15707
|
exports.useNodes = useNodes;
|
|
15268
15708
|
exports.usePostProcessing = usePostProcessing;
|
|
15709
|
+
exports.useRenderTarget = useRenderTarget;
|
|
15269
15710
|
exports.useStore = useStore;
|
|
15270
15711
|
exports.useTexture = useTexture;
|
|
15271
15712
|
exports.useTextures = useTextures;
|