@react-three/fiber 8.0.0-beta-02 → 8.0.0-beta-03

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,6 +1,7 @@
1
- import * as THREE from 'three';
2
1
  import Reconciler from 'react-reconciler';
3
- import { DefaultEventPriority, IdleEventPriority } from 'react-reconciler/constants';
2
+ import { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
3
+ import { DefaultEventPriority } from 'react-reconciler/constants';
4
+ import * as THREE from 'three';
4
5
  import * as React from 'react';
5
6
  import { suspend, preload, clear } from 'suspend-react';
6
7
  import create from 'zustand';
@@ -13,7 +14,34 @@ const DEFAULT = '__default';
13
14
  const isDiffSet = def => def && !!def.memoized && !!def.changes;
14
15
  function calculateDpr(dpr) {
15
16
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
16
- } // A collection of compare functions
17
+ }
18
+ /**
19
+ * Picks or omits keys from an object
20
+ * `omit` will filter out keys, and otherwise cherry-pick them.
21
+ */
22
+
23
+ function filterKeys(obj, omit, ...keys) {
24
+ const keysToSelect = new Set(keys);
25
+ return Object.entries(obj).reduce((acc, [key, value]) => {
26
+ const shouldInclude = !omit;
27
+
28
+ if (keysToSelect.has(key) === shouldInclude) {
29
+ acc[key] = value;
30
+ }
31
+
32
+ return acc;
33
+ }, {});
34
+ }
35
+ /**
36
+ * Clones an object and cherry-picks keys.
37
+ */
38
+
39
+ const pick = (obj, keys) => filterKeys(obj, false, ...keys);
40
+ /**
41
+ * Clones an object and prunes or omits keys.
42
+ */
43
+
44
+ const omit = (obj, keys) => filterKeys(obj, true, ...keys); // A collection of compare functions
17
45
 
18
46
  const is = {
19
47
  obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
@@ -62,8 +90,7 @@ function dispose(obj) {
62
90
  if (obj.dispose && obj.type !== 'Scene') obj.dispose();
63
91
 
64
92
  for (const p in obj) {
65
- var _dispose, _ref;
66
- (_dispose = (_ref = p).dispose) == null ? void 0 : _dispose.call(_ref);
93
+ p.dispose == null ? void 0 : p.dispose();
67
94
  delete obj[p];
68
95
  }
69
96
  } // Each object in the scene carries a small LocalState descriptor
@@ -84,6 +111,49 @@ function prepare(object, state) {
84
111
  }
85
112
 
86
113
  return object;
114
+ }
115
+
116
+ function resolve(instance, key) {
117
+ let target = instance;
118
+
119
+ if (key.includes('-')) {
120
+ const entries = key.split('-');
121
+ const last = entries.pop();
122
+ target = entries.reduce((acc, key) => acc[key], instance);
123
+ return {
124
+ target,
125
+ key: last
126
+ };
127
+ } else return {
128
+ target,
129
+ key
130
+ };
131
+ }
132
+
133
+ function attach(parent, child, type) {
134
+ if (is.str(type)) {
135
+ const {
136
+ target,
137
+ key
138
+ } = resolve(parent, type);
139
+ parent.__r3f.previousAttach = target[key];
140
+ target[key] = child;
141
+ } else if (is.arr(type)) {
142
+ const [attach] = type;
143
+ if (is.str(attach)) parent[attach](child);else if (is.fun(attach)) attach(parent, child);
144
+ }
145
+ }
146
+ function detach(parent, child, type) {
147
+ if (is.str(type)) {
148
+ const {
149
+ target,
150
+ key
151
+ } = resolve(parent, type);
152
+ target[key] = parent.__r3f.previousAttach;
153
+ } else if (is.arr(type)) {
154
+ const [, detach] = type;
155
+ if (is.str(detach)) parent[detach](child);else if (is.fun(detach)) detach(parent, child);
156
+ }
87
157
  } // Shallow check arrays, but check objects atomically
