@react-three/fiber 8.0.0-beta-04 → 8.0.0-beta.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.
@@ -70,29 +70,38 @@ const pick = (obj, keys) => filterKeys(obj, false, ...keys);
70
70
  * Clones an object and prunes or omits keys.
71
71
  */
72
72
 
73
- const omit = (obj, keys) => filterKeys(obj, true, ...keys); // A collection of compare functions
74
-
73
+ const omit = (obj, keys) => filterKeys(obj, true, ...keys);
74
+ // A collection of compare functions
75
75
  const is = {
76
76
  obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
77
77
  fun: a => typeof a === 'function',
78
78
  str: a => typeof a === 'string',
79
79
  num: a => typeof a === 'number',
80
+ boo: a => typeof a === 'boolean',
80
81
  und: a => a === void 0,
81
82
  arr: a => Array.isArray(a),
82
83
 
83
- equ(a, b) {
84
+ equ(a, b, {
85
+ arrays = 'shallow',
86
+ objects = 'reference',
87
+ strict = true
88
+ } = {}) {
84
89
  // Wrong type or one of the two undefined, doesn't match
85
90
  if (typeof a !== typeof b || !!a !== !!b) return false; // Atomic, just compare a against b
86
91
 
87
- if (is.str(a) || is.num(a) || is.obj(a)) return a === b; // Array, shallow compare first to see if it's a match
92
+ if (is.str(a) || is.num(a)) return a === b;
93
+ const isObj = is.obj(a);
94
+ if (isObj && objects === 'reference') return a === b;
95
+ const isArr = is.arr(a);
96
+ if (isArr && arrays === 'reference') return a === b; // Array or Object, shallow compare first to see if it's a match
88
97
 
89
- if (is.arr(a) && a == b) return true; // Last resort, go through keys
98
+ if ((isArr || isObj) && a == b) return true; // Last resort, go through keys
90
99
 
91
100
  let i;
92
101
 
93
102
  for (i in a) if (!(i in b)) return false;
94
103
 
95
- for (i in b) if (a[i] !== b[i]) return false;
104
+ for (i in strict ? b : a) if (a[i] !== b[i]) return false;
96
105
 
97
106
  return is.und(i) ? a === b : true;
98
107
  }
@@ -183,15 +192,8 @@ function detach(parent, child, type) {
183
192
  const [, detach] = type;
184
193
  if (is.str(detach)) parent[detach](child);else if (is.fun(detach)) detach(parent, child);
185
194
  }
186
- } // Shallow check arrays, but check objects atomically
187
-
188
- function checkShallow(a, b) {
189
- if (is.arr(a) && is.equ(a, b)) return true;
190
- if (a === b) return true;
191
- return false;
192
195
  } // This function prepares a set of changes to be applied to the instance
193
196
 
194
-
195
197
  function diffProps(instance, {
196
198
  children: cN,
197
199
  key: kN,
@@ -223,7 +225,7 @@ function diffProps(instance, {
223
225
  // Bail out on primitive object
224
226
  if ((_instance$__r3f2 = instance.__r3f) != null && _instance$__r3f2.primitive && key === 'object') return; // When props match bail out
225
227
 
226
- if (checkShallow(value, previous[key])) return; // Collect handlers and bail out
228
+ if (is.equ(value, previous[key])) return; // Collect handlers and bail out
227
229
 
228
230
  if (/^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/.test(key)) return changes.push([key, value, true, []]); // Split dashed props
229
231
 
@@ -420,7 +422,7 @@ function createEvents(store) {
420
422
  /** Sets up defaultRaycaster */
421
423
 
422
424
  function prepareRay(event) {
423
- var _raycaster$computeOff;
425
+ var _customOffsets$offset, _customOffsets$offset2, _customOffsets$width, _customOffsets$height;
424
426
 
425
427
  const state = store.getState();
426
428
  const {
@@ -431,14 +433,11 @@ function createEvents(store) {
431
433
  } = state; // https://github.com/pmndrs/react-three-fiber/pull/782
432
434
  // Events trigger outside of canvas when moved
433
435
 
434
- const {
435
- offsetX,
436
- offsetY
437
- } = (_raycaster$computeOff = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state)) != null ? _raycaster$computeOff : event;
438
- const {
439
- width,
440
- height
441
- } = size;
436
+ const customOffsets = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state);
437
+ const offsetX = (_customOffsets$offset = customOffsets == null ? void 0 : customOffsets.offsetX) != null ? _customOffsets$offset : event.offsetX;
438
+ const offsetY = (_customOffsets$offset2 = customOffsets == null ? void 0 : customOffsets.offsetY) != null ? _customOffsets$offset2 : event.offsetY;
439
+ const width = (_customOffsets$width = customOffsets == null ? void 0 : customOffsets.width) != null ? _customOffsets$width : size.width;
440
+ const height = (_customOffsets$height = customOffsets == null ? void 0 : customOffsets.height) != null ? _customOffsets$height : size.height;
442
441
  mouse.set(offsetX / width * 2 - 1, -(offsetY / height) * 2 + 1);
443
442
  raycaster.setFromCamera(mouse, camera);
444
443
  }
@@ -997,10 +996,13 @@ function createRenderer(roots, getEventPriority) {
997
996
 
998
997
  instance.__r3f.objects = [];
999
998
  removeChild(parent, instance);
1000
- appendChild(parent, newInstance) // This evil hack switches the react-internal fiber node
1001
- // https://github.com/facebook/react/issues/14983
1002
- // https://github.com/facebook/react/pull/15021
1003
- ;
999
+ appendChild(parent, newInstance); // Re-bind event handlers
1000
+
1001
+ if (newInstance.raycast && newInstance.__r3f.eventCount) {
1002
+ const rootState = newInstance.__r3f.root.getState();
1003
+
1004
+ rootState.internal.interaction.push(newInstance);
1005
+ } // This evil hack switches the react-internal fiber node
1004
1006
  [fiber, fiber.alternate].forEach(fiber => {
1005
1007
  if (fiber !== null) {
1006
1008
  fiber.stateNode = newInstance;
@@ -1094,7 +1096,7 @@ function createRenderer(roots, getEventPriority) {
1094
1096
  isPrimaryRenderer: false,
1095
1097
  getCurrentEventPriority: () => getEventPriority ? getEventPriority() : constants.DefaultEventPriority,
1096
1098
  // @ts-ignore
1097
- now: is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : undefined,
1099
+ now: typeof performance !== 'undefined' && is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : undefined,
1098
1100
  // @ts-ignore
1099
1101
  scheduleTimeout: is.fun(setTimeout) ? setTimeout : undefined,
1100
1102
  // @ts-ignore
@@ -1152,63 +1154,8 @@ const isRenderer = def => !!(def != null && def.render);
1152
1154
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1153
1155
  const context = /*#__PURE__*/React__namespace.createContext(null);
1154
1156
 
1155
- const createStore = (applyProps, invalidate, advance, props) => {
1156
- const {
1157
- gl,
1158
- size,
1159
- shadows = false,
1160
- linear = false,
1161
- flat = false,
1162
- orthographic = false,
1163
- frameloop = 'always',
1164
- dpr = [1, 2],
1165
- performance,
1166
- clock = new THREE__namespace.Clock(),
1167
- raycaster: raycastOptions,
1168
- camera: cameraOptions,
1169
- onPointerMissed
1170
- } = props; // Set shadowmap
1171
-
1172
- if (shadows) {
1173
- gl.shadowMap.enabled = true;
1174
- if (typeof shadows === 'object') Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1175
- } // Set color preferences
1176
-
1177
-
1178
- if (linear) gl.outputEncoding = THREE__namespace.LinearEncoding;
1179
- if (flat) gl.toneMapping = THREE__namespace.NoToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1180
-
1181
- if (frameloop === 'never') {
1182
- clock.stop();
1183
- clock.elapsedTime = 0;
1184
- }
1185
-
1157
+ const createStore = (invalidate, advance) => {
1186
1158
  const rootState = create__default['default']((set, get) => {
1187
- // Create custom raycaster
1188
- const raycaster = new THREE__namespace.Raycaster();
1189
- const {
1190
- params,
1191
- ...options
1192
- } = raycastOptions || {};
1193
- applyProps(raycaster, {
1194
- enabled: true,
1195
- ...options,
1196
- params: { ...raycaster.params,
1197
- ...params
1198
- }
1199
- }); // Create default camera
1200
-
1201
- const isCamera = cameraOptions instanceof THREE__namespace.Camera;
1202
- const camera = isCamera ? cameraOptions : orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
1203
-
1204
- if (!isCamera) {
1205
- camera.position.z = 5;
1206
- if (cameraOptions) applyProps(camera, cameraOptions); // Always look at center by default
1207
-
1208
- if (!(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
1209
- }
1210
-
1211
- const initialDpr = calculateDpr(dpr);
1212
1159
  const position = new THREE__namespace.Vector3();
1213
1160
  const defaultTarget = new THREE__namespace.Vector3();
1214
1161
  const tempTarget = new THREE__namespace.Vector3();
@@ -1252,60 +1199,34 @@ const createStore = (applyProps, invalidate, advance, props) => {
1252
1199
  performance: { ...state.performance,
1253
1200
  current
1254
1201
  }
1255
- })); // Handle frame behavior in WebXR
1256
-
1257
-
1258
- const handleXRFrame = timestamp => {
1259
- const state = get();
1260
- if (state.frameloop === 'never') return;
1261
- advance(timestamp, true);
1262
- }; // Toggle render switching on session
1263
-
1264
-
1265
- const handleSessionChange = () => {
1266
- gl.xr.enabled = gl.xr.isPresenting;
1267
- gl.setAnimationLoop(gl.xr.isPresenting ? handleXRFrame : null); // If exiting session, request frame
1268
-
1269
- if (!gl.xr.isPresenting) invalidate(get());
1270
- }; // WebXR session manager
1271
-
1272
-
1273
- const xr = {
1274
- connect() {
1275
- gl.xr.addEventListener('sessionstart', handleSessionChange);
1276
- gl.xr.addEventListener('sessionend', handleSessionChange);
1277
- },
1278
-
1279
- disconnect() {
1280
- gl.xr.removeEventListener('sessionstart', handleSessionChange);
1281
- gl.xr.removeEventListener('sessionend', handleSessionChange);
1282
- }
1283
-
1284
- }; // Subscribe to WebXR session events
1202
+ }));
1285
1203
 
1286
- if (gl.xr) xr.connect();
1287
1204
  return {
1288
- gl,
1205
+ // Mock objects that have to be configured
1206
+ gl: null,
1207
+ camera: null,
1208
+ raycaster: null,
1209
+ events: {
1210
+ connected: false
1211
+ },
1212
+ xr: null,
1289
1213
  set,
1290
1214
  get,
1291
1215
  invalidate: () => invalidate(get()),
1292
1216
  advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1293
- linear,
1294
- flat,
1217
+ linear: false,
1218
+ flat: false,
1295
1219
  scene: prepare(new THREE__namespace.Scene()),
1296
- camera,
1297
1220
  controls: null,
1298
- raycaster,
1299
- clock,
1221
+ clock: new THREE__namespace.Clock(),
1300
1222
  mouse: new THREE__namespace.Vector2(),
1301
- frameloop,
1302
- onPointerMissed,
1223
+ frameloop: 'always',
1224
+ onPointerMissed: undefined,
1303
1225
  performance: {
1304
1226
  current: 1,
1305
1227
  min: 0.5,
1306
1228
  max: 1,
1307
1229
  debounce: 200,
1308
- ...performance,
1309
1230
  regress: () => {
1310
1231
  const state = get(); // Clear timeout
1311
1232
 
@@ -1321,8 +1242,8 @@ const createStore = (applyProps, invalidate, advance, props) => {
1321
1242
  height: 0
1322
1243
  },
1323
1244
  viewport: {
1324
- initialDpr,
1325
- dpr: initialDpr,
1245
+ initialDpr: 0,
1246
+ dpr: 0,
1326
1247
  width: 0,
1327
1248
  height: 0,
1328
1249
  aspect: 0,
@@ -1331,6 +1252,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1331
1252
  getCurrentViewport
1332
1253
  },
1333
1254
  setSize: (width, height) => {
1255
+ const camera = get().camera;
1334
1256
  const size = {
1335
1257
  width,
1336
1258
  height
@@ -1342,22 +1264,34 @@ const createStore = (applyProps, invalidate, advance, props) => {
1342
1264
  }
1343
1265
  }));
1344
1266
  },
1345
- setDpr: dpr => set(state => ({
1346
- viewport: { ...state.viewport,
1347
- dpr: calculateDpr(dpr)
1267
+ setDpr: dpr => set(state => {
1268
+ const resolved = calculateDpr(dpr);
1269
+ return {
1270
+ viewport: { ...state.viewport,
1271
+ dpr: resolved,
1272
+ initialDpr: state.viewport.initialDpr || resolved
1273
+ }
1274
+ };
1275
+ }),
1276
+ setFrameloop: (frameloop = 'always') => {
1277
+ const clock = get().clock; // if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
1278
+
1279
+ clock.stop();
1280
+ clock.elapsedTime = 0;
1281
+
1282
+ if (frameloop !== 'never') {
1283
+ clock.start();
1284
+ clock.elapsedTime = 0;
1348
1285
  }
1349
- })),
1350
- setFrameloop: (frameloop = 'always') => set(() => ({
1351
- frameloop
1352
- })),
1353
- events: {
1354
- connected: false
1286
+
1287
+ set(() => ({
1288
+ frameloop
1289
+ }));
1355
1290
  },
1356
1291
  internal: {
1357
1292
  active: false,
1358
1293
  priority: 0,
1359
1294
  frames: 0,
1360
- lastProps: props,
1361
1295
  lastEvent: /*#__PURE__*/React__namespace.createRef(),
1362
1296
  interaction: [],
1363
1297
  hovered: new Map(),
@@ -1365,7 +1299,6 @@ const createStore = (applyProps, invalidate, advance, props) => {
1365
1299
  initialClick: [0, 0],
1366
1300
  initialHits: [],
1367
1301
  capturedMap: new Map(),
1368
- xr,
1369
1302
  subscribe: (ref, priority = 0) => {
1370
1303
  set(({
1371
1304
  internal
@@ -1409,13 +1342,14 @@ const createStore = (applyProps, invalidate, advance, props) => {
1409
1342
  camera,
1410
1343
  size,
1411
1344
  viewport,
1412
- internal
1345
+ internal,
1346
+ gl
1413
1347
  } = rootState.getState();
1414
1348
 
1415
1349
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1416
1350
  // https://github.com/pmndrs/react-three-fiber/issues/92
1417
1351
  // Do not mess with the camera if it belongs to the user
1418
- if (!camera.manual && !(internal.lastProps.camera instanceof THREE__namespace.Camera)) {
1352
+ if (!camera.manual) {
1419
1353
  if (isOrthographicCamera(camera)) {
1420
1354
  camera.left = size.width / -2;
1421
1355
  camera.right = size.width / 2;
@@ -1437,69 +1371,13 @@ const createStore = (applyProps, invalidate, advance, props) => {
1437
1371
  oldSize = size;
1438
1372
  oldDpr = viewport.dpr;
1439
1373
  }
1440
- }); // Update size
1441
-
1442
- if (size) state.setSize(size.width, size.height); // Invalidate on any change
1374
+ }); // Invalidate on any change
1443
1375
 
1444
1376
  rootState.subscribe(state => invalidate(state)); // Return root state
1445
1377
 
1446
1378
  return rootState;
1447
1379
  };
1448
1380
 
1449
- function useStore() {
1450
- const store = React__namespace.useContext(context);
1451
- if (!store) throw `R3F hooks can only be used within the Canvas component!`;
1452
- return store;
1453
- }
1454
- function useThree(selector = state => state, equalityFn) {
1455
- return useStore()(selector, equalityFn);
1456
- }
1457
- function useFrame(callback, renderPriority = 0) {
1458
- const subscribe = useStore().getState().internal.subscribe; // Update ref
1459
-
1460
- const ref = React__namespace.useRef(callback);
1461
- React__namespace.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe on mount, unsubscribe on unmount
1462
-
1463
- React__namespace.useLayoutEffect(() => subscribe(ref, renderPriority), [renderPriority, subscribe]);
1464
- return null;
1465
- }
1466
- function useGraph(object) {
1467
- return React__namespace.useMemo(() => buildGraph(object), [object]);
1468
- }
1469
-
1470
- function loadingFn(extensions, onProgress) {
1471
- return function (Proto, ...input) {
1472
- // Construct new loader and run extensions
1473
- const loader = new Proto();
1474
- if (extensions) extensions(loader); // Go through the urls and load them
1475
-
1476
- return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => {
1477
- if (data.scene) Object.assign(data, buildGraph(data.scene));
1478
- res(data);
1479
- }, onProgress, error => reject(`Could not load ${input}: ${error.message}`)))));
1480
- };
1481
- }
1482
-
1483
- function useLoader(Proto, input, extensions, onProgress) {
1484
- // Use suspense to load async assets
1485
- const keys = Array.isArray(input) ? input : [input];
1486
- const results = suspendReact.suspend(loadingFn(extensions, onProgress), [Proto, ...keys], {
1487
- equal: is.equ
1488
- }); // Return the object/s
1489
-
1490
- return Array.isArray(input) ? results : results[0];
1491
- }
1492
-
1493
- useLoader.preload = function (Proto, input, extensions) {
1494
- const keys = Array.isArray(input) ? input : [input];
1495
- return suspendReact.preload(loadingFn(extensions), [Proto, ...keys]);
1496
- };
1497
-
1498
- useLoader.clear = function (Proto, input) {
1499
- const keys = Array.isArray(input) ? input : [input];
1500
- return suspendReact.clear([Proto, ...keys]);
1501
- };
1502
-
1503
1381
  function createSubs(callback, subs) {
1504
1382
  const index = subs.length;
1505
1383
  subs.push(callback);
@@ -1518,7 +1396,9 @@ function run(effects, timestamp) {
1518
1396
  for (i = 0; i < effects.length; i++) effects[i](timestamp);
1519
1397
  }
1520
1398
 
1521
- function render$1(timestamp, state) {
1399
+ let subscribers;
1400
+
1401
+ function render$1(timestamp, state, frame) {
1522
1402
  // Run local effects
1523
1403
  let delta = state.clock.getDelta(); // In frameloop='never' mode, clock times are updated using the provided timestamp
1524
1404
 
@@ -1529,7 +1409,9 @@ function render$1(timestamp, state) {
1529
1409
  } // Call subscribers (useFrame)
1530
1410
 
1531
1411
 
1532
- for (i = 0; i < state.internal.subscribers.length; i++) state.internal.subscribers[i].ref.current(state, delta); // Render content
1412
+ subscribers = state.internal.subscribers;
1413
+
1414
+ for (i = 0; i < subscribers.length; i++) subscribers[i].ref.current(state, delta, frame); // Render content
1533
1415
 
1534
1416
 
1535
1417
  if (!state.internal.priority && state.gl.render) state.gl.render(state.scene, state.camera); // Decrease frame count
@@ -1541,29 +1423,35 @@ function render$1(timestamp, state) {
1541
1423
  function createLoop(roots) {
1542
1424
  let running = false;
1543
1425
  let repeat;
1426
+ let frame;
1427
+ let state;
1544
1428
 
1545
1429
  function loop(timestamp) {
1430
+ frame = requestAnimationFrame(loop);
1546
1431
  running = true;
1547
1432
  repeat = 0; // Run effects
1548
1433
 
1549
- run(globalEffects, timestamp); // Render all roots
1434
+ if (globalEffects.length) run(globalEffects, timestamp); // Render all roots
1550
1435
 
1551
1436
  roots.forEach(root => {
1552
1437
  var _state$gl$xr;
1553
1438
 
1554
- const state = root.store.getState(); // If the frameloop is invalidated, do not run another frame
1439
+ state = root.store.getState(); // If the frameloop is invalidated, do not run another frame
1555
1440
 
1556
1441
  if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) {
1557
1442
  repeat += render$1(timestamp, state);
1558
1443
  }
1559
1444
  }); // Run after-effects
1560
1445
 
1561
- run(globalAfterEffects, timestamp); // Keep on looping if anything invalidates the frameloop
1446
+ if (globalAfterEffects.length) run(globalAfterEffects, timestamp); // Stop the loop if nothing invalidates it
1562
1447
 
1563
- if (repeat > 0) return requestAnimationFrame(loop); // Tail call effects, they are called when rendering stops
1564
- else run(globalTailEffects, timestamp); // Flag end of operation
1448
+ if (repeat === 0) {
1449
+ // Tail call effects, they are called when rendering stops
1450
+ if (globalTailEffects.length) run(globalTailEffects, timestamp); // Flag end of operation
1565
1451
 
1566
- running = false;
1452
+ running = false;
1453
+ return cancelAnimationFrame(frame);
1454
+ }
1567
1455
  }
1568
1456
 
1569
1457
  function invalidate(state) {
@@ -1580,9 +1468,9 @@ function createLoop(roots) {
1580
1468
  }
1581
1469
  }
1582
1470
 
1583
- function advance(timestamp, runGlobalEffects = true, state) {
1471
+ function advance(timestamp, runGlobalEffects = true, state, frame) {
1584
1472
  if (runGlobalEffects) run(globalEffects, timestamp);
1585
- if (!state) roots.forEach(root => render$1(timestamp, root.store.getState()));else render$1(timestamp, state);
1473
+ if (!state) roots.forEach(root => render$1(timestamp, root.store.getState()));else render$1(timestamp, state, frame);
1586
1474
  if (runGlobalEffects) run(globalAfterEffects, timestamp);
1587
1475
  }
1588
1476
 
@@ -1593,6 +1481,60 @@ function createLoop(roots) {
1593
1481
  };
1594
1482
  }
1595
1483
 
1484
+ function useStore() {
1485
+ const store = React__namespace.useContext(context);
1486
+ if (!store) throw `R3F hooks can only be used within the Canvas component!`;
1487
+ return store;
1488
+ }
1489
+ function useThree(selector = state => state, equalityFn) {
1490
+ return useStore()(selector, equalityFn);
1491
+ }
1492
+ function useFrame(callback, renderPriority = 0) {
1493
+ const subscribe = useStore().getState().internal.subscribe; // Update ref
1494
+
1495
+ const ref = React__namespace.useRef(callback);
1496
+ React__namespace.useLayoutEffect(() => void (ref.current = callback), [callback]); // Subscribe on mount, unsubscribe on unmount
1497
+
1498
+ React__namespace.useLayoutEffect(() => subscribe(ref, renderPriority), [renderPriority, subscribe]);
1499
+ return null;
1500
+ }
1501
+ function useGraph(object) {
1502
+ return React__namespace.useMemo(() => buildGraph(object), [object]);
1503
+ }
1504
+
1505
+ function loadingFn(extensions, onProgress) {
1506
+ return function (Proto, ...input) {
1507
+ // Construct new loader and run extensions
1508
+ const loader = new Proto();
1509
+ if (extensions) extensions(loader); // Go through the urls and load them
1510
+
1511
+ return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => {
1512
+ if (data.scene) Object.assign(data, buildGraph(data.scene));
1513
+ res(data);
1514
+ }, onProgress, error => reject(`Could not load ${input}: ${error.message}`)))));
1515
+ };
1516
+ }
1517
+
1518
+ function useLoader(Proto, input, extensions, onProgress) {
1519
+ // Use suspense to load async assets
1520
+ const keys = Array.isArray(input) ? input : [input];
1521
+ const results = suspendReact.suspend(loadingFn(extensions, onProgress), [Proto, ...keys], {
1522
+ equal: is.equ
1523
+ }); // Return the object/s
1524
+
1525
+ return Array.isArray(input) ? results : results[0];
1526
+ }
1527
+
1528
+ useLoader.preload = function (Proto, input, extensions) {
1529
+ const keys = Array.isArray(input) ? input : [input];
1530
+ return suspendReact.preload(loadingFn(extensions), [Proto, ...keys]);
1531
+ };
1532
+
1533
+ useLoader.clear = function (Proto, input) {
1534
+ const keys = Array.isArray(input) ? input : [input];
1535
+ return suspendReact.clear([Proto, ...keys]);
1536
+ };
1537
+
1596
1538
  const roots = new Map();
1597
1539
  const {
1598
1540
  invalidate,
@@ -1602,113 +1544,216 @@ const {
1602
1544
  reconciler,
1603
1545
  applyProps
1604
1546
  } = createRenderer(roots, getEventPriority);
1547
+ const shallowLoose = {
1548
+ objects: 'shallow',
1549
+ strict: false
1550
+ };
1605
1551
 
1606
1552
  const createRendererInstance = (gl, canvas) => {
1607
1553
  const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1608
- if (isRenderer(customRenderer)) return customRenderer;
1609
- const renderer = new THREE__namespace.WebGLRenderer({
1554
+ if (isRenderer(customRenderer)) return customRenderer;else return new THREE__namespace.WebGLRenderer({
1610
1555
  powerPreference: 'high-performance',
1611
1556
  canvas: canvas,
1612
1557
  antialias: true,
1613
1558
  alpha: true,
1614
1559
  ...gl
1615
- }); // Set color management
1560
+ });
1561
+ };
1616
1562
 
1617
- renderer.outputEncoding = THREE__namespace.sRGBEncoding;
1618
- renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping; // Set gl props
1563
+ function createRoot(canvas) {
1564
+ // Check against mistaken use of createRoot
1565
+ let prevRoot = roots.get(canvas);
1566
+ let prevFiber = prevRoot == null ? void 0 : prevRoot.fiber;
1567
+ let prevStore = prevRoot == null ? void 0 : prevRoot.store;
1568
+ if (prevRoot) console.warn('R3F.createRoot should only be called once!'); // Create store
1619
1569
 
1620
- if (gl) applyProps(renderer, gl);
1621
- return renderer;
1622
- };
1570
+ const store = prevStore || createStore(invalidate, advance); // Create renderer
1623
1571
 
1624
- function createRoot(canvas, config) {
1572
+ const fiber = prevFiber || reconciler.createContainer(store, constants.ConcurrentRoot, false, null); // Map it
1573
+
1574
+ if (!prevRoot) roots.set(canvas, {
1575
+ fiber,
1576
+ store
1577
+ }); // Locals
1578
+
1579
+ let onCreated;
1580
+ let configured = false;
1625
1581
  return {
1626
- render: element => {
1627
- var _store;
1582
+ configure(props = {}) {
1583
+ var _canvas$parentElement, _canvas$parentElement2, _canvas$parentElement3, _canvas$parentElement4;
1628
1584
 
1629
1585
  let {
1630
- gl,
1586
+ gl: glConfig,
1631
1587
  size,
1632
1588
  events,
1633
- onCreated,
1634
- ...props
1635
- } = config || {}; // Allow size to take on container bounds initially
1589
+ onCreated: onCreatedCallback,
1590
+ shadows = false,
1591
+ linear = false,
1592
+ flat = false,
1593
+ orthographic = false,
1594
+ frameloop = 'always',
1595
+ dpr = [1, 2],
1596
+ performance,
1597
+ raycaster: raycastOptions,
1598
+ camera: cameraOptions,
1599
+ onPointerMissed
1600
+ } = props;
1601
+ let state = store.getState(); // Set up renderer (one time only!)
1602
+
1603
+ let gl = state.gl;
1604
+ if (!state.gl) state.set({
1605
+ gl: gl = createRendererInstance(glConfig, canvas)
1606
+ }); // Set up raycaster (one time only!)
1607
+
1608
+ let raycaster = state.raycaster;
1609
+ if (!raycaster) state.set({
1610
+ raycaster: raycaster = new THREE__namespace.Raycaster()
1611
+ }); // Set raycaster options
1636
1612
 
1637
- if (!size) {
1638
- var _canvas$parentElement, _canvas$parentElement2, _canvas$parentElement3, _canvas$parentElement4;
1613
+ const {
1614
+ params,
1615
+ ...options
1616
+ } = raycastOptions || {};
1617
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, {
1618
+ enabled: true,
1619
+ ...options
1620
+ });
1621
+ if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
1622
+ params: { ...raycaster.params,
1623
+ ...params
1624
+ }
1625
+ }); // Create default camera (one time only!)
1639
1626
 
1640
- size = {
1641
- width: (_canvas$parentElement = (_canvas$parentElement2 = canvas.parentElement) == null ? void 0 : _canvas$parentElement2.clientWidth) != null ? _canvas$parentElement : 0,
1642
- height: (_canvas$parentElement3 = (_canvas$parentElement4 = canvas.parentElement) == null ? void 0 : _canvas$parentElement4.clientHeight) != null ? _canvas$parentElement3 : 0
1643
- };
1644
- }
1627
+ if (!state.camera) {
1628
+ const isCamera = cameraOptions instanceof THREE__namespace.Camera;
1629
+ const camera = isCamera ? cameraOptions : orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
1630
+
1631
+ if (!isCamera) {
1632
+ camera.position.z = 5;
1633
+ if (cameraOptions) applyProps(camera, cameraOptions); // Always look at center by default
1634
+
1635
+ if (!(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
1636
+ }
1645
1637
 
1646
- let root = roots.get(canvas);
1647
- let fiber = root == null ? void 0 : root.fiber;
1648
- let store = root == null ? void 0 : root.store;
1649
- let state = (_store = store) == null ? void 0 : _store.getState();
1638
+ state.set({
1639
+ camera
1640
+ });
1641
+ } // Set up XR (one time only!)
1650
1642
 
1651
- if (fiber && state) {
1652
- // When a root was found, see if any fundamental props must be changed or exchanged
1653
- // Check pixelratio
1654
- if (props.dpr !== undefined && state.viewport.dpr !== calculateDpr(props.dpr)) state.setDpr(props.dpr); // Check size
1655
1643
 
1656
- if (state.size.width !== size.width || state.size.height !== size.height) state.setSize(size.width, size.height); // Check frameloop
1644
+ if (!state.xr) {
1645
+ // Handle frame behavior in WebXR
1646
+ const handleXRFrame = (timestamp, frame) => {
1647
+ const state = store.getState();
1648
+ if (state.frameloop === 'never') return;
1649
+ advance(timestamp, true, state, frame);
1650
+ }; // Toggle render switching on session
1657
1651
 
1658
- if (state.frameloop !== props.frameloop) state.setFrameloop(props.frameloop); // For some props we want to reset the entire root
1659
- // Changes to the color-space
1660
1652
 
1661
- const linearChanged = props.linear !== state.internal.lastProps.linear;
1653
+ const handleSessionChange = () => {
1654
+ const gl = store.getState().gl;
1655
+ gl.xr.enabled = gl.xr.isPresenting; // @ts-expect-error
1656
+ // WebXRManager's signature is incorrect.
1657
+ // See: https://github.com/pmndrs/react-three-fiber/pull/2017#discussion_r790134505
1662
1658
 
1663
- if (linearChanged) {
1664
- unmountComponentAtNode(canvas);
1665
- fiber = undefined;
1666
- }
1667
- }
1659
+ gl.xr.setAnimationLoop(gl.xr.isPresenting ? handleXRFrame : null);
1660
+ }; // WebXR session manager
1668
1661
 
1669
- if (!fiber) {
1670
- // If no root has been found, make one
1671
- // Create gl
1672
- const glRenderer = createRendererInstance(gl, canvas); // Create store
1673
1662
 
1674
- store = createStore(applyProps, invalidate, advance, {
1675
- gl: glRenderer,
1676
- size,
1677
- ...props
1678
- });
1679
- const state = store.getState(); // Create renderer
1663
+ const xr = {
1664
+ connect() {
1665
+ const gl = store.getState().gl;
1666
+ gl.xr.addEventListener('sessionstart', handleSessionChange);
1667
+ gl.xr.addEventListener('sessionend', handleSessionChange);
1668
+ },
1680
1669
 
1681
- fiber = reconciler.createContainer(store, constants.ConcurrentRoot, false, null); // Map it
1670
+ disconnect() {
1671
+ const gl = store.getState().gl;
1672
+ gl.xr.removeEventListener('sessionstart', handleSessionChange);
1673
+ gl.xr.removeEventListener('sessionend', handleSessionChange);
1674
+ }
1682
1675
 
1683
- roots.set(canvas, {
1684
- fiber,
1685
- store
1686
- }); // Store events internally
1676
+ }; // Subscribe to WebXR session events
1687
1677
 
1688
- if (events) state.set({
1689
- events: events(store)
1678
+ if (gl.xr) xr.connect();
1679
+ state.set({
1680
+ xr
1690
1681
  });
1691
- }
1682
+ } // Set shadowmap
1692
1683
 
1693
- if (store && fiber) {
1694
- reconciler.updateContainer( /*#__PURE__*/React__namespace.createElement(Provider, {
1695
- store: store,
1696
- element: element,
1697
- onCreated: onCreated,
1698
- target: canvas
1699
- }), fiber, null, () => undefined);
1700
- return store;
1701
- } else {
1702
- throw 'Error creating root!';
1703
- }
1684
+
1685
+ if (gl.shadowMap) {
1686
+ const isBoolean = is.boo(shadows);
1687
+
1688
+ if (isBoolean && gl.shadowMap.enabled !== shadows || !is.equ(shadows, gl.shadowMap, shallowLoose)) {
1689
+ const old = gl.shadowMap.enabled;
1690
+ gl.shadowMap.enabled = !!shadows;
1691
+ if (!isBoolean) Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1692
+ if (old !== gl.shadowMap.enabled) gl.shadowMap.needsUpdate = true;
1693
+ }
1694
+ } // Set color management
1695
+
1696
+
1697
+ const outputEncoding = linear ? THREE__namespace.LinearEncoding : THREE__namespace.sRGBEncoding;
1698
+ const toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1699
+ if (gl.outputEncoding !== outputEncoding) gl.outputEncoding = outputEncoding;
1700
+ if (gl.toneMapping !== toneMapping) gl.toneMapping = toneMapping; // Set gl props
1701
+
1702
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig); // Store events internally
1703
+
1704
+ if (events && !state.events.handlers) state.set({
1705
+ events: events(store)
1706
+ }); // Check pixelratio
1707
+
1708
+ if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr); // Check size, allow it to take on container bounds initially
1709
+
1710
+ size = size || {
1711
+ width: (_canvas$parentElement = (_canvas$parentElement2 = canvas.parentElement) == null ? void 0 : _canvas$parentElement2.clientWidth) != null ? _canvas$parentElement : 0,
1712
+ height: (_canvas$parentElement3 = (_canvas$parentElement4 = canvas.parentElement) == null ? void 0 : _canvas$parentElement4.clientHeight) != null ? _canvas$parentElement3 : 0
1713
+ };
1714
+ if (!is.equ(size, state.size, shallowLoose)) state.setSize(size.width, size.height); // Check frameloop
1715
+
1716
+ if (state.frameloop !== frameloop) state.setFrameloop(frameloop); // Check pointer missed
1717
+
1718
+ if (!state.onPointerMissed) state.set({
1719
+ onPointerMissed
1720
+ }); // Check performance
1721
+
1722
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1723
+ performance: { ...state.performance,
1724
+ ...performance
1725
+ }
1726
+ })); // Set locals
1727
+
1728
+ onCreated = onCreatedCallback;
1729
+ configured = true;
1730
+ return this;
1731
+ },
1732
+
1733
+ render(element) {
1734
+ // The root has to be configured before it can be rendered
1735
+ if (!configured) this.configure();
1736
+ reconciler.updateContainer( /*#__PURE__*/React__namespace.createElement(Provider, {
1737
+ store: store,
1738
+ element: element,
1739
+ onCreated: onCreated,
1740
+ target: canvas
1741
+ }), fiber, null, () => undefined);
1742
+ return store;
1704
1743
  },
1705
- unmount: () => unmountComponentAtNode(canvas)
1744
+
1745
+ unmount() {
1746
+ unmountComponentAtNode(canvas);
1747
+ }
1748
+
1706
1749
  };
1707
1750
  }
1708
1751
 
1709
1752
  function render(element, canvas, config = {}) {
1710
1753
  console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
1711
- return createRoot(canvas, config).render(element);
1754
+ const root = createRoot(canvas);
1755
+ root.configure(config);
1756
+ return root.render(element);
1712
1757
  }
1713
1758
 
1714
1759
  function Provider({
@@ -1751,7 +1796,7 @@ function unmountComponentAtNode(canvas, callback) {
1751
1796
  state.events.disconnect == null ? void 0 : state.events.disconnect();
1752
1797
  (_state$gl = state.gl) == null ? void 0 : (_state$gl$renderLists = _state$gl.renderLists) == null ? void 0 : _state$gl$renderLists.dispose == null ? void 0 : _state$gl$renderLists.dispose();
1753
1798
  (_state$gl2 = state.gl) == null ? void 0 : _state$gl2.forceContextLoss == null ? void 0 : _state$gl2.forceContextLoss();
1754
- if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.internal.xr.disconnect();
1799
+ if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.xr.disconnect();
1755
1800
  dispose(state);
1756
1801
  roots.delete(canvas);
1757
1802
  if (callback) callback(canvas);
@@ -1782,7 +1827,6 @@ exports.addEffect = addEffect;
1782
1827
  exports.addTail = addTail;
1783
1828
  exports.advance = advance;
1784
1829
  exports.applyProps = applyProps;
1785
- exports.buildGraph = buildGraph;
1786
1830
  exports.context = context;
1787
1831
  exports.createEvents = createEvents;
1788
1832
  exports.createPortal = createPortal;
@@ -1790,7 +1834,6 @@ exports.createRoot = createRoot;
1790
1834
  exports.dispose = dispose;
1791
1835
  exports.extend = extend;
1792
1836
  exports.invalidate = invalidate;
1793
- exports.is = is;
1794
1837
  exports.omit = omit;
1795
1838
  exports.pick = pick;
1796
1839
  exports.reconciler = reconciler;