@react-three/fiber 7.0.14 → 7.0.18

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.18
4
+
5
+ ### Patch Changes
6
+
7
+ - 6780f58: fix unmount pointer capture
8
+
9
+ ## 7.0.17
10
+
11
+ ### Patch Changes
12
+
13
+ - 894c550: fix: event count
14
+
15
+ ## 7.0.16
16
+
17
+ ### Patch Changes
18
+
19
+ - c7a4220: patch: applyprops returns the same instance
20
+
21
+ ## 7.0.15
22
+
23
+ ### Patch Changes
24
+
25
+ - c5645e8: fix primitve leftovers on switch
26
+
3
27
  ## 7.0.14
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];
@@ -76,6 +76,23 @@ const is = {
76
76
  function makeId(event) {
77
77
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
78
78
  }
79
+ /** Release pointer captures.
80
+ * This is called by releasePointerCapture in the API, and when an object is removed.
81
+ */
82
+
83
+
84
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
85
+ const captureData = captures.get(obj);
86
+
87
+ if (captureData) {
88
+ captures.delete(obj); // If this was the last capturing object for this pointer
89
+
90
+ if (captures.size === 0) {
91
+ capturedMap.delete(pointerId);
92
+ captureData.target.releasePointerCapture(pointerId);
93
+ }
94
+ }
95
+ }
79
96
 
80
97
  function removeInteractivity(store, object) {
81
98
  const {
@@ -89,6 +106,9 @@ function removeInteractivity(store, object) {
89
106
  internal.hovered.delete(key);
90
107
  }
91
108
  });
109
+ internal.capturedMap.forEach((captures, pointerId) => {
110
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
111
+ });
92
112
  }