88
158
 
89
159
  function checkShallow(a, b) {
@@ -113,7 +183,9 @@ function diffProps(instance, {
113
183
  if (remove) {
114
184
  const previousKeys = Object.keys(previous);
115
185
 
116
- for (let i = 0; i < previousKeys.length; i++) if (!props.hasOwnProperty(previousKeys[i])) entries.unshift([previousKeys[i], DEFAULT + 'remove']);
186
+ for (let i = 0; i < previousKeys.length; i++) {
187
+ if (!props.hasOwnProperty(previousKeys[i])) entries.unshift([previousKeys[i], DEFAULT + 'remove']);
188
+ }
117
189
  }
118
190
 
119
191
  entries.forEach(([key, value]) => {
@@ -184,7 +256,9 @@ function applyProps(instance, data) {
184
256
  value = defaultClassCall[targetProp]; // destory the instance
185
257
 
186
258
  if (defaultClassCall.dispose) defaultClassCall.dispose(); // instance does not have constructor, just set it to 0
187
- } else value = 0;
259
+ } else {
260
+ value = 0;
261
+ }
188
262
  } // Deal with pointer events ...
189
263
 
190
264
 
@@ -193,35 +267,39 @@ function applyProps(instance, data) {
193
267
  localState.eventCount = Object.keys(localState.handlers).length;
194
268
  } // Special treatment for objects with support for set/copy, and layers
195
269
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE.Layers)) {
196
- // If value is an array
197
- if (Array.isArray(value)) {
198
- if (targetProp.fromArray) targetProp.fromArray(value);else targetProp.set(...value);
199
- } // Test again target.copy(class) next ...
200
- else if (targetProp.copy && value && value.constructor && targetProp.constructor.name === value.constructor.name) targetProp.copy(value); // If nothing else fits, just set the single value, ignore undefined
201
- // https://github.com/react-spring/react-three-fiber/issues/274
202
- else if (value !== undefined) {
203
- const isColor = targetProp instanceof THREE.Color; // Allow setting array scalars
204
-
205
- if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
206
- else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) targetProp.mask = value.mask; // Otherwise just set ...
207
- else targetProp.set(value); // Auto-convert sRGB colors, for now ...
208
- // https://github.com/react-spring/react-three-fiber/issues/344
209
-
210
- if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
211
- } // Else, just overwrite the value
270
+ // If value is an array
271
+ if (Array.isArray(value)) {
272
+ if (targetProp.fromArray) targetProp.fromArray(value);else targetProp.set(...value);
273
+ } // Test again target.copy(class) next ...
274
+ else if (targetProp.copy && value && value.constructor && targetProp.constructor.name === value.constructor.name) {
275
+ targetProp.copy(value);
276
+ } // If nothing else fits, just set the single value, ignore undefined
277
+ // https://github.com/pmndrs/react-three-fiber/issues/274
278
+ else if (value !== undefined) {
279
+ const isColor = targetProp instanceof THREE.Color; // Allow setting array scalars
280
+
281
+ if (!isColor && targetProp.setScalar) targetProp.setScalar(value); // Layers have no copy function, we must therefore copy the mask property
282
+ else if (targetProp instanceof THREE.Layers && value instanceof THREE.Layers) targetProp.mask = value.mask; // Otherwise just set ...
283
+ else targetProp.set(value); // Auto-convert sRGB colors, for now ...
284
+ // https://github.com/pmndrs/react-three-fiber/issues/344
285
+
286
+ if (!rootState.linear && isColor) targetProp.convertSRGBToLinear();
287
+ } // Else, just overwrite the value
212
288
 
213
- } else {
214
- currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
215
- // https://github.com/react-spring/react-three-fiber/issues/344
289
+ } else {
290
+ currentInstance[key] = value; // Auto-convert sRGB textures, for now ...
291
+ // https://github.com/pmndrs/react-three-fiber/issues/344
216
292
 
217
- if (!rootState.linear && currentInstance[key] instanceof THREE.Texture) currentInstance[key].encoding = THREE.sRGBEncoding;
218
- }
293
+ if (!rootState.linear && currentInstance[key] instanceof THREE.Texture) {
294
+ currentInstance[key].encoding = THREE.sRGBEncoding;
295
+ }
296
+ }
219
297
 
220
298
  invalidateInstance(instance);
221
299
  return instance;
222
300
  });
223
301
 
224
- if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
302
+ if (localState.parent && rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
225
303
  // Pre-emptively remove the instance from the interaction manager
226
304
  const index = rootState.internal.interaction.indexOf(instance);
227
305
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
@@ -245,7 +323,8 @@ function updateInstance(instance) {
245
323
  function makeId(event) {
246
324
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
247
325
  }
248
- /** Release pointer captures.
326
+ /**
327
+ * Release pointer captures.
249
328
  * This is called by releasePointerCapture in the API, and when an object is removed.
250
329
  */
251
330
 
@@ -532,13 +611,16 @@ function createEvents(store) {
532
611
 
533
612
  case 'onLostPointerCapture':
534
613
  return event => {
535
- if ('pointerId' in event) {
614
+ const {
615
+ internal
616
+ } = store.getState();
617
+
618
+ if ('pointerId' in event && !internal.capturedMap.has(event.pointerId)) {
536
619
  // If the object event interface had onLostPointerCapture, we'd call it here on every
537
620
  // object that's getting removed.
538
- store.getState().internal.capturedMap.delete(event.pointerId);
621
+ internal.capturedMap.delete(event.pointerId);
622
+ cancelPointer([]);
539
623
  }
540
-
541
- cancelPointer([]);
542
624
  };
543
625
  } // Any other pointer goes here ...
544
626
 
@@ -548,7 +630,8 @@ function createEvents(store) {
548
630
  onPointerMissed,
549
631
  internal
550
632
  } = store.getState();
551
- prepareRay(event); // Get fresh intersects
633
+ prepareRay(event);
634
+ internal.lastEvent.current = event; // Get fresh intersects
552
635
 
553
636
  const isPointerMove = name === 'onPointerMove';
554
637
  const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
@@ -606,12 +689,17 @@ function createEvents(store) {
606
689
  if (handler) {
607
690
  // Forward all events back to their respective handlers with the exception of click events,
608
691
  // which must use the initial target
609
- if (name !== 'onClick' && name !== 'onContextMenu' && name !== 'onDoubleClick' || internal.initialHits.includes(eventObject)) {
692
+ if (!isClickEvent || internal.initialHits.includes(eventObject)) {
610
693
  // Missed events have to come first
611
694
  pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object))); // Now call the handler
612
695
 
613
696
  handler(data);
614
697
  }
698
+ } else {
699
+ // Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
700
+ if (isClickEvent && internal.initialHits.includes(eventObject)) {
701
+ pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
702
+ }
615
703
  }
616
704
  }
617
705
  });
@@ -657,6 +745,7 @@ let extend = objects => void (catalogue = { ...catalogue,
657
745
  function createRenderer(roots, getEventPriority) {
658
746
  function createInstance(type, {
659
747
  args = [],
748
+ attach,
660
749
  ...props
661
750
  }, root, hostContext, internalInstanceHandle) {
662
751
  let name = `${type[0].toUpperCase()}${type.slice(1)}`;
@@ -673,41 +762,39 @@ function createRenderer(roots, getEventPriority) {
673
762
  } // Assert that by now we have a valid root
674
763
 
675
764
 
676
- if (!root || !isStore(root)) throw `No valid root for ${name}!`;
765
+ if (!root || !isStore(root)) throw `No valid root for ${name}!`; // Auto-attach geometries and materials
766
+
767
+ if (attach === undefined) {
768
+ if (name.endsWith('Geometry')) attach = 'geometry';else if (name.endsWith('Material')) attach = 'material';
769
+ }
677
770
 
678
771
  if (type === 'primitive') {
679
772
  if (props.object === undefined) throw `Primitives without 'object' are invalid!`;
680
773
  const object = props.object;
681
774
  instance = prepare(object, {
682
775
  root,
776
+ attach,
683
777
  primitive: true
684
778
  });
685
779
  } else {
686
- const target = catalogue[name] || THREE[name];
687
- if (!target) throw `${name} is not part of the THREE namespace! Did you forget to extend? See: https://github.com/pmndrs/react-three-fiber/blob/master/markdown/api.md#using-3rd-party-objects-declaratively`; // Instanciate new object, link it to the root
780
+ const target = catalogue[name];
781
+
782
+ if (!target) {
783
+ throw `${name} is not part of the THREE namespace! Did you forget to extend? See: https://github.com/pmndrs/react-three-fiber/blob/master/markdown/api.md#using-3rd-party-objects-declaratively`;
784
+ } // Throw if an object or literal was passed for args
785
+
786
+
787
+ if (!Array.isArray(args)) throw 'The args prop must be an array!'; // Instanciate new object, link it to the root
688
788
  // Append memoized props with args so it's not forgotten
689
789
 
690
790
  instance = prepare(new target(...args), {
691
791
  root,
792
+ attach,
793
+ // TODO: Figure out what this is for
692
794
  memoizedProps: {
693
795
  args: args.length === 0 ? null : args
694
796
  }
695
797
  });
696
- } // Auto-attach geometries and materials
697
-
698
-
699
- if (!('attachFns' in props)) {
700
- if (name.endsWith('Geometry')) {
701
- props = {
702
- attach: 'geometry',
703
- ...props
704
- };
705
- } else if (name.endsWith('Material')) {
706
- props = {
707
- attach: 'material',
708
- ...props
709
- };
710
- }
711
798
  } // It should NOT call onUpdate on object instanciation, because it hasn't been added to the
712
799
  // view yet. If the callback relies on references for instance, they won't be ready yet, this is
713
800
  // why it passes "true" here
@@ -718,42 +805,22 @@ function createRenderer(roots, getEventPriority) {
718
805
  }
719
806
 
720
807
  function appendChild(parentInstance, child) {
721
- let addedAsChild = false;
808
+ let added = false;
722
809
 
723
810
  if (child) {
724
811
  // The attach attribute implies that the object attaches itself on the parent
725
- if (child.attachArray) {
726
- if (!is.arr(parentInstance[child.attachArray])) parentInstance[child.attachArray] = [];
727
- parentInstance[child.attachArray].push(child);
728
- } else if (child.attachObject) {
729
- if (!is.obj(parentInstance[child.attachObject[0]])) parentInstance[child.attachObject[0]] = {};
730
- parentInstance[child.attachObject[0]][child.attachObject[1]] = child;
731
- } else if (child.attach && !is.fun(child.attach)) {
732
- parentInstance[child.attach] = child;
733
- } else if (is.arr(child.attachFns)) {
734
- const [attachFn] = child.attachFns;
735
-
736
- if (is.str(attachFn) && is.fun(parentInstance[attachFn])) {
737
- parentInstance[attachFn](child);
738
- } else if (is.fun(attachFn)) {
739
- attachFn(child, parentInstance);
740
- }
812
+ if (child.__r3f.attach) {
813
+ attach(parentInstance, child, child.__r3f.attach);
741
814
  } else if (child.isObject3D && parentInstance.isObject3D) {
742
815
  // add in the usual parent-child way
743
816
  parentInstance.add(child);
744
- addedAsChild = true;
745
- }
746
-
747
- if (!addedAsChild) {
748
- // This is for anything that used attach, and for non-Object3Ds that don't get attached to props;
749
- // that is, anything that's a child in React but not a child in the scenegraph.
750
- parentInstance.__r3f.objects.push(child);
751
- }
817
+ added = true;
818
+ } // This is for anything that used attach, and for non-Object3Ds that don't get attached to props;
819
+ // that is, anything that's a child in React but not a child in the scenegraph.
752
820
 
753
- if (!child.__r3f) {
754
- prepare(child, {});
755
- }
756
821
 
822
+ if (!added) parentInstance.__r3f.objects.push(child);
823
+ if (!child.__r3f) prepare(child, {});
757
824
  child.__r3f.parent = parentInstance;
758
825
  updateInstance(child);
759
826
  invalidateInstance(child);
@@ -764,13 +831,8 @@ function createRenderer(roots, getEventPriority) {
764
831
  let added = false;
765
832
 
766
833
  if (child) {
767
- if (child.attachArray) {
768
- const array = parentInstance[child.attachArray];
769
- if (!is.arr(array)) parentInstance[child.attachArray] = [];
770
- array.splice(array.indexOf(beforeChild), 0, child);
771
- } else if (child.attachObject || child.attach && !is.fun(child.attach)) {
772
- // attach and attachObject don't have an order anyway, so just append
773
- return appendChild(parentInstance, child);
834
+ if (child.__r3f.attach) {
835
+ attach(parentInstance, child, child.__r3f.attach);
774
836
  } else if (child.isObject3D && parentInstance.isObject3D) {
775
837
  child.parent = parentInstance;
776
838
  child.dispatchEvent({
@@ -782,14 +844,8 @@ function createRenderer(roots, getEventPriority) {
782
844
  added = true;
783
845
  }
784
846
 
785
- if (!added) {
786
- parentInstance.__r3f.objects.push(child);
787
- }
788
-
789
- if (!child.__r3f) {
790
- prepare(child, {});
791
- }
792
-
847
+ if (!added) parentInstance.__r3f.objects.push(child);
848
+ if (!child.__r3f) prepare(child, {});
793
849
  child.__r3f.parent = parentInstance;
794
850
  updateInstance(child);
795
851
  invalidateInstance(child);
@@ -804,29 +860,13 @@ function createRenderer(roots, getEventPriority) {
804
860
  if (child) {
805
861
  var _parentInstance$__r3f, _child$__r3f2;
806
862
 
807
- if (child.__r3f) {
808
- child.__r3f.parent = null;
809
- }
810
-
811
- if ((_parentInstance$__r3f = parentInstance.__r3f) != null && _parentInstance$__r3f.objects) {
812
- parentInstance.__r3f.objects = parentInstance.__r3f.objects.filter(x => x !== child);
813
- } // Remove attachment
863
+ // Clear the parent reference
864
+ if (child.__r3f) child.__r3f.parent = null; // Remove child from the parents objects
814
865
 
866
+ if ((_parentInstance$__r3f = parentInstance.__r3f) != null && _parentInstance$__r3f.objects) parentInstance.__r3f.objects = parentInstance.__r3f.objects.filter(x => x !== child); // Remove attachment
815
867
 
816
- if (child.attachArray) {
817
- parentInstance[child.attachArray] = parentInstance[child.attachArray].filter(x => x !== child);
818
- } else if (child.attachObject) {
819
- delete parentInstance[child.attachObject[0]][child.attachObject[1]];
820
- } else if (child.attach && !is.fun(child.attach)) {
821
- parentInstance[child.attach] = null;
822
- } else if (is.arr(child.attachFns)) {
823
- const [, detachFn] = child.attachFns;
824
-
825
- if (is.str(detachFn) && is.fun(parentInstance[detachFn])) {
826
- parentInstance[detachFn](child);
827
- } else if (is.fun(detachFn)) {
828
- detachFn(child, parentInstance);
829
- }
868
+ if (child.__r3f.attach) {
869
+ detach(parentInstance, child, child.__r3f.attach);
830
870
  } else if (child.isObject3D && parentInstance.isObject3D) {
831
871
  var _child$__r3f;
832
872
 
@@ -868,7 +908,7 @@ function createRenderer(roots, getEventPriority) {
868
908
 
869
909
 
870
910
  if (shouldDispose && child.dispose && child.type !== 'Scene') {
871
- reconciler.runWithPriority(IdleEventPriority, () => {
911
+ unstable_scheduleCallback(unstable_IdlePriority, () => {
872
912
  try {
873
913
  child.dispose();
874
914
  } catch (e) {
@@ -929,7 +969,9 @@ function createRenderer(roots, getEventPriority) {
929
969
  insertInContainerBefore: (parentInstance, child, beforeChild) => insertBefore(getContainer(parentInstance, child).container, child, beforeChild),
930
970
 
931
971
  prepareUpdate(instance, type, oldProps, newProps) {
932
- if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) return [true];else {
972
+ if (instance.__r3f.primitive && newProps.object && newProps.object !== instance) {
973
+ return [true];
974
+ } else {
933
975
  // This is a data object, let's extract critical information about it
934
976
  const {
935
977
  args: argsNew = [],
@@ -940,7 +982,9 @@ function createRenderer(roots, getEventPriority) {
940
982
  args: argsOld = [],
941
983
  children: cO,
942
984
  ...restOld
943
- } = oldProps; // If it has new props or arguments, then it needs to be re-instanciated
985
+ } = oldProps; // Throw if an object or literal was passed for args
986
+
987
+ if (!Array.isArray(argsNew)) throw 'The args prop must be an array!'; // If it has new props or arguments, then it needs to be re-instanciated
944
988
 
945
989
  if (argsNew.some((value, index) => value !== argsOld[index])) return [true]; // Create a diff-set, flag if there are any changes
946
990
 
@@ -958,17 +1002,29 @@ function createRenderer(roots, getEventPriority) {
958
1002
  },
959
1003
 
960
1004
  hideInstance(instance) {
961
- if (instance.isObject3D) {
962
- instance.visible = false;
963
- invalidateInstance(instance);
964
- }
1005
+ var _instance$__r3f2;
1006
+
1007
+ // Deatch while the instance is hidden
1008
+ const {
1009
+ attach: type,
1010
+ parent
1011
+ } = (_instance$__r3f2 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f2 : {};
1012
+ if (type && parent) detach(parent, instance, type);
1013
+ if (instance.isObject3D) instance.visible = false;
1014
+ invalidateInstance(instance);
965
1015
  },
966
1016
 
967
1017
  unhideInstance(instance, props) {
968
- if (instance.isObject3D && props.visible == null || props.visible) {
969
- instance.visible = true;
970
- invalidateInstance(instance);
971
- }
1018
+ var _instance$__r3f3;
1019
+
1020
+ // Re-attach when the instance is unhidden
1021
+ const {
1022
+ attach: type,
1023
+ parent
1024
+ } = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
1025
+ if (type && parent) attach(parent, instance, type);
1026
+ if (instance.isObject3D && props.visible == null || props.visible) instance.visible = true;
1027
+ invalidateInstance(instance);
972
1028
  },
973
1029
 
974
1030
  createInstance,
@@ -997,8 +1053,30 @@ function createRenderer(roots, getEventPriority) {
997
1053
  getRootHostContext: () => null,
998
1054
  getChildHostContext: parentHostContext => parentHostContext,
999
1055
  createTextInstance: () => {},
1000
- finalizeInitialChildren: () => false,
1001
- commitMount: () => {},
1056
+
1057
+ finalizeInitialChildren(instance) {
1058
+ var _instance$__r3f4;
1059
+
1060
+ // https://github.com/facebook/react/issues/20271
1061
+ // Returning true will trigger commitMount
1062
+ const localState = (_instance$__r3f4 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f4 : {};
1063
+ return !!localState.handlers;
1064
+ },
1065
+
1066
+ commitMount(instance)
1067
+ /*, type, props*/
1068
+ {
1069
+ var _instance$__r3f5;
1070
+
1071
+ // https://github.com/facebook/react/issues/20271
1072
+ // This will make sure events are only added once to the central container
1073
+ const localState = (_instance$__r3f5 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f5 : {};
1074
+
1075
+ if (instance.raycast && localState.handlers && localState.eventCount) {
1076
+ instance.__r3f.root.getState().internal.interaction.push(instance);
1077
+ }
1078
+ },
1079
+
1002
1080
  shouldDeprioritizeSubtree: () => false,
1003
1081
  prepareForCommit: () => null,
1004
1082
  preparePortalMount: containerInfo => prepare(containerInfo),
@@ -1212,6 +1290,9 @@ const createStore = (applyProps, invalidate, advance, props) => {
1212
1290
  dpr: calculateDpr(dpr)
1213
1291
  }
1214
1292
  })),
1293
+ setFrameloop: (frameloop = 'always') => set(() => ({
1294
+ frameloop
1295
+ })),
1215
1296
  events: {
1216
1297
  connected: false
1217
1298
  },
@@ -1220,6 +1301,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1220
1301
  priority: 0,
1221
1302
  frames: 0,
1222
1303
  lastProps: props,
1304
+ lastEvent: /*#__PURE__*/React.createRef(),
1223
1305
  interaction: [],
1224
1306
  hovered: new Map(),
1225
1307
  subscribers: [],
@@ -1276,7 +1358,7 @@ const createStore = (applyProps, invalidate, advance, props) => {
1276
1358
  if (size !== oldSize || viewport.dpr !== oldDpr) {
1277
1359
  // https://github.com/pmndrs/react-three-fiber/issues/92
1278
1360
  // Do not mess with the camera if it belongs to the user
1279
- if (!(internal.lastProps.camera instanceof THREE.Camera)) {
1361
+ if (!camera.manual && !(internal.lastProps.camera instanceof THREE.Camera)) {
1280
1362
  if (isOrthographicCamera(camera)) {
1281
1363
  camera.left = size.width / -2;
1282
1364
  camera.right = size.width / 2;
@@ -1360,7 +1442,9 @@ function createLoop(roots) {
1360
1442
 
1361
1443
  const state = root.store.getState(); // If the frameloop is invalidated, do not run another frame
1362
1444
 
1363
- if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) repeat += render(timestamp, state);
1445
+ if (state.internal.active && (state.frameloop === 'always' || state.internal.frames > 0) && !((_state$gl$xr = state.gl.xr) != null && _state$gl$xr.isPresenting)) {
1446
+ repeat += render(timestamp, state);
1447
+ }
1364
1448
  }); // Run after-effects
1365
1449
 
1366
1450
  run(globalAfterEffects, timestamp); // Keep on looping if anything invalidates the frameloop
@@ -1452,4 +1536,4 @@ useLoader.clear = function (Proto, input) {
1452
1536
  return clear([Proto, ...keys]);
1453
1537
  };
1454
1538
 
1455
- export { createLoop as a, createRenderer as b, createEvents as c, calculateDpr as d, createStore as e, context as f, dispose as g, extend as h, isRenderer as i, addEffect as j, addAfterEffect as k, addTail as l, useThree as m, useFrame as n, useGraph as o, useLoader as p, buildGraph as q, is as r, threeTypes as t, useStore as u };
1539
+ export { createLoop as a, createRenderer as b, createEvents as c, calculateDpr as d, createStore as e, context as f, dispose as g, extend as h, isRenderer as i, addEffect as j, addAfterEffect as k, addTail as l, useThree as m, useFrame as n, omit as o, pick as p, useGraph as q, useLoader as r, buildGraph as s, threeTypes as t, useStore as u, is as v };