@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/index.cjs
CHANGED
|
@@ -51,6 +51,8 @@ const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
|
51
51
|
Inspector: Inspector_js.Inspector,
|
|
52
52
|
R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
|
|
53
53
|
R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
|
|
54
|
+
RenderTargetCompat: webgpu.RenderTarget,
|
|
55
|
+
WebGLRenderTarget: three.WebGLRenderTarget,
|
|
54
56
|
WebGLRenderer: three.WebGLRenderer
|
|
55
57
|
}, [webgpu__namespace]);
|
|
56
58
|
|
|
@@ -159,6 +161,13 @@ function updateCamera(camera, size) {
|
|
|
159
161
|
}
|
|
160
162
|
camera.updateProjectionMatrix();
|
|
161
163
|
}
|
|
164
|
+
const frustumMatrix = new webgpu.Matrix4();
|
|
165
|
+
function updateFrustum(camera, frustum) {
|
|
166
|
+
const target = frustum ?? new webgpu.Frustum();
|
|
167
|
+
frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
168
|
+
target.setFromProjectionMatrix(frustumMatrix);
|
|
169
|
+
return target;
|
|
170
|
+
}
|
|
162
171
|
|
|
163
172
|
const REACT_INTERNAL_PROPS = ["children", "key", "ref"];
|
|
164
173
|
function findInitialRoot(instance) {
|
|
@@ -239,6 +248,205 @@ function invalidateInstance(instance) {
|
|
|
239
248
|
if (state && state.internal.frames === 0) state.invalidate();
|
|
240
249
|
}
|
|
241
250
|
|
|
251
|
+
const tempFrustum = new webgpu.Frustum();
|
|
252
|
+
let hasWarnedWebGL = false;
|
|
253
|
+
let tslModule = null;
|
|
254
|
+
async function loadTSL() {
|
|
255
|
+
if (tslModule) return tslModule;
|
|
256
|
+
try {
|
|
257
|
+
const tsl = await import('three/tsl');
|
|
258
|
+
tslModule = { uniform: tsl.uniform, nodeObject: tsl.nodeObject };
|
|
259
|
+
return tslModule;
|
|
260
|
+
} catch {
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
function createOcclusionObserverNode(store, uniform) {
|
|
265
|
+
const node = new webgpu.Node("float");
|
|
266
|
+
node.updateType = webgpu.NodeUpdateType.OBJECT;
|
|
267
|
+
node.update = function(frame) {
|
|
268
|
+
const { internal } = store.getState();
|
|
269
|
+
const registry = internal.visibilityRegistry;
|
|
270
|
+
const cache = internal.occlusionCache;
|
|
271
|
+
for (const entry of registry.values()) {
|
|
272
|
+
const { object, handlers } = entry;
|
|
273
|
+
if (handlers.onOccluded || handlers.onVisible) {
|
|
274
|
+
const isOccluded = frame.renderer.isOccluded(object);
|
|
275
|
+
cache.set(object, isOccluded);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
node.setup = function() {
|
|
280
|
+
return uniform(0);
|
|
281
|
+
};
|
|
282
|
+
return node;
|
|
283
|
+
}
|
|
284
|
+
let occlusionSetupPromise = null;
|
|
285
|
+
function enableOcclusion(store) {
|
|
286
|
+
const state = store.getState();
|
|
287
|
+
const { internal, renderer, rootScene } = state;
|
|
288
|
+
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
289
|
+
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
290
|
+
if (!hasOcclusionSupport) {
|
|
291
|
+
if (!hasWarnedWebGL) {
|
|
292
|
+
console.warn(
|
|
293
|
+
"[R3F] Warning: onOccluded/onVisible occlusion queries require WebGPU renderer. Occlusion events will not fire on WebGL."
|
|
294
|
+
);
|
|
295
|
+
hasWarnedWebGL = true;
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
occlusionSetupPromise = setupOcclusion(store);
|
|
300
|
+
}
|
|
301
|
+
async function setupOcclusion(store) {
|
|
302
|
+
const state = store.getState();
|
|
303
|
+
const { internal, rootScene, set } = state;
|
|
304
|
+
const tsl = await loadTSL();
|
|
305
|
+
if (!tsl) {
|
|
306
|
+
console.warn("[R3F] Warning: TSL module not available. Occlusion queries disabled.");
|
|
307
|
+
occlusionSetupPromise = null;
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
const { uniform, nodeObject } = tsl;
|
|
311
|
+
let helperGroup = internal.helperGroup;
|
|
312
|
+
if (!helperGroup) {
|
|
313
|
+
helperGroup = new webgpu.Group();
|
|
314
|
+
helperGroup.name = "__r3fInternal";
|
|
315
|
+
helperGroup.__r3fInternal = true;
|
|
316
|
+
rootScene.add(helperGroup);
|
|
317
|
+
}
|
|
318
|
+
const geometry = new webgpu.BoxGeometry(1, 1, 1);
|
|
319
|
+
const material = new webgpu.MeshBasicNodeMaterial({
|
|
320
|
+
transparent: true,
|
|
321
|
+
opacity: 0
|
|
322
|
+
});
|
|
323
|
+
const observerNode = nodeObject(createOcclusionObserverNode(store, uniform));
|
|
324
|
+
material.colorNode = observerNode;
|
|
325
|
+
material.needsUpdate = true;
|
|
326
|
+
const mesh = new webgpu.Mesh(geometry, material);
|
|
327
|
+
mesh.name = "__r3fOcclusionObserver";
|
|
328
|
+
mesh.scale.setScalar(1e-4);
|
|
329
|
+
mesh.frustumCulled = false;
|
|
330
|
+
mesh.__r3fInternal = true;
|
|
331
|
+
helperGroup.add(mesh);
|
|
332
|
+
set((state2) => ({
|
|
333
|
+
internal: {
|
|
334
|
+
...state2.internal,
|
|
335
|
+
helperGroup,
|
|
336
|
+
occlusionObserver: mesh,
|
|
337
|
+
occlusionEnabled: true
|
|
338
|
+
}
|
|
339
|
+
}));
|
|
340
|
+
occlusionSetupPromise = null;
|
|
341
|
+
}
|
|
342
|
+
function disableOcclusion(store) {
|
|
343
|
+
const { internal, set } = store.getState();
|
|
344
|
+
if (!internal.occlusionEnabled) return;
|
|
345
|
+
if (internal.occlusionObserver) {
|
|
346
|
+
internal.occlusionObserver.removeFromParent();
|
|
347
|
+
internal.occlusionObserver.geometry.dispose();
|
|
348
|
+
internal.occlusionObserver.material.dispose();
|
|
349
|
+
}
|
|
350
|
+
internal.occlusionCache.clear();
|
|
351
|
+
set((state) => ({
|
|
352
|
+
internal: {
|
|
353
|
+
...state.internal,
|
|
354
|
+
occlusionObserver: null,
|
|
355
|
+
occlusionEnabled: false
|
|
356
|
+
}
|
|
357
|
+
}));
|
|
358
|
+
}
|
|
359
|
+
function cleanupHelperGroup(store) {
|
|
360
|
+
const { internal, set } = store.getState();
|
|
361
|
+
disableOcclusion(store);
|
|
362
|
+
if (internal.helperGroup) {
|
|
363
|
+
internal.helperGroup.removeFromParent();
|
|
364
|
+
set((state) => ({
|
|
365
|
+
internal: {
|
|
366
|
+
...state.internal,
|
|
367
|
+
helperGroup: null
|
|
368
|
+
}
|
|
369
|
+
}));
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
function registerVisibility(store, object, handlers) {
|
|
373
|
+
const { internal } = store.getState();
|
|
374
|
+
const registry = internal.visibilityRegistry;
|
|
375
|
+
const entry = {
|
|
376
|
+
object,
|
|
377
|
+
handlers,
|
|
378
|
+
lastFramedState: null,
|
|
379
|
+
lastOccludedState: null,
|
|
380
|
+
lastVisibleState: null
|
|
381
|
+
};
|
|
382
|
+
registry.set(object.uuid, entry);
|
|
383
|
+
if (handlers.onOccluded || handlers.onVisible) {
|
|
384
|
+
object.occlusionTest = true;
|
|
385
|
+
if (!internal.occlusionEnabled) {
|
|
386
|
+
enableOcclusion(store);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
function unregisterVisibility(store, object) {
|
|
391
|
+
const { internal } = store.getState();
|
|
392
|
+
internal.visibilityRegistry.delete(object.uuid);
|
|
393
|
+
internal.occlusionCache.delete(object);
|
|
394
|
+
}
|
|
395
|
+
function checkVisibility(state) {
|
|
396
|
+
const { internal, camera } = state;
|
|
397
|
+
const registry = internal.visibilityRegistry;
|
|
398
|
+
if (registry.size === 0) return;
|
|
399
|
+
updateFrustum(camera, tempFrustum);
|
|
400
|
+
for (const entry of registry.values()) {
|
|
401
|
+
const { object, handlers, lastFramedState, lastOccludedState, lastVisibleState } = entry;
|
|
402
|
+
let inFrustum = null;
|
|
403
|
+
const computeFrustum = () => {
|
|
404
|
+
if (inFrustum === null) {
|
|
405
|
+
if (object.geometry?.boundingSphere === null) {
|
|
406
|
+
object.geometry?.computeBoundingSphere();
|
|
407
|
+
}
|
|
408
|
+
inFrustum = tempFrustum.intersectsObject(object);
|
|
409
|
+
}
|
|
410
|
+
return inFrustum;
|
|
411
|
+
};
|
|
412
|
+
if (handlers.onFramed) {
|
|
413
|
+
const currentInFrustum = computeFrustum();
|
|
414
|
+
if (currentInFrustum !== lastFramedState) {
|
|
415
|
+
entry.lastFramedState = currentInFrustum;
|
|
416
|
+
handlers.onFramed(currentInFrustum);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
let currentOcclusion = null;
|
|
420
|
+
if (handlers.onOccluded && internal.occlusionEnabled) {
|
|
421
|
+
currentOcclusion = internal.occlusionCache.get(object) ?? null;
|
|
422
|
+
if (currentOcclusion !== null && currentOcclusion !== lastOccludedState) {
|
|
423
|
+
entry.lastOccludedState = currentOcclusion;
|
|
424
|
+
handlers.onOccluded(currentOcclusion);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (handlers.onVisible) {
|
|
428
|
+
const currentInFrustum = computeFrustum();
|
|
429
|
+
if (!handlers.onFramed && currentInFrustum !== lastFramedState) {
|
|
430
|
+
entry.lastFramedState = currentInFrustum;
|
|
431
|
+
}
|
|
432
|
+
let isOccluded = currentOcclusion;
|
|
433
|
+
if (isOccluded === null && internal.occlusionEnabled) {
|
|
434
|
+
isOccluded = internal.occlusionCache.get(object) ?? null;
|
|
435
|
+
}
|
|
436
|
+
if (isOccluded === null) isOccluded = false;
|
|
437
|
+
const isVisible = currentInFrustum && !isOccluded && object.visible;
|
|
438
|
+
if (isVisible !== lastVisibleState) {
|
|
439
|
+
entry.lastVisibleState = isVisible;
|
|
440
|
+
handlers.onVisible(isVisible);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
function hasVisibilityHandlers(handlers) {
|
|
446
|
+
if (!handlers) return false;
|
|
447
|
+
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
448
|
+
}
|
|
449
|
+
|
|
242
450
|
const RESERVED_PROPS = [
|
|
243
451
|
"children",
|
|
244
452
|
"key",
|
|
@@ -253,6 +461,7 @@ const RESERVED_PROPS = [
|
|
|
253
461
|
"dispose"
|
|
254
462
|
];
|
|
255
463
|
const EVENT_REGEX = /^on(Pointer|Drag|Drop|Click|DoubleClick|ContextMenu|Wheel)/;
|
|
464
|
+
const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
|
|
256
465
|
const INDEX_REGEX = /-\d+$/;
|
|
257
466
|
const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
|
|
258
467
|
const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
|
|
@@ -347,6 +556,12 @@ function applyProps(object, props) {
|
|
|
347
556
|
instance.eventCount = Object.keys(instance.handlers).length;
|
|
348
557
|
continue;
|
|
349
558
|
}
|
|
559
|
+
if (instance && VISIBILITY_EVENT_REGEX.test(prop)) {
|
|
560
|
+
if (typeof value === "function") instance.handlers[prop] = value;
|
|
561
|
+
else delete instance.handlers[prop];
|
|
562
|
+
instance.eventCount = Object.keys(instance.handlers).length;
|
|
563
|
+
continue;
|
|
564
|
+
}
|
|
350
565
|
if (value === void 0) continue;
|
|
351
566
|
let { root, key, target } = resolve(object, prop);
|
|
352
567
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
@@ -383,6 +598,17 @@ function applyProps(object, props) {
|
|
|
383
598
|
if (instance.eventCount && object2.raycast !== null) {
|
|
384
599
|
rootState.internal.interaction.push(object2);
|
|
385
600
|
}
|
|
601
|
+
const root = findInitialRoot(instance);
|
|
602
|
+
const visibilityHandlers = {
|
|
603
|
+
onFramed: instance.handlers.onFramed,
|
|
604
|
+
onOccluded: instance.handlers.onOccluded,
|
|
605
|
+
onVisible: instance.handlers.onVisible
|
|
606
|
+
};
|
|
607
|
+
if (hasVisibilityHandlers(visibilityHandlers)) {
|
|
608
|
+
registerVisibility(root, object2, visibilityHandlers);
|
|
609
|
+
} else {
|
|
610
|
+
unregisterVisibility(root, object2);
|
|
611
|
+
}
|
|
386
612
|
}
|
|
387
613
|
if (instance && instance.props.attach === void 0) {
|
|
388
614
|
if (instance.object.isBufferGeometry) instance.props.attach = "geometry";
|
|
@@ -417,6 +643,7 @@ function removeInteractivity(store, object) {
|
|
|
417
643
|
internal.capturedMap.forEach((captures, pointerId) => {
|
|
418
644
|
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
419
645
|
});
|
|
646
|
+
unregisterVisibility(store, object);
|
|
420
647
|
}
|
|
421
648
|
function createEvents(store) {
|
|
422
649
|
function calculateDistance(event) {
|
|
@@ -799,8 +1026,21 @@ function formatLocation(url, line) {
|
|
|
799
1026
|
const file = clean.split("/").pop() ?? clean;
|
|
800
1027
|
return `${file}:${line}`;
|
|
801
1028
|
}
|
|
1029
|
+
function notifyAlpha({ message, link }) {
|
|
1030
|
+
if (typeof process !== "undefined" && (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) && process.env.R3F_SHOW_ALPHA_WARNINGS !== "true") {
|
|
1031
|
+
return;
|
|
1032
|
+
}
|
|
1033
|
+
if (shownNotices.has(message)) return;
|
|
1034
|
+
shownNotices.add(message);
|
|
1035
|
+
const boxStyle = "background: #6366f1; color: #ffffff; padding: 6px 10px; border-radius: 4px; font-weight: 500;";
|
|
1036
|
+
console.log(`%c\u{1F52C} ${message}`, boxStyle);
|
|
1037
|
+
{
|
|
1038
|
+
console.log(`%cMore info: ${link}`, "color: #6366f1; font-weight: normal;");
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
802
1041
|
|
|
803
|
-
const
|
|
1042
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
1043
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React__namespace.createContext(null));
|
|
804
1044
|
const createStore = (invalidate, advance) => {
|
|
805
1045
|
const rootStore = traditional.createWithEqualityFn((set, get) => {
|
|
806
1046
|
const position = new webgpu.Vector3();
|
|
@@ -831,6 +1071,8 @@ const createStore = (invalidate, advance) => {
|
|
|
831
1071
|
gl: null,
|
|
832
1072
|
renderer: null,
|
|
833
1073
|
camera: null,
|
|
1074
|
+
frustum: new webgpu.Frustum(),
|
|
1075
|
+
autoUpdateFrustum: true,
|
|
834
1076
|
raycaster: null,
|
|
835
1077
|
events: { priority: 1, enabled: true, connected: false },
|
|
836
1078
|
scene: null,
|
|
@@ -912,6 +1154,13 @@ const createStore = (invalidate, advance) => {
|
|
|
912
1154
|
initialHits: [],
|
|
913
1155
|
capturedMap: /* @__PURE__ */ new Map(),
|
|
914
1156
|
lastEvent: React__namespace.createRef(),
|
|
1157
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1158
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1159
|
+
// Occlusion system (WebGPU only)
|
|
1160
|
+
occlusionEnabled: false,
|
|
1161
|
+
occlusionObserver: null,
|
|
1162
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1163
|
+
helperGroup: null,
|
|
915
1164
|
// Updates
|
|
916
1165
|
active: false,
|
|
917
1166
|
frames: 0,
|
|
@@ -997,7 +1246,15 @@ const createStore = (invalidate, advance) => {
|
|
|
997
1246
|
}
|
|
998
1247
|
if (camera !== oldCamera) {
|
|
999
1248
|
oldCamera = camera;
|
|
1249
|
+
const { rootScene } = rootStore.getState();
|
|
1250
|
+
if (camera && rootScene && !camera.parent) {
|
|
1251
|
+
rootScene.add(camera);
|
|
1252
|
+
}
|
|
1000
1253
|
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1254
|
+
const currentState = rootStore.getState();
|
|
1255
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
1256
|
+
updateFrustum(camera, currentState.frustum);
|
|
1257
|
+
}
|
|
1001
1258
|
}
|
|
1002
1259
|
});
|
|
1003
1260
|
rootStore.subscribe((state2) => invalidate(state2));
|
|
@@ -1361,6 +1618,10 @@ const _Scheduler = class _Scheduler {
|
|
|
1361
1618
|
__publicField(this, "jobStateListeners", /* @__PURE__ */ new Map());
|
|
1362
1619
|
__publicField(this, "pendingFrames", 0);
|
|
1363
1620
|
__publicField(this, "_frameloop", "always");
|
|
1621
|
+
//* Independent Mode & Error Handling State ================================
|
|
1622
|
+
__publicField(this, "_independent", false);
|
|
1623
|
+
__publicField(this, "errorHandler", null);
|
|
1624
|
+
__publicField(this, "rootReadyCallbacks", /* @__PURE__ */ new Set());
|
|
1364
1625
|
//* Core Loop Execution Methods ================================
|
|
1365
1626
|
/**
|
|
1366
1627
|
* Main RAF loop callback.
|
|
@@ -1383,6 +1644,12 @@ const _Scheduler = class _Scheduler {
|
|
|
1383
1644
|
});
|
|
1384
1645
|
this.phaseGraph = new PhaseGraph();
|
|
1385
1646
|
}
|
|
1647
|
+
static get instance() {
|
|
1648
|
+
return globalThis[_Scheduler.INSTANCE_KEY] ?? null;
|
|
1649
|
+
}
|
|
1650
|
+
static set instance(value) {
|
|
1651
|
+
globalThis[_Scheduler.INSTANCE_KEY] = value;
|
|
1652
|
+
}
|
|
1386
1653
|
/**
|
|
1387
1654
|
* Get the global scheduler instance (creates if doesn't exist).
|
|
1388
1655
|
* Uses HMR data to preserve instance across hot reloads.
|
|
@@ -1431,29 +1698,43 @@ const _Scheduler = class _Scheduler {
|
|
|
1431
1698
|
get isRunning() {
|
|
1432
1699
|
return this.loopState.running;
|
|
1433
1700
|
}
|
|
1701
|
+
get isReady() {
|
|
1702
|
+
return this.roots.size > 0;
|
|
1703
|
+
}
|
|
1704
|
+
get independent() {
|
|
1705
|
+
return this._independent;
|
|
1706
|
+
}
|
|
1707
|
+
set independent(value) {
|
|
1708
|
+
this._independent = value;
|
|
1709
|
+
if (value) this.ensureDefaultRoot();
|
|
1710
|
+
}
|
|
1434
1711
|
//* Root Management Methods ================================
|
|
1435
1712
|
/**
|
|
1436
1713
|
* Register a root (Canvas) with the scheduler.
|
|
1437
1714
|
* The first root to register starts the RAF loop (if frameloop='always').
|
|
1438
1715
|
* @param {string} id - Unique identifier for this root
|
|
1439
|
-
* @param {
|
|
1716
|
+
* @param {RootOptions} [options] - Optional configuration with getState and onError callbacks
|
|
1440
1717
|
* @returns {() => void} Unsubscribe function to remove this root
|
|
1441
1718
|
*/
|
|
1442
|
-
registerRoot(id,
|
|
1719
|
+
registerRoot(id, options = {}) {
|
|
1443
1720
|
if (this.roots.has(id)) {
|
|
1444
1721
|
console.warn(`[Scheduler] Root "${id}" already registered`);
|
|
1445
1722
|
return () => this.unregisterRoot(id);
|
|
1446
1723
|
}
|
|
1447
1724
|
const entry = {
|
|
1448
1725
|
id,
|
|
1449
|
-
getState,
|
|
1726
|
+
getState: options.getState ?? (() => ({})),
|
|
1450
1727
|
jobs: /* @__PURE__ */ new Map(),
|
|
1451
1728
|
sortedJobs: [],
|
|
1452
1729
|
needsRebuild: false
|
|
1453
1730
|
};
|
|
1731
|
+
if (options.onError) {
|
|
1732
|
+
this.errorHandler = options.onError;
|
|
1733
|
+
}
|
|
1454
1734
|
this.roots.set(id, entry);
|
|
1455
|
-
if (this.roots.size === 1
|
|
1456
|
-
this.
|
|
1735
|
+
if (this.roots.size === 1) {
|
|
1736
|
+
this.notifyRootReady();
|
|
1737
|
+
if (this._frameloop === "always") this.start();
|
|
1457
1738
|
}
|
|
1458
1739
|
return () => this.unregisterRoot(id);
|
|
1459
1740
|
}
|
|
@@ -1473,8 +1754,61 @@ const _Scheduler = class _Scheduler {
|
|
|
1473
1754
|
this.roots.delete(id);
|
|
1474
1755
|
if (this.roots.size === 0) {
|
|
1475
1756
|
this.stop();
|
|
1757
|
+
this.errorHandler = null;
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
/**
|
|
1761
|
+
* Subscribe to be notified when a root becomes available.
|
|
1762
|
+
* Fires immediately if a root already exists.
|
|
1763
|
+
* @param {() => void} callback - Function called when first root registers
|
|
1764
|
+
* @returns {() => void} Unsubscribe function
|
|
1765
|
+
*/
|
|
1766
|
+
onRootReady(callback) {
|
|
1767
|
+
if (this.roots.size > 0) {
|
|
1768
|
+
callback();
|
|
1769
|
+
return () => {
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
this.rootReadyCallbacks.add(callback);
|
|
1773
|
+
return () => this.rootReadyCallbacks.delete(callback);
|
|
1774
|
+
}
|
|
1775
|
+
/**
|
|
1776
|
+
* Notify all registered root-ready callbacks.
|
|
1777
|
+
* Called when the first root registers.
|
|
1778
|
+
* @returns {void}
|
|
1779
|
+
* @private
|
|
1780
|
+
*/
|
|
1781
|
+
notifyRootReady() {
|
|
1782
|
+
for (const cb of this.rootReadyCallbacks) {
|
|
1783
|
+
try {
|
|
1784
|
+
cb();
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
console.error("[Scheduler] Error in root-ready callback:", error);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
this.rootReadyCallbacks.clear();
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Ensure a default root exists for independent mode.
|
|
1793
|
+
* Creates a minimal root with no state provider.
|
|
1794
|
+
* @returns {void}
|
|
1795
|
+
* @private
|
|
1796
|
+
*/
|
|
1797
|
+
ensureDefaultRoot() {
|
|
1798
|
+
if (!this.roots.has("__default__")) {
|
|
1799
|
+
this.registerRoot("__default__");
|
|
1476
1800
|
}
|
|
1477
1801
|
}
|
|
1802
|
+
/**
|
|
1803
|
+
* Trigger error handling for job errors.
|
|
1804
|
+
* Uses the bound error handler if available, otherwise logs to console.
|
|
1805
|
+
* @param {Error} error - The error to handle
|
|
1806
|
+
* @returns {void}
|
|
1807
|
+
*/
|
|
1808
|
+
triggerError(error) {
|
|
1809
|
+
if (this.errorHandler) this.errorHandler(error);
|
|
1810
|
+
else console.error("[Scheduler]", error);
|
|
1811
|
+
}
|
|
1478
1812
|
//* Phase Management Methods ================================
|
|
1479
1813
|
/**
|
|
1480
1814
|
* Add a named phase to the scheduler's execution order.
|
|
@@ -1833,9 +2167,9 @@ const _Scheduler = class _Scheduler {
|
|
|
1833
2167
|
const deltaMs = this.loopState.lastTime !== null ? now - this.loopState.lastTime : 0;
|
|
1834
2168
|
const delta = deltaMs / 1e3;
|
|
1835
2169
|
const elapsed = now - this.loopState.createdAt;
|
|
1836
|
-
const
|
|
2170
|
+
const providedState = root.getState?.() ?? {};
|
|
1837
2171
|
const frameState = {
|
|
1838
|
-
...
|
|
2172
|
+
...providedState,
|
|
1839
2173
|
time: now,
|
|
1840
2174
|
delta,
|
|
1841
2175
|
elapsed,
|
|
@@ -1845,6 +2179,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1845
2179
|
job.callback(frameState, delta);
|
|
1846
2180
|
} catch (error) {
|
|
1847
2181
|
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2182
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
1848
2183
|
}
|
|
1849
2184
|
}
|
|
1850
2185
|
/**
|
|
@@ -1886,7 +2221,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1886
2221
|
/**
|
|
1887
2222
|
* Execute all jobs for a single root in sorted order.
|
|
1888
2223
|
* Rebuilds sorted job list if needed, then dispatches each job.
|
|
1889
|
-
* Errors are caught and propagated
|
|
2224
|
+
* Errors are caught and propagated via triggerError.
|
|
1890
2225
|
* @param {RootEntry} root - The root entry to tick
|
|
1891
2226
|
* @param {number} timestamp - RAF timestamp in milliseconds
|
|
1892
2227
|
* @param {number} delta - Time since last frame in seconds
|
|
@@ -1898,10 +2233,9 @@ const _Scheduler = class _Scheduler {
|
|
|
1898
2233
|
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
1899
2234
|
root.needsRebuild = false;
|
|
1900
2235
|
}
|
|
1901
|
-
const
|
|
1902
|
-
if (!rootState) return;
|
|
2236
|
+
const providedState = root.getState?.() ?? {};
|
|
1903
2237
|
const frameState = {
|
|
1904
|
-
...
|
|
2238
|
+
...providedState,
|
|
1905
2239
|
time: timestamp,
|
|
1906
2240
|
delta,
|
|
1907
2241
|
elapsed: this.loopState.elapsedTime / 1e3,
|
|
@@ -1914,7 +2248,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1914
2248
|
job.callback(frameState, delta);
|
|
1915
2249
|
} catch (error) {
|
|
1916
2250
|
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
1917
|
-
|
|
2251
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
1918
2252
|
}
|
|
1919
2253
|
}
|
|
1920
2254
|
}
|
|
@@ -1999,8 +2333,11 @@ const _Scheduler = class _Scheduler {
|
|
|
1999
2333
|
return /* @__PURE__ */ new Set([value]);
|
|
2000
2334
|
}
|
|
2001
2335
|
};
|
|
2002
|
-
//* Static State & Methods (
|
|
2003
|
-
|
|
2336
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2337
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2338
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2339
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2340
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
2004
2341
|
let Scheduler = _Scheduler;
|
|
2005
2342
|
const getScheduler = () => Scheduler.get();
|
|
2006
2343
|
if (hmrData) {
|
|
@@ -2008,11 +2345,9 @@ if (hmrData) {
|
|
|
2008
2345
|
}
|
|
2009
2346
|
|
|
2010
2347
|
function useFrame(callback, priorityOrOptions) {
|
|
2011
|
-
const store =
|
|
2012
|
-
const
|
|
2013
|
-
|
|
2014
|
-
return state.internal.rootId;
|
|
2015
|
-
}, [store]);
|
|
2348
|
+
const store = React__namespace.useContext(context);
|
|
2349
|
+
const isInsideCanvas = store !== null;
|
|
2350
|
+
const scheduler = getScheduler();
|
|
2016
2351
|
const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
|
|
2017
2352
|
id: priorityOrOptions.id,
|
|
2018
2353
|
phase: priorityOrOptions.phase,
|
|
@@ -2032,55 +2367,71 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2032
2367
|
const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
|
|
2033
2368
|
useIsomorphicLayoutEffect(() => {
|
|
2034
2369
|
if (!callback) return;
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
if (isLegacyPriority) {
|
|
2039
|
-
state.internal.priority++;
|
|
2040
|
-
let parentRoot = state.previousRoot;
|
|
2041
|
-
while (parentRoot) {
|
|
2042
|
-
const parentState = parentRoot.getState();
|
|
2043
|
-
if (parentState?.internal) parentState.internal.priority++;
|
|
2044
|
-
parentRoot = parentState?.previousRoot;
|
|
2045
|
-
}
|
|
2046
|
-
notifyDepreciated({
|
|
2047
|
-
heading: "useFrame with numeric priority is deprecated",
|
|
2048
|
-
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 })',
|
|
2049
|
-
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
2050
|
-
});
|
|
2051
|
-
}
|
|
2052
|
-
const wrappedCallback = (frameState, delta) => {
|
|
2053
|
-
const localState = store.getState();
|
|
2054
|
-
const mergedState = {
|
|
2055
|
-
...localState,
|
|
2056
|
-
time: frameState.time,
|
|
2057
|
-
delta: frameState.delta,
|
|
2058
|
-
elapsed: frameState.elapsed,
|
|
2059
|
-
frame: frameState.frame
|
|
2060
|
-
};
|
|
2061
|
-
callbackRef.current?.(mergedState, delta);
|
|
2062
|
-
};
|
|
2063
|
-
const unregister = scheduler.register(wrappedCallback, {
|
|
2064
|
-
id,
|
|
2065
|
-
rootId,
|
|
2066
|
-
...options
|
|
2067
|
-
});
|
|
2068
|
-
return () => {
|
|
2069
|
-
unregister();
|
|
2370
|
+
if (isInsideCanvas) {
|
|
2371
|
+
const state = store.getState();
|
|
2372
|
+
const rootId = state.internal.rootId;
|
|
2070
2373
|
if (isLegacyPriority) {
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2374
|
+
state.internal.priority++;
|
|
2375
|
+
let parentRoot = state.previousRoot;
|
|
2376
|
+
while (parentRoot) {
|
|
2377
|
+
const parentState = parentRoot.getState();
|
|
2378
|
+
if (parentState?.internal) parentState.internal.priority++;
|
|
2379
|
+
parentRoot = parentState?.previousRoot;
|
|
2380
|
+
}
|
|
2381
|
+
notifyDepreciated({
|
|
2382
|
+
heading: "useFrame with numeric priority is deprecated",
|
|
2383
|
+
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 })',
|
|
2384
|
+
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
const wrappedCallback = (frameState, delta) => {
|
|
2388
|
+
const localState = store.getState();
|
|
2389
|
+
const mergedState = {
|
|
2390
|
+
...localState,
|
|
2391
|
+
time: frameState.time,
|
|
2392
|
+
delta: frameState.delta,
|
|
2393
|
+
elapsed: frameState.elapsed,
|
|
2394
|
+
frame: frameState.frame
|
|
2395
|
+
};
|
|
2396
|
+
callbackRef.current?.(mergedState, delta);
|
|
2397
|
+
};
|
|
2398
|
+
const unregister = scheduler.register(wrappedCallback, {
|
|
2399
|
+
id,
|
|
2400
|
+
rootId,
|
|
2401
|
+
...options
|
|
2402
|
+
});
|
|
2403
|
+
return () => {
|
|
2404
|
+
unregister();
|
|
2405
|
+
if (isLegacyPriority) {
|
|
2406
|
+
const currentState = store.getState();
|
|
2407
|
+
if (currentState.internal) {
|
|
2408
|
+
currentState.internal.priority--;
|
|
2409
|
+
let parentRoot = currentState.previousRoot;
|
|
2410
|
+
while (parentRoot) {
|
|
2411
|
+
const parentState = parentRoot.getState();
|
|
2412
|
+
if (parentState?.internal) parentState.internal.priority--;
|
|
2413
|
+
parentRoot = parentState?.previousRoot;
|
|
2414
|
+
}
|
|
2079
2415
|
}
|
|
2080
2416
|
}
|
|
2417
|
+
};
|
|
2418
|
+
} else {
|
|
2419
|
+
const registerOutside = () => {
|
|
2420
|
+
return scheduler.register((state, delta) => callbackRef.current?.(state, delta), { id, ...options });
|
|
2421
|
+
};
|
|
2422
|
+
if (scheduler.independent || scheduler.isReady) {
|
|
2423
|
+
return registerOutside();
|
|
2081
2424
|
}
|
|
2082
|
-
|
|
2083
|
-
|
|
2425
|
+
let unregisterJob = null;
|
|
2426
|
+
const unsubReady = scheduler.onRootReady(() => {
|
|
2427
|
+
unregisterJob = registerOutside();
|
|
2428
|
+
});
|
|
2429
|
+
return () => {
|
|
2430
|
+
unsubReady();
|
|
2431
|
+
unregisterJob?.();
|
|
2432
|
+
};
|
|
2433
|
+
}
|
|
2434
|
+
}, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
|
|
2084
2435
|
const isPaused = React__namespace.useSyncExternalStore(
|
|
2085
2436
|
// Subscribe function
|
|
2086
2437
|
React__namespace.useCallback(
|
|
@@ -2095,7 +2446,7 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2095
2446
|
React__namespace.useCallback(() => false, [])
|
|
2096
2447
|
);
|
|
2097
2448
|
const controls = React__namespace.useMemo(() => {
|
|
2098
|
-
const
|
|
2449
|
+
const scheduler2 = getScheduler();
|
|
2099
2450
|
return {
|
|
2100
2451
|
/** The job's unique ID */
|
|
2101
2452
|
id,
|
|
@@ -2103,7 +2454,7 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2103
2454
|
* Access to the global scheduler for frame loop control.
|
|
2104
2455
|
* Use for controlling the entire frame loop, adding phases, etc.
|
|
2105
2456
|
*/
|
|
2106
|
-
scheduler,
|
|
2457
|
+
scheduler: scheduler2,
|
|
2107
2458
|
/**
|
|
2108
2459
|
* Manually step this job only.
|
|
2109
2460
|
* Bypasses FPS limiting - always runs.
|
|
@@ -2351,6 +2702,18 @@ function useTextures() {
|
|
|
2351
2702
|
}, [store]);
|
|
2352
2703
|
}
|
|
2353
2704
|
|
|
2705
|
+
function useRenderTarget(width, height, options) {
|
|
2706
|
+
const isLegacy = useThree((s) => s.isLegacy);
|
|
2707
|
+
const size = useThree((s) => s.size);
|
|
2708
|
+
return React.useMemo(() => {
|
|
2709
|
+
const w = width ?? size.width;
|
|
2710
|
+
const h = height ?? size.height;
|
|
2711
|
+
{
|
|
2712
|
+
return isLegacy ? new three.WebGLRenderTarget(w, h, options) : new webgpu.RenderTarget(w, h, options);
|
|
2713
|
+
}
|
|
2714
|
+
}, [width, height, size.width, size.height, options, isLegacy]);
|
|
2715
|
+
}
|
|
2716
|
+
|
|
2354
2717
|
function useStore() {
|
|
2355
2718
|
const store = React.useContext(context);
|
|
2356
2719
|
if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
|
|
@@ -2402,7 +2765,7 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
|
|
|
2402
2765
|
getScheduler().step(timestamp);
|
|
2403
2766
|
}
|
|
2404
2767
|
|
|
2405
|
-
const version = "10.0.0-alpha.
|
|
2768
|
+
const version = "10.0.0-alpha.1";
|
|
2406
2769
|
const packageData = {
|
|
2407
2770
|
version: version};
|
|
2408
2771
|
|
|
@@ -13703,7 +14066,8 @@ function createReconciler(config) {
|
|
|
13703
14066
|
return reconciler2;
|
|
13704
14067
|
}
|
|
13705
14068
|
const NoEventPriority = 0;
|
|
13706
|
-
const
|
|
14069
|
+
const R3F_CATALOGUE = Symbol.for("@react-three/fiber.catalogue");
|
|
14070
|
+
const catalogue = globalThis[R3F_CATALOGUE] ?? (globalThis[R3F_CATALOGUE] = {});
|
|
13707
14071
|
const PREFIX_REGEX = /^three(?=[A-Z])/;
|
|
13708
14072
|
const toPascalCase = (type) => `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
13709
14073
|
let i = 0;
|
|
@@ -14201,7 +14565,9 @@ function createRoot(canvas) {
|
|
|
14201
14565
|
camera: cameraOptions,
|
|
14202
14566
|
onPointerMissed,
|
|
14203
14567
|
onDragOverMissed,
|
|
14204
|
-
onDropMissed
|
|
14568
|
+
onDropMissed,
|
|
14569
|
+
autoUpdateFrustum = true,
|
|
14570
|
+
occlusion = false
|
|
14205
14571
|
} = props;
|
|
14206
14572
|
let state = store.getState();
|
|
14207
14573
|
const defaultGLProps = {
|
|
@@ -14231,7 +14597,9 @@ function createRoot(canvas) {
|
|
|
14231
14597
|
state.set({ isLegacy: true, gl: renderer, renderer });
|
|
14232
14598
|
} else if (!wantsGL && !state.internal.actualRenderer) {
|
|
14233
14599
|
renderer = await resolveRenderer(rendererConfig, defaultGPUProps, webgpu.WebGPURenderer);
|
|
14234
|
-
|
|
14600
|
+
if (!renderer.hasInitialized?.()) {
|
|
14601
|
+
await renderer.init();
|
|
14602
|
+
}
|
|
14235
14603
|
const backend = renderer.backend;
|
|
14236
14604
|
const isWebGPUBackend = backend && "isWebGPUBackend" in backend;
|
|
14237
14605
|
state.internal.actualRenderer = renderer;
|
|
@@ -14279,6 +14647,8 @@ function createRoot(canvas) {
|
|
|
14279
14647
|
rootScene: scene,
|
|
14280
14648
|
internal: { ...prev.internal, container: scene }
|
|
14281
14649
|
}));
|
|
14650
|
+
const camera = state.camera;
|
|
14651
|
+
if (camera && !camera.parent) scene.add(camera);
|
|
14282
14652
|
}
|
|
14283
14653
|
if (events && !state.events.handlers) {
|
|
14284
14654
|
state.set({ events: events(store) });
|
|
@@ -14306,6 +14676,10 @@ function createRoot(canvas) {
|
|
|
14306
14676
|
if (!state.onPointerMissed) state.set({ onPointerMissed });
|
|
14307
14677
|
if (!state.onDragOverMissed) state.set({ onDragOverMissed });
|
|
14308
14678
|
if (!state.onDropMissed) state.set({ onDropMissed });
|
|
14679
|
+
if (state.autoUpdateFrustum !== autoUpdateFrustum) state.set({ autoUpdateFrustum });
|
|
14680
|
+
if (occlusion && !state.internal.occlusionEnabled) {
|
|
14681
|
+
enableOcclusion(store);
|
|
14682
|
+
}
|
|
14309
14683
|
if (performance && !is.equ(performance, lastConfiguredProps.performance, shallowLoose)) {
|
|
14310
14684
|
state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
|
|
14311
14685
|
lastConfiguredProps.performance = performance;
|
|
@@ -14406,7 +14780,37 @@ function createRoot(canvas) {
|
|
|
14406
14780
|
const rootId = state.internal.rootId;
|
|
14407
14781
|
if (!rootId) {
|
|
14408
14782
|
const newRootId = scheduler.generateRootId();
|
|
14409
|
-
const unregisterRoot = scheduler.registerRoot(newRootId,
|
|
14783
|
+
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14784
|
+
getState: () => store.getState(),
|
|
14785
|
+
onError: (err) => store.getState().setError(err)
|
|
14786
|
+
});
|
|
14787
|
+
const unregisterFrustum = scheduler.register(
|
|
14788
|
+
() => {
|
|
14789
|
+
const state2 = store.getState();
|
|
14790
|
+
if (state2.autoUpdateFrustum && state2.camera) {
|
|
14791
|
+
updateFrustum(state2.camera, state2.frustum);
|
|
14792
|
+
}
|
|
14793
|
+
},
|
|
14794
|
+
{
|
|
14795
|
+
id: `${newRootId}_frustum`,
|
|
14796
|
+
rootId: newRootId,
|
|
14797
|
+
phase: "preRender",
|
|
14798
|
+
system: true
|
|
14799
|
+
}
|
|
14800
|
+
);
|
|
14801
|
+
const unregisterVisibility = scheduler.register(
|
|
14802
|
+
() => {
|
|
14803
|
+
const state2 = store.getState();
|
|
14804
|
+
checkVisibility(state2);
|
|
14805
|
+
},
|
|
14806
|
+
{
|
|
14807
|
+
id: `${newRootId}_visibility`,
|
|
14808
|
+
rootId: newRootId,
|
|
14809
|
+
phase: "preRender",
|
|
14810
|
+
system: true,
|
|
14811
|
+
after: `${newRootId}_frustum`
|
|
14812
|
+
}
|
|
14813
|
+
);
|
|
14410
14814
|
const unregisterRender = scheduler.register(
|
|
14411
14815
|
() => {
|
|
14412
14816
|
const state2 = store.getState();
|
|
@@ -14434,6 +14838,8 @@ function createRoot(canvas) {
|
|
|
14434
14838
|
rootId: newRootId,
|
|
14435
14839
|
unregisterRoot: () => {
|
|
14436
14840
|
unregisterRoot();
|
|
14841
|
+
unregisterFrustum();
|
|
14842
|
+
unregisterVisibility();
|
|
14437
14843
|
unregisterRender();
|
|
14438
14844
|
},
|
|
14439
14845
|
scheduler
|
|
@@ -14491,6 +14897,7 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14491
14897
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14492
14898
|
if (unregisterRoot) unregisterRoot();
|
|
14493
14899
|
state.events.disconnect?.();
|
|
14900
|
+
cleanupHelperGroup(root.store);
|
|
14494
14901
|
renderer?.renderLists?.dispose?.();
|
|
14495
14902
|
renderer?.forceContextLoss?.();
|
|
14496
14903
|
if (renderer?.xr) state.xr.disconnect();
|
|
@@ -14670,7 +15077,22 @@ function CanvasImpl({
|
|
|
14670
15077
|
effectActiveRef.current = true;
|
|
14671
15078
|
const canvas = canvasRef.current;
|
|
14672
15079
|
if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
|
|
14673
|
-
if (!root.current)
|
|
15080
|
+
if (!root.current) {
|
|
15081
|
+
root.current = createRoot(canvas);
|
|
15082
|
+
notifyAlpha({
|
|
15083
|
+
message: "React Three Fiber v10 is in ALPHA - expect breaking changes",
|
|
15084
|
+
link: "https://github.com/pmndrs/react-three-fiber/discussions"
|
|
15085
|
+
});
|
|
15086
|
+
const rootEntry = _roots.get(canvas);
|
|
15087
|
+
if (rootEntry?.store) {
|
|
15088
|
+
if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
|
|
15089
|
+
unsubscribeErrorRef.current = rootEntry.store.subscribe((state) => {
|
|
15090
|
+
if (state.error && effectActiveRef.current) {
|
|
15091
|
+
setError(state.error);
|
|
15092
|
+
}
|
|
15093
|
+
});
|
|
15094
|
+
}
|
|
15095
|
+
}
|
|
14674
15096
|
async function run() {
|
|
14675
15097
|
if (!effectActiveRef.current || !root.current) return;
|
|
14676
15098
|
await root.current.configure({
|
|
@@ -14711,15 +15133,9 @@ function CanvasImpl({
|
|
|
14711
15133
|
}
|
|
14712
15134
|
});
|
|
14713
15135
|
if (!effectActiveRef.current || !root.current) return;
|
|
14714
|
-
|
|
15136
|
+
root.current.render(
|
|
14715
15137
|
/* @__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 }) }) })
|
|
14716
15138
|
);
|
|
14717
|
-
if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
|
|
14718
|
-
unsubscribeErrorRef.current = store.subscribe((state) => {
|
|
14719
|
-
if (state.error && effectActiveRef.current) {
|
|
14720
|
-
setError(state.error);
|
|
14721
|
-
}
|
|
14722
|
-
});
|
|
14723
15139
|
}
|
|
14724
15140
|
run();
|
|
14725
15141
|
}
|
|
@@ -14819,6 +15235,7 @@ exports.removeInteractivity = removeInteractivity;
|
|
|
14819
15235
|
exports.resolve = resolve;
|
|
14820
15236
|
exports.unmountComponentAtNode = unmountComponentAtNode;
|
|
14821
15237
|
exports.updateCamera = updateCamera;
|
|
15238
|
+
exports.updateFrustum = updateFrustum;
|
|
14822
15239
|
exports.useBridge = useBridge;
|
|
14823
15240
|
exports.useFrame = useFrame;
|
|
14824
15241
|
exports.useGraph = useGraph;
|
|
@@ -14826,6 +15243,7 @@ exports.useInstanceHandle = useInstanceHandle;
|
|
|
14826
15243
|
exports.useIsomorphicLayoutEffect = useIsomorphicLayoutEffect;
|
|
14827
15244
|
exports.useLoader = useLoader;
|
|
14828
15245
|
exports.useMutableCallback = useMutableCallback;
|
|
15246
|
+
exports.useRenderTarget = useRenderTarget;
|
|
14829
15247
|
exports.useStore = useStore;
|
|
14830
15248
|
exports.useTexture = useTexture;
|
|
14831
15249
|
exports.useTextures = useTextures;
|