@react-three/fiber 7.0.15 → 7.0.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @react-three/fiber
2
2
 
3
+ ## 7.0.19
4
+
5
+ ### Patch Changes
6
+
7
+ - 7aa2eab: fix: remove zustand subcribe selector
8
+
9
+ ## 7.0.18
10
+
11
+ ### Patch Changes
12
+
13
+ - 6780f58: fix unmount pointer capture
14
+
15
+ ## 7.0.17
16
+
17
+ ### Patch Changes
18
+
19
+ - 894c550: fix: event count
20
+
21
+ ## 7.0.16
22
+
23
+ ### Patch Changes
24
+
25
+ - c7a4220: patch: applyprops returns the same instance
26
+
3
27
  ## 7.0.15
4
28
 
5
29
  ### Patch Changes
@@ -53,6 +53,10 @@ export interface EventManager<TTarget> {
53
53
  connect?: (target: TTarget) => void;
54
54
  disconnect?: () => void;
55
55
  }
56
+ export interface PointerCaptureTarget {
57
+ intersection: Intersection;
58
+ target: Element;
59
+ }
56
60
  export declare function removeInteractivity(store: UseStore<RootState>, object: THREE.Object3D): void;
57
61
  export declare function createEvents(store: UseStore<RootState>): {
58
62
  handlePointer: (name: string) => (event: DomEvent) => void;
@@ -12,9 +12,8 @@ export declare type LocalState = {
12
12
  objects: Instance[];
13
13
  parent: Instance | null;
14
14
  primitive?: boolean;
15
- handlers: {
16
- count: number;
17
- } & Partial<EventHandlers>;
15
+ eventCount: number;
16
+ handlers: Partial<EventHandlers>;
18
17
  memoizedProps: {
19
18
  [key: string]: any;
20
19
  };
@@ -57,6 +56,6 @@ declare let extend: (objects: object) => void;
57
56
  declare function prepare<T = THREE.Object3D>(object: T, state?: Partial<LocalState>): T;
58
57
  declare function createRenderer<TCanvas>(roots: Map<TCanvas, Root>): {
59
58
  reconciler: Reconciler.Reconciler<unknown, unknown, unknown, unknown, unknown>;
60
- applyProps: (instance: Instance, data: InstanceProps | DiffSet) => void;
59
+ applyProps: (instance: Instance, data: InstanceProps | DiffSet) => Instance;
61
60
  };
62
61
  export { prepare, createRenderer, extend };
@@ -3,7 +3,7 @@ import * as React from 'react';
3
3
  import * as ReactThreeFiber from '../three-types';
4
4
  import { GetState, SetState, UseStore } from 'zustand';
5
5
  import { Instance, InstanceProps } from './renderer';
6
- import { DomEvent, EventManager, ThreeEvent } from './events';
6
+ import { DomEvent, EventManager, PointerCaptureTarget, ThreeEvent } from './events';
7
7
  export interface Intersection extends THREE.Intersection {
8
8
  eventObject: THREE.Object3D;
9
9
  }
@@ -37,7 +37,10 @@ export declare type Performance = {
37
37
  debounce: number;
38
38
  regress: () => void;
39
39
  };
40
- export declare const isRenderer: (def: THREE.WebGLRenderer) => def is THREE.WebGLRenderer;
40
+ export declare type Renderer = {
41
+ render: (scene: THREE.Scene, camera: THREE.Camera) => any;
42
+ };
43
+ export declare const isRenderer: (def: Renderer) => boolean;
41
44
  export declare const isOrthographicCamera: (def: THREE.Camera) => def is THREE.OrthographicCamera;
42
45
  export declare type InternalState = {
43
46
  active: boolean;
@@ -47,7 +50,7 @@ export declare type InternalState = {
47
50
  interaction: THREE.Object3D[];
48
51
  hovered: Map<string, DomEvent>;
49
52
  subscribers: Subscription[];
50
- capturedMap: Map<number, Map<THREE.Object3D, Intersection>>;
53
+ capturedMap: Map<number, Map<THREE.Object3D, PointerCaptureTarget>>;
51
54
  initialClick: [x: number, y: number];
52
55
  initialHits: THREE.Object3D[];
53
56
  subscribe: (callback: React.MutableRefObject<RenderCallback>, priority?: number) => () => void;
@@ -2,7 +2,7 @@
2
2
  import * as THREE from 'three';
3
3
  import * as React from 'react';
4
4
  import { UseStore } from 'zustand';
5
- import { StoreProps, context, RootState, Size } from '../core/store';
5
+ import { Renderer, StoreProps, context, RootState, Size } from '../core/store';
6
6
  import { extend, Root } from '../core/renderer';
7
7
  import { addEffect, addAfterEffect, addTail } from '../core/loop';
8
8
  import { createPointerEvents as events } from './events';
@@ -11,9 +11,13 @@ import { EventManager } from '../core/events';
11
11
  declare const roots: Map<Element, Root>;
12
12
  declare const modes: readonly ["legacy", "blocking", "concurrent"];
13
13
  declare const invalidate: (state?: RootState | undefined) => void, advance: (timestamp: number, runGlobalEffects?: boolean, state?: RootState | undefined) => void;
14
- declare const reconciler: import("react-reconciler").Reconciler<unknown, unknown, unknown, unknown, unknown>, applyProps: (instance: import("../core/renderer").Instance, data: import("../core/renderer").InstanceProps | import("../core/renderer").DiffSet) => void;
14
+ declare const reconciler: import("react-reconciler").Reconciler<unknown, unknown, unknown, unknown, unknown>, applyProps: (instance: import("../core/renderer").Instance, data: import("../core/renderer").InstanceProps | import("../core/renderer").DiffSet) => import("../core/renderer").Instance;
15
+ declare type Properties<T> = Pick<T, {
16
+ [K in keyof T]: T[K] extends (_: any) => any ? never : K;
17
+ }[keyof T]>;
18
+ declare type GLProps = Renderer | ((canvas: HTMLCanvasElement) => Renderer) | Partial<Properties<THREE.WebGLRenderer> | THREE.WebGLRendererParameters> | undefined;
15
19
  export declare type RenderProps<TCanvas extends Element> = Omit<StoreProps, 'gl' | 'events' | 'size'> & {
16
- gl?: THREE.WebGLRenderer | Partial<THREE.WebGLRendererParameters>;
20
+ gl?: GLProps;
17
21
  events?: (store: UseStore<RootState>) => EventManager<TCanvas>;
18
22
  size?: Size;
19
23
  mode?: typeof modes[number];
@@ -5,7 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var THREE = require('three');
6
6
  var React = require('react');
7
7
  var create = require('zustand');
8
- var shallow = require('zustand/shallow');
9
8
  var Reconciler = require('react-reconciler');
10
9
  var scheduler = require('scheduler');
11
10
  var useAsset = require('use-asset');
@@ -37,7 +36,6 @@ function _interopNamespace(e) {
37
36
  var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
38
37
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
39
38
  var create__default = /*#__PURE__*/_interopDefault(create);
40
- var shallow__default = /*#__PURE__*/_interopDefault(shallow);
41
39
  var Reconciler__default = /*#__PURE__*/_interopDefault(Reconciler);
42
40
  var mergeRefs__default = /*#__PURE__*/_interopDefault(mergeRefs);
43
41
  var useMeasure__default = /*#__PURE__*/_interopDefault(useMeasure);
@@ -76,6 +74,23 @@ const is = {
76
74
  function makeId(event) {
77
75
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
78
76
  }
77
+ /** Release pointer captures.
78
+ * This is called by releasePointerCapture in the API, and when an object is removed.
79
+ */
80
+
81
+
82
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
83
+ const captureData = captures.get(obj);
84
+
85
+ if (captureData) {
86
+ captures.delete(obj); // If this was the last capturing object for this pointer
87
+
88
+ if (captures.size === 0) {
89
+ capturedMap.delete(pointerId);
90
+ captureData.target.releasePointerCapture(pointerId);
91
+ }
92
+ }
93
+ }
79
94
 
80
95
  function removeInteractivity(store, object) {
81
96
  const {
@@ -89,6 +104,9 @@ function removeInteractivity(store, object) {
89
104
  internal.hovered.delete(key);
90
105
  }
91
106
  });
107
+ internal.capturedMap.forEach((captures, pointerId) => {
108
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
109
+ });
92
110
  }
93
111
  function createEvents(store) {
94
112
  const temp = new THREE__namespace.Vector3();
@@ -168,7 +186,7 @@ function createEvents(store) {
168
186
  while (eventObject) {
169
187
  var _r3f2;
170
188
 
171
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.handlers.count) intersections.push({ ...intersect,
189
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
172
190
  eventObject
173
191
  });
174
192
  eventObject = eventObject.parent;
@@ -187,7 +205,9 @@ function createEvents(store) {
187
205
  // intersect.
188
206
 
189
207
  if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
190
- intersections.push(...internal.capturedMap.get(event.pointerId).values());
208
+ for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
209
+ intersections.push(captureData.intersection);
210
+ }
191
211
  }
192
212
 
193
213
  return intersections;
@@ -205,9 +225,6 @@ function createEvents(store) {
205
225
 
206
226
  if (intersections.length) {
207
227
  const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
208
-
209
- const releasePointerCapture = id => event.target.releasePointerCapture(id);
210
-
211
228
  const localState = {
212
229
  stopped: false
213
230
  };
@@ -220,23 +237,36 @@ function createEvents(store) {
220
237
  };
221
238
 
222
239
  const setPointerCapture = id => {
240
+ const captureData = {
241
+ intersection: hit,
242
+ target: event.target
243
+ };
244
+
223
245
  if (internal.capturedMap.has(id)) {
224
246
  // if the pointerId was previously captured, we add the hit to the
225
247
  // event capturedMap.
226
- internal.capturedMap.get(id).set(hit.eventObject, hit);
248
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
227
249
  } else {
228
250
  // if the pointerId was not previously captured, we create a map
229
251
  // containing the hitObject, and the hit. hitObject is used for
230
252
  // faster access.
231
- internal.capturedMap.set(id, new Map([[hit.eventObject, hit]]));
253
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
232
254
  } // Call the original event now
233
255
  event.target.setPointerCapture(id);
256
+ };
257
+
258
+ const releasePointerCapture = id => {
259
+ const captures = internal.capturedMap.get(id);
260
+
261
+ if (captures) {
262
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
263
+ }
234
264
  }; // Add native event props
235
265
 
236
266
 
237
- let extractEventProps = {};
267
+ let extractEventProps = {}; // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
238
268
 
239
- for (let prop in Object.getPrototypeOf(event)) {
269
+ for (let prop in event) {
240
270
  let property = event[prop]; // Only copy over atomics, leave functions alone as these should be
241
271
  // called as event.nativeEvent.fn()
242
272
 
@@ -305,13 +335,12 @@ function createEvents(store) {
305
335
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
306
336
  // we call onPointerOut and delete the object from the hovered-elements map
307
337
  if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
308
- var _r3f3;
309
-
310
338
  const eventObject = hoveredObj.eventObject;
311
- const handlers = (_r3f3 = eventObject.__r3f) == null ? void 0 : _r3f3.handlers;
339
+ const instance = eventObject.__r3f;
340
+ const handlers = instance == null ? void 0 : instance.handlers;
312
341
  internal.hovered.delete(makeId(hoveredObj));
313
342
 
314
- if (handlers != null && handlers.count) {
343
+ if (instance != null && instance.eventCount) {
315
344
  // Clear out intersects, they are outdated by now
316
345
  const data = { ...hoveredObj,
317
346
  intersections: hits || []
@@ -333,9 +362,8 @@ function createEvents(store) {
333
362
  case 'onLostPointerCapture':
334
363
  return event => {
335
364
  if ('pointerId' in event) {
336
- // this will be a problem if one target releases the pointerId
337
- // and another one is still keeping it, as the line below
338
- // indifferently deletes all capturing references.
365
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
366
+ // object that's getting removed.
339
367
  store.getState().internal.capturedMap.delete(event.pointerId);
340
368
  }
341
369
 
@@ -374,12 +402,11 @@ function createEvents(store) {
374
402
 
375
403
  if (isPointerMove) cancelPointer(hits);
376
404
  handleIntersects(hits, event, delta, data => {
377
- var _r3f4;
378
-
379
405
  const eventObject = data.eventObject;
380
- const handlers = (_r3f4 = eventObject.__r3f) == null ? void 0 : _r3f4.handlers; // Check presence of handlers
406
+ const instance = eventObject.__r3f;
407
+ const handlers = instance == null ? void 0 : instance.handlers; // Check presence of handlers
381
408
 
382
- if (!(handlers != null && handlers.count)) return;
409
+ if (!(instance != null && instance.eventCount)) return;
383
410
 
384
411
  if (isPointerMove) {
385
412
  // Move event ...
@@ -422,9 +449,9 @@ function createEvents(store) {
422
449
 
423
450
  function pointerMissed(event, objects) {
424
451
  objects.forEach(object => {
425
- var _r3f5;
452
+ var _r3f3;
426
453
 
427
- return (_r3f5 = object.__r3f) == null ? void 0 : _r3f5.handlers.onPointerMissed == null ? void 0 : _r3f5.handlers.onPointerMissed(event);
454
+ return (_r3f3 = object.__r3f) == null ? void 0 : _r3f3.handlers.onPointerMissed == null ? void 0 : _r3f3.handlers.onPointerMissed(event);
428
455
  });
429
456
  }
430
457
 
@@ -475,9 +502,8 @@ function prepare(object, state) {
475
502
  instance.__r3f = {
476
503
  root: null,
477
504
  memoizedProps: {},
478
- handlers: {
479
- count: 0
480
- },
505
+ eventCount: 0,
506
+ handlers: {},
481
507
  objects: [],
482
508
  parent: null,
483
509
  ...state
@@ -538,7 +564,7 @@ function createRenderer(roots) {
538
564
  }
539
565
 
540
566
  function applyProps(instance, data) {
541
- var _instance$__r3f3, _root$getState, _localState$handlers, _localState$handlers2, _instance$__r3f4;
567
+ var _instance$__r3f3, _root$getState, _instance$__r3f4;
542
568
 
543
569
  // Filter equals, events and reserved props
544
570
  const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
@@ -548,7 +574,7 @@ function createRenderer(roots) {
548
574
  memoized,
549
575
  changes
550
576
  } = isDiffSet(data) ? data : diffProps(instance, data);
551
- const prevHandlers = (_localState$handlers = localState.handlers) == null ? void 0 : _localState$handlers.count; // Prepare memoized props
577
+ const prevHandlers = localState.eventCount; // Prepare memoized props
552
578
 
553
579
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
554
580
  changes.forEach(([key, value, isEvent, keys]) => {
@@ -587,7 +613,7 @@ function createRenderer(roots) {
587
613
 
588
614
  if (isEvent) {
589
615
  if (value) localState.handlers[key] = value;else delete localState.handlers[key];
590
- localState.handlers.count = Object.keys(localState.handlers).length;
616
+ localState.eventCount = Object.keys(localState.handlers).length;
591
617
  } // Special treatment for objects with support for set/copy, and layers
592
618
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE__namespace.Layers)) {
593
619
  // If value is an array
@@ -617,16 +643,17 @@ function createRenderer(roots) {
617
643
  invalidateInstance(instance);
618
644
  });
619
645
 
620
- if (rootState.internal && instance.raycast && prevHandlers !== ((_localState$handlers2 = localState.handlers) == null ? void 0 : _localState$handlers2.count)) {
646
+ if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
621
647
  // Pre-emptively remove the instance from the interaction manager
622
648
  const index = rootState.internal.interaction.indexOf(instance);
623
649
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
624
650
 
625
- if (localState.handlers.count) rootState.internal.interaction.push(instance);
651
+ if (localState.eventCount) rootState.internal.interaction.push(instance);
626
652
  } // Call the update lifecycle when it is being updated
627
653
 
628
654
 
629
655
  if (changes.length && (_instance$__r3f4 = instance.__r3f) != null && _instance$__r3f4.parent) updateInstance(instance);
656
+ return instance;
630
657
  }
631
658
 
632
659
  function invalidateInstance(instance) {
@@ -812,7 +839,7 @@ function createRenderer(roots) {
812
839
  } else if (is.fun(detachFn)) {
813
840
  detachFn(child, parentInstance);
814
841
  }
815
- } else if (child.isObject3D) {
842
+ } else if (child.isObject3D && parentInstance.isObject3D) {
816
843
  var _child$__r3f;
817
844
 
818
845
  parentInstance.remove(child); // Remove interactivity
@@ -1030,7 +1057,7 @@ function createRenderer(roots) {
1030
1057
  };
1031
1058
  }
1032
1059
 
1033
- const isRenderer = def => def && !!def.render;
1060
+ const isRenderer = def => !!(def != null && def.render);
1034
1061
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1035
1062
  function calculateDpr(dpr) {
1036
1063
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
@@ -1058,11 +1085,11 @@ const createStore = (applyProps, invalidate, advance, props) => {
1058
1085
  if (shadows) {
1059
1086
  gl.shadowMap.enabled = true;
1060
1087
  if (typeof shadows === 'object') Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1061
- } // Set color management
1088
+ } // Set color preferences
1062
1089
 
1063
1090
 
1064
- if (!linear) gl.outputEncoding = THREE__namespace.sRGBEncoding;
1065
- if (!flat) gl.toneMapping = THREE__namespace.ACESFilmicToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1091
+ if (linear) gl.outputEncoding = THREE__namespace.LinearEncoding;
1092
+ if (flat) gl.toneMapping = THREE__namespace.NoToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1066
1093
 
1067
1094
  if (frameloop === 'never') {
1068
1095
  clock.stop();
@@ -1251,38 +1278,45 @@ const createStore = (applyProps, invalidate, advance, props) => {
1251
1278
  }
1252
1279
  }
1253
1280
  };
1254
- }); // Resize camera and renderer on changes to size and pixelratio
1281
+ });
1282
+ const state = rootState.getState(); // Resize camera and renderer on changes to size and pixelratio
1255
1283
 
1284
+ let oldSize = state.size;
1285
+ let oldDpr = state.viewport.dpr;
1256
1286
  rootState.subscribe(() => {
1257
1287
  const {
1258
1288
  camera,
1259
1289
  size,
1260
1290
  viewport,
1261
1291
  internal
1262
- } = rootState.getState(); // https://github.com/pmndrs/react-three-fiber/issues/92
1263
- // Do not mess with the camera if it belongs to the user
1264
-
1265
- if (!(internal.lastProps.camera instanceof THREE__namespace.Camera)) {
1266
- if (isOrthographicCamera(camera)) {
1267
- camera.left = size.width / -2;
1268
- camera.right = size.width / 2;
1269
- camera.top = size.height / 2;
1270
- camera.bottom = size.height / -2;
1271
- } else {
1272
- camera.aspect = size.width / size.height;
1273
- }
1292
+ } = rootState.getState();
1293
+
1294
+ if (size !== oldSize || viewport.dpr !== oldDpr) {
1295
+ // https://github.com/pmndrs/react-three-fiber/issues/92
1296
+ // Do not mess with the camera if it belongs to the user
1297
+ if (!(internal.lastProps.camera instanceof THREE__namespace.Camera)) {
1298
+ if (isOrthographicCamera(camera)) {
1299
+ camera.left = size.width / -2;
1300
+ camera.right = size.width / 2;
1301
+ camera.top = size.height / 2;
1302
+ camera.bottom = size.height / -2;
1303
+ } else {
1304
+ camera.aspect = size.width / size.height;
1305
+ }
1274
1306
 
1275
- camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1276
- // Update matrix world since the renderer is a frame late
1307
+ camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1308
+ // Update matrix world since the renderer is a frame late
1277
1309
 
1278
- camera.updateMatrixWorld();
1279
- } // Update renderer
1310
+ camera.updateMatrixWorld();
1311
+ } // Update renderer
1280
1312
 
1281
1313
 
1282
- gl.setPixelRatio(viewport.dpr);
1283
- gl.setSize(size.width, size.height);
1284
- }, state => [state.viewport.dpr, state.size], shallow__default['default']);
1285
- const state = rootState.getState(); // Update size
1314
+ gl.setPixelRatio(viewport.dpr);
1315
+ gl.setSize(size.width, size.height);
1316
+ oldSize = size;
1317
+ oldDpr = viewport.dpr;
1318
+ }
1319
+ }); // Update size
1286
1320
 
1287
1321
  if (size) state.setSize(size.width, size.height); // Invalidate on any change
1288
1322
 
@@ -1637,13 +1671,23 @@ const {
1637
1671
  applyProps
1638
1672
  } = createRenderer();
1639
1673
 
1640
- const createRendererInstance = (gl, canvas) => isRenderer(gl) ? gl : new THREE__namespace.WebGLRenderer({
1641
- powerPreference: 'high-performance',
1642
- canvas: canvas,
1643
- antialias: true,
1644
- alpha: true,
1645
- ...gl
1646
- });
1674
+ const createRendererInstance = (gl, canvas) => {
1675
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1676
+ if (isRenderer(customRenderer)) return customRenderer;
1677
+ const renderer = new THREE__namespace.WebGLRenderer({
1678
+ powerPreference: 'high-performance',
1679
+ canvas: canvas,
1680
+ antialias: true,
1681
+ alpha: true,
1682
+ ...gl
1683
+ }); // Set color management
1684
+
1685
+ renderer.outputEncoding = THREE__namespace.sRGBEncoding;
1686
+ renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping; // Set gl props
1687
+
1688
+ if (gl) applyProps(renderer, gl);
1689
+ return renderer;
1690
+ };
1647
1691
 
1648
1692
  function render(element, canvas, {
1649
1693
  gl,
@@ -5,7 +5,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
5
5
  var THREE = require('three');
6
6
  var React = require('react');
7
7
  var create = require('zustand');
8
- var shallow = require('zustand/shallow');
9
8
  var Reconciler = require('react-reconciler');
10
9
  var scheduler = require('scheduler');
11
10
  var useAsset = require('use-asset');
@@ -37,7 +36,6 @@ function _interopNamespace(e) {
37
36
  var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
38
37
  var React__namespace = /*#__PURE__*/_interopNamespace(React);
39
38
  var create__default = /*#__PURE__*/_interopDefault(create);
40
- var shallow__default = /*#__PURE__*/_interopDefault(shallow);
41
39
  var Reconciler__default = /*#__PURE__*/_interopDefault(Reconciler);
42
40
  var mergeRefs__default = /*#__PURE__*/_interopDefault(mergeRefs);
43
41
  var useMeasure__default = /*#__PURE__*/_interopDefault(useMeasure);
@@ -76,6 +74,23 @@ const is = {
76
74
  function makeId(event) {
77
75
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
78
76
  }
77
+ /** Release pointer captures.
78
+ * This is called by releasePointerCapture in the API, and when an object is removed.
79
+ */
80
+
81
+
82
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
83
+ const captureData = captures.get(obj);
84
+
85
+ if (captureData) {
86
+ captures.delete(obj); // If this was the last capturing object for this pointer
87
+
88
+ if (captures.size === 0) {
89
+ capturedMap.delete(pointerId);
90
+ captureData.target.releasePointerCapture(pointerId);
91
+ }
92
+ }
93
+ }
79
94
 
80
95
  function removeInteractivity(store, object) {
81
96
  const {
@@ -89,6 +104,9 @@ function removeInteractivity(store, object) {
89
104
  internal.hovered.delete(key);
90
105
  }
91
106
  });
107
+ internal.capturedMap.forEach((captures, pointerId) => {
108
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
109
+ });
92
110
  }
93
111
  function createEvents(store) {
94
112
  const temp = new THREE__namespace.Vector3();
@@ -168,7 +186,7 @@ function createEvents(store) {
168
186
  while (eventObject) {
169
187
  var _r3f2;
170
188
 
171
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.handlers.count) intersections.push({ ...intersect,
189
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
172
190
  eventObject
173
191
  });
174
192
  eventObject = eventObject.parent;
@@ -187,7 +205,9 @@ function createEvents(store) {
187
205
  // intersect.
188
206
 
189
207
  if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
190
- intersections.push(...internal.capturedMap.get(event.pointerId).values());
208
+ for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
209
+ intersections.push(captureData.intersection);
210
+ }
191
211
  }
192
212
 
193
213
  return intersections;
@@ -205,9 +225,6 @@ function createEvents(store) {
205
225
 
206
226
  if (intersections.length) {
207
227
  const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
208
-
209
- const releasePointerCapture = id => event.target.releasePointerCapture(id);
210
-
211
228
  const localState = {
212
229
  stopped: false
213
230
  };
@@ -220,23 +237,36 @@ function createEvents(store) {
220
237
  };
221
238
 
222
239
  const setPointerCapture = id => {
240
+ const captureData = {
241
+ intersection: hit,
242
+ target: event.target
243
+ };
244
+
223
245
  if (internal.capturedMap.has(id)) {
224
246
  // if the pointerId was previously captured, we add the hit to the
225
247
  // event capturedMap.
226
- internal.capturedMap.get(id).set(hit.eventObject, hit);
248
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
227
249
  } else {
228
250
  // if the pointerId was not previously captured, we create a map
229
251
  // containing the hitObject, and the hit. hitObject is used for
230
252
  // faster access.
231
- internal.capturedMap.set(id, new Map([[hit.eventObject, hit]]));
253
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
232
254
  } // Call the original event now
233
255
  event.target.setPointerCapture(id);
256
+ };
257
+
258
+ const releasePointerCapture = id => {
259
+ const captures = internal.capturedMap.get(id);
260
+
261
+ if (captures) {
262
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
263
+ }
234
264
  }; // Add native event props
235
265
 
236
266
 
237
- let extractEventProps = {};
267
+ let extractEventProps = {}; // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
238
268
 
239
- for (let prop in Object.getPrototypeOf(event)) {
269
+ for (let prop in event) {
240
270
  let property = event[prop]; // Only copy over atomics, leave functions alone as these should be
241
271
  // called as event.nativeEvent.fn()
242
272
 
@@ -305,13 +335,12 @@ function createEvents(store) {
305
335
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
306
336
  // we call onPointerOut and delete the object from the hovered-elements map
307
337
  if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
308
- var _r3f3;
309
-
310
338
  const eventObject = hoveredObj.eventObject;
311
- const handlers = (_r3f3 = eventObject.__r3f) == null ? void 0 : _r3f3.handlers;
339
+ const instance = eventObject.__r3f;
340
+ const handlers = instance == null ? void 0 : instance.handlers;
312
341
  internal.hovered.delete(makeId(hoveredObj));
313
342
 
314
- if (handlers != null && handlers.count) {
343
+ if (instance != null && instance.eventCount) {
315
344
  // Clear out intersects, they are outdated by now
316
345
  const data = { ...hoveredObj,
317
346
  intersections: hits || []
@@ -333,9 +362,8 @@ function createEvents(store) {
333
362
  case 'onLostPointerCapture':
334
363
  return event => {
335
364
  if ('pointerId' in event) {
336
- // this will be a problem if one target releases the pointerId
337
- // and another one is still keeping it, as the line below
338
- // indifferently deletes all capturing references.
365
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
366
+ // object that's getting removed.
339
367
  store.getState().internal.capturedMap.delete(event.pointerId);
340
368
  }
341
369
 
@@ -374,12 +402,11 @@ function createEvents(store) {
374
402
 
375
403
  if (isPointerMove) cancelPointer(hits);
376
404
  handleIntersects(hits, event, delta, data => {
377
- var _r3f4;
378
-
379
405
  const eventObject = data.eventObject;
380
- const handlers = (_r3f4 = eventObject.__r3f) == null ? void 0 : _r3f4.handlers; // Check presence of handlers
406
+ const instance = eventObject.__r3f;
407
+ const handlers = instance == null ? void 0 : instance.handlers; // Check presence of handlers
381
408
 
382
- if (!(handlers != null && handlers.count)) return;
409
+ if (!(instance != null && instance.eventCount)) return;
383
410
 
384
411
  if (isPointerMove) {
385
412
  // Move event ...
@@ -422,9 +449,9 @@ function createEvents(store) {
422
449
 
423
450
  function pointerMissed(event, objects) {
424
451
  objects.forEach(object => {
425
- var _r3f5;
452
+ var _r3f3;
426
453
 
427
- return (_r3f5 = object.__r3f) == null ? void 0 : _r3f5.handlers.onPointerMissed == null ? void 0 : _r3f5.handlers.onPointerMissed(event);
454
+ return (_r3f3 = object.__r3f) == null ? void 0 : _r3f3.handlers.onPointerMissed == null ? void 0 : _r3f3.handlers.onPointerMissed(event);
428
455
  });
429
456
  }
430
457
 
@@ -475,9 +502,8 @@ function prepare(object, state) {
475
502
  instance.__r3f = {
476
503
  root: null,
477
504
  memoizedProps: {},
478
- handlers: {
479
- count: 0
480
- },
505
+ eventCount: 0,
506
+ handlers: {},
481
507
  objects: [],
482
508
  parent: null,
483
509
  ...state
@@ -538,7 +564,7 @@ function createRenderer(roots) {
538
564
  }
539
565
 
540
566
  function applyProps(instance, data) {
541
- var _instance$__r3f3, _root$getState, _localState$handlers, _localState$handlers2, _instance$__r3f4;
567
+ var _instance$__r3f3, _root$getState, _instance$__r3f4;
542
568
 
543
569
  // Filter equals, events and reserved props
544
570
  const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
@@ -548,7 +574,7 @@ function createRenderer(roots) {
548
574
  memoized,
549
575
  changes
550
576
  } = isDiffSet(data) ? data : diffProps(instance, data);
551
- const prevHandlers = (_localState$handlers = localState.handlers) == null ? void 0 : _localState$handlers.count; // Prepare memoized props
577
+ const prevHandlers = localState.eventCount; // Prepare memoized props
552
578
 
553
579
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
554
580
  changes.forEach(([key, value, isEvent, keys]) => {
@@ -587,7 +613,7 @@ function createRenderer(roots) {
587
613
 
588
614
  if (isEvent) {
589
615
  if (value) localState.handlers[key] = value;else delete localState.handlers[key];
590
- localState.handlers.count = Object.keys(localState.handlers).length;
616
+ localState.eventCount = Object.keys(localState.handlers).length;
591
617
  } // Special treatment for objects with support for set/copy, and layers
592
618
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE__namespace.Layers)) {
593
619
  // If value is an array
@@ -617,16 +643,17 @@ function createRenderer(roots) {
617
643
  invalidateInstance(instance);
618
644
  });
619
645
 
620
- if (rootState.internal && instance.raycast && prevHandlers !== ((_localState$handlers2 = localState.handlers) == null ? void 0 : _localState$handlers2.count)) {
646
+ if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
621
647
  // Pre-emptively remove the instance from the interaction manager
622
648
  const index = rootState.internal.interaction.indexOf(instance);
623
649
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
624
650
 
625
- if (localState.handlers.count) rootState.internal.interaction.push(instance);
651
+ if (localState.eventCount) rootState.internal.interaction.push(instance);
626
652
  } // Call the update lifecycle when it is being updated
627
653
 
628
654
 
629
655
  if (changes.length && (_instance$__r3f4 = instance.__r3f) != null && _instance$__r3f4.parent) updateInstance(instance);
656
+ return instance;
630
657
  }
631
658
 
632
659
  function invalidateInstance(instance) {
@@ -812,7 +839,7 @@ function createRenderer(roots) {
812
839
  } else if (is.fun(detachFn)) {
813
840
  detachFn(child, parentInstance);
814
841
  }
815
- } else if (child.isObject3D) {
842
+ } else if (child.isObject3D && parentInstance.isObject3D) {
816
843
  var _child$__r3f;
817
844
 
818
845
  parentInstance.remove(child); // Remove interactivity
@@ -1030,7 +1057,7 @@ function createRenderer(roots) {
1030
1057
  };
1031
1058
  }
1032
1059
 
1033
- const isRenderer = def => def && !!def.render;
1060
+ const isRenderer = def => !!(def != null && def.render);
1034
1061
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1035
1062
  function calculateDpr(dpr) {
1036
1063
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
@@ -1058,11 +1085,11 @@ const createStore = (applyProps, invalidate, advance, props) => {
1058
1085
  if (shadows) {
1059
1086
  gl.shadowMap.enabled = true;
1060
1087
  if (typeof shadows === 'object') Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1061
- } // Set color management
1088
+ } // Set color preferences
1062
1089
 
1063
1090
 
1064
- if (!linear) gl.outputEncoding = THREE__namespace.sRGBEncoding;
1065
- if (!flat) gl.toneMapping = THREE__namespace.ACESFilmicToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1091
+ if (linear) gl.outputEncoding = THREE__namespace.LinearEncoding;
1092
+ if (flat) gl.toneMapping = THREE__namespace.NoToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1066
1093
 
1067
1094
  if (frameloop === 'never') {
1068
1095
  clock.stop();
@@ -1251,38 +1278,45 @@ const createStore = (applyProps, invalidate, advance, props) => {
1251
1278
  }
1252
1279
  }
1253
1280
  };
1254
- }); // Resize camera and renderer on changes to size and pixelratio
1281
+ });
1282
+ const state = rootState.getState(); // Resize camera and renderer on changes to size and pixelratio
1255
1283
 
1284
+ let oldSize = state.size;
1285
+ let oldDpr = state.viewport.dpr;
1256
1286
  rootState.subscribe(() => {
1257
1287
  const {
1258
1288
  camera,
1259
1289
  size,
1260
1290
  viewport,
1261
1291
  internal
1262
- } = rootState.getState(); // https://github.com/pmndrs/react-three-fiber/issues/92
1263
- // Do not mess with the camera if it belongs to the user
1264
-
1265
- if (!(internal.lastProps.camera instanceof THREE__namespace.Camera)) {
1266
- if (isOrthographicCamera(camera)) {
1267
- camera.left = size.width / -2;
1268
- camera.right = size.width / 2;
1269
- camera.top = size.height / 2;
1270
- camera.bottom = size.height / -2;
1271
- } else {
1272
- camera.aspect = size.width / size.height;
1273
- }
1292
+ } = rootState.getState();
1293
+
1294
+ if (size !== oldSize || viewport.dpr !== oldDpr) {
1295
+ // https://github.com/pmndrs/react-three-fiber/issues/92
1296
+ // Do not mess with the camera if it belongs to the user
1297
+ if (!(internal.lastProps.camera instanceof THREE__namespace.Camera)) {
1298
+ if (isOrthographicCamera(camera)) {
1299
+ camera.left = size.width / -2;
1300
+ camera.right = size.width / 2;
1301
+ camera.top = size.height / 2;
1302
+ camera.bottom = size.height / -2;
1303
+ } else {
1304
+ camera.aspect = size.width / size.height;
1305
+ }
1274
1306
 
1275
- camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1276
- // Update matrix world since the renderer is a frame late
1307
+ camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1308
+ // Update matrix world since the renderer is a frame late
1277
1309
 
1278
- camera.updateMatrixWorld();
1279
- } // Update renderer
1310
+ camera.updateMatrixWorld();
1311
+ } // Update renderer
1280
1312
 
1281
1313
 
1282
- gl.setPixelRatio(viewport.dpr);
1283
- gl.setSize(size.width, size.height);
1284
- }, state => [state.viewport.dpr, state.size], shallow__default['default']);
1285
- const state = rootState.getState(); // Update size
1314
+ gl.setPixelRatio(viewport.dpr);
1315
+ gl.setSize(size.width, size.height);
1316
+ oldSize = size;
1317
+ oldDpr = viewport.dpr;
1318
+ }
1319
+ }); // Update size
1286
1320
 
1287
1321
  if (size) state.setSize(size.width, size.height); // Invalidate on any change
1288
1322
 
@@ -1637,13 +1671,23 @@ const {
1637
1671
  applyProps
1638
1672
  } = createRenderer();
1639
1673
 
1640
- const createRendererInstance = (gl, canvas) => isRenderer(gl) ? gl : new THREE__namespace.WebGLRenderer({
1641
- powerPreference: 'high-performance',
1642
- canvas: canvas,
1643
- antialias: true,
1644
- alpha: true,
1645
- ...gl
1646
- });
1674
+ const createRendererInstance = (gl, canvas) => {
1675
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1676
+ if (isRenderer(customRenderer)) return customRenderer;
1677
+ const renderer = new THREE__namespace.WebGLRenderer({
1678
+ powerPreference: 'high-performance',
1679
+ canvas: canvas,
1680
+ antialias: true,
1681
+ alpha: true,
1682
+ ...gl
1683
+ }); // Set color management
1684
+
1685
+ renderer.outputEncoding = THREE__namespace.sRGBEncoding;
1686
+ renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping; // Set gl props
1687
+
1688
+ if (gl) applyProps(renderer, gl);
1689
+ return renderer;
1690
+ };
1647
1691
 
1648
1692
  function render(element, canvas, {
1649
1693
  gl,
@@ -1,7 +1,6 @@
1
1
  import * as THREE from 'three';
2
2
  import * as React from 'react';
3
3
  import create from 'zustand';
4
- import shallow from 'zustand/shallow';
5
4
  import Reconciler from 'react-reconciler';
6
5
  import { unstable_now, unstable_runWithPriority, unstable_IdlePriority } from 'scheduler';
7
6
  import { useAsset } from 'use-asset';
@@ -42,6 +41,23 @@ const is = {
42
41
  function makeId(event) {
43
42
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
44
43
  }
44
+ /** Release pointer captures.
45
+ * This is called by releasePointerCapture in the API, and when an object is removed.
46
+ */
47
+
48
+
49
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
50
+ const captureData = captures.get(obj);
51
+
52
+ if (captureData) {
53
+ captures.delete(obj); // If this was the last capturing object for this pointer
54
+
55
+ if (captures.size === 0) {
56
+ capturedMap.delete(pointerId);
57
+ captureData.target.releasePointerCapture(pointerId);
58
+ }
59
+ }
60
+ }
45
61
 
46
62
  function removeInteractivity(store, object) {
47
63
  const {
@@ -55,6 +71,9 @@ function removeInteractivity(store, object) {
55
71
  internal.hovered.delete(key);
56
72
  }
57
73
  });
74
+ internal.capturedMap.forEach((captures, pointerId) => {
75
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
76
+ });
58
77
  }
59
78
  function createEvents(store) {
60
79
  const temp = new THREE.Vector3();
@@ -134,7 +153,7 @@ function createEvents(store) {
134
153
  while (eventObject) {
135
154
  var _r3f2;
136
155
 
137
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.handlers.count) intersections.push({ ...intersect,
156
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
138
157
  eventObject
139
158
  });
140
159
  eventObject = eventObject.parent;
@@ -153,7 +172,9 @@ function createEvents(store) {
153
172
  // intersect.
154
173
 
155
174
  if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
156
- intersections.push(...internal.capturedMap.get(event.pointerId).values());
175
+ for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
176
+ intersections.push(captureData.intersection);
177
+ }
157
178
  }
158
179
 
159
180
  return intersections;
@@ -171,9 +192,6 @@ function createEvents(store) {
171
192
 
172
193
  if (intersections.length) {
173
194
  const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
174
-
175
- const releasePointerCapture = id => event.target.releasePointerCapture(id);
176
-
177
195
  const localState = {
178
196
  stopped: false
179
197
  };
@@ -186,23 +204,36 @@ function createEvents(store) {
186
204
  };
187
205
 
188
206
  const setPointerCapture = id => {
207
+ const captureData = {
208
+ intersection: hit,
209
+ target: event.target
210
+ };
211
+
189
212
  if (internal.capturedMap.has(id)) {
190
213
  // if the pointerId was previously captured, we add the hit to the
191
214
  // event capturedMap.
192
- internal.capturedMap.get(id).set(hit.eventObject, hit);
215
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
193
216
  } else {
194
217
  // if the pointerId was not previously captured, we create a map
195
218
  // containing the hitObject, and the hit. hitObject is used for
196
219
  // faster access.
197
- internal.capturedMap.set(id, new Map([[hit.eventObject, hit]]));
220
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
198
221
  } // Call the original event now
199
222
  event.target.setPointerCapture(id);
223
+ };
224
+
225
+ const releasePointerCapture = id => {
226
+ const captures = internal.capturedMap.get(id);
227
+
228
+ if (captures) {
229
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
230
+ }
200
231
  }; // Add native event props
201
232
 
202
233
 
203
- let extractEventProps = {};
234
+ let extractEventProps = {}; // This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
204
235
 
205
- for (let prop in Object.getPrototypeOf(event)) {
236
+ for (let prop in event) {
206
237
  let property = event[prop]; // Only copy over atomics, leave functions alone as these should be
207
238
  // called as event.nativeEvent.fn()
208
239
 
@@ -271,13 +302,12 @@ function createEvents(store) {
271
302
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
272
303
  // we call onPointerOut and delete the object from the hovered-elements map
273
304
  if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
274
- var _r3f3;
275
-
276
305
  const eventObject = hoveredObj.eventObject;
277
- const handlers = (_r3f3 = eventObject.__r3f) == null ? void 0 : _r3f3.handlers;
306
+ const instance = eventObject.__r3f;
307
+ const handlers = instance == null ? void 0 : instance.handlers;
278
308
  internal.hovered.delete(makeId(hoveredObj));
279
309
 
280
- if (handlers != null && handlers.count) {
310
+ if (instance != null && instance.eventCount) {
281
311
  // Clear out intersects, they are outdated by now
282
312
  const data = { ...hoveredObj,
283
313
  intersections: hits || []
@@ -299,9 +329,8 @@ function createEvents(store) {
299
329
  case 'onLostPointerCapture':
300
330
  return event => {
301
331
  if ('pointerId' in event) {
302
- // this will be a problem if one target releases the pointerId
303
- // and another one is still keeping it, as the line below
304
- // indifferently deletes all capturing references.
332
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
333
+ // object that's getting removed.
305
334
  store.getState().internal.capturedMap.delete(event.pointerId);
306
335
  }
307
336
 
@@ -340,12 +369,11 @@ function createEvents(store) {
340
369
 
341
370
  if (isPointerMove) cancelPointer(hits);
342
371
  handleIntersects(hits, event, delta, data => {
343
- var _r3f4;
344
-
345
372
  const eventObject = data.eventObject;
346
- const handlers = (_r3f4 = eventObject.__r3f) == null ? void 0 : _r3f4.handlers; // Check presence of handlers
373
+ const instance = eventObject.__r3f;
374
+ const handlers = instance == null ? void 0 : instance.handlers; // Check presence of handlers
347
375
 
348
- if (!(handlers != null && handlers.count)) return;
376
+ if (!(instance != null && instance.eventCount)) return;
349
377
 
350
378
  if (isPointerMove) {
351
379
  // Move event ...
@@ -388,9 +416,9 @@ function createEvents(store) {
388
416
 
389
417
  function pointerMissed(event, objects) {
390
418
  objects.forEach(object => {
391
- var _r3f5;
419
+ var _r3f3;
392
420
 
393
- return (_r3f5 = object.__r3f) == null ? void 0 : _r3f5.handlers.onPointerMissed == null ? void 0 : _r3f5.handlers.onPointerMissed(event);
421
+ return (_r3f3 = object.__r3f) == null ? void 0 : _r3f3.handlers.onPointerMissed == null ? void 0 : _r3f3.handlers.onPointerMissed(event);
394
422
  });
395
423
  }
396
424
 
@@ -441,9 +469,8 @@ function prepare(object, state) {
441
469
  instance.__r3f = {
442
470
  root: null,
443
471
  memoizedProps: {},
444
- handlers: {
445
- count: 0
446
- },
472
+ eventCount: 0,
473
+ handlers: {},
447
474
  objects: [],
448
475
  parent: null,
449
476
  ...state
@@ -504,7 +531,7 @@ function createRenderer(roots) {
504
531
  }
505
532
 
506
533
  function applyProps(instance, data) {
507
- var _instance$__r3f3, _root$getState, _localState$handlers, _localState$handlers2, _instance$__r3f4;
534
+ var _instance$__r3f3, _root$getState, _instance$__r3f4;
508
535
 
509
536
  // Filter equals, events and reserved props
510
537
  const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
@@ -514,7 +541,7 @@ function createRenderer(roots) {
514
541
  memoized,
515
542
  changes
516
543
  } = isDiffSet(data) ? data : diffProps(instance, data);
517
- const prevHandlers = (_localState$handlers = localState.handlers) == null ? void 0 : _localState$handlers.count; // Prepare memoized props
544
+ const prevHandlers = localState.eventCount; // Prepare memoized props
518
545
 
519
546
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
520
547
  changes.forEach(([key, value, isEvent, keys]) => {
@@ -553,7 +580,7 @@ function createRenderer(roots) {
553
580
 
554
581
  if (isEvent) {
555
582
  if (value) localState.handlers[key] = value;else delete localState.handlers[key];
556
- localState.handlers.count = Object.keys(localState.handlers).length;
583
+ localState.eventCount = Object.keys(localState.handlers).length;
557
584
  } // Special treatment for objects with support for set/copy, and layers
558
585
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE.Layers)) {
559
586
  // If value is an array
@@ -583,16 +610,17 @@ function createRenderer(roots) {
583
610
  invalidateInstance(instance);
584
611
  });
585
612
 
586
- if (rootState.internal && instance.raycast && prevHandlers !== ((_localState$handlers2 = localState.handlers) == null ? void 0 : _localState$handlers2.count)) {
613
+ if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
587
614
  // Pre-emptively remove the instance from the interaction manager
588
615
  const index = rootState.internal.interaction.indexOf(instance);
589
616
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
590
617
 
591
- if (localState.handlers.count) rootState.internal.interaction.push(instance);
618
+ if (localState.eventCount) rootState.internal.interaction.push(instance);
592
619
  } // Call the update lifecycle when it is being updated
593
620
 
594
621
 
595
622
  if (changes.length && (_instance$__r3f4 = instance.__r3f) != null && _instance$__r3f4.parent) updateInstance(instance);
623
+ return instance;
596
624
  }
597
625
 
598
626
  function invalidateInstance(instance) {
@@ -778,7 +806,7 @@ function createRenderer(roots) {
778
806
  } else if (is.fun(detachFn)) {
779
807
  detachFn(child, parentInstance);
780
808
  }
781
- } else if (child.isObject3D) {
809
+ } else if (child.isObject3D && parentInstance.isObject3D) {
782
810
  var _child$__r3f;
783
811
 
784
812
  parentInstance.remove(child); // Remove interactivity
@@ -996,7 +1024,7 @@ function createRenderer(roots) {
996
1024
  };
997
1025
  }
998
1026
 
999
- const isRenderer = def => def && !!def.render;
1027
+ const isRenderer = def => !!(def != null && def.render);
1000
1028
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1001
1029
  function calculateDpr(dpr) {
1002
1030
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
@@ -1024,11 +1052,11 @@ const createStore = (applyProps, invalidate, advance, props) => {
1024
1052
  if (shadows) {
1025
1053
  gl.shadowMap.enabled = true;
1026
1054
  if (typeof shadows === 'object') Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE.PCFSoftShadowMap;
1027
- } // Set color management
1055
+ } // Set color preferences
1028
1056
 
1029
1057
 
1030
- if (!linear) gl.outputEncoding = THREE.sRGBEncoding;
1031
- if (!flat) gl.toneMapping = THREE.ACESFilmicToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1058
+ if (linear) gl.outputEncoding = THREE.LinearEncoding;
1059
+ if (flat) gl.toneMapping = THREE.NoToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1032
1060
 
1033
1061
  if (frameloop === 'never') {
1034
1062
  clock.stop();
@@ -1217,38 +1245,45 @@ const createStore = (applyProps, invalidate, advance, props) => {
1217
1245
  }
1218
1246
  }
1219
1247
  };
1220
- }); // Resize camera and renderer on changes to size and pixelratio
1248
+ });
1249
+ const state = rootState.getState(); // Resize camera and renderer on changes to size and pixelratio
1221
1250
 
1251
+ let oldSize = state.size;
1252
+ let oldDpr = state.viewport.dpr;
1222
1253
  rootState.subscribe(() => {
1223
1254
  const {
1224
1255
  camera,
1225
1256
  size,
1226
1257
  viewport,
1227
1258
  internal
1228
- } = rootState.getState(); // https://github.com/pmndrs/react-three-fiber/issues/92
1229
- // Do not mess with the camera if it belongs to the user
1230
-
1231
- if (!(internal.lastProps.camera instanceof THREE.Camera)) {
1232
- if (isOrthographicCamera(camera)) {
1233
- camera.left = size.width / -2;
1234
- camera.right = size.width / 2;
1235
- camera.top = size.height / 2;
1236
- camera.bottom = size.height / -2;
1237
- } else {
1238
- camera.aspect = size.width / size.height;
1239
- }
1259
+ } = rootState.getState();
1260
+
1261
+ if (size !== oldSize || viewport.dpr !== oldDpr) {
1262
+ // https://github.com/pmndrs/react-three-fiber/issues/92
1263
+ // Do not mess with the camera if it belongs to the user
1264
+ if (!(internal.lastProps.camera instanceof THREE.Camera)) {
1265
+ if (isOrthographicCamera(camera)) {
1266
+ camera.left = size.width / -2;
1267
+ camera.right = size.width / 2;
1268
+ camera.top = size.height / 2;
1269
+ camera.bottom = size.height / -2;
1270
+ } else {
1271
+ camera.aspect = size.width / size.height;
1272
+ }
1240
1273
 
1241
- camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1242
- // Update matrix world since the renderer is a frame late
1274
+ camera.updateProjectionMatrix(); // https://github.com/pmndrs/react-three-fiber/issues/178
1275
+ // Update matrix world since the renderer is a frame late
1243
1276
 
1244
- camera.updateMatrixWorld();
1245
- } // Update renderer
1277
+ camera.updateMatrixWorld();
1278
+ } // Update renderer
1246
1279
 
1247
1280
 
1248
- gl.setPixelRatio(viewport.dpr);
1249
- gl.setSize(size.width, size.height);
1250
- }, state => [state.viewport.dpr, state.size], shallow);
1251
- const state = rootState.getState(); // Update size
1281
+ gl.setPixelRatio(viewport.dpr);
1282
+ gl.setSize(size.width, size.height);
1283
+ oldSize = size;
1284
+ oldDpr = viewport.dpr;
1285
+ }
1286
+ }); // Update size
1252
1287
 
1253
1288
  if (size) state.setSize(size.width, size.height); // Invalidate on any change
1254
1289
 
@@ -1603,13 +1638,23 @@ const {
1603
1638
  applyProps
1604
1639
  } = createRenderer();
1605
1640
 
1606
- const createRendererInstance = (gl, canvas) => isRenderer(gl) ? gl : new THREE.WebGLRenderer({
1607
- powerPreference: 'high-performance',
1608
- canvas: canvas,
1609
- antialias: true,
1610
- alpha: true,
1611
- ...gl
1612
- });
1641
+ const createRendererInstance = (gl, canvas) => {
1642
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1643
+ if (isRenderer(customRenderer)) return customRenderer;
1644
+ const renderer = new THREE.WebGLRenderer({
1645
+ powerPreference: 'high-performance',
1646
+ canvas: canvas,
1647
+ antialias: true,
1648
+ alpha: true,
1649
+ ...gl
1650
+ }); // Set color management
1651
+
1652
+ renderer.outputEncoding = THREE.sRGBEncoding;
1653
+ renderer.toneMapping = THREE.ACESFilmicToneMapping; // Set gl props
1654
+
1655
+ if (gl) applyProps(renderer, gl);
1656
+ return renderer;
1657
+ };
1613
1658
 
1614
1659
  function render(element, canvas, {
1615
1660
  gl,
@@ -0,0 +1,11 @@
1
+ // are you seeing an error that a default export doesn't exist but your source file has a default export?
2
+ // you should run `yarn` or `yarn preconstruct dev` if preconstruct dev isn't in your postinstall hook
3
+
4
+ // curious why you need to?
5
+ // this file exists so that you can import from the entrypoint normally
6
+ // except that it points to your source file and you don't need to run build constantly
7
+ // which means we need to re-export all of the modules from your source file
8
+ // and since export * doesn't include default exports, we need to read your source file
9
+ // to check for a default export and re-export it if it exists
10
+ // it's not ideal, but it works pretty well ¯\_(ツ)_/¯
11
+ export * from "../../src/native";
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ // this file might look strange and you might be wondering what it's for
3
+ // it's lets you import your source files by importing this entrypoint
4
+ // as you would import it if it was built with preconstruct build
5
+ // this file is slightly different to some others though
6
+ // it has a require hook which compiles your code with Babel
7
+ // this means that you don't have to set up @babel/register or anything like that
8
+ // but you can still require this module and it'll be compiled
9
+
10
+ // this bit of code imports the require hook and registers it
11
+ let unregister = require("../../../../node_modules/@preconstruct/hook").___internalHook(typeof __dirname === 'undefined' ? undefined : __dirname, "../../../..", "../..");
12
+
13
+ // this re-exports the source file
14
+ module.exports = require("../../src/native.tsx");
15
+
16
+ unregister();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/fiber",
3
- "version": "7.0.15",
3
+ "version": "7.0.19",
4
4
  "description": "A React renderer for Threejs",
5
5
  "keywords": [
6
6
  "react",