@react-three/fiber 8.0.0-beta-05 → 8.0.0-beta.10

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.
@@ -1,12 +1,12 @@
1
1
  'use strict';
2
2
 
3
- var React = require('react');
4
- var suspendReact = require('suspend-react');
5
3
  var THREE = require('three');
4
+ var React = require('react');
6
5
  var constants = require('react-reconciler/constants');
7
6
  var create = require('zustand');
8
7
  var Reconciler = require('react-reconciler');
9
8
  var scheduler = require('scheduler');
9
+ var suspendReact = require('suspend-react');
10
10
 
11
11
  function _interopDefault (e) { return e && e.__esModule ? e : { 'default': e }; }
12
12
 
@@ -30,8 +30,8 @@ function _interopNamespace(e) {
30
30
  return Object.freeze(n);
31
31
  }
32
32
 
33
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
34
33
  var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
34
+ var React__namespace = /*#__PURE__*/_interopNamespace(React);
35
35
  var create__default = /*#__PURE__*/_interopDefault(create);
36
36
  var Reconciler__default = /*#__PURE__*/_interopDefault(Reconciler);
37
37
 
@@ -44,6 +44,15 @@ const isDiffSet = def => def && !!def.memoized && !!def.changes;
44
44
  function calculateDpr(dpr) {
45
45
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
46
46
  }
47
+ /**
48
+ * Returns instance root state
49
+ */
50
+
51
+ const getRootState = obj => {
52
+ var _r3f;
53
+
54
+ return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
55
+ };
47
56
  /**
48
57
  * Picks or omits keys from an object
49
58
  * `omit` will filter out keys, and otherwise cherry-pick them.
@@ -53,11 +62,7 @@ function filterKeys(obj, omit, ...keys) {
53
62
  const keysToSelect = new Set(keys);
54
63
  return Object.entries(obj).reduce((acc, [key, value]) => {
55
64
  const shouldInclude = !omit;
56
-
57
- if (keysToSelect.has(key) === shouldInclude) {
58
- acc[key] = value;
59
- }
60
-
65
+ if (keysToSelect.has(key) === shouldInclude) acc[key] = value;
61
66
  return acc;
62
67
  }, {});
63
68
  }
@@ -70,31 +75,46 @@ const pick = (obj, keys) => filterKeys(obj, false, ...keys);
70
75
  * Clones an object and prunes or omits keys.
71
76
  */
72
77
 
73
- const omit = (obj, keys) => filterKeys(obj, true, ...keys); // A collection of compare functions
74
-
78
+ const omit = (obj, keys) => filterKeys(obj, true, ...keys);
79
+ // A collection of compare functions
75
80
  const is = {
76
81
  obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
77
82
  fun: a => typeof a === 'function',
78
83
  str: a => typeof a === 'string',
79
84
  num: a => typeof a === 'number',
85
+ boo: a => typeof a === 'boolean',
80
86
  und: a => a === void 0,
81
87
  arr: a => Array.isArray(a),
82
88
 
83
- equ(a, b) {
89
+ equ(a, b, {
90
+ arrays = 'shallow',
91
+ objects = 'reference',
92
+ strict = true
93
+ } = {}) {
84
94
  // Wrong type or one of the two undefined, doesn't match
85
95
  if (typeof a !== typeof b || !!a !== !!b) return false; // Atomic, just compare a against b
86
96
 
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
97
+ if (is.str(a) || is.num(a)) return a === b;
98
+ const isObj = is.obj(a);
99
+ if (isObj && objects === 'reference') return a === b;
100
+ const isArr = is.arr(a);
101
+ if (isArr && arrays === 'reference') return a === b; // Array or Object, shallow compare first to see if it's a match
88
102
 
89
- if (is.arr(a) && a == b) return true; // Last resort, go through keys
103
+ if ((isArr || isObj) && a === b) return true; // Last resort, go through keys
90
104
 
91
105
  let i;
92
106
 
93
107
  for (i in a) if (!(i in b)) return false;
94
108
 
95
- for (i in b) if (a[i] !== b[i]) return false;
109
+ for (i in strict ? b : a) if (a[i] !== b[i]) return false;
110
+
111
+ if (is.und(i)) {
112
+ if (isArr && a.length === 0 && b.length === 0) return true;
113
+ if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
114
+ if (a !== b) return false;
115
+ }
96
116
 
97
- return is.und(i) ? a === b : true;
117
+ return true;
98
118
  }
99
119
 
100
120
  }; // Collects nodes and materials from a THREE.Object3D
@@ -129,7 +149,9 @@ function prepare(object, state) {
129
149
 
130
150
  if (state != null && state.primitive || !instance.__r3f) {
131
151
  instance.__r3f = {
152
+ type: '',
132
153
  root: null,
154
+ previousAttach: null,
133
155
  memoizedProps: {},
134
156
  eventCount: 0,
135
157
  handlers: {},
@@ -157,41 +179,44 @@ function resolve(instance, key) {
157
179
  target,
158
180
  key
159
181
  };
160
- }
182
+ } // Checks if a dash-cased string ends with an integer
183
+
161
184
 
185
+ const INDEX_REGEX = /-\d+$/;
162
186
  function attach(parent, child, type) {
163
187
  if (is.str(type)) {
188
+ // If attaching into an array (foo-0), create one
189
+ if (INDEX_REGEX.test(type)) {
190
+ const root = type.replace(INDEX_REGEX, '');
191
+ const {
192
+ target,
193
+ key
194
+ } = resolve(parent, root);
195
+ if (!Array.isArray(target[key])) target[key] = [];
196
+ }
197
+
164
198
  const {
165
199
  target,
166
200
  key
167
201
  } = resolve(parent, type);
168
- parent.__r3f.previousAttach = target[key];
202
+ child.__r3f.previousAttach = target[key];
169
203
  target[key] = child;
170
- } else if (is.arr(type)) {
171
- const [attach] = type;
172
- if (is.str(attach)) parent[attach](child);else if (is.fun(attach)) attach(parent, child);
173
- }
204
+ } else child.__r3f.previousAttach = type(parent, child);
174
205
  }
175
206
  function detach(parent, child, type) {
207
+ var _child$__r3f, _child$__r3f2;
208
+
176
209
  if (is.str(type)) {
177
210
  const {
178
211
  target,
179
212
  key
180
213
  } = resolve(parent, type);
181
- target[key] = parent.__r3f.previousAttach;
182
- } else if (is.arr(type)) {
183
- const [, detach] = type;
184
- if (is.str(detach)) parent[detach](child);else if (is.fun(detach)) detach(parent, child);
185
- }
186
- } // Shallow check arrays, but check objects atomically
214
+ target[key] = child.__r3f.previousAttach;
215
+ } else (_child$__r3f = child.__r3f) == null ? void 0 : _child$__r3f.previousAttach == null ? void 0 : _child$__r3f.previousAttach(parent, child);
187
216
 
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;
217
+ (_child$__r3f2 = child.__r3f) == null ? true : delete _child$__r3f2.previousAttach;
192
218
  } // This function prepares a set of changes to be applied to the instance
193
219
 