93
113
  function createEvents(store) {
94
114
  const temp = new THREE__namespace.Vector3();
@@ -168,7 +188,7 @@ function createEvents(store) {
168
188
  while (eventObject) {
169
189
  var _r3f2;
170
190
 
171
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.handlers.count) intersections.push({ ...intersect,
191
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
172
192
  eventObject
173
193
  });
174
194
  eventObject = eventObject.parent;
@@ -187,7 +207,9 @@ function createEvents(store) {
187
207
  // intersect.
188
208
 
189
209
  if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
190
- intersections.push(...internal.capturedMap.get(event.pointerId).values());
210
+ for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
211
+ intersections.push(captureData.intersection);
212
+ }
191
213
  }
192
214
 
193
215
  return intersections;
@@ -205,9 +227,6 @@ function createEvents(store) {
205
227
 
206
228
  if (intersections.length) {
207
229
  const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
208
-
209
- const releasePointerCapture = id => event.target.releasePointerCapture(id);
210
-
211
230
  const localState = {
212
231
  stopped: false
213
232
  };
@@ -220,23 +239,36 @@ function createEvents(store) {
220
239
  };
221
240
 
222
241
  const setPointerCapture = id => {
242
+ const captureData = {
243
+ intersection: hit,
244
+ target: event.target
245
+ };
246
+
223
247
  if (internal.capturedMap.has(id)) {
224
248
  // if the pointerId was previously captured, we add the hit to the
225
249
  // event capturedMap.
226
- internal.capturedMap.get(id).set(hit.eventObject, hit);
250
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
227
251
  } else {
228
252
  // if the pointerId was not previously captured, we create a map
229
253
  // containing the hitObject, and the hit. hitObject is used for
230
254
  // faster access.
231
- internal.capturedMap.set(id, new Map([[hit.eventObject, hit]]));
255
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
232
256
  } // Call the original event now
233
257
  event.target.setPointerCapture(id);
258
+ };
259
+
260
+ const releasePointerCapture = id => {
261
+ const captures = internal.capturedMap.get(id);
262
+
263
+ if (captures) {
264
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
265
+ }
234
266
  }; // Add native event props
235
267
 
236
268
 
237
- let extractEventProps = {};
269
+ 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
270
 
239
- for (let prop in Object.getPrototypeOf(event)) {
271
+ for (let prop in event) {
240
272
  let property = event[prop]; // Only copy over atomics, leave functions alone as these should be
241
273
  // called as event.nativeEvent.fn()
242
274
 
@@ -305,13 +337,12 @@ function createEvents(store) {
305
337
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
306
338
  // we call onPointerOut and delete the object from the hovered-elements map
307
339
  if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
308
- var _r3f3;
309
-
310
340
  const eventObject = hoveredObj.eventObject;
311
- const handlers = (_r3f3 = eventObject.__r3f) == null ? void 0 : _r3f3.handlers;
341
+ const instance = eventObject.__r3f;
342
+ const handlers = instance == null ? void 0 : instance.handlers;
312
343
  internal.hovered.delete(makeId(hoveredObj));
313
344
 
314
- if (handlers != null && handlers.count) {
345
+ if (instance != null && instance.eventCount) {
315
346
  // Clear out intersects, they are outdated by now
316
347
  const data = { ...hoveredObj,
317
348
  intersections: hits || []
@@ -333,9 +364,8 @@ function createEvents(store) {
333
364
  case 'onLostPointerCapture':
334
365
  return event => {
335
366
  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.
367
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
368
+ // object that's getting removed.
339
369
  store.getState().internal.capturedMap.delete(event.pointerId);
340
370
  }
341
371
 
@@ -374,12 +404,11 @@ function createEvents(store) {
374
404
 
375
405
  if (isPointerMove) cancelPointer(hits);
376
406
  handleIntersects(hits, event, delta, data => {
377
- var _r3f4;
378
-
379
407
  const eventObject = data.eventObject;
380
- const handlers = (_r3f4 = eventObject.__r3f) == null ? void 0 : _r3f4.handlers; // Check presence of handlers
408
+ const instance = eventObject.__r3f;
409
+ const handlers = instance == null ? void 0 : instance.handlers; // Check presence of handlers
381
410
 
382
- if (!(handlers != null && handlers.count)) return;
411
+ if (!(instance != null && instance.eventCount)) return;
383
412
 
384
413
  if (isPointerMove) {
385
414
  // Move event ...
@@ -422,9 +451,9 @@ function createEvents(store) {
422
451
 
423
452
  function pointerMissed(event, objects) {
424
453
  objects.forEach(object => {
425
- var _r3f5;
454
+ var _r3f3;
426
455
 
427
- return (_r3f5 = object.__r3f) == null ? void 0 : _r3f5.handlers.onPointerMissed == null ? void 0 : _r3f5.handlers.onPointerMissed(event);
456
+ return (_r3f3 = object.__r3f) == null ? void 0 : _r3f3.handlers.onPointerMissed == null ? void 0 : _r3f3.handlers.onPointerMissed(event);
428
457
  });
429
458
  }
430
459
 
@@ -475,9 +504,8 @@ function prepare(object, state) {
475
504
  instance.__r3f = {
476
505
  root: null,
477
506
  memoizedProps: {},
478
- handlers: {
479
- count: 0
480
- },
507
+ eventCount: 0,
508
+ handlers: {},
481
509
  objects: [],
482
510
  parent: null,
483
511
  ...state
@@ -538,7 +566,7 @@ function createRenderer(roots) {
538
566
  }
539
567
 
540
568
  function applyProps(instance, data) {
541
- var _instance$__r3f3, _root$getState, _localState$handlers, _localState$handlers2, _instance$__r3f4;
569
+ var _instance$__r3f3, _root$getState, _instance$__r3f4;
542
570
 
543
571
  // Filter equals, events and reserved props
544
572
  const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
@@ -548,7 +576,7 @@ function createRenderer(roots) {
548
576
  memoized,
549
577
  changes
550
578
  } = isDiffSet(data) ? data : diffProps(instance, data);
551
- const prevHandlers = (_localState$handlers = localState.handlers) == null ? void 0 : _localState$handlers.count; // Prepare memoized props
579
+ const prevHandlers = localState.eventCount; // Prepare memoized props
552
580
 
553
581
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
554
582
  changes.forEach(([key, value, isEvent, keys]) => {
@@ -587,7 +615,7 @@ function createRenderer(roots) {
587
615
 
588
616
  if (isEvent) {
589
617
  if (value) localState.handlers[key] = value;else delete localState.handlers[key];
590
- localState.handlers.count = Object.keys(localState.handlers).length;
618
+ localState.eventCount = Object.keys(localState.handlers).length;
591
619
  } // Special treatment for objects with support for set/copy, and layers
592
620
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE__namespace.Layers)) {
593
621
  // If value is an array
@@ -617,16 +645,17 @@ function createRenderer(roots) {
617
645
  invalidateInstance(instance);
618
646
  });
619
647
 
620
- if (rootState.internal && instance.raycast && prevHandlers !== ((_localState$handlers2 = localState.handlers) == null ? void 0 : _localState$handlers2.count)) {
648
+ if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
621
649
  // Pre-emptively remove the instance from the interaction manager
622
650
  const index = rootState.internal.interaction.indexOf(instance);
623
651
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
624
652
 
625
- if (localState.handlers.count) rootState.internal.interaction.push(instance);
653
+ if (localState.eventCount) rootState.internal.interaction.push(instance);
626
654
  } // Call the update lifecycle when it is being updated
627
655
 
628
656
 
629
657
  if (changes.length && (_instance$__r3f4 = instance.__r3f) != null && _instance$__r3f4.parent) updateInstance(instance);
658
+ return instance;
630
659
  }
631
660
 
632
661
  function invalidateInstance(instance) {
@@ -669,14 +698,13 @@ function createRenderer(roots) {
669
698
  });
670
699
  } else {
671
700
  const target = catalogue[name] || THREE__namespace[name];
672
- 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`;
673
- const isArgsArr = is.arr(args); // Instanciate new object, link it to the root
701
+ 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
702
+ // Append memoized props with args so it's not forgotten
674
703
 
675
- instance = prepare(isArgsArr ? new target(...args) : new target(args), {
704
+ instance = prepare(new target(...args), {
676
705
  root,
677
- // append memoized props with args so it's not forgotten
678
706
  memoizedProps: {
679
- args: isArgsArr && args.length === 0 ? null : args
707
+ args: args.length === 0 ? null : args
680
708
  }
681
709
  });
682
710
  } // Auto-attach geometries and materials
@@ -813,7 +841,7 @@ function createRenderer(roots) {
813
841
  } else if (is.fun(detachFn)) {
814
842
  detachFn(child, parentInstance);
815
843
  }
816
- } else if (child.isObject3D) {
844
+ } else if (child.isObject3D && parentInstance.isObject3D) {
817
845
  var _child$__r3f;
818
846
 
819
847
  parentInstance.remove(child); // Remove interactivity
@@ -875,8 +903,9 @@ function createRenderer(roots) {
875
903
  const newInstance = createInstance(type, newProps, instance.__r3f.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
876
904
  // When args change the instance has to be re-constructed, which then
877
905
  // forces r3f to re-parent the children and non-scene objects
906
+ // This can not include primitives, which should not have declarative children
878
907
 
879
- if (instance.children) {
908
+ if (type !== 'primitive' && instance.children) {
880
909
  instance.children.forEach(child => appendChild(newInstance, child));
881
910
  instance.children = [];
882
911
  }
@@ -955,7 +984,6 @@ function createRenderer(roots) {
955
984
  },
956
985
 
957
986
  commitUpdate(instance, [reconstruct, diff], type, oldProps, newProps, fiber) {
958
- //console.log(type)
959
987
  // Reconstruct when args or <primitive object={...} have changes
960
988
  if (reconstruct) switchInstance(instance, type, newProps, fiber); // Otherwise just overwrite props
961
989
  else applyProps(instance, diff);
@@ -1031,7 +1059,7 @@ function createRenderer(roots) {
1031
1059
  };
1032
1060
  }
1033
1061
 
1034
- const isRenderer = def => def && !!def.render;
1062
+ const isRenderer = def => !!(def != null && def.render);
1035
1063
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1036
1064
  function calculateDpr(dpr) {
1037
1065
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
@@ -1059,11 +1087,11 @@ const createStore = (applyProps, invalidate, advance, props) => {
1059
1087
  if (shadows) {
1060
1088
  gl.shadowMap.enabled = true;
1061
1089
  if (typeof shadows === 'object') Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1062
- } // Set color management
1090
+ } // Set color preferences
1063
1091
 
1064
1092
 
1065
- if (!linear) gl.outputEncoding = THREE__namespace.sRGBEncoding;
1066
- if (!flat) gl.toneMapping = THREE__namespace.ACESFilmicToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1093
+ if (linear) gl.outputEncoding = THREE__namespace.LinearEncoding;
1094
+ if (flat) gl.toneMapping = THREE__namespace.NoToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1067
1095
 
1068
1096
  if (frameloop === 'never') {
1069
1097
  clock.stop();
@@ -1638,13 +1666,23 @@ const {
1638
1666
  applyProps
1639
1667
  } = createRenderer();
1640
1668
 
1641
- const createRendererInstance = (gl, canvas) => isRenderer(gl) ? gl : new THREE__namespace.WebGLRenderer({
1642
- powerPreference: 'high-performance',
1643
- canvas: canvas,
1644
- antialias: true,
1645
- alpha: true,
1646
- ...gl
1647
- });
1669
+ const createRendererInstance = (gl, canvas) => {
1670
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1671
+ if (isRenderer(customRenderer)) return customRenderer;
1672
+ const renderer = new THREE__namespace.WebGLRenderer({
1673
+ powerPreference: 'high-performance',
1674
+ canvas: canvas,
1675
+ antialias: true,
1676
+ alpha: true,
1677
+ ...gl
1678
+ }); // Set color management
1679
+
1680
+ renderer.outputEncoding = THREE__namespace.sRGBEncoding;
1681
+ renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping; // Set gl props
1682
+
1683
+ if (gl) applyProps(renderer, gl);
1684
+ return renderer;
1685
+ };
1648
1686
 
1649
1687
  function render(element, canvas, {
1650
1688
  gl,
@@ -76,6 +76,23 @@ const is = {
76
76
  function makeId(event) {
77
77
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
78
78
  }
79
+ /** Release pointer captures.
80
+ * This is called by releasePointerCapture in the API, and when an object is removed.
81
+ */
82
+
83
+
84
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
85
+ const captureData = captures.get(obj);
86
+
87
+ if (captureData) {
88
+ captures.delete(obj); // If this was the last capturing object for this pointer
89
+
90
+ if (captures.size === 0) {
91
+ capturedMap.delete(pointerId);
92
+ captureData.target.releasePointerCapture(pointerId);
93
+ }
94
+ }
95
+ }
79
96
 
80
97
  function removeInteractivity(store, object) {
81
98
  const {
@@ -89,6 +106,9 @@ function removeInteractivity(store, object) {
89
106
  internal.hovered.delete(key);
90
107
  }
91
108
  });
109
+ internal.capturedMap.forEach((captures, pointerId) => {
110
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
111
+ });
92
112
  }
93
113
  function createEvents(store) {
94
114
  const temp = new THREE__namespace.Vector3();
@@ -168,7 +188,7 @@ function createEvents(store) {
168
188
  while (eventObject) {
169
189
  var _r3f2;
170
190
 
171
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.handlers.count) intersections.push({ ...intersect,
191
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
172
192
  eventObject
173
193
  });
174
194
  eventObject = eventObject.parent;
@@ -187,7 +207,9 @@ function createEvents(store) {
187
207
  // intersect.
188
208
 
189
209
  if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
190
- intersections.push(...internal.capturedMap.get(event.pointerId).values());
210
+ for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
211
+ intersections.push(captureData.intersection);
212
+ }
191
213
  }
192
214
 
193
215
  return intersections;
@@ -205,9 +227,6 @@ function createEvents(store) {
205
227
 
206
228
  if (intersections.length) {
207
229
  const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
208
-
209
- const releasePointerCapture = id => event.target.releasePointerCapture(id);
210
-
211
230
  const localState = {
212
231
  stopped: false
213
232
  };
@@ -220,23 +239,36 @@ function createEvents(store) {
220
239
  };
221
240
 
222
241
  const setPointerCapture = id => {
242
+ const captureData = {
243
+ intersection: hit,
244
+ target: event.target
245
+ };
246
+
223
247
  if (internal.capturedMap.has(id)) {
224
248
  // if the pointerId was previously captured, we add the hit to the
225
249
  // event capturedMap.
226
- internal.capturedMap.get(id).set(hit.eventObject, hit);
250
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
227
251
  } else {
228
252
  // if the pointerId was not previously captured, we create a map
229
253
  // containing the hitObject, and the hit. hitObject is used for
230
254
  // faster access.
231
- internal.capturedMap.set(id, new Map([[hit.eventObject, hit]]));
255
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
232
256
  } // Call the original event now
233
257
  event.target.setPointerCapture(id);
258
+ };
259
+
260
+ const releasePointerCapture = id => {
261
+ const captures = internal.capturedMap.get(id);
262
+
263
+ if (captures) {
264
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
265
+ }
234
266
  }; // Add native event props
235
267
 
236
268
 
237
- let extractEventProps = {};
269
+ 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
270
 
239
- for (let prop in Object.getPrototypeOf(event)) {
271
+ for (let prop in event) {
240
272
  let property = event[prop]; // Only copy over atomics, leave functions alone as these should be
241
273
  // called as event.nativeEvent.fn()
242
274
 
@@ -305,13 +337,12 @@ function createEvents(store) {
305
337
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
306
338
  // we call onPointerOut and delete the object from the hovered-elements map
307
339
  if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
308
- var _r3f3;
309
-
310
340
  const eventObject = hoveredObj.eventObject;
311
- const handlers = (_r3f3 = eventObject.__r3f) == null ? void 0 : _r3f3.handlers;
341
+ const instance = eventObject.__r3f;
342
+ const handlers = instance == null ? void 0 : instance.handlers;
312
343
  internal.hovered.delete(makeId(hoveredObj));
313
344
 
314
- if (handlers != null && handlers.count) {
345
+ if (instance != null && instance.eventCount) {
315
346
  // Clear out intersects, they are outdated by now
316
347
  const data = { ...hoveredObj,
317
348
  intersections: hits || []
@@ -333,9 +364,8 @@ function createEvents(store) {
333
364
  case 'onLostPointerCapture':
334
365
  return event => {
335
366
  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.
367
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
368
+ // object that's getting removed.
339
369
  store.getState().internal.capturedMap.delete(event.pointerId);
340
370
  }
341
371
 
@@ -374,12 +404,11 @@ function createEvents(store) {
374
404
 
375
405
  if (isPointerMove) cancelPointer(hits);
376
406
  handleIntersects(hits, event, delta, data => {
377
- var _r3f4;
378
-
379
407
  const eventObject = data.eventObject;
380
- const handlers = (_r3f4 = eventObject.__r3f) == null ? void 0 : _r3f4.handlers; // Check presence of handlers
408
+ const instance = eventObject.__r3f;
409
+ const handlers = instance == null ? void 0 : instance.handlers; // Check presence of handlers
381
410
 
382
- if (!(handlers != null && handlers.count)) return;
411
+ if (!(instance != null && instance.eventCount)) return;
383
412
 
384
413
  if (isPointerMove) {
385
414
  // Move event ...
@@ -422,9 +451,9 @@ function createEvents(store) {
422
451
 
423
452
  function pointerMissed(event, objects) {
424
453
  objects.forEach(object => {
425
- var _r3f5;
454
+ var _r3f3;
426
455
 
427
- return (_r3f5 = object.__r3f) == null ? void 0 : _r3f5.handlers.onPointerMissed == null ? void 0 : _r3f5.handlers.onPointerMissed(event);
456
+ return (_r3f3 = object.__r3f) == null ? void 0 : _r3f3.handlers.onPointerMissed == null ? void 0 : _r3f3.handlers.onPointerMissed(event);
428
457
  });
429
458
  }
430
459
 
@@ -475,9 +504,8 @@ function prepare(object, state) {
475
504
  instance.__r3f = {
476
505
  root: null,
477
506
  memoizedProps: {},
478
- handlers: {
479
- count: 0
480
- },
507
+ eventCount: 0,
508
+ handlers: {},
481
509
  objects: [],
482
510
  parent: null,
483
511
  ...state
@@ -538,7 +566,7 @@ function createRenderer(roots) {
538
566
  }
539
567
 
540
568
  function applyProps(instance, data) {
541
- var _instance$__r3f3, _root$getState, _localState$handlers, _localState$handlers2, _instance$__r3f4;
569
+ var _instance$__r3f3, _root$getState, _instance$__r3f4;
542
570
 
543
571
  // Filter equals, events and reserved props
544
572
  const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
@@ -548,7 +576,7 @@ function createRenderer(roots) {
548
576
  memoized,
549
577
  changes
550
578
  } = isDiffSet(data) ? data : diffProps(instance, data);
551
- const prevHandlers = (_localState$handlers = localState.handlers) == null ? void 0 : _localState$handlers.count; // Prepare memoized props
579
+ const prevHandlers = localState.eventCount; // Prepare memoized props
552
580
 
553
581
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
554
582
  changes.forEach(([key, value, isEvent, keys]) => {
@@ -587,7 +615,7 @@ function createRenderer(roots) {
587
615
 
588
616
  if (isEvent) {
589
617
  if (value) localState.handlers[key] = value;else delete localState.handlers[key];
590
- localState.handlers.count = Object.keys(localState.handlers).length;
618
+ localState.eventCount = Object.keys(localState.handlers).length;
591
619
  } // Special treatment for objects with support for set/copy, and layers
592
620
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE__namespace.Layers)) {
593
621
  // If value is an array
@@ -617,16 +645,17 @@ function createRenderer(roots) {
617
645
  invalidateInstance(instance);
618
646
  });
619
647
 
620
- if (rootState.internal && instance.raycast && prevHandlers !== ((_localState$handlers2 = localState.handlers) == null ? void 0 : _localState$handlers2.count)) {
648
+ if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
621
649
  // Pre-emptively remove the instance from the interaction manager
622
650
  const index = rootState.internal.interaction.indexOf(instance);
623
651
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
624
652
 
625
- if (localState.handlers.count) rootState.internal.interaction.push(instance);
653
+ if (localState.eventCount) rootState.internal.interaction.push(instance);
626
654
  } // Call the update lifecycle when it is being updated
627
655
 
628
656
 
629
657
  if (changes.length && (_instance$__r3f4 = instance.__r3f) != null && _instance$__r3f4.parent) updateInstance(instance);
658
+ return instance;
630
659
  }
631
660
 
632
661
  function invalidateInstance(instance) {
@@ -669,14 +698,13 @@ function createRenderer(roots) {
669
698
  });
670
699
  } else {
671
700
  const target = catalogue[name] || THREE__namespace[name];
672
- 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`;
673
- const isArgsArr = is.arr(args); // Instanciate new object, link it to the root
701
+ 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
702
+ // Append memoized props with args so it's not forgotten
674
703
 
675
- instance = prepare(isArgsArr ? new target(...args) : new target(args), {
704
+ instance = prepare(new target(...args), {
676
705
  root,
677
- // append memoized props with args so it's not forgotten
678
706
  memoizedProps: {
679
- args: isArgsArr && args.length === 0 ? null : args
707
+ args: args.length === 0 ? null : args
680
708
  }
681
709
  });
682
710
  } // Auto-attach geometries and materials
@@ -813,7 +841,7 @@ function createRenderer(roots) {
813
841
  } else if (is.fun(detachFn)) {
814
842
  detachFn(child, parentInstance);
815
843
  }
816
- } else if (child.isObject3D) {
844
+ } else if (child.isObject3D && parentInstance.isObject3D) {
817
845
  var _child$__r3f;
818
846
 
819
847
  parentInstance.remove(child); // Remove interactivity
@@ -875,8 +903,9 @@ function createRenderer(roots) {
875
903
  const newInstance = createInstance(type, newProps, instance.__r3f.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
876
904
  // When args change the instance has to be re-constructed, which then
877
905
  // forces r3f to re-parent the children and non-scene objects
906
+ // This can not include primitives, which should not have declarative children
878
907
 
879
- if (instance.children) {
908
+ if (type !== 'primitive' && instance.children) {
880
909
  instance.children.forEach(child => appendChild(newInstance, child));
881
910
  instance.children = [];
882
911
  }
@@ -955,7 +984,6 @@ function createRenderer(roots) {
955
984
  },
956
985
 
957
986
  commitUpdate(instance, [reconstruct, diff], type, oldProps, newProps, fiber) {
958
- //console.log(type)
959
987
  // Reconstruct when args or <primitive object={...} have changes
960
988
  if (reconstruct) switchInstance(instance, type, newProps, fiber); // Otherwise just overwrite props
961
989
  else applyProps(instance, diff);
@@ -1031,7 +1059,7 @@ function createRenderer(roots) {
1031
1059
  };
1032
1060
  }
1033
1061
 
1034
- const isRenderer = def => def && !!def.render;
1062
+ const isRenderer = def => !!(def != null && def.render);
1035
1063
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1036
1064
  function calculateDpr(dpr) {
1037
1065
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
@@ -1059,11 +1087,11 @@ const createStore = (applyProps, invalidate, advance, props) => {
1059
1087
  if (shadows) {
1060
1088
  gl.shadowMap.enabled = true;
1061
1089
  if (typeof shadows === 'object') Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE__namespace.PCFSoftShadowMap;
1062
- } // Set color management
1090
+ } // Set color preferences
1063
1091
 
1064
1092
 
1065
- if (!linear) gl.outputEncoding = THREE__namespace.sRGBEncoding;
1066
- if (!flat) gl.toneMapping = THREE__namespace.ACESFilmicToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1093
+ if (linear) gl.outputEncoding = THREE__namespace.LinearEncoding;
1094
+ if (flat) gl.toneMapping = THREE__namespace.NoToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1067
1095
 
1068
1096
  if (frameloop === 'never') {
1069
1097
  clock.stop();
@@ -1638,13 +1666,23 @@ const {
1638
1666
  applyProps
1639
1667
  } = createRenderer();
1640
1668
 
1641
- const createRendererInstance = (gl, canvas) => isRenderer(gl) ? gl : new THREE__namespace.WebGLRenderer({
1642
- powerPreference: 'high-performance',
1643
- canvas: canvas,
1644
- antialias: true,
1645
- alpha: true,
1646
- ...gl
1647
- });
1669
+ const createRendererInstance = (gl, canvas) => {
1670
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1671
+ if (isRenderer(customRenderer)) return customRenderer;
1672
+ const renderer = new THREE__namespace.WebGLRenderer({
1673
+ powerPreference: 'high-performance',
1674
+ canvas: canvas,
1675
+ antialias: true,
1676
+ alpha: true,
1677
+ ...gl
1678
+ }); // Set color management
1679
+
1680
+ renderer.outputEncoding = THREE__namespace.sRGBEncoding;
1681
+ renderer.toneMapping = THREE__namespace.ACESFilmicToneMapping; // Set gl props
1682
+
1683
+ if (gl) applyProps(renderer, gl);
1684
+ return renderer;
1685
+ };
1648
1686
 
1649
1687
  function render(element, canvas, {
1650
1688
  gl,
@@ -42,6 +42,23 @@ const is = {
42
42
  function makeId(event) {
43
43
  return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
44
44
  }
45
+ /** Release pointer captures.
46
+ * This is called by releasePointerCapture in the API, and when an object is removed.
47
+ */
48
+
49
+
50
+ function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
51
+ const captureData = captures.get(obj);
52
+
53
+ if (captureData) {
54
+ captures.delete(obj); // If this was the last capturing object for this pointer
55
+
56
+ if (captures.size === 0) {
57
+ capturedMap.delete(pointerId);
58
+ captureData.target.releasePointerCapture(pointerId);
59
+ }
60
+ }
61
+ }
45
62
 
46
63
  function removeInteractivity(store, object) {
47
64
  const {
@@ -55,6 +72,9 @@ function removeInteractivity(store, object) {
55
72
  internal.hovered.delete(key);
56
73
  }
57
74
  });
75
+ internal.capturedMap.forEach((captures, pointerId) => {
76
+ releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
77
+ });
58
78
  }
59
79
  function createEvents(store) {
60
80
  const temp = new THREE.Vector3();
@@ -134,7 +154,7 @@ function createEvents(store) {
134
154
  while (eventObject) {
135
155
  var _r3f2;
136
156
 
137
- if ((_r3f2 = eventObject.__r3f) != null && _r3f2.handlers.count) intersections.push({ ...intersect,
157
+ if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({ ...intersect,
138
158
  eventObject
139
159
  });
140
160
  eventObject = eventObject.parent;
@@ -153,7 +173,9 @@ function createEvents(store) {
153
173
  // intersect.
154
174
 
155
175
  if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
156
- intersections.push(...internal.capturedMap.get(event.pointerId).values());
176
+ for (let captureData of internal.capturedMap.get(event.pointerId).values()) {
177
+ intersections.push(captureData.intersection);
178
+ }
157
179
  }
158
180
 
159
181
  return intersections;
@@ -171,9 +193,6 @@ function createEvents(store) {
171
193
 
172
194
  if (intersections.length) {
173
195
  const unprojectedPoint = temp.set(mouse.x, mouse.y, 0).unproject(camera);
174
-
175
- const releasePointerCapture = id => event.target.releasePointerCapture(id);
176
-
177
196
  const localState = {
178
197
  stopped: false
179
198
  };
@@ -186,23 +205,36 @@ function createEvents(store) {
186
205
  };
187
206
 
188
207
  const setPointerCapture = id => {
208
+ const captureData = {
209
+ intersection: hit,
210
+ target: event.target
211
+ };
212
+
189
213
  if (internal.capturedMap.has(id)) {
190
214
  // if the pointerId was previously captured, we add the hit to the
191
215
  // event capturedMap.
192
- internal.capturedMap.get(id).set(hit.eventObject, hit);
216
+ internal.capturedMap.get(id).set(hit.eventObject, captureData);
193
217
  } else {
194
218
  // if the pointerId was not previously captured, we create a map
195
219
  // containing the hitObject, and the hit. hitObject is used for
196
220
  // faster access.
197
- internal.capturedMap.set(id, new Map([[hit.eventObject, hit]]));
221
+ internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
198
222
  } // Call the original event now
199
223
  event.target.setPointerCapture(id);
224
+ };
225
+
226
+ const releasePointerCapture = id => {
227
+ const captures = internal.capturedMap.get(id);
228
+
229
+ if (captures) {
230
+ releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
231
+ }
200
232
  }; // Add native event props
201
233
 
202
234
 
203
- let extractEventProps = {};
235
+ 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
236
 
205
- for (let prop in Object.getPrototypeOf(event)) {
237
+ for (let prop in event) {
206
238
  let property = event[prop]; // Only copy over atomics, leave functions alone as these should be
207
239
  // called as event.nativeEvent.fn()
208
240
 
@@ -271,13 +303,12 @@ function createEvents(store) {
271
303
  // When no objects were hit or the the hovered object wasn't found underneath the cursor
272
304
  // we call onPointerOut and delete the object from the hovered-elements map
273
305
  if (!hits.length || !hits.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
274
- var _r3f3;
275
-
276
306
  const eventObject = hoveredObj.eventObject;
277
- const handlers = (_r3f3 = eventObject.__r3f) == null ? void 0 : _r3f3.handlers;
307
+ const instance = eventObject.__r3f;
308
+ const handlers = instance == null ? void 0 : instance.handlers;
278
309
  internal.hovered.delete(makeId(hoveredObj));
279
310
 
280
- if (handlers != null && handlers.count) {
311
+ if (instance != null && instance.eventCount) {
281
312
  // Clear out intersects, they are outdated by now
282
313
  const data = { ...hoveredObj,
283
314
  intersections: hits || []
@@ -299,9 +330,8 @@ function createEvents(store) {
299
330
  case 'onLostPointerCapture':
300
331
  return event => {
301
332
  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.
333
+ // If the object event interface had onLostPointerCapture, we'd call it here on every
334
+ // object that's getting removed.
305
335
  store.getState().internal.capturedMap.delete(event.pointerId);
306
336
  }
307
337
 
@@ -340,12 +370,11 @@ function createEvents(store) {
340
370
 
341
371
  if (isPointerMove) cancelPointer(hits);
342
372
  handleIntersects(hits, event, delta, data => {
343
- var _r3f4;
344
-
345
373
  const eventObject = data.eventObject;
346
- const handlers = (_r3f4 = eventObject.__r3f) == null ? void 0 : _r3f4.handlers; // Check presence of handlers
374
+ const instance = eventObject.__r3f;
375
+ const handlers = instance == null ? void 0 : instance.handlers; // Check presence of handlers
347
376
 
348
- if (!(handlers != null && handlers.count)) return;
377
+ if (!(instance != null && instance.eventCount)) return;
349
378
 
350
379
  if (isPointerMove) {
351
380
  // Move event ...
@@ -388,9 +417,9 @@ function createEvents(store) {
388
417
 
389
418
  function pointerMissed(event, objects) {
390
419
  objects.forEach(object => {
391
- var _r3f5;
420
+ var _r3f3;
392
421
 
393
- return (_r3f5 = object.__r3f) == null ? void 0 : _r3f5.handlers.onPointerMissed == null ? void 0 : _r3f5.handlers.onPointerMissed(event);
422
+ return (_r3f3 = object.__r3f) == null ? void 0 : _r3f3.handlers.onPointerMissed == null ? void 0 : _r3f3.handlers.onPointerMissed(event);
394
423
  });
395
424
  }
396
425
 
@@ -441,9 +470,8 @@ function prepare(object, state) {
441
470
  instance.__r3f = {
442
471
  root: null,
443
472
  memoizedProps: {},
444
- handlers: {
445
- count: 0
446
- },
473
+ eventCount: 0,
474
+ handlers: {},
447
475
  objects: [],
448
476
  parent: null,
449
477
  ...state
@@ -504,7 +532,7 @@ function createRenderer(roots) {
504
532
  }
505
533
 
506
534
  function applyProps(instance, data) {
507
- var _instance$__r3f3, _root$getState, _localState$handlers, _localState$handlers2, _instance$__r3f4;
535
+ var _instance$__r3f3, _root$getState, _instance$__r3f4;
508
536
 
509
537
  // Filter equals, events and reserved props
510
538
  const localState = (_instance$__r3f3 = instance == null ? void 0 : instance.__r3f) != null ? _instance$__r3f3 : {};
@@ -514,7 +542,7 @@ function createRenderer(roots) {
514
542
  memoized,
515
543
  changes
516
544
  } = isDiffSet(data) ? data : diffProps(instance, data);
517
- const prevHandlers = (_localState$handlers = localState.handlers) == null ? void 0 : _localState$handlers.count; // Prepare memoized props
545
+ const prevHandlers = localState.eventCount; // Prepare memoized props
518
546
 
519
547
  if (instance.__r3f) instance.__r3f.memoizedProps = memoized;
520
548
  changes.forEach(([key, value, isEvent, keys]) => {
@@ -553,7 +581,7 @@ function createRenderer(roots) {
553
581
 
554
582
  if (isEvent) {
555
583
  if (value) localState.handlers[key] = value;else delete localState.handlers[key];
556
- localState.handlers.count = Object.keys(localState.handlers).length;
584
+ localState.eventCount = Object.keys(localState.handlers).length;
557
585
  } // Special treatment for objects with support for set/copy, and layers
558
586
  else if (targetProp && targetProp.set && (targetProp.copy || targetProp instanceof THREE.Layers)) {
559
587
  // If value is an array
@@ -583,16 +611,17 @@ function createRenderer(roots) {
583
611
  invalidateInstance(instance);
584
612
  });
585
613
 
586
- if (rootState.internal && instance.raycast && prevHandlers !== ((_localState$handlers2 = localState.handlers) == null ? void 0 : _localState$handlers2.count)) {
614
+ if (rootState.internal && instance.raycast && prevHandlers !== localState.eventCount) {
587
615
  // Pre-emptively remove the instance from the interaction manager
588
616
  const index = rootState.internal.interaction.indexOf(instance);
589
617
  if (index > -1) rootState.internal.interaction.splice(index, 1); // Add the instance to the interaction manager only when it has handlers
590
618
 
591
- if (localState.handlers.count) rootState.internal.interaction.push(instance);
619
+ if (localState.eventCount) rootState.internal.interaction.push(instance);
592
620
  } // Call the update lifecycle when it is being updated
593
621
 
594
622
 
595
623
  if (changes.length && (_instance$__r3f4 = instance.__r3f) != null && _instance$__r3f4.parent) updateInstance(instance);
624
+ return instance;
596
625
  }
597
626
 
598
627
  function invalidateInstance(instance) {
@@ -635,14 +664,13 @@ function createRenderer(roots) {
635
664
  });
636
665
  } else {
637
666
  const target = catalogue[name] || THREE[name];
638
- 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`;
639
- const isArgsArr = is.arr(args); // Instanciate new object, link it to the root
667
+ 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
668
+ // Append memoized props with args so it's not forgotten
640
669
 
641
- instance = prepare(isArgsArr ? new target(...args) : new target(args), {
670
+ instance = prepare(new target(...args), {
642
671
  root,
643
- // append memoized props with args so it's not forgotten
644
672
  memoizedProps: {
645
- args: isArgsArr && args.length === 0 ? null : args
673
+ args: args.length === 0 ? null : args
646
674
  }
647
675
  });
648
676
  } // Auto-attach geometries and materials
@@ -779,7 +807,7 @@ function createRenderer(roots) {
779
807
  } else if (is.fun(detachFn)) {
780
808
  detachFn(child, parentInstance);
781
809
  }
782
- } else if (child.isObject3D) {
810
+ } else if (child.isObject3D && parentInstance.isObject3D) {
783
811
  var _child$__r3f;
784
812
 
785
813
  parentInstance.remove(child); // Remove interactivity
@@ -841,8 +869,9 @@ function createRenderer(roots) {
841
869
  const newInstance = createInstance(type, newProps, instance.__r3f.root); // https://github.com/pmndrs/react-three-fiber/issues/1348
842
870
  // When args change the instance has to be re-constructed, which then
843
871
  // forces r3f to re-parent the children and non-scene objects
872
+ // This can not include primitives, which should not have declarative children
844
873
 
845
- if (instance.children) {
874
+ if (type !== 'primitive' && instance.children) {
846
875
  instance.children.forEach(child => appendChild(newInstance, child));
847
876
  instance.children = [];
848
877
  }
@@ -921,7 +950,6 @@ function createRenderer(roots) {
921
950
  },
922
951
 
923
952
  commitUpdate(instance, [reconstruct, diff], type, oldProps, newProps, fiber) {
924
- //console.log(type)
925
953
  // Reconstruct when args or <primitive object={...} have changes
926
954
  if (reconstruct) switchInstance(instance, type, newProps, fiber); // Otherwise just overwrite props
927
955
  else applyProps(instance, diff);
@@ -997,7 +1025,7 @@ function createRenderer(roots) {
997
1025
  };
998
1026
  }
999
1027
 
1000
- const isRenderer = def => def && !!def.render;
1028
+ const isRenderer = def => !!(def != null && def.render);
1001
1029
  const isOrthographicCamera = def => def && def.isOrthographicCamera;
1002
1030
  function calculateDpr(dpr) {
1003
1031
  return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], window.devicePixelRatio), dpr[1]) : dpr;
@@ -1025,11 +1053,11 @@ const createStore = (applyProps, invalidate, advance, props) => {
1025
1053
  if (shadows) {
1026
1054
  gl.shadowMap.enabled = true;
1027
1055
  if (typeof shadows === 'object') Object.assign(gl.shadowMap, shadows);else gl.shadowMap.type = THREE.PCFSoftShadowMap;
1028
- } // Set color management
1056
+ } // Set color preferences
1029
1057
 
1030
1058
 
1031
- if (!linear) gl.outputEncoding = THREE.sRGBEncoding;
1032
- if (!flat) gl.toneMapping = THREE.ACESFilmicToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1059
+ if (linear) gl.outputEncoding = THREE.LinearEncoding;
1060
+ if (flat) gl.toneMapping = THREE.NoToneMapping; // clock.elapsedTime is updated using advance(timestamp)
1033
1061
 
1034
1062
  if (frameloop === 'never') {
1035
1063
  clock.stop();
@@ -1604,13 +1632,23 @@ const {
1604
1632
  applyProps
1605
1633
  } = createRenderer();
1606
1634
 
1607
- const createRendererInstance = (gl, canvas) => isRenderer(gl) ? gl : new THREE.WebGLRenderer({
1608
- powerPreference: 'high-performance',
1609
- canvas: canvas,
1610
- antialias: true,
1611
- alpha: true,
1612
- ...gl
1613
- });
1635
+ const createRendererInstance = (gl, canvas) => {
1636
+ const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1637
+ if (isRenderer(customRenderer)) return customRenderer;
1638
+ const renderer = new THREE.WebGLRenderer({
1639
+ powerPreference: 'high-performance',
1640
+ canvas: canvas,
1641
+ antialias: true,
1642
+ alpha: true,
1643
+ ...gl
1644
+ }); // Set color management
1645
+
1646
+ renderer.outputEncoding = THREE.sRGBEncoding;
1647
+ renderer.toneMapping = THREE.ACESFilmicToneMapping; // Set gl props
1648
+
1649
+ if (gl) applyProps(renderer, gl);
1650
+ return renderer;
1651
+ };
1614
1652
 
1615
1653
  function render(element, canvas, {
1616
1654
  gl,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-three/fiber",
3
- "version": "7.0.14",
3
+ "version": "7.0.18",
4
4
  "description": "A React renderer for Threejs",
5
5
  "keywords": [
6
6
  "react",