@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/legacy.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as three from 'three';
|
|
2
|
-
import { Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, SRGBColorSpace, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, ColorManagement, LinearSRGBColorSpace, NoToneMapping, ACESFilmicToneMapping, WebGLRenderer } from 'three';
|
|
2
|
+
import { WebGLRenderTarget, Frustum, Matrix4, Group, BoxGeometry, MeshBasicNodeMaterial, Mesh, Node, NodeUpdateType, Layers, RGBAFormat, UnsignedByteType, Vector3, Vector2, TextureLoader, Texture as Texture$1, SRGBColorSpace, Raycaster, OrthographicCamera, PerspectiveCamera, Scene, PCFSoftShadowMap, VSMShadowMap, PCFShadowMap, BasicShadowMap, ColorManagement, LinearSRGBColorSpace, NoToneMapping, ACESFilmicToneMapping, WebGLRenderer } from 'three';
|
|
3
3
|
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import React__default, { useMemo, useLayoutEffect, useEffect, useContext, useRef, useImperativeHandle, useCallback, useState } from 'react';
|
|
@@ -33,12 +33,15 @@ const WebGPURenderer = class WebGPURenderer2 {
|
|
|
33
33
|
throw new Error("WebGPURenderer is not available in legacy builds. Use @react-three/fiber/webgpu instead.");
|
|
34
34
|
}
|
|
35
35
|
};
|
|
36
|
+
const RenderTarget = null;
|
|
36
37
|
|
|
37
38
|
const THREE = /*#__PURE__*/_mergeNamespaces({
|
|
38
39
|
__proto__: null,
|
|
39
40
|
Inspector: Inspector,
|
|
40
41
|
R3F_BUILD_LEGACY: R3F_BUILD_LEGACY,
|
|
41
42
|
R3F_BUILD_WEBGPU: R3F_BUILD_WEBGPU,
|
|
43
|
+
RenderTarget: RenderTarget,
|
|
44
|
+
RenderTargetCompat: WebGLRenderTarget,
|
|
42
45
|
WebGPURenderer: WebGPURenderer
|
|
43
46
|
}, [three]);
|
|
44
47
|
|
|
@@ -147,6 +150,13 @@ function updateCamera(camera, size) {
|
|
|
147
150
|
}
|
|
148
151
|
camera.updateProjectionMatrix();
|
|
149
152
|
}
|
|
153
|
+
const frustumMatrix = new Matrix4();
|
|
154
|
+
function updateFrustum(camera, frustum) {
|
|
155
|
+
const target = frustum ?? new Frustum();
|
|
156
|
+
frustumMatrix.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse);
|
|
157
|
+
target.setFromProjectionMatrix(frustumMatrix);
|
|
158
|
+
return target;
|
|
159
|
+
}
|
|
150
160
|
|
|
151
161
|
const REACT_INTERNAL_PROPS = ["children", "key", "ref"];
|
|
152
162
|
function findInitialRoot(instance) {
|
|
@@ -227,6 +237,205 @@ function invalidateInstance(instance) {
|
|
|
227
237
|
if (state && state.internal.frames === 0) state.invalidate();
|
|
228
238
|
}
|
|
229
239
|
|
|
240
|
+
const tempFrustum = new Frustum();
|
|
241
|
+
let hasWarnedWebGL = false;
|
|
242
|
+
let tslModule = null;
|
|
243
|
+
async function loadTSL() {
|
|
244
|
+
if (tslModule) return tslModule;
|
|
245
|
+
try {
|
|
246
|
+
const tsl = await import('three/tsl');
|
|
247
|
+
tslModule = { uniform: tsl.uniform, nodeObject: tsl.nodeObject };
|
|
248
|
+
return tslModule;
|
|
249
|
+
} catch {
|
|
250
|
+
return null;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
function createOcclusionObserverNode(store, uniform) {
|
|
254
|
+
const node = new Node("float");
|
|
255
|
+
node.updateType = NodeUpdateType.OBJECT;
|
|
256
|
+
node.update = function(frame) {
|
|
257
|
+
const { internal } = store.getState();
|
|
258
|
+
const registry = internal.visibilityRegistry;
|
|
259
|
+
const cache = internal.occlusionCache;
|
|
260
|
+
for (const entry of registry.values()) {
|
|
261
|
+
const { object, handlers } = entry;
|
|
262
|
+
if (handlers.onOccluded || handlers.onVisible) {
|
|
263
|
+
const isOccluded = frame.renderer.isOccluded(object);
|
|
264
|
+
cache.set(object, isOccluded);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
node.setup = function() {
|
|
269
|
+
return uniform(0);
|
|
270
|
+
};
|
|
271
|
+
return node;
|
|
272
|
+
}
|
|
273
|
+
let occlusionSetupPromise = null;
|
|
274
|
+
function enableOcclusion(store) {
|
|
275
|
+
const state = store.getState();
|
|
276
|
+
const { internal, renderer, rootScene } = state;
|
|
277
|
+
if (internal.occlusionEnabled || occlusionSetupPromise) return;
|
|
278
|
+
const hasOcclusionSupport = typeof renderer?.isOccluded === "function";
|
|
279
|
+
if (!hasOcclusionSupport) {
|
|
280
|
+
if (!hasWarnedWebGL) {
|
|
281
|
+
console.warn(
|
|
282
|
+
"[R3F] Warning: onOccluded/onVisible occlusion queries require WebGPU renderer. Occlusion events will not fire on WebGL."
|
|
283
|
+
);
|
|
284
|
+
hasWarnedWebGL = true;
|
|
285
|
+
}
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
occlusionSetupPromise = setupOcclusion(store);
|
|
289
|
+
}
|
|
290
|
+
async function setupOcclusion(store) {
|
|
291
|
+
const state = store.getState();
|
|
292
|
+
const { internal, rootScene, set } = state;
|
|
293
|
+
const tsl = await loadTSL();
|
|
294
|
+
if (!tsl) {
|
|
295
|
+
console.warn("[R3F] Warning: TSL module not available. Occlusion queries disabled.");
|
|
296
|
+
occlusionSetupPromise = null;
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const { uniform, nodeObject } = tsl;
|
|
300
|
+
let helperGroup = internal.helperGroup;
|
|
301
|
+
if (!helperGroup) {
|
|
302
|
+
helperGroup = new Group();
|
|
303
|
+
helperGroup.name = "__r3fInternal";
|
|
304
|
+
helperGroup.__r3fInternal = true;
|
|
305
|
+
rootScene.add(helperGroup);
|
|
306
|
+
}
|
|
307
|
+
const geometry = new BoxGeometry(1, 1, 1);
|
|
308
|
+
const material = new MeshBasicNodeMaterial({
|
|
309
|
+
transparent: true,
|
|
310
|
+
opacity: 0
|
|
311
|
+
});
|
|
312
|
+
const observerNode = nodeObject(createOcclusionObserverNode(store, uniform));
|
|
313
|
+
material.colorNode = observerNode;
|
|
314
|
+
material.needsUpdate = true;
|
|
315
|
+
const mesh = new Mesh(geometry, material);
|
|
316
|
+
mesh.name = "__r3fOcclusionObserver";
|
|
317
|
+
mesh.scale.setScalar(1e-4);
|
|
318
|
+
mesh.frustumCulled = false;
|
|
319
|
+
mesh.__r3fInternal = true;
|
|
320
|
+
helperGroup.add(mesh);
|
|
321
|
+
set((state2) => ({
|
|
322
|
+
internal: {
|
|
323
|
+
...state2.internal,
|
|
324
|
+
helperGroup,
|
|
325
|
+
occlusionObserver: mesh,
|
|
326
|
+
occlusionEnabled: true
|
|
327
|
+
}
|
|
328
|
+
}));
|
|
329
|
+
occlusionSetupPromise = null;
|
|
330
|
+
}
|
|
331
|
+
function disableOcclusion(store) {
|
|
332
|
+
const { internal, set } = store.getState();
|
|
333
|
+
if (!internal.occlusionEnabled) return;
|
|
334
|
+
if (internal.occlusionObserver) {
|
|
335
|
+
internal.occlusionObserver.removeFromParent();
|
|
336
|
+
internal.occlusionObserver.geometry.dispose();
|
|
337
|
+
internal.occlusionObserver.material.dispose();
|
|
338
|
+
}
|
|
339
|
+
internal.occlusionCache.clear();
|
|
340
|
+
set((state) => ({
|
|
341
|
+
internal: {
|
|
342
|
+
...state.internal,
|
|
343
|
+
occlusionObserver: null,
|
|
344
|
+
occlusionEnabled: false
|
|
345
|
+
}
|
|
346
|
+
}));
|
|
347
|
+
}
|
|
348
|
+
function cleanupHelperGroup(store) {
|
|
349
|
+
const { internal, set } = store.getState();
|
|
350
|
+
disableOcclusion(store);
|
|
351
|
+
if (internal.helperGroup) {
|
|
352
|
+
internal.helperGroup.removeFromParent();
|
|
353
|
+
set((state) => ({
|
|
354
|
+
internal: {
|
|
355
|
+
...state.internal,
|
|
356
|
+
helperGroup: null
|
|
357
|
+
}
|
|
358
|
+
}));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
function registerVisibility(store, object, handlers) {
|
|
362
|
+
const { internal } = store.getState();
|
|
363
|
+
const registry = internal.visibilityRegistry;
|
|
364
|
+
const entry = {
|
|
365
|
+
object,
|
|
366
|
+
handlers,
|
|
367
|
+
lastFramedState: null,
|
|
368
|
+
lastOccludedState: null,
|
|
369
|
+
lastVisibleState: null
|
|
370
|
+
};
|
|
371
|
+
registry.set(object.uuid, entry);
|
|
372
|
+
if (handlers.onOccluded || handlers.onVisible) {
|
|
373
|
+
object.occlusionTest = true;
|
|
374
|
+
if (!internal.occlusionEnabled) {
|
|
375
|
+
enableOcclusion(store);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
function unregisterVisibility(store, object) {
|
|
380
|
+
const { internal } = store.getState();
|
|
381
|
+
internal.visibilityRegistry.delete(object.uuid);
|
|
382
|
+
internal.occlusionCache.delete(object);
|
|
383
|
+
}
|
|
384
|
+
function checkVisibility(state) {
|
|
385
|
+
const { internal, camera } = state;
|
|
386
|
+
const registry = internal.visibilityRegistry;
|
|
387
|
+
if (registry.size === 0) return;
|
|
388
|
+
updateFrustum(camera, tempFrustum);
|
|
389
|
+
for (const entry of registry.values()) {
|
|
390
|
+
const { object, handlers, lastFramedState, lastOccludedState, lastVisibleState } = entry;
|
|
391
|
+
let inFrustum = null;
|
|
392
|
+
const computeFrustum = () => {
|
|
393
|
+
if (inFrustum === null) {
|
|
394
|
+
if (object.geometry?.boundingSphere === null) {
|
|
395
|
+
object.geometry?.computeBoundingSphere();
|
|
396
|
+
}
|
|
397
|
+
inFrustum = tempFrustum.intersectsObject(object);
|
|
398
|
+
}
|
|
399
|
+
return inFrustum;
|
|
400
|
+
};
|
|
401
|
+
if (handlers.onFramed) {
|
|
402
|
+
const currentInFrustum = computeFrustum();
|
|
403
|
+
if (currentInFrustum !== lastFramedState) {
|
|
404
|
+
entry.lastFramedState = currentInFrustum;
|
|
405
|
+
handlers.onFramed(currentInFrustum);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
let currentOcclusion = null;
|
|
409
|
+
if (handlers.onOccluded && internal.occlusionEnabled) {
|
|
410
|
+
currentOcclusion = internal.occlusionCache.get(object) ?? null;
|
|
411
|
+
if (currentOcclusion !== null && currentOcclusion !== lastOccludedState) {
|
|
412
|
+
entry.lastOccludedState = currentOcclusion;
|
|
413
|
+
handlers.onOccluded(currentOcclusion);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
if (handlers.onVisible) {
|
|
417
|
+
const currentInFrustum = computeFrustum();
|
|
418
|
+
if (!handlers.onFramed && currentInFrustum !== lastFramedState) {
|
|
419
|
+
entry.lastFramedState = currentInFrustum;
|
|
420
|
+
}
|
|
421
|
+
let isOccluded = currentOcclusion;
|
|
422
|
+
if (isOccluded === null && internal.occlusionEnabled) {
|
|
423
|
+
isOccluded = internal.occlusionCache.get(object) ?? null;
|
|
424
|
+
}
|
|
425
|
+
if (isOccluded === null) isOccluded = false;
|
|
426
|
+
const isVisible = currentInFrustum && !isOccluded && object.visible;
|
|
427
|
+
if (isVisible !== lastVisibleState) {
|
|
428
|
+
entry.lastVisibleState = isVisible;
|
|
429
|
+
handlers.onVisible(isVisible);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
function hasVisibilityHandlers(handlers) {
|
|
435
|
+
if (!handlers) return false;
|
|
436
|
+
return !!(handlers.onFramed || handlers.onOccluded || handlers.onVisible);
|
|
437
|
+
}
|
|
438
|
+
|
|
230
439
|
const RESERVED_PROPS = [
|
|
231
440
|
"children",
|
|
232
441
|
"key",
|
|
@@ -241,6 +450,7 @@ const RESERVED_PROPS = [
|
|
|
241
450
|
"dispose"
|
|
242
451
|
];
|
|
243
452
|
const EVENT_REGEX = /^on(Pointer|Drag|Drop|Click|DoubleClick|ContextMenu|Wheel)/;
|
|
453
|
+
const VISIBILITY_EVENT_REGEX = /^on(Framed|Occluded|Visible)$/;
|
|
244
454
|
const INDEX_REGEX = /-\d+$/;
|
|
245
455
|
const MEMOIZED_PROTOTYPES = /* @__PURE__ */ new Map();
|
|
246
456
|
const colorMaps = ["map", "emissiveMap", "sheenColorMap", "specularColorMap", "envMap"];
|
|
@@ -335,6 +545,12 @@ function applyProps(object, props) {
|
|
|
335
545
|
instance.eventCount = Object.keys(instance.handlers).length;
|
|
336
546
|
continue;
|
|
337
547
|
}
|
|
548
|
+
if (instance && VISIBILITY_EVENT_REGEX.test(prop)) {
|
|
549
|
+
if (typeof value === "function") instance.handlers[prop] = value;
|
|
550
|
+
else delete instance.handlers[prop];
|
|
551
|
+
instance.eventCount = Object.keys(instance.handlers).length;
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
338
554
|
if (value === void 0) continue;
|
|
339
555
|
let { root, key, target } = resolve(object, prop);
|
|
340
556
|
if (target === void 0 && (typeof root !== "object" || root === null)) {
|
|
@@ -371,6 +587,17 @@ function applyProps(object, props) {
|
|
|
371
587
|
if (instance.eventCount && object2.raycast !== null) {
|
|
372
588
|
rootState.internal.interaction.push(object2);
|
|
373
589
|
}
|
|
590
|
+
const root = findInitialRoot(instance);
|
|
591
|
+
const visibilityHandlers = {
|
|
592
|
+
onFramed: instance.handlers.onFramed,
|
|
593
|
+
onOccluded: instance.handlers.onOccluded,
|
|
594
|
+
onVisible: instance.handlers.onVisible
|
|
595
|
+
};
|
|
596
|
+
if (hasVisibilityHandlers(visibilityHandlers)) {
|
|
597
|
+
registerVisibility(root, object2, visibilityHandlers);
|
|
598
|
+
} else {
|
|
599
|
+
unregisterVisibility(root, object2);
|
|
600
|
+
}
|
|
374
601
|
}
|
|
375
602
|
if (instance && instance.props.attach === void 0) {
|
|
376
603
|
if (instance.object.isBufferGeometry) instance.props.attach = "geometry";
|
|
@@ -405,6 +632,7 @@ function removeInteractivity(store, object) {
|
|
|
405
632
|
internal.capturedMap.forEach((captures, pointerId) => {
|
|
406
633
|
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
407
634
|
});
|
|
635
|
+
unregisterVisibility(store, object);
|
|
408
636
|
}
|
|
409
637
|
function createEvents(store) {
|
|
410
638
|
function calculateDistance(event) {
|
|
@@ -787,8 +1015,21 @@ function formatLocation(url, line) {
|
|
|
787
1015
|
const file = clean.split("/").pop() ?? clean;
|
|
788
1016
|
return `${file}:${line}`;
|
|
789
1017
|
}
|
|
1018
|
+
function notifyAlpha({ message, link }) {
|
|
1019
|
+
if (typeof process !== "undefined" && (process.env.NODE_ENV === "test" || process.env.JEST_WORKER_ID !== void 0) && process.env.R3F_SHOW_ALPHA_WARNINGS !== "true") {
|
|
1020
|
+
return;
|
|
1021
|
+
}
|
|
1022
|
+
if (shownNotices.has(message)) return;
|
|
1023
|
+
shownNotices.add(message);
|
|
1024
|
+
const boxStyle = "background: #6366f1; color: #ffffff; padding: 6px 10px; border-radius: 4px; font-weight: 500;";
|
|
1025
|
+
console.log(`%c\u{1F52C} ${message}`, boxStyle);
|
|
1026
|
+
{
|
|
1027
|
+
console.log(`%cMore info: ${link}`, "color: #6366f1; font-weight: normal;");
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
790
1030
|
|
|
791
|
-
const
|
|
1031
|
+
const R3F_CONTEXT = Symbol.for("@react-three/fiber.context");
|
|
1032
|
+
const context = globalThis[R3F_CONTEXT] ?? (globalThis[R3F_CONTEXT] = React.createContext(null));
|
|
792
1033
|
const createStore = (invalidate, advance) => {
|
|
793
1034
|
const rootStore = createWithEqualityFn((set, get) => {
|
|
794
1035
|
const position = new Vector3();
|
|
@@ -819,6 +1060,8 @@ const createStore = (invalidate, advance) => {
|
|
|
819
1060
|
gl: null,
|
|
820
1061
|
renderer: null,
|
|
821
1062
|
camera: null,
|
|
1063
|
+
frustum: new Frustum(),
|
|
1064
|
+
autoUpdateFrustum: true,
|
|
822
1065
|
raycaster: null,
|
|
823
1066
|
events: { priority: 1, enabled: true, connected: false },
|
|
824
1067
|
scene: null,
|
|
@@ -900,6 +1143,13 @@ const createStore = (invalidate, advance) => {
|
|
|
900
1143
|
initialHits: [],
|
|
901
1144
|
capturedMap: /* @__PURE__ */ new Map(),
|
|
902
1145
|
lastEvent: React.createRef(),
|
|
1146
|
+
// Visibility tracking (onFramed, onOccluded, onVisible)
|
|
1147
|
+
visibilityRegistry: /* @__PURE__ */ new Map(),
|
|
1148
|
+
// Occlusion system (WebGPU only)
|
|
1149
|
+
occlusionEnabled: false,
|
|
1150
|
+
occlusionObserver: null,
|
|
1151
|
+
occlusionCache: /* @__PURE__ */ new Map(),
|
|
1152
|
+
helperGroup: null,
|
|
903
1153
|
// Updates
|
|
904
1154
|
active: false,
|
|
905
1155
|
frames: 0,
|
|
@@ -985,7 +1235,15 @@ const createStore = (invalidate, advance) => {
|
|
|
985
1235
|
}
|
|
986
1236
|
if (camera !== oldCamera) {
|
|
987
1237
|
oldCamera = camera;
|
|
1238
|
+
const { rootScene } = rootStore.getState();
|
|
1239
|
+
if (camera && rootScene && !camera.parent) {
|
|
1240
|
+
rootScene.add(camera);
|
|
1241
|
+
}
|
|
988
1242
|
set((state2) => ({ viewport: { ...state2.viewport, ...state2.viewport.getCurrentViewport(camera) } }));
|
|
1243
|
+
const currentState = rootStore.getState();
|
|
1244
|
+
if (currentState.autoUpdateFrustum && camera) {
|
|
1245
|
+
updateFrustum(camera, currentState.frustum);
|
|
1246
|
+
}
|
|
989
1247
|
}
|
|
990
1248
|
});
|
|
991
1249
|
rootStore.subscribe((state2) => invalidate(state2));
|
|
@@ -1349,6 +1607,10 @@ const _Scheduler = class _Scheduler {
|
|
|
1349
1607
|
__publicField(this, "jobStateListeners", /* @__PURE__ */ new Map());
|
|
1350
1608
|
__publicField(this, "pendingFrames", 0);
|
|
1351
1609
|
__publicField(this, "_frameloop", "always");
|
|
1610
|
+
//* Independent Mode & Error Handling State ================================
|
|
1611
|
+
__publicField(this, "_independent", false);
|
|
1612
|
+
__publicField(this, "errorHandler", null);
|
|
1613
|
+
__publicField(this, "rootReadyCallbacks", /* @__PURE__ */ new Set());
|
|
1352
1614
|
//* Core Loop Execution Methods ================================
|
|
1353
1615
|
/**
|
|
1354
1616
|
* Main RAF loop callback.
|
|
@@ -1371,6 +1633,12 @@ const _Scheduler = class _Scheduler {
|
|
|
1371
1633
|
});
|
|
1372
1634
|
this.phaseGraph = new PhaseGraph();
|
|
1373
1635
|
}
|
|
1636
|
+
static get instance() {
|
|
1637
|
+
return globalThis[_Scheduler.INSTANCE_KEY] ?? null;
|
|
1638
|
+
}
|
|
1639
|
+
static set instance(value) {
|
|
1640
|
+
globalThis[_Scheduler.INSTANCE_KEY] = value;
|
|
1641
|
+
}
|
|
1374
1642
|
/**
|
|
1375
1643
|
* Get the global scheduler instance (creates if doesn't exist).
|
|
1376
1644
|
* Uses HMR data to preserve instance across hot reloads.
|
|
@@ -1419,29 +1687,43 @@ const _Scheduler = class _Scheduler {
|
|
|
1419
1687
|
get isRunning() {
|
|
1420
1688
|
return this.loopState.running;
|
|
1421
1689
|
}
|
|
1690
|
+
get isReady() {
|
|
1691
|
+
return this.roots.size > 0;
|
|
1692
|
+
}
|
|
1693
|
+
get independent() {
|
|
1694
|
+
return this._independent;
|
|
1695
|
+
}
|
|
1696
|
+
set independent(value) {
|
|
1697
|
+
this._independent = value;
|
|
1698
|
+
if (value) this.ensureDefaultRoot();
|
|
1699
|
+
}
|
|
1422
1700
|
//* Root Management Methods ================================
|
|
1423
1701
|
/**
|
|
1424
1702
|
* Register a root (Canvas) with the scheduler.
|
|
1425
1703
|
* The first root to register starts the RAF loop (if frameloop='always').
|
|
1426
1704
|
* @param {string} id - Unique identifier for this root
|
|
1427
|
-
* @param {
|
|
1705
|
+
* @param {RootOptions} [options] - Optional configuration with getState and onError callbacks
|
|
1428
1706
|
* @returns {() => void} Unsubscribe function to remove this root
|
|
1429
1707
|
*/
|
|
1430
|
-
registerRoot(id,
|
|
1708
|
+
registerRoot(id, options = {}) {
|
|
1431
1709
|
if (this.roots.has(id)) {
|
|
1432
1710
|
console.warn(`[Scheduler] Root "${id}" already registered`);
|
|
1433
1711
|
return () => this.unregisterRoot(id);
|
|
1434
1712
|
}
|
|
1435
1713
|
const entry = {
|
|
1436
1714
|
id,
|
|
1437
|
-
getState,
|
|
1715
|
+
getState: options.getState ?? (() => ({})),
|
|
1438
1716
|
jobs: /* @__PURE__ */ new Map(),
|
|
1439
1717
|
sortedJobs: [],
|
|
1440
1718
|
needsRebuild: false
|
|
1441
1719
|
};
|
|
1720
|
+
if (options.onError) {
|
|
1721
|
+
this.errorHandler = options.onError;
|
|
1722
|
+
}
|
|
1442
1723
|
this.roots.set(id, entry);
|
|
1443
|
-
if (this.roots.size === 1
|
|
1444
|
-
this.
|
|
1724
|
+
if (this.roots.size === 1) {
|
|
1725
|
+
this.notifyRootReady();
|
|
1726
|
+
if (this._frameloop === "always") this.start();
|
|
1445
1727
|
}
|
|
1446
1728
|
return () => this.unregisterRoot(id);
|
|
1447
1729
|
}
|
|
@@ -1461,8 +1743,61 @@ const _Scheduler = class _Scheduler {
|
|
|
1461
1743
|
this.roots.delete(id);
|
|
1462
1744
|
if (this.roots.size === 0) {
|
|
1463
1745
|
this.stop();
|
|
1746
|
+
this.errorHandler = null;
|
|
1464
1747
|
}
|
|
1465
1748
|
}
|
|
1749
|
+
/**
|
|
1750
|
+
* Subscribe to be notified when a root becomes available.
|
|
1751
|
+
* Fires immediately if a root already exists.
|
|
1752
|
+
* @param {() => void} callback - Function called when first root registers
|
|
1753
|
+
* @returns {() => void} Unsubscribe function
|
|
1754
|
+
*/
|
|
1755
|
+
onRootReady(callback) {
|
|
1756
|
+
if (this.roots.size > 0) {
|
|
1757
|
+
callback();
|
|
1758
|
+
return () => {
|
|
1759
|
+
};
|
|
1760
|
+
}
|
|
1761
|
+
this.rootReadyCallbacks.add(callback);
|
|
1762
|
+
return () => this.rootReadyCallbacks.delete(callback);
|
|
1763
|
+
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Notify all registered root-ready callbacks.
|
|
1766
|
+
* Called when the first root registers.
|
|
1767
|
+
* @returns {void}
|
|
1768
|
+
* @private
|
|
1769
|
+
*/
|
|
1770
|
+
notifyRootReady() {
|
|
1771
|
+
for (const cb of this.rootReadyCallbacks) {
|
|
1772
|
+
try {
|
|
1773
|
+
cb();
|
|
1774
|
+
} catch (error) {
|
|
1775
|
+
console.error("[Scheduler] Error in root-ready callback:", error);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
this.rootReadyCallbacks.clear();
|
|
1779
|
+
}
|
|
1780
|
+
/**
|
|
1781
|
+
* Ensure a default root exists for independent mode.
|
|
1782
|
+
* Creates a minimal root with no state provider.
|
|
1783
|
+
* @returns {void}
|
|
1784
|
+
* @private
|
|
1785
|
+
*/
|
|
1786
|
+
ensureDefaultRoot() {
|
|
1787
|
+
if (!this.roots.has("__default__")) {
|
|
1788
|
+
this.registerRoot("__default__");
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Trigger error handling for job errors.
|
|
1793
|
+
* Uses the bound error handler if available, otherwise logs to console.
|
|
1794
|
+
* @param {Error} error - The error to handle
|
|
1795
|
+
* @returns {void}
|
|
1796
|
+
*/
|
|
1797
|
+
triggerError(error) {
|
|
1798
|
+
if (this.errorHandler) this.errorHandler(error);
|
|
1799
|
+
else console.error("[Scheduler]", error);
|
|
1800
|
+
}
|
|
1466
1801
|
//* Phase Management Methods ================================
|
|
1467
1802
|
/**
|
|
1468
1803
|
* Add a named phase to the scheduler's execution order.
|
|
@@ -1821,9 +2156,9 @@ const _Scheduler = class _Scheduler {
|
|
|
1821
2156
|
const deltaMs = this.loopState.lastTime !== null ? now - this.loopState.lastTime : 0;
|
|
1822
2157
|
const delta = deltaMs / 1e3;
|
|
1823
2158
|
const elapsed = now - this.loopState.createdAt;
|
|
1824
|
-
const
|
|
2159
|
+
const providedState = root.getState?.() ?? {};
|
|
1825
2160
|
const frameState = {
|
|
1826
|
-
...
|
|
2161
|
+
...providedState,
|
|
1827
2162
|
time: now,
|
|
1828
2163
|
delta,
|
|
1829
2164
|
elapsed,
|
|
@@ -1833,6 +2168,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1833
2168
|
job.callback(frameState, delta);
|
|
1834
2169
|
} catch (error) {
|
|
1835
2170
|
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
2171
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
1836
2172
|
}
|
|
1837
2173
|
}
|
|
1838
2174
|
/**
|
|
@@ -1874,7 +2210,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1874
2210
|
/**
|
|
1875
2211
|
* Execute all jobs for a single root in sorted order.
|
|
1876
2212
|
* Rebuilds sorted job list if needed, then dispatches each job.
|
|
1877
|
-
* Errors are caught and propagated
|
|
2213
|
+
* Errors are caught and propagated via triggerError.
|
|
1878
2214
|
* @param {RootEntry} root - The root entry to tick
|
|
1879
2215
|
* @param {number} timestamp - RAF timestamp in milliseconds
|
|
1880
2216
|
* @param {number} delta - Time since last frame in seconds
|
|
@@ -1886,10 +2222,9 @@ const _Scheduler = class _Scheduler {
|
|
|
1886
2222
|
root.sortedJobs = rebuildSortedJobs(root.jobs, this.phaseGraph);
|
|
1887
2223
|
root.needsRebuild = false;
|
|
1888
2224
|
}
|
|
1889
|
-
const
|
|
1890
|
-
if (!rootState) return;
|
|
2225
|
+
const providedState = root.getState?.() ?? {};
|
|
1891
2226
|
const frameState = {
|
|
1892
|
-
...
|
|
2227
|
+
...providedState,
|
|
1893
2228
|
time: timestamp,
|
|
1894
2229
|
delta,
|
|
1895
2230
|
elapsed: this.loopState.elapsedTime / 1e3,
|
|
@@ -1902,7 +2237,7 @@ const _Scheduler = class _Scheduler {
|
|
|
1902
2237
|
job.callback(frameState, delta);
|
|
1903
2238
|
} catch (error) {
|
|
1904
2239
|
console.error(`[Scheduler] Error in job "${job.id}":`, error);
|
|
1905
|
-
|
|
2240
|
+
this.triggerError(error instanceof Error ? error : new Error(String(error)));
|
|
1906
2241
|
}
|
|
1907
2242
|
}
|
|
1908
2243
|
}
|
|
@@ -1987,8 +2322,11 @@ const _Scheduler = class _Scheduler {
|
|
|
1987
2322
|
return /* @__PURE__ */ new Set([value]);
|
|
1988
2323
|
}
|
|
1989
2324
|
};
|
|
1990
|
-
//* Static State & Methods (
|
|
1991
|
-
|
|
2325
|
+
//* Static State & Methods (Singleton Usage) ================================
|
|
2326
|
+
//* Cross-Bundle Singleton Key ==============================
|
|
2327
|
+
// Use Symbol.for() to ensure scheduler is shared across bundle boundaries
|
|
2328
|
+
// This prevents issues when mixing imports from @react-three/fiber and @react-three/fiber/webgpu
|
|
2329
|
+
__publicField(_Scheduler, "INSTANCE_KEY", Symbol.for("@react-three/fiber.scheduler"));
|
|
1992
2330
|
let Scheduler = _Scheduler;
|
|
1993
2331
|
const getScheduler = () => Scheduler.get();
|
|
1994
2332
|
if (hmrData) {
|
|
@@ -1996,11 +2334,9 @@ if (hmrData) {
|
|
|
1996
2334
|
}
|
|
1997
2335
|
|
|
1998
2336
|
function useFrame(callback, priorityOrOptions) {
|
|
1999
|
-
const store =
|
|
2000
|
-
const
|
|
2001
|
-
|
|
2002
|
-
return state.internal.rootId;
|
|
2003
|
-
}, [store]);
|
|
2337
|
+
const store = React.useContext(context);
|
|
2338
|
+
const isInsideCanvas = store !== null;
|
|
2339
|
+
const scheduler = getScheduler();
|
|
2004
2340
|
const optionsKey = typeof priorityOrOptions === "number" ? `p:${priorityOrOptions}` : priorityOrOptions ? JSON.stringify({
|
|
2005
2341
|
id: priorityOrOptions.id,
|
|
2006
2342
|
phase: priorityOrOptions.phase,
|
|
@@ -2020,55 +2356,71 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2020
2356
|
const isLegacyPriority = typeof priorityOrOptions === "number" && priorityOrOptions > 0;
|
|
2021
2357
|
useIsomorphicLayoutEffect(() => {
|
|
2022
2358
|
if (!callback) return;
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
if (isLegacyPriority) {
|
|
2027
|
-
state.internal.priority++;
|
|
2028
|
-
let parentRoot = state.previousRoot;
|
|
2029
|
-
while (parentRoot) {
|
|
2030
|
-
const parentState = parentRoot.getState();
|
|
2031
|
-
if (parentState?.internal) parentState.internal.priority++;
|
|
2032
|
-
parentRoot = parentState?.previousRoot;
|
|
2033
|
-
}
|
|
2034
|
-
notifyDepreciated({
|
|
2035
|
-
heading: "useFrame with numeric priority is deprecated",
|
|
2036
|
-
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 })',
|
|
2037
|
-
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
2038
|
-
});
|
|
2039
|
-
}
|
|
2040
|
-
const wrappedCallback = (frameState, delta) => {
|
|
2041
|
-
const localState = store.getState();
|
|
2042
|
-
const mergedState = {
|
|
2043
|
-
...localState,
|
|
2044
|
-
time: frameState.time,
|
|
2045
|
-
delta: frameState.delta,
|
|
2046
|
-
elapsed: frameState.elapsed,
|
|
2047
|
-
frame: frameState.frame
|
|
2048
|
-
};
|
|
2049
|
-
callbackRef.current?.(mergedState, delta);
|
|
2050
|
-
};
|
|
2051
|
-
const unregister = scheduler.register(wrappedCallback, {
|
|
2052
|
-
id,
|
|
2053
|
-
rootId,
|
|
2054
|
-
...options
|
|
2055
|
-
});
|
|
2056
|
-
return () => {
|
|
2057
|
-
unregister();
|
|
2359
|
+
if (isInsideCanvas) {
|
|
2360
|
+
const state = store.getState();
|
|
2361
|
+
const rootId = state.internal.rootId;
|
|
2058
2362
|
if (isLegacyPriority) {
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2363
|
+
state.internal.priority++;
|
|
2364
|
+
let parentRoot = state.previousRoot;
|
|
2365
|
+
while (parentRoot) {
|
|
2366
|
+
const parentState = parentRoot.getState();
|
|
2367
|
+
if (parentState?.internal) parentState.internal.priority++;
|
|
2368
|
+
parentRoot = parentState?.previousRoot;
|
|
2369
|
+
}
|
|
2370
|
+
notifyDepreciated({
|
|
2371
|
+
heading: "useFrame with numeric priority is deprecated",
|
|
2372
|
+
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 })',
|
|
2373
|
+
link: "https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe"
|
|
2374
|
+
});
|
|
2375
|
+
}
|
|
2376
|
+
const wrappedCallback = (frameState, delta) => {
|
|
2377
|
+
const localState = store.getState();
|
|
2378
|
+
const mergedState = {
|
|
2379
|
+
...localState,
|
|
2380
|
+
time: frameState.time,
|
|
2381
|
+
delta: frameState.delta,
|
|
2382
|
+
elapsed: frameState.elapsed,
|
|
2383
|
+
frame: frameState.frame
|
|
2384
|
+
};
|
|
2385
|
+
callbackRef.current?.(mergedState, delta);
|
|
2386
|
+
};
|
|
2387
|
+
const unregister = scheduler.register(wrappedCallback, {
|
|
2388
|
+
id,
|
|
2389
|
+
rootId,
|
|
2390
|
+
...options
|
|
2391
|
+
});
|
|
2392
|
+
return () => {
|
|
2393
|
+
unregister();
|
|
2394
|
+
if (isLegacyPriority) {
|
|
2395
|
+
const currentState = store.getState();
|
|
2396
|
+
if (currentState.internal) {
|
|
2397
|
+
currentState.internal.priority--;
|
|
2398
|
+
let parentRoot = currentState.previousRoot;
|
|
2399
|
+
while (parentRoot) {
|
|
2400
|
+
const parentState = parentRoot.getState();
|
|
2401
|
+
if (parentState?.internal) parentState.internal.priority--;
|
|
2402
|
+
parentRoot = parentState?.previousRoot;
|
|
2403
|
+
}
|
|
2067
2404
|
}
|
|
2068
2405
|
}
|
|
2406
|
+
};
|
|
2407
|
+
} else {
|
|
2408
|
+
const registerOutside = () => {
|
|
2409
|
+
return scheduler.register((state, delta) => callbackRef.current?.(state, delta), { id, ...options });
|
|
2410
|
+
};
|
|
2411
|
+
if (scheduler.independent || scheduler.isReady) {
|
|
2412
|
+
return registerOutside();
|
|
2069
2413
|
}
|
|
2070
|
-
|
|
2071
|
-
|
|
2414
|
+
let unregisterJob = null;
|
|
2415
|
+
const unsubReady = scheduler.onRootReady(() => {
|
|
2416
|
+
unregisterJob = registerOutside();
|
|
2417
|
+
});
|
|
2418
|
+
return () => {
|
|
2419
|
+
unsubReady();
|
|
2420
|
+
unregisterJob?.();
|
|
2421
|
+
};
|
|
2422
|
+
}
|
|
2423
|
+
}, [store, scheduler, id, optionsKey, isLegacyPriority, isInsideCanvas]);
|
|
2072
2424
|
const isPaused = React.useSyncExternalStore(
|
|
2073
2425
|
// Subscribe function
|
|
2074
2426
|
React.useCallback(
|
|
@@ -2083,7 +2435,7 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2083
2435
|
React.useCallback(() => false, [])
|
|
2084
2436
|
);
|
|
2085
2437
|
const controls = React.useMemo(() => {
|
|
2086
|
-
const
|
|
2438
|
+
const scheduler2 = getScheduler();
|
|
2087
2439
|
return {
|
|
2088
2440
|
/** The job's unique ID */
|
|
2089
2441
|
id,
|
|
@@ -2091,7 +2443,7 @@ function useFrame(callback, priorityOrOptions) {
|
|
|
2091
2443
|
* Access to the global scheduler for frame loop control.
|
|
2092
2444
|
* Use for controlling the entire frame loop, adding phases, etc.
|
|
2093
2445
|
*/
|
|
2094
|
-
scheduler,
|
|
2446
|
+
scheduler: scheduler2,
|
|
2095
2447
|
/**
|
|
2096
2448
|
* Manually step this job only.
|
|
2097
2449
|
* Bypasses FPS limiting - always runs.
|
|
@@ -2339,6 +2691,16 @@ function useTextures() {
|
|
|
2339
2691
|
}, [store]);
|
|
2340
2692
|
}
|
|
2341
2693
|
|
|
2694
|
+
function useRenderTarget(width, height, options) {
|
|
2695
|
+
const isLegacy = useThree((s) => s.isLegacy);
|
|
2696
|
+
const size = useThree((s) => s.size);
|
|
2697
|
+
return useMemo(() => {
|
|
2698
|
+
const w = width ?? size.width;
|
|
2699
|
+
const h = height ?? size.height;
|
|
2700
|
+
return new WebGLRenderTarget(w, h, options);
|
|
2701
|
+
}, [width, height, size.width, size.height, options, isLegacy]);
|
|
2702
|
+
}
|
|
2703
|
+
|
|
2342
2704
|
function useStore() {
|
|
2343
2705
|
const store = useContext(context);
|
|
2344
2706
|
if (!store) throw new Error("R3F: Hooks can only be used within the Canvas component!");
|
|
@@ -2390,7 +2752,7 @@ function advance(timestamp, runGlobalEffects = true, state, frame) {
|
|
|
2390
2752
|
getScheduler().step(timestamp);
|
|
2391
2753
|
}
|
|
2392
2754
|
|
|
2393
|
-
const version = "10.0.0-alpha.
|
|
2755
|
+
const version = "10.0.0-alpha.1";
|
|
2394
2756
|
const packageData = {
|
|
2395
2757
|
version: version};
|
|
2396
2758
|
|
|
@@ -13691,7 +14053,8 @@ function createReconciler(config) {
|
|
|
13691
14053
|
return reconciler2;
|
|
13692
14054
|
}
|
|
13693
14055
|
const NoEventPriority = 0;
|
|
13694
|
-
const
|
|
14056
|
+
const R3F_CATALOGUE = Symbol.for("@react-three/fiber.catalogue");
|
|
14057
|
+
const catalogue = globalThis[R3F_CATALOGUE] ?? (globalThis[R3F_CATALOGUE] = {});
|
|
13695
14058
|
const PREFIX_REGEX = /^three(?=[A-Z])/;
|
|
13696
14059
|
const toPascalCase = (type) => `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
13697
14060
|
let i = 0;
|
|
@@ -14189,7 +14552,9 @@ function createRoot(canvas) {
|
|
|
14189
14552
|
camera: cameraOptions,
|
|
14190
14553
|
onPointerMissed,
|
|
14191
14554
|
onDragOverMissed,
|
|
14192
|
-
onDropMissed
|
|
14555
|
+
onDropMissed,
|
|
14556
|
+
autoUpdateFrustum = true,
|
|
14557
|
+
occlusion = false
|
|
14193
14558
|
} = props;
|
|
14194
14559
|
let state = store.getState();
|
|
14195
14560
|
const defaultGLProps = {
|
|
@@ -14255,6 +14620,8 @@ function createRoot(canvas) {
|
|
|
14255
14620
|
rootScene: scene,
|
|
14256
14621
|
internal: { ...prev.internal, container: scene }
|
|
14257
14622
|
}));
|
|
14623
|
+
const camera = state.camera;
|
|
14624
|
+
if (camera && !camera.parent) scene.add(camera);
|
|
14258
14625
|
}
|
|
14259
14626
|
if (events && !state.events.handlers) {
|
|
14260
14627
|
state.set({ events: events(store) });
|
|
@@ -14282,6 +14649,10 @@ function createRoot(canvas) {
|
|
|
14282
14649
|
if (!state.onPointerMissed) state.set({ onPointerMissed });
|
|
14283
14650
|
if (!state.onDragOverMissed) state.set({ onDragOverMissed });
|
|
14284
14651
|
if (!state.onDropMissed) state.set({ onDropMissed });
|
|
14652
|
+
if (state.autoUpdateFrustum !== autoUpdateFrustum) state.set({ autoUpdateFrustum });
|
|
14653
|
+
if (occlusion && !state.internal.occlusionEnabled) {
|
|
14654
|
+
enableOcclusion(store);
|
|
14655
|
+
}
|
|
14285
14656
|
if (performance && !is.equ(performance, lastConfiguredProps.performance, shallowLoose)) {
|
|
14286
14657
|
state.set((state2) => ({ performance: { ...state2.performance, ...performance } }));
|
|
14287
14658
|
lastConfiguredProps.performance = performance;
|
|
@@ -14374,7 +14745,37 @@ function createRoot(canvas) {
|
|
|
14374
14745
|
const rootId = state.internal.rootId;
|
|
14375
14746
|
if (!rootId) {
|
|
14376
14747
|
const newRootId = scheduler.generateRootId();
|
|
14377
|
-
const unregisterRoot = scheduler.registerRoot(newRootId,
|
|
14748
|
+
const unregisterRoot = scheduler.registerRoot(newRootId, {
|
|
14749
|
+
getState: () => store.getState(),
|
|
14750
|
+
onError: (err) => store.getState().setError(err)
|
|
14751
|
+
});
|
|
14752
|
+
const unregisterFrustum = scheduler.register(
|
|
14753
|
+
() => {
|
|
14754
|
+
const state2 = store.getState();
|
|
14755
|
+
if (state2.autoUpdateFrustum && state2.camera) {
|
|
14756
|
+
updateFrustum(state2.camera, state2.frustum);
|
|
14757
|
+
}
|
|
14758
|
+
},
|
|
14759
|
+
{
|
|
14760
|
+
id: `${newRootId}_frustum`,
|
|
14761
|
+
rootId: newRootId,
|
|
14762
|
+
phase: "preRender",
|
|
14763
|
+
system: true
|
|
14764
|
+
}
|
|
14765
|
+
);
|
|
14766
|
+
const unregisterVisibility = scheduler.register(
|
|
14767
|
+
() => {
|
|
14768
|
+
const state2 = store.getState();
|
|
14769
|
+
checkVisibility(state2);
|
|
14770
|
+
},
|
|
14771
|
+
{
|
|
14772
|
+
id: `${newRootId}_visibility`,
|
|
14773
|
+
rootId: newRootId,
|
|
14774
|
+
phase: "preRender",
|
|
14775
|
+
system: true,
|
|
14776
|
+
after: `${newRootId}_frustum`
|
|
14777
|
+
}
|
|
14778
|
+
);
|
|
14378
14779
|
const unregisterRender = scheduler.register(
|
|
14379
14780
|
() => {
|
|
14380
14781
|
const state2 = store.getState();
|
|
@@ -14402,6 +14803,8 @@ function createRoot(canvas) {
|
|
|
14402
14803
|
rootId: newRootId,
|
|
14403
14804
|
unregisterRoot: () => {
|
|
14404
14805
|
unregisterRoot();
|
|
14806
|
+
unregisterFrustum();
|
|
14807
|
+
unregisterVisibility();
|
|
14405
14808
|
unregisterRender();
|
|
14406
14809
|
},
|
|
14407
14810
|
scheduler
|
|
@@ -14459,6 +14862,7 @@ function unmountComponentAtNode(canvas, callback) {
|
|
|
14459
14862
|
const unregisterRoot = state.internal.unregisterRoot;
|
|
14460
14863
|
if (unregisterRoot) unregisterRoot();
|
|
14461
14864
|
state.events.disconnect?.();
|
|
14865
|
+
cleanupHelperGroup(root.store);
|
|
14462
14866
|
renderer?.renderLists?.dispose?.();
|
|
14463
14867
|
renderer?.forceContextLoss?.();
|
|
14464
14868
|
if (renderer?.xr) state.xr.disconnect();
|
|
@@ -14638,7 +15042,22 @@ function CanvasImpl({
|
|
|
14638
15042
|
effectActiveRef.current = true;
|
|
14639
15043
|
const canvas = canvasRef.current;
|
|
14640
15044
|
if (containerRect.width > 0 && containerRect.height > 0 && canvas) {
|
|
14641
|
-
if (!root.current)
|
|
15045
|
+
if (!root.current) {
|
|
15046
|
+
root.current = createRoot(canvas);
|
|
15047
|
+
notifyAlpha({
|
|
15048
|
+
message: "React Three Fiber v10 is in ALPHA - expect breaking changes",
|
|
15049
|
+
link: "https://github.com/pmndrs/react-three-fiber/discussions"
|
|
15050
|
+
});
|
|
15051
|
+
const rootEntry = _roots.get(canvas);
|
|
15052
|
+
if (rootEntry?.store) {
|
|
15053
|
+
if (unsubscribeErrorRef.current) unsubscribeErrorRef.current();
|
|
15054
|
+
unsubscribeErrorRef.current = rootEntry.store.subscribe((state) => {
|
|
15055
|
+
if (state.error && effectActiveRef.current) {
|
|
15056
|
+
setError(state.error);
|
|
15057
|
+
}
|
|
15058
|
+
});
|
|
15059
|
+
}
|
|
15060
|
+
}
|
|
14642
15061
|
async function run() {
|
|
14643
15062
|
if (!effectActiveRef.current || !root.current) return;
|
|
14644
15063
|
await root.current.configure({
|
|
@@ -14679,15 +15098,9 @@ function CanvasImpl({
|
|
|
14679
15098
|
}
|
|
14680
15099
|
});
|
|
14681
15100
|
if (!effectActiveRef.current || !root.current) return;
|
|
14682
|
-
|
|
15101
|
+
root.current.render(
|
|
14683
15102
|
/* @__PURE__ */ jsx(Bridge, { children: /* @__PURE__ */ jsx(ErrorBoundary, { set: setError, children: /* @__PURE__ */ jsx(React.Suspense, { fallback: /* @__PURE__ */ jsx(Block, { set: setBlock }), children: children ?? null }) }) })
|
|
14684
15103
|
);
|
|
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
15104
|
}
|
|
14692
15105
|
run();
|
|
14693
15106
|
}
|
|
@@ -14732,4 +15145,4 @@ function Canvas(props) {
|
|
|
14732
15145
|
|
|
14733
15146
|
extend(THREE);
|
|
14734
15147
|
|
|
14735
|
-
export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, reconciler, removeInteractivity, resolve, unmountComponentAtNode, updateCamera, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useStore, useTexture, useTextures, useThree };
|
|
15148
|
+
export { Block, Canvas, ErrorBoundary, IsObject, R3F_BUILD_LEGACY, R3F_BUILD_WEBGPU, REACT_INTERNAL_PROPS, RESERVED_PROPS, Scheduler, Texture, _roots, act, addAfterEffect, addEffect, addTail, advance, applyProps, attach, buildGraph, calculateDpr, context, createEvents, createPointerEvents, createPortal, createRoot, createStore, detach, diffProps, dispose, createPointerEvents as events, extend, findInitialRoot, flushSync, getInstanceProps, getRootState, getScheduler, getUuidPrefix, hasConstructor, invalidate, invalidateInstance, is, isColorRepresentation, isCopyable, isObject3D, isOrthographicCamera, isRef, isRenderer, isTexture, isVectorLike, prepare, reconciler, removeInteractivity, resolve, unmountComponentAtNode, updateCamera, updateFrustum, useBridge, useFrame, useGraph, useInstanceHandle, useIsomorphicLayoutEffect, useLoader, useMutableCallback, useRenderTarget, useStore, useTexture, useTextures, useThree };
|