194
-
195
220
  function diffProps(instance, {
196
221
  children: cN,
197
222
  key: kN,
@@ -223,7 +248,7 @@ function diffProps(instance, {
223
248
  // Bail out on primitive object
224
249
  if ((_instance$__r3f2 = instance.__r3f) != null && _instance$__r3f2.primitive && key === 'object') return; // When props match bail out
225
250
 
226
- if (checkShallow(value, previous[key])) return; // Collect handlers and bail out
251
+ if (is.equ(value, previous[key])) return; // Collect handlers and bail out
227
252
 
228
253
  if (/^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/.test(key)) return changes.push([key, value, true, []]); // Split dashed props
229
254
 
@@ -277,11 +302,11 @@ function applyProps$1(instance, data) {
277
302
  if (value === DEFAULT + 'remove') {
278
303
  if (targetProp && targetProp.constructor) {
279
304
  // use the prop constructor to find the default it should be
280
- value = new targetProp.constructor(memoized.args);
305
+ value = new targetProp.constructor(...memoized.args);
281
306
  } else if (currentInstance.constructor) {
282
307
  // create a blank slate of the instance and copy the particular parameter.
283
308
  // @ts-ignore
284
- const defaultClassCall = new currentInstance.constructor(currentInstance.__r3f.memoizedProps.args);
309
+ const defaultClassCall = new currentInstance.constructor(...currentInstance.__r3f.memoizedProps.args);
285
310
  value = defaultClassCall[targetProp]; // destory the instance
286
311
 
287
312
  if (defaultClassCall.dispose) defaultClassCall.dispose(); // instance does not have constructor, just set it to 0
@@ -309,23 +334,12 @@ function applyProps$1(instance, data) {
309
334
 
310
335
  if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
311
336
  else if (targetProp instanceof THREE__namespace.Layers && value instanceof THREE__namespace.Layers) targetProp.mask = value.mask; // Otherwise just set ...
312
- else targetProp.set(value); // Auto-convert sRGB colors, for now ...
313
- // https://github.com/pmndrs/react-three-fiber/issues/344
314
-
315
- if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
337
+ else targetProp.set(value);
316
338
  } // Else, just overwrite the value
317
339
 
318
- } else {
319
- currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
320
- // https://github.com/pmndrs/react-three-fiber/issues/344
321
-
322
- if (!rootState.linear && currentInstance[key] instanceof THREE__namespace.Texture) {
323
- currentInstance[key].encoding = THREE__namespace.sRGBEncoding;
324
- }
325
- }
340
+ } else currentInstance[key] = value;
326
341
 
327
342
  invalidateInstance(instance);
328
- return instance;
329
343
  });
330
344
 
331
345
  if (localState.parent && rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
@@ -338,6 +352,7 @@ function applyProps$1(instance, data) {
338
352
 
339
353
 
340
354
  if (changes.length && instance.parent) updateInstance(instance);
355
+ return instance;
341
356
  }
342
357
  function invalidateInstance(instance) {
343
358
  var _instance$__r3f4, _instance$__r3f4$root;
@@ -401,6 +416,7 @@ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
401
416
 
402
417
  function removeInteractivity(store, object) {
403
418
  const {
419
+ events,
404
420
  internal
405
421
  } = store.getState(); // Removes every trace of an object from the data store
406
422
 
@@ -408,6 +424,7 @@ function removeInteractivity(store, object) {
408
424
  internal.initialHits = internal.initialHits.filter(o => o !== object);
409
425
  internal.hovered.forEach((value, key) => {
410
426
  if (value.eventObject === object || value.object === object) {
427
+ // Clear out intersects, they are outdated by now
411
428
  internal.hovered.delete(key);
412
429
  }
413
430
  });
@@ -417,31 +434,8 @@ function removeInteractivity(store, object) {
417
434
  }
418
435
  function createEvents(store) {
419
436
  const temp = new THREE__namespace.Vector3();
420
- /** Sets up defaultRaycaster */
421
-
422
- function prepareRay(event) {
423
- var _customOffsets$offset, _customOffsets$offset2, _customOffsets$width, _customOffsets$height;
424
-
425
- const state = store.getState();
426
- const {
427
- raycaster,
428
- mouse,
429
- camera,
430
- size
431
- } = state; // https://github.com/pmndrs/react-three-fiber/pull/782
432
- // Events trigger outside of canvas when moved
433
-
434
- const customOffsets = raycaster.computeOffsets == null ? void 0 : raycaster.computeOffsets(event, state);
435
- const offsetX = (_customOffsets$offset = customOffsets == null ? void 0 : customOffsets.offsetX) != null ? _customOffsets$offset : event.offsetX;
436
- const offsetY = (_customOffsets$offset2 = customOffsets == null ? void 0 : customOffsets.offsetY) != null ? _customOffsets$offset2 : event.offsetY;
437
- const width = (_customOffsets$width = customOffsets == null ? void 0 : customOffsets.width) != null ? _customOffsets$width : size.width;
438
- const height = (_customOffsets$height = customOffsets == null ? void 0 : customOffsets.height) != null ? _customOffsets$height : size.height;
439
- mouse.set(offsetX / width * 2 - 1, -(offsetY / height) * 2 + 1);
440
- raycaster.setFromCamera(mouse, camera);
441
- }
442
437
  /** Calculates delta */
443
438
 
444
-
445
439
  function calculateDistance(event) {
446
440
  const {
447
441
  internal
@@ -461,55 +455,70 @@ function createEvents(store) {
461
455
  }));
462
456
  }
463
457
 
464
- function intersect(filter) {
458
+ function intersect(event, filter) {
465
459
  const state = store.getState();
466
- const {
467
- raycaster,
468
- internal
469
- } = state; // Skip event handling when noEvents is set
470
-
471
- if (!raycaster.enabled) return [];
472
- const seen = new Set();
460
+ const duplicates = new Set();
473
461
  const intersections = []; // Allow callers to eliminate event objects
474
462
 
475
- const eventsObjects = filter ? filter(internal.interaction) : internal.interaction; // Intersect known handler objects and filter against duplicates
463
+ const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction; // Reset all raycaster cameras to undefined
464
+
465
+ eventsObjects.forEach(obj => {
466
+ const state = getRootState(obj);
467
+
468
+ if (state) {
469
+ state.raycaster.camera = undefined;
470
+ }
471
+ }); // Collect events
472
+
473
+ let hits = eventsObjects // Intersect objects
474
+ .flatMap(obj => {
475
+ const state = getRootState(obj); // Skip event handling when noEvents is set, or when the raycasters camera is null
476
+
477
+ if (!state || !state.events.enabled || state.raycaster.camera === null) return []; // When the camera is undefined we have to call the event layers update function
478
+
479
+ if (state.raycaster.camera === undefined) {
480
+ var _state$previousRoot;
476
481
 
477
- let intersects = raycaster.intersectObjects(eventsObjects, true).filter(item => {
482
+ state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState()); // If the camera is still undefined we have to skip this layer entirely
483
+
484
+ if (state.raycaster.camera === undefined) state.raycaster.camera = null;
485
+ } // Intersect object by object
486
+
487
+
488
+ return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
489
+ }) // Sort by event priority and distance
490
+ .sort((a, b) => {
491
+ const aState = getRootState(a.object);
492
+ const bState = getRootState(b.object);
493
+ if (!aState || !bState) return 0;
494
+ return bState.events.priority - aState.events.priority || a.distance - b.distance;
495
+ }) // Filter out duplicates
496
+ .filter(item => {
478
497
  const id = makeId(item);
479
- if (seen.has(id)) return false;
480
- seen.add(id);
498
+ if (duplicates.has(id)) return false;
499
+ duplicates.add(id);
481
500
  return true;
482
501
  }); // https://github.com/mrdoob/three.js/issues/16031
483
- // Allow custom userland intersect sort order
502
+ // Allow custom userland intersect sort order, this likely only makes sense on the root filter
484
503
 
485
- if (raycaster.filter) intersects = raycaster.filter(intersects, state);
504
+ if (state.events.filter) hits = state.events.filter(hits, state); // Bubble up the events, find the event source (eventObject)
486
505
 
487
- for (const intersect of intersects) {
488
- let eventObject = intersect.object; // Bubble event up
506
+ for (const hit of hits) {
507
+ let eventObject = hit.object; // Bubble event up
489
508
 
490
509
  while (eventObject) {
491
510
  var _r3f2;
492
511
 
493
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
512
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...hit,
494
513
  eventObject
495
514
  });
496
515
  eventObject = eventObject.parent;
497
516
  }
498
- }
499
-
500
- return intersections;
501
- }
502
- /** Creates filtered intersects and returns an array of positive hits */
517
+ } // If the interaction is captured, make all capturing targets part of the intersect.
503
518
 
504
519
 
505
- function patchIntersects(intersections, event) {
506
- const {
507
- internal
508
- } = store.getState(); // If the interaction is captured, make all capturing targets part of the
509
- // intersect.
510
-
511
- if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
512
- for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
520
+ if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
521
+ for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
513
522
  intersections.push(captureData.intersection);
514
523
  }
515
524
  }
@@ -522,13 +531,13 @@ function createEvents(store) {
522
531
  function handleIntersects(intersections, event, delta, callback) {
523
532
  const {
524
533
  raycaster,
525
- mouse,
534
+ pointer,
526
535
  camera,
527
536
  internal
528
537
  } = store.getState(); // If anything has been found, forward it to the event listeners
529
538
 
530
539
  if (intersections.length) {
531
- const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
540
+ const unprojectedPoint = temp.set(pointer.x, pointer.y, 0).unproject(camera);
532
541
  const localState = {
533
542
  stopped: false
534
543
  };
@@ -579,8 +588,7 @@ function createEvents(store) {
579
588
 
580
589
  let raycastEvent = { ...hit,
581
590
  ...extractEventProps,
582
- spaceX: mouse.x,
583
- spaceY: mouse.y,
591
+ pointer,
584
592
  intersections,
585
593
  stopped: localState.stopped,
586
594
  delta,
@@ -617,8 +625,6 @@ function createEvents(store) {
617
625
  setPointerCapture,
618
626
  releasePointerCapture
619
627
  },
620
- sourceEvent: event,
621
- // deprecated
622
628
  nativeEvent: event
623
629
  }; // Call subscribers
624
630
 
@@ -683,14 +689,15 @@ function createEvents(store) {
683
689
  const {
684
690
  onPointerMissed,
685
691
  internal
686
- } = store.getState();
687
- prepareRay(event);
692
+ } = store.getState(); //prepareRay(event)
693
+
688
694
  internal.lastEvent.current = event; // Get fresh intersects
689
695
 
690
696
  const isPointerMove = name === 'onPointerMove';
691
697
  const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
692
- const filter = isPointerMove ? filterPointerEvents : undefined;
693
- const hits = patchIntersects(intersect(filter), event);
698
+ const filter = isPointerMove ? filterPointerEvents : undefined; //const hits = patchIntersects(intersect(filter), event)
699
+
700
+ const hits = intersect(event, filter);
694
701
  const delta = isClickEvent ? calculateDistance(event) : 0; // Save initial coordinates on pointer-down
695
702
 
696
703
  if (name === 'onPointerDown') {
@@ -773,23 +780,6 @@ function createEvents(store) {
773
780
  };
774
781
  }
775
782
 
776
- // Type guard to tell a store from a portal
777
- const isStore = def => def && !!def.getState;
778
-
779
- const getContainer = (container, child) => {
780
- var _container$__r3f$root, _container$__r3f;
781
-
782
- return {
783
- // If the container is not a root-store then it must be a THREE.Object3D into which part of the
784
- // scene is portalled into. Now there can be two variants of this, either that object is part of
785
- // the regular jsx tree, in which case it already has __r3f with a valid root attached, or it lies
786
- // outside react, in which case we must take the root of the child that is about to be attached to it.
787
- root: isStore(container) ? container : (_container$__r3f$root = (_container$__r3f = container.__r3f) == null ? void 0 : _container$__r3f.root) != null ? _container$__r3f$root : child.__r3f.root,
788
- // The container is the eventual target into which objects are mounted, it has to be a THREE.Object3D
789
- container: isStore(container) ? container.getState().scene : container
790
- };
791
- };
792
-
793
783
  let catalogue = {};
794
784
 
795
785
  let extend = objects => void (catalogue = { ...catalogue,
@@ -801,22 +791,9 @@ function createRenderer(roots, getEventPriority) {
801
791
  args = [],
802
792
  attach,
803
793
  ...props
804
- }, root, hostContext, internalInstanceHandle) {
794
+ }, root) {
805
795
  let name = `${type[0].toUpperCase()}${type.slice(1)}`;
806
- let instance; // https://github.com/facebook/react/issues/17147
807
- // Portals do not give us a root, they are themselves treated as a root by the reconciler
808
- // In order to figure out the actual root we have to climb through fiber internals :(
809
-
810
- if (!isStore(root) && internalInstanceHandle) {
811
- const fn = node => {
812
- if (!node.return) return node.stateNode && node.stateNode.containerInfo;else return fn(node.return);
813
- };
814
-
815
- root = fn(internalInstanceHandle);
816
- } // Assert that by now we have a valid root
817
-
818
-
819
- if (!root || !isStore(root)) throw `No valid root for ${name}!`; // Auto-attach geometries and materials
796
+ let instance; // Auto-attach geometries and materials
820
797
 
821
798
  if (attach === undefined) {
822
799
  if (name.endsWith('Geometry')) attach = 'geometry';else if (name.endsWith('Material')) attach = 'material';
@@ -826,6 +803,7 @@ function createRenderer(roots, getEventPriority) {
826
803
  if (props.object === undefined) throw `Primitives without 'object' are invalid!`;
827
804
  const object = props.object;
828
805
  instance = prepare(object, {
806
+ type,
829
807
  root,
830
808
  attach,
831
809
  primitive: true
@@ -842,19 +820,21 @@ function createRenderer(roots, getEventPriority) {
842
820
  // Append memoized props with args so it's not forgotten
843
821
 
844
822
  instance = prepare(new target(...args), {
823
+ type,
845
824
  root,
846
825
  attach,
847
826
  // TODO: Figure out what this is for
848
827
  memoizedProps: {
849
- args: args.length === 0 ? null : args
828
+ args
850
829
  }
851
830
  });
852
831
  } // It should NOT call onUpdate on object instanciation, because it hasn't been added to the
853
832
  // view yet. If the callback relies on references for instance, they won't be ready yet, this is
854
833
  // why it passes "true" here
834
+ // There is no reason to apply props to injects
855
835
 
856
836
 
857
- applyProps$1(instance, props);
837
+ if (name !== 'inject') applyProps$1(instance, props);
858
838
  return instance;
859
839
  }
860
840
 
@@ -976,11 +956,11 @@ function createRenderer(roots, getEventPriority) {
976
956
  }
977
957
 
978
958
  function switchInstance(instance, type, newProps, fiber) {
979
- var _instance$__r3f;
959
+ var _instance$__r3f, _instance$__r3f2;
980
960
 
981
961
  const parent = (_instance$__r3f = instance.__r3f) == null ? void 0 : _instance$__r3f.parent;
982
962
  if (!parent) return;
983
- const newInstance = createInstance(type, newProps, instance.__r3f.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
963
+ const newInstance = createInstance(type, newProps, (_instance$__r3f2 = instance.__r3f) == null ? void 0 : _instance$__r3f2.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
984
964
  // When args change the instance has to be re-constructed, which then
985
965
  // forces r3f to re-parent the children and non-scene objects
986
966
  // This can not include primitives, which should not have declarative children
@@ -1013,19 +993,38 @@ function createRenderer(roots, getEventPriority) {
1013
993
  }
1014
994
 
1015
995
  const reconciler = Reconciler__default['default']({
1016
- appendChildToContainer: (parentInstance, child) => {
1017
- const {
1018
- container,
1019
- root
1020
- } = getContainer(parentInstance, child); // Link current root to the default scene
996
+ createInstance,
997
+ removeChild,
998
+ appendChild,
999
+ appendInitialChild: appendChild,
1000
+ insertBefore,
1001
+ supportsMicrotask: true,
1002
+ warnsIfNotActing: true,
1003
+ supportsMutation: true,
1004
+ isPrimaryRenderer: false,
1005
+ noTimeout: -1,
1006
+ appendChildToContainer: (container, child) => {
1007
+ const scene = container.getState().scene; // Link current root to the default scene
1008
+
1009
+ scene.__r3f.root = container;
1010
+ appendChild(scene, child);
1011
+ },
1012
+ removeChildFromContainer: (container, child) => removeChild(container.getState().scene, child),
1013
+ insertInContainerBefore: (container, child, beforeChild) => insertBefore(container.getState().scene, child, beforeChild),
1014
+ getRootHostContext: () => null,
1015
+ getChildHostContext: parentHostContext => parentHostContext,
1021
1016
 
1022
- container.__r3f.root = root;
1023
- appendChild(container, child);
1017
+ finalizeInitialChildren(instance) {
1018
+ var _instance$__r3f3;
1019
+
1020
+ const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {}; // https://github.com/facebook/react/issues/20271
1021
+ // Returning true will trigger commitMount
1022
+
1023
+ return !!localState.handlers;
1024
1024
  },
1025
- removeChildFromContainer: (parentInstance, child) => removeChild(getContainer(parentInstance, child).container, child),
1026
- insertInContainerBefore: (parentInstance, child, beforeChild) => insertBefore(getContainer(parentInstance, child).container, child, beforeChild),
1027
1025
 
1028
1026
  prepareUpdate(instance, type, oldProps, newProps) {
1027
+ // Create diff-sets
1029
1028
  if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) {
1030
1029
  return [true];
1031
1030
  } else {
@@ -1058,40 +1057,58 @@ function createRenderer(roots, getEventPriority) {
1058
1057
  else applyProps$1(instance, diff);
1059
1058
  },
1060
1059
 
1060
+ commitMount(instance, type, props, int) {
1061
+ var _instance$__r3f4;
1062
+
1063
+ // https://github.com/facebook/react/issues/20271
1064
+ // This will make sure events are only added once to the central container
1065
+ const localState = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1066
+
1067
+ if (instance.raycast && localState.handlers && localState.eventCount) {
1068
+ instance.__r3f.root.getState().internal.interaction.push(instance);
1069
+ }
1070
+ },
1071
+
1072
+ getPublicInstance: instance => instance,
1073
+ shouldDeprioritizeSubtree: () => false,
1074
+ prepareForCommit: () => null,
1075
+ preparePortalMount: container => prepare(container.getState().scene),
1076
+ resetAfterCommit: () => {},
1077
+ shouldSetTextContent: () => false,
1078
+ clearContainer: () => false,
1079
+ detachDeletedInstance: () => {},
1080
+ createTextInstance: () => {},
1081
+
1061
1082
  hideInstance(instance) {
1062
- var _instance$__r3f2;
1083
+ var _instance$__r3f5;
1063
1084
 
1064
1085
  // Deatch while the instance is hidden
1065
1086
  const {
1066
1087
  attach: type,
1067
1088
  parent
1068
- } = (_instance$__r3f2 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f2 : {};
1089
+ } = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1069
1090
  if (type && parent) detach(parent, instance, type);
1070
1091
  if (instance.isObject3D) instance.visible = false;
1071
1092
  invalidateInstance(instance);
1072
1093
  },
1073
1094
 
1074
1095
  unhideInstance(instance, props) {
1075
- var _instance$__r3f3;
1096
+ var _instance$__r3f6;
1076
1097
 
1077
1098
  // Re-attach when the instance is unhidden
1078
1099
  const {
1079
1100
  attach: type,
1080
1101
  parent
1081
- } = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
1102
+ } = (_instance$__r3f6 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f6 : {};
1082
1103
  if (type && parent) attach(parent, instance, type);
1083
1104
  if (instance.isObject3D && props.visible == null || props.visible) instance.visible = true;
1084
1105
  invalidateInstance(instance);
1085
1106
  },
1086
1107
 
1087
- createInstance,
1088
- removeChild,
1089
- appendChild,
1090
- appendInitialChild: appendChild,
1091
- insertBefore,
1092
- warnsIfNotActing: true,
1093
- supportsMutation: true,
1094
- isPrimaryRenderer: false,
1108
+ hideTextInstance: () => {
1109
+ throw new Error('Text is not allowed in the R3F tree.');
1110
+ },
1111
+ // prettier-ignore
1095
1112
  getCurrentEventPriority: () => getEventPriority ? getEventPriority() : constants.DefaultEventPriority,
1096
1113
  // @ts-ignore
1097
1114
  now: typeof performance !== 'undefined' && is.fun(performance.now) ? performance.now : is.fun(Date.now) ? Date.now : undefined,
@@ -1100,47 +1117,7 @@ function createRenderer(roots, getEventPriority) {
1100
1117
  // @ts-ignore
1101
1118
  cancelTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
1102
1119
  setTimeout: is.fun(setTimeout) ? setTimeout : undefined,
1103
- clearTimeout: is.fun(clearTimeout) ? clearTimeout : undefined,
1104
- noTimeout: -1,
1105
- hideTextInstance: () => {
1106
- throw new Error('Text is not allowed in the R3F tree.');
1107
- },
1108
- // prettier-ignore
1109
- getPublicInstance: instance => instance,
1110
- getRootHostContext: () => null,
1111
- getChildHostContext: parentHostContext => parentHostContext,
1112
- createTextInstance: () => {},
1113
-
1114
- finalizeInitialChildren(instance) {
1115
- var _instance$__r3f4;
1116
-
1117
- // https://github.com/facebook/react/issues/20271
1118
- // Returning true will trigger commitMount
1119
- const localState = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1120
- return !!localState.handlers;
1121
- },
1122
-
1123
- commitMount(instance)
1124
- /*, type, props*/
1125
- {
1126
- var _instance$__r3f5;
1127
-
1128
- // https://github.com/facebook/react/issues/20271
1129
- // This will make sure events are only added once to the central container
1130
- const localState = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1131
-
1132
- if (instance.raycast && localState.handlers && localState.eventCount) {
1133
- instance.__r3f.root.getState().internal.interaction.push(instance);
1134
- }
1135
- },
1136
-
1137
- shouldDeprioritizeSubtree: () => false,
1138
- prepareForCommit: () => null,
1139
- preparePortalMount: containerInfo => prepare(containerInfo),
1140
- resetAfterCommit: () => {},
1141
- shouldSetTextContent: () => false,
1142
- clearContainer: () => false,
1143
- detachDeletedInstance: () => {}
1120
+ clearTimeout: is.fun(clearTimeout) ? clearTimeout : undefined
1144
1121
  });
1145
1122
  return {
1146
1123
  reconciler,
@@ -1152,63 +1129,8 @@ const isRenderer = def => !!(def != null && def.render);
1152
1129
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1153
1130
  const context = /*#__PURE__*/React__namespace.createContext(null);
1154
1131
 
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
-
1132
+ const createStore = (invalidate, advance) => {
1186
1133
  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
1134
  const position = new THREE__namespace.Vector3();
1213
1135
  const defaultTarget = new THREE__namespace.Vector3();
1214
1136
  const tempTarget = new THREE__namespace.Vector3();
@@ -1252,61 +1174,38 @@ const createStore = (applyProps, invalidate, advance, props) => {
1252
1174
  performance: { ...state.performance,
1253
1175
  current
1254
1176
  }
1255
- })); // Handle frame behavior in WebXR
1256
-
1257
-
1258
- const handleXRFrame = (timestamp, frame) => {
1259
- const state = get();
1260
- if (state.frameloop === 'never') return;
1261
- advance(timestamp, true, state, frame);
1262
- }; // Toggle render switching on session
1263
-
1264
-
1265
- const handleSessionChange = () => {
1266
- gl.xr.enabled = gl.xr.isPresenting; // @ts-expect-error
1267
- // WebXRManager's signature is incorrect.
1268
- // See: https://github.com/pmndrs/react-three-fiber/pull/2017#discussion_r790134505
1269
-
1270
- gl.xr.setAnimationLoop(gl.xr.isPresenting ? handleXRFrame : null);
1271
- }; // WebXR session manager
1272
-
1273
-
1274
- const xr = {
1275
- connect() {
1276
- gl.xr.addEventListener('sessionstart', handleSessionChange);
1277
- gl.xr.addEventListener('sessionend', handleSessionChange);
1278
- },
1279
-
1280
- disconnect() {
1281
- gl.xr.removeEventListener('sessionstart', handleSessionChange);
1282
- gl.xr.removeEventListener('sessionend', handleSessionChange);
1283
- }
1284
-
1285
- }; // Subscribe to WebXR session events
1177
+ }));
1286
1178
 
1287
- if (gl.xr) xr.connect();
1179
+ const pointer = new THREE__namespace.Vector2();
1288
1180
  return {
1289
- gl,
1290
1181
  set,
1291
1182
  get,
1183
+ // Mock objects that have to be configured
1184
+ gl: null,
1185
+ camera: null,
1186
+ raycaster: null,
1187
+ events: {
1188
+ priority: 1,
1189
+ enabled: true,
1190
+ connected: false
1191
+ },
1192
+ xr: null,
1292
1193
  invalidate: () => invalidate(get()),
1293
1194
  advance: (timestamp, runGlobalEffects) => advance(timestamp, runGlobalEffects, get()),
1294
- linear,
1295
- flat,
1195
+ linear: false,
1196
+ flat: false,
1296
1197
  scene: prepare(new THREE__namespace.Scene()),
1297
- camera,
1298
1198
  controls: null,
1299
- raycaster,
1300
- clock,
1301
- mouse: new THREE__namespace.Vector2(),
1302
- frameloop,
1303
- onPointerMissed,
1199
+ clock: new THREE__namespace.Clock(),
1200
+ pointer,
1201
+ mouse: pointer,
1202
+ frameloop: 'always',
1203
+ onPointerMissed: undefined,
1304
1204
  performance: {
1305
1205
  current: 1,
1306
1206
  min: 0.5,
1307
1207
  max: 1,
1308
1208
  debounce: 200,
1309
- ...performance,
1310
1209
  regress: () => {
1311
1210
  const state = get(); // Clear timeout
1312
1211
 
@@ -1322,8 +1221,8 @@ const createStore = (applyProps, invalidate, advance, props) => {
1322
1221
  height: 0
1323
1222
  },
1324
1223
  viewport: {
1325
- initialDpr,
1326
- dpr: initialDpr,
1224
+ initialDpr: 0,
1225
+ dpr: 0,
1327
1226
  width: 0,
1328
1227
  height: 0,
1329
1228
  aspect: 0,
@@ -1331,7 +1230,13 @@ const createStore = (applyProps, invalidate, advance, props) => {
1331
1230
  factor: 0,
1332
1231
  getCurrentViewport
1333
1232
  },
1233
+ setEvents: events => set(state => ({ ...state,
1234
+ events: { ...state.events,
1235
+ events
1236
+ }
1237
+ })),
1334
1238
  setSize: (width, height) => {
1239
+ const camera = get().camera;
1335
1240
  const size = {
1336
1241
  width,
1337
1242
  height
@@ -1343,22 +1248,35 @@ const createStore = (applyProps, invalidate, advance, props) => {
1343
1248
  }
1344
1249
  }));
1345
1250
  },
1346
- setDpr: dpr => set(state => ({
1347
- viewport: { ...state.viewport,
1348
- dpr: calculateDpr(dpr)
1251
+ setDpr: dpr => set(state => {
1252
+ const resolved = calculateDpr(dpr);
1253
+ return {
1254
+ viewport: { ...state.viewport,
1255
+ dpr: resolved,
1256
+ initialDpr: state.viewport.initialDpr || resolved
1257
+ }
1258
+ };
1259
+ }),
1260
+ setFrameloop: (frameloop = 'always') => {
1261
+ const clock = get().clock; // if frameloop === "never" clock.elapsedTime is updated using advance(timestamp)
1262
+
1263
+ clock.stop();
1264
+ clock.elapsedTime = 0;
1265
+
1266
+ if (frameloop !== 'never') {
1267
+ clock.start();
1268
+ clock.elapsedTime = 0;
1349
1269
  }
1350
- })),
1351
- setFrameloop: (frameloop = 'always') => set(() => ({
1352
- frameloop
1353
- })),
1354
- events: {
1355
- connected: false
1270
+
1271
+ set(() => ({
1272
+ frameloop
1273
+ }));
1356
1274
  },
1275
+ previousRoot: undefined,
1357
1276
  internal: {
1358
1277
  active: false,
1359
1278
  priority: 0,
1360
1279
  frames: 0,
1361
- lastProps: props,
1362
1280
  lastEvent: /*#__PURE__*/React__namespace.createRef(),
1363
1281
  interaction: [],
1364
1282
  hovered: new Map(),
@@ -1366,7 +1284,6 @@ const createStore = (applyProps, invalidate, advance, props) => {
1366
1284
  initialClick: [0, 0],
1367
1285
  initialHits: [],
1368
1286
  capturedMap: new Map(),
1369
- xr,
1370
1287
  subscribe: (ref, priority = 0) => {
1371
1288
  set(({
1372
1289
  internal
@@ -1410,13 +1327,13 @@ const createStore = (applyProps, invalidate, advance, props) => {
1410
1327
  camera,
1411
1328
  size,
1412
1329
  viewport,
1413
- internal
1330
+ gl
1414
1331
  } = rootState.getState();
1415
1332
 
1416
1333
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1417
1334
  // https://github.com/pmndrs/react-three-fiber/issues/92
1418
1335
  // Do not mess with the camera if it belongs to the user
1419
- if (!camera.manual && !(internal.lastProps.camera instanceof THREE__namespace.Camera)) {
1336
+ if (!camera.manual) {
1420
1337
  if (isOrthographicCamera(camera)) {
1421
1338
  camera.left = size.width / -2;
1422
1339
  camera.right = size.width / 2;
@@ -1438,9 +1355,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1438
1355
  oldSize = size;
1439
1356
  oldDpr = viewport.dpr;
1440
1357
  }
1441
- }); // Update size
1442
-
1443
- if (size) state.setSize(size.width, size.height); // Invalidate on any change
1358
+ }); // Invalidate on any change
1444
1359
 
1445
1360
  rootState.subscribe(state => invalidate(state)); // Return root state
1446
1361
 
@@ -1465,6 +1380,8 @@ function run(effects, timestamp) {
1465
1380
  for (i = 0; i < effects.length; i++) effects[i](timestamp);
1466
1381
  }
1467
1382
 
1383
+ let subscribers;
1384
+
1468
1385
  function render$1(timestamp, state, frame) {
1469
1386
  // Run local effects
1470
1387
  let delta = state.clock.getDelta(); // In frameloop='never' mode, clock times are updated using the provided timestamp
@@ -1476,7 +1393,9 @@ function render$1(timestamp, state, frame) {
1476
1393
  } // Call subscribers (useFrame)
1477
1394
 
1478
1395
 
1479
- for (i = 0; i < state.internal.subscribers.length; i++) state.internal.subscribers[i].ref.current(state, delta, frame); // Render content
1396
+ subscribers = state.internal.subscribers;
1397
+
1398
+ for (i = 0; i < subscribers.length; i++) subscribers[i].ref.current(state, delta, frame); // Render content
1480
1399
 
1481
1400
 
1482
1401
  if (!state.internal.priority && state.gl.render) state.gl.render(state.scene, state.camera); // Decrease frame count
@@ -1488,29 +1407,35 @@ function render$1(timestamp, state, frame) {
1488
1407
  function createLoop(roots) {
1489
1408
  let running = false;
1490
1409
  let repeat;
1410
+ let frame;
1411
+ let state;
1491
1412
 
1492
1413
  function loop(timestamp) {
1414
+ frame = requestAnimationFrame(loop);
1493
1415
  running = true;
1494
1416
  repeat = 0; // Run effects
1495
1417
 
1496
- run(globalEffects, timestamp); // Render all roots
1418
+ if (globalEffects.length) run(globalEffects, timestamp); // Render all roots
1497
1419
 
1498
1420
  roots.forEach(root => {
1499
1421
  var _state$gl$xr;
1500
1422
 
1501
- const state = root.store.getState(); // If the frameloop is invalidated, do not run another frame
1423
+ state = root.store.getState(); // If the frameloop is invalidated, do not run another frame
1502
1424
 
1503
1425
  if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) {
1504
1426
  repeat += render$1(timestamp, state);
1505
1427
  }
1506
1428
  }); // Run after-effects
1507
1429
 
1508
- run(globalAfterEffects, timestamp); // Keep on looping if anything invalidates the frameloop
1430
+ if (globalAfterEffects.length) run(globalAfterEffects, timestamp); // Stop the loop if nothing invalidates it
1509
1431
 
1510
- if (repeat > 0) return requestAnimationFrame(loop); // Tail call effects, they are called when rendering stops
1511
- else run(globalTailEffects, timestamp); // Flag end of operation
1432
+ if (repeat === 0) {
1433
+ // Tail call effects, they are called when rendering stops
1434
+ if (globalTailEffects.length) run(globalTailEffects, timestamp); // Flag end of operation
1512
1435
 
1513
- running = false;
1436
+ running = false;
1437
+ return cancelAnimationFrame(frame);
1438
+ }
1514
1439
  }
1515
1440
 
1516
1441
  function invalidate(state) {
@@ -1574,6 +1499,11 @@ function loadingFn(extensions, onProgress) {
1574
1499
  };
1575
1500
  }
1576
1501
 
1502
+ function useMemoizedFn(fn) {
1503
+ const fnRef = React__namespace.useRef(fn);
1504
+ React__namespace.useLayoutEffect(() => void (fnRef.current = fn), [fn]);
1505
+ return (...args) => fnRef.current == null ? void 0 : fnRef.current(...args);
1506
+ }
1577
1507
  function useLoader(Proto, input, extensions, onProgress) {
1578
1508
  // Use suspense to load async assets
1579
1509
  const keys = Array.isArray(input) ? input : [input];
@@ -1603,137 +1533,240 @@ const {
1603
1533
  reconciler,
1604
1534
  applyProps
1605
1535
  } = createRenderer(roots, getEventPriority);
1536
+ const shallowLoose = {
1537
+ objects: 'shallow',
1538
+ strict: false
1539
+ };
1606
1540
 
1607
1541
  const createRendererInstance = (gl, canvas) => {
1608
1542
  const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1609
- if (isRenderer(customRenderer)) return customRenderer;
1610
- const renderer = new THREE__namespace.WebGLRenderer({
1543
+ if (isRenderer(customRenderer)) return customRenderer;else return new THREE__namespace.WebGLRenderer({
1611
1544
  powerPreference: 'high-performance',
1612
1545
  canvas: canvas,
1613
1546
  antialias: true,
1614
1547
  alpha: true,
1615
1548
  ...gl
1616
- }); // Set color management
1549
+ });
1550
+ };
1617
1551
 
1618
- renderer.outputEncoding = THREE__namespace.sRGBEncoding;
1619
- renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping; // Set gl props
1552
+ function createRoot(canvas) {
1553
+ // Check against mistaken use of createRoot
1554
+ let prevRoot = roots.get(canvas);
1555
+ let prevFiber = prevRoot == null ? void 0 : prevRoot.fiber;
1556
+ let prevStore = prevRoot == null ? void 0 : prevRoot.store;
1557
+ if (prevRoot) console.warn('R3F.createRoot should only be called once!'); // Create store
1620
1558
 
1621
- if (gl) applyProps(renderer, gl);
1622
- return renderer;
1623
- };
1559
+ const store = prevStore || createStore(invalidate, advance); // Create renderer
1560
+
1561
+ const fiber = prevFiber || reconciler.createContainer(store, constants.ConcurrentRoot, false, null); // Map it
1624
1562
 
1625
- function createRoot(canvas, config) {
1563
+ if (!prevRoot) roots.set(canvas, {
1564
+ fiber,
1565
+ store
1566
+ }); // Locals
1567
+
1568
+ let onCreated;
1569
+ let configured = false;
1626
1570
  return {
1627
- render: element => {
1628
- var _store;
1571
+ configure(props = {}) {
1572
+ var _canvas$parentElement, _canvas$parentElement2, _canvas$parentElement3, _canvas$parentElement4;
1629
1573
 
1630
1574
  let {
1631
- gl,
1575
+ gl: glConfig,
1632
1576
  size,
1633
1577
  events,
1634
- onCreated,
1635
- ...props
1636
- } = config || {}; // Allow size to take on container bounds initially
1578
+ onCreated: onCreatedCallback,
1579
+ shadows = false,
1580
+ linear = false,
1581
+ flat = false,
1582
+ orthographic = false,
1583
+ frameloop = 'always',
1584
+ dpr = [1, 2],
1585
+ performance,
1586
+ raycaster: raycastOptions,
1587
+ camera: cameraOptions,
1588
+ onPointerMissed
1589
+ } = props;
1590
+ let state = store.getState(); // Set up renderer (one time only!)
1591
+
1592
+ let gl = state.gl;
1593
+ if (!state.gl) state.set({
1594
+ gl: gl = createRendererInstance(glConfig, canvas)
1595
+ }); // Set up raycaster (one time only!)
1596
+
1597
+ let raycaster = state.raycaster;
1598
+ if (!raycaster) state.set({
1599
+ raycaster: raycaster = new THREE__namespace.Raycaster()
1600
+ }); // Set raycaster options
1601
+
1602
+ const {
1603
+ params,
1604
+ ...options
1605
+ } = raycastOptions || {};
1606
+ if (!is.equ(options, raycaster, shallowLoose)) applyProps(raycaster, { ...options
1607
+ });
1608
+ if (!is.equ(params, raycaster.params, shallowLoose)) applyProps(raycaster, {
1609
+ params: { ...raycaster.params,
1610
+ ...params
1611
+ }
1612
+ }); // Create default camera (one time only!)
1637
1613
 
1638
- if (!size) {
1639
- var _canvas$parentElement, _canvas$parentElement2, _canvas$parentElement3, _canvas$parentElement4;
1614
+ if (!state.camera) {
1615
+ const isCamera = cameraOptions instanceof THREE__namespace.Camera;
1616
+ 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);
1640
1617
 
1641
- size = {
1642
- width: (_canvas$parentElement = (_canvas$parentElement2 = canvas.parentElement) == null ? void 0 : _canvas$parentElement2.clientWidth) != null ? _canvas$parentElement : 0,
1643
- height: (_canvas$parentElement3 = (_canvas$parentElement4 = canvas.parentElement) == null ? void 0 : _canvas$parentElement4.clientHeight) != null ? _canvas$parentElement3 : 0
1644
- };
1645
- }
1618
+ if (!isCamera) {
1619
+ camera.position.z = 5;
1620
+ if (cameraOptions) applyProps(camera, cameraOptions); // Always look at center by default
1621
+
1622
+ if (!(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
1623
+ }
1646
1624
 
1647
- let root = roots.get(canvas);
1648
- let fiber = root == null ? void 0 : root.fiber;
1649
- let store = root == null ? void 0 : root.store;
1650
- let state = (_store = store) == null ? void 0 : _store.getState();
1625
+ state.set({
1626
+ camera
1627
+ });
1628
+ } // Set up XR (one time only!)
1651
1629
 
1652
- if (fiber && state) {
1653
- // When a root was found, see if any fundamental props must be changed or exchanged
1654
- // Check pixelratio
1655
- if (props.dpr !== undefined && state.viewport.dpr !== calculateDpr(props.dpr)) state.setDpr(props.dpr); // Check size
1656
1630
 
1657
- if (state.size.width !== size.width || state.size.height !== size.height) state.setSize(size.width, size.height); // Check frameloop
1631
+ if (!state.xr) {
1632
+ // Handle frame behavior in WebXR
1633
+ const handleXRFrame = (timestamp, frame) => {
1634
+ const state = store.getState();
1635
+ if (state.frameloop === 'never') return;
1636
+ advance(timestamp, true, state, frame);
1637
+ }; // Toggle render switching on session
1658
1638
 
1659
- if (state.frameloop !== props.frameloop) state.setFrameloop(props.frameloop); // For some props we want to reset the entire root
1660
- // Changes to the color-space
1661
1639
 
1662
- const linearChanged = props.linear !== state.internal.lastProps.linear;
1640
+ const handleSessionChange = () => {
1641
+ const gl = store.getState().gl;
1642
+ gl.xr.enabled = gl.xr.isPresenting; // @ts-ignore
1643
+ // WebXRManager's signature is incorrect.
1644
+ // See: https://github.com/pmndrs/react-three-fiber/pull/2017#discussion_r790134505
1663
1645
 
1664
- if (linearChanged) {
1665
- unmountComponentAtNode(canvas);
1666
- fiber = undefined;
1667
- }
1668
- }
1646
+ gl.xr.setAnimationLoop(gl.xr.isPresenting ? handleXRFrame : null);
1647
+ }; // WebXR session manager
1669
1648
 
1670
- if (!fiber) {
1671
- // If no root has been found, make one
1672
- // Create gl
1673
- const glRenderer = createRendererInstance(gl, canvas); // Create store
1674
1649
 
1675
- store = createStore(applyProps, invalidate, advance, {
1676
- gl: glRenderer,
1677
- size,
1678
- ...props
1679
- });
1680
- const state = store.getState(); // Create renderer
1650
+ const xr = {
1651
+ connect() {
1652
+ const gl = store.getState().gl;
1653
+ gl.xr.addEventListener('sessionstart', handleSessionChange);
1654
+ gl.xr.addEventListener('sessionend', handleSessionChange);
1655
+ },
1681
1656
 
1682
- fiber = reconciler.createContainer(store, constants.ConcurrentRoot, false, null); // Map it
1657
+ disconnect() {
1658
+ const gl = store.getState().gl;
1659
+ gl.xr.removeEventListener('sessionstart', handleSessionChange);
1660
+ gl.xr.removeEventListener('sessionend', handleSessionChange);
1661
+ }
1683
1662
 
1684
- roots.set(canvas, {
1685
- fiber,
1686
- store
1687
- }); // Store events internally
1663
+ }; // Subscribe to WebXR session events
1688
1664
 
1689
- if (events) state.set({
1690
- events: events(store)
1665
+ if (gl.xr) xr.connect();
1666
+ state.set({
1667
+ xr
1691
1668
  });
1692
- }
1669
+ } // Set shadowmap
1693
1670
 
1694
- if (store && fiber) {
1695
- reconciler.updateContainer( /*#__PURE__*/React__namespace.createElement(Provider, {
1696
- store: store,
1697
- element: element,
1698
- onCreated: onCreated,
1699
- target: canvas
1700
- }), fiber, null, () => undefined);
1701
- return store;
1702
- } else {
1703
- throw 'Error creating root!';
1704
- }
1671
+
1672
+ if (gl.shadowMap) {
1673
+ const isBoolean = is.boo(shadows);
1674
+
1675
+ if (isBoolean && gl.shadowMap.enabled !== shadows || !is.equ(shadows, gl.shadowMap, shallowLoose)) {
1676
+ const old = gl.shadowMap.enabled;
1677
+ gl.shadowMap.enabled = !!shadows;
1678
+ if (!isBoolean) Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1679
+ if (old !== gl.shadowMap.enabled) gl.shadowMap.needsUpdate = true;
1680
+ }
1681
+ } // Set color management
1682
+
1683
+
1684
+ if (THREE__namespace.ColorManagement) THREE__namespace.ColorManagement.legacyMode = false;
1685
+ const outputEncoding = linear ? THREE__namespace.LinearEncoding : THREE__namespace.sRGBEncoding;
1686
+ const toneMapping = flat ? THREE__namespace.NoToneMapping : THREE__namespace.ACESFilmicToneMapping;
1687
+ if (gl.outputEncoding !== outputEncoding) gl.outputEncoding = outputEncoding;
1688
+ if (gl.toneMapping !== toneMapping) gl.toneMapping = toneMapping; // Set gl props
1689
+
1690
+ if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig); // Store events internally
1691
+
1692
+ if (events && !state.events.handlers) state.set({
1693
+ events: events(store)
1694
+ }); // Check pixelratio
1695
+
1696
+ if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr); // Check size, allow it to take on container bounds initially
1697
+
1698
+ size = size || {
1699
+ width: (_canvas$parentElement = (_canvas$parentElement2 = canvas.parentElement) == null ? void 0 : _canvas$parentElement2.clientWidth) != null ? _canvas$parentElement : 0,
1700
+ height: (_canvas$parentElement3 = (_canvas$parentElement4 = canvas.parentElement) == null ? void 0 : _canvas$parentElement4.clientHeight) != null ? _canvas$parentElement3 : 0
1701
+ };
1702
+ if (!is.equ(size, state.size, shallowLoose)) state.setSize(size.width, size.height); // Check frameloop
1703
+
1704
+ if (state.frameloop !== frameloop) state.setFrameloop(frameloop); // Check pointer missed
1705
+
1706
+ if (!state.onPointerMissed) state.set({
1707
+ onPointerMissed
1708
+ }); // Check performance
1709
+
1710
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1711
+ performance: { ...state.performance,
1712
+ ...performance
1713
+ }
1714
+ })); // Set locals
1715
+
1716
+ onCreated = onCreatedCallback;
1717
+ configured = true;
1718
+ return this;
1705
1719
  },
1706
- unmount: () => unmountComponentAtNode(canvas)
1720
+
1721
+ render(children) {
1722
+ // The root has to be configured before it can be rendered
1723
+ if (!configured) this.configure();
1724
+ reconciler.updateContainer( /*#__PURE__*/React__namespace.createElement(Provider, {
1725
+ store: store,
1726
+ children: children,
1727
+ onCreated: onCreated,
1728
+ rootElement: canvas
1729
+ }), fiber, null, () => undefined);
1730
+ return store;
1731
+ },
1732
+
1733
+ unmount() {
1734
+ unmountComponentAtNode(canvas);
1735
+ }
1736
+
1707
1737
  };
1708
1738
  }
1709
1739
 
1710
- function render(element, canvas, config = {}) {
1740
+ function render(children, canvas, config) {
1711
1741
  console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
1712
- return createRoot(canvas, config).render(element);
1742
+ const root = createRoot(canvas);
1743
+ root.configure(config);
1744
+ return root.render(children);
1713
1745
  }
1714
1746
 
1715
1747
  function Provider({
1716
1748
  store,
1717
- element,
1749
+ children,
1718
1750
  onCreated,
1719
- target
1751
+ rootElement
1720
1752
  }) {
1721
- React__namespace.useEffect(() => {
1753
+ React__namespace.useLayoutEffect(() => {
1722
1754
  const state = store.getState(); // Flag the canvas active, rendering will now begin
1723
1755
 
1724
1756
  state.set(state => ({
1725
1757
  internal: { ...state.internal,
1726
1758
  active: true
1727
1759
  }
1728
- })); // Connect events
1760
+ })); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1729
1761
 
1730
- state.events.connect == null ? void 0 : state.events.connect(target); // Notifiy that init is completed, the scene graph exists, but nothing has yet rendered
1762
+ if (onCreated) onCreated(state); // Connect events to the targets parent, this is done to ensure events are registered on
1763
+ // a shared target, and not on the canvas itself
1731
1764
 
1732
- if (onCreated) onCreated(state); // eslint-disable-next-line react-hooks/exhaustive-deps
1765
+ if (!store.getState().events.connected) state.events.connect == null ? void 0 : state.events.connect(rootElement); // eslint-disable-next-line react-hooks/exhaustive-deps
1733
1766
  }, []);
1734
1767
  return /*#__PURE__*/React__namespace.createElement(context.Provider, {
1735
1768
  value: store
1736
- }, element);
1769
+ }, children);
1737
1770
  }
1738
1771
 
1739
1772
  function unmountComponentAtNode(canvas, callback) {
@@ -1752,7 +1785,7 @@ function unmountComponentAtNode(canvas, callback) {
1752
1785
  state.events.disconnect == null ? void 0 : state.events.disconnect();
1753
1786
  (_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();
1754
1787
  (_state$gl2 = state.gl) == null ? void 0 : _state$gl2.forceContextLoss == null ? void 0 : _state$gl2.forceContextLoss();
1755
- if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.internal.xr.disconnect();
1788
+ if ((_state$gl3 = state.gl) != null && _state$gl3.xr) state.xr.disconnect();
1756
1789
  dispose(state);
1757
1790
  roots.delete(canvas);
1758
1791
  if (callback) callback(canvas);
@@ -1765,10 +1798,67 @@ function unmountComponentAtNode(canvas, callback) {
1765
1798
  }
1766
1799
  }
1767
1800
 
1768
- const act = React__namespace.unstable_act;
1801
+ function createPortal(children, container, state) {
1802
+ return /*#__PURE__*/React__namespace.createElement(Portal, {
1803
+ children: children,
1804
+ container: container,
1805
+ state: state
1806
+ });
1807
+ }
1808
+
1809
+ function Portal({
1810
+ state = {},
1811
+ children,
1812
+ container
1813
+ }) {
1814
+ /** This has to be a component because it would not be able to call useThree/useStore otherwise since
1815
+ * if this is our environment, then we are not in r3f's renderer but in react-dom, it would trigger
1816
+ * the "R3F hooks can only be used within the Canvas component!" warning:
1817
+ * <Canvas>
1818
+ * {createPortal(...)} */
1819
+ const {
1820
+ events,
1821
+ ...rest
1822
+ } = state;
1823
+ const previousRoot = useStore();
1824
+ const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
1825
+ const inject = React__namespace.useCallback((state, injectState) => {
1826
+ const intersect = { ...state
1827
+ };
1828
+
1829
+ if (injectState) {
1830
+ // Only the fields of "state" that do not differ from injectState
1831
+ Object.keys(state).forEach(key => {
1832
+ if ( // Some props should be off-limits
1833
+ !['size', 'viewport', 'internal', 'performance'].includes(key) && // Otherwise filter out the props that are different and let the inject layer take precedence
1834
+ state[key] !== injectState[key]) delete intersect[key];
1835
+ });
1836
+ }
1769
1837
 
1770
- function createPortal(children, container) {
1771
- return reconciler.createPortal(children, container, null, null);
1838
+ return { ...intersect,
1839
+ scene: container,
1840
+ previousRoot,
1841
+ raycaster,
1842
+ events: { ...state.events,
1843
+ ...events
1844
+ },
1845
+ ...rest
1846
+ };
1847
+ }, [state]);
1848
+ const [useInjectStore] = React__namespace.useState(() => {
1849
+ const store = create__default['default']((set, get) => ({ ...inject(previousRoot.getState()),
1850
+ set,
1851
+ get
1852
+ }));
1853
+ previousRoot.subscribe(state => useInjectStore.setState(injectState => inject(state, injectState)));
1854
+ return store;
1855
+ });
1856
+ React__namespace.useEffect(() => {
1857
+ useInjectStore.setState(injectState => inject(previousRoot.getState(), injectState));
1858
+ }, [inject]);
1859
+ return /*#__PURE__*/React__namespace.createElement(React__namespace.Fragment, null, reconciler.createPortal( /*#__PURE__*/React__namespace.createElement(context.Provider, {
1860
+ value: useInjectStore
1861
+ }, children), useInjectStore, null));
1772
1862
  }
1773
1863
 
1774
1864
  reconciler.injectIntoDevTools({
@@ -1776,6 +1866,7 @@ reconciler.injectIntoDevTools({
1776
1866
  rendererPackageName: '@react-three/fiber',
1777
1867
  version: '18.0.0'
1778
1868
  });
1869
+ const act = React__namespace.unstable_act;
1779
1870
 
1780
1871
  exports.act = act;
1781
1872
  exports.addAfterEffect = addAfterEffect;
@@ -1789,6 +1880,7 @@ exports.createPortal = createPortal;
1789
1880
  exports.createRoot = createRoot;
1790
1881
  exports.dispose = dispose;
1791
1882
  exports.extend = extend;
1883
+ exports.getRootState = getRootState;
1792
1884
  exports.invalidate = invalidate;
1793
1885
  exports.omit = omit;
1794
1886
  exports.pick = pick;
@@ -1800,5 +1892,6 @@ exports.unmountComponentAtNode = unmountComponentAtNode;
1800
1892
  exports.useFrame = useFrame;
1801
1893
  exports.useGraph = useGraph;
1802
1894
  exports.useLoader = useLoader;
1895
+ exports.useMemoizedFn = useMemoizedFn;
1803
1896
  exports.useStore = useStore;
1804
1897
  exports.useThree = useThree;