@react-three/fiber 9.0.0-rc.1 → 9.0.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,863 +1,425 @@
1
1
  import * as THREE from 'three';
2
2
  import * as React from 'react';
3
- import { NoEventPriority, DefaultEventPriority, ContinuousEventPriority, DiscreteEventPriority, ConcurrentRoot } from 'react-reconciler/constants';
3
+ import { DefaultEventPriority, ContinuousEventPriority, DiscreteEventPriority, ConcurrentRoot } from 'react-reconciler/constants';
4
4
  import { createWithEqualityFn } from 'zustand/traditional';
5
+ import Reconciler from 'react-reconciler';
6
+ import { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
5
7
  import { suspend, preload, clear } from 'suspend-react';
6
8
  import { jsx, Fragment } from 'react/jsx-runtime';
7
9
  import { useFiber, useContextBridge, traverseFiber } from 'its-fine';
8
- import Reconciler from 'react-reconciler';
9
- import { unstable_scheduleCallback, unstable_IdlePriority } from 'scheduler';
10
10
 
11
11
  var threeTypes = /*#__PURE__*/Object.freeze({
12
12
  __proto__: null
13
13
  });
14
14
 
15
- // TODO: upstream to DefinitelyTyped for React 19
16
- // https://github.com/facebook/react/issues/28956
17
-
18
- const createReconciler = Reconciler;
19
-
20
- // TODO: handle constructor overloads
21
- // https://github.com/pmndrs/react-three-fiber/pull/2931
22
- // https://github.com/microsoft/TypeScript/issues/37079
23
-
24
- const catalogue = {};
25
- let i = 0;
26
- const isConstructor$1 = object => typeof object === 'function';
27
- function extend(objects) {
28
- if (isConstructor$1(objects)) {
29
- const Component = `${i++}`;
30
- catalogue[Component] = objects;
31
- return Component;
32
- } else {
33
- Object.assign(catalogue, objects);
34
- }
15
+ /**
16
+ * Returns the instance's initial (outmost) root.
17
+ */
18
+ function findInitialRoot(instance) {
19
+ let root = instance.root;
20
+ while (root.getState().previousRoot) root = root.getState().previousRoot;
21
+ return root;
35
22
  }
36
- function validateInstance(type, props) {
37
- // Get target from catalogue
38
- const name = `${type[0].toUpperCase()}${type.slice(1)}`;
39
- const target = catalogue[name];
40
-
41
- // Validate element target
42
- if (type !== 'primitive' && !target) throw new Error(`R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`);
23
+ /**
24
+ * Safely flush async effects when testing, simulating a legacy root.
25
+ */
26
+ const act = React.act;
27
+ const isOrthographicCamera = def => def && def.isOrthographicCamera;
28
+ const isRef = obj => obj && obj.hasOwnProperty('current');
29
+ const isColorRepresentation = value => value != null && (typeof value === 'string' || typeof value === 'number' || value.isColor);
43
30
 
44
- // Validate primitives
45
- if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
31
+ /**
32
+ * An SSR-friendly useLayoutEffect.
33
+ *
34
+ * React currently throws a warning when using useLayoutEffect on the server.
35
+ * To get around it, we can conditionally useEffect on the server (no-op) and
36
+ * useLayoutEffect elsewhere.
37
+ *
38
+ * @see https://github.com/facebook/react/issues/14927
39
+ */
40
+ const useIsomorphicLayoutEffect = /* @__PURE__ */((_window$document, _window$navigator) => typeof window !== 'undefined' && (((_window$document = window.document) == null ? void 0 : _window$document.createElement) || ((_window$navigator = window.navigator) == null ? void 0 : _window$navigator.product) === 'ReactNative'))() ? React.useLayoutEffect : React.useEffect;
41
+ function useMutableCallback(fn) {
42
+ const ref = React.useRef(fn);
43
+ useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
44
+ return ref;
45
+ }
46
+ /**
47
+ * Bridges renderer Context and StrictMode from a primary renderer.
48
+ */
49
+ function useBridge() {
50
+ const fiber = useFiber();
51
+ const ContextBridge = useContextBridge();
52
+ return React.useMemo(() => ({
53
+ children
54
+ }) => {
55
+ const strict = !!traverseFiber(fiber, true, node => node.type === React.StrictMode);
56
+ const Root = strict ? React.StrictMode : React.Fragment;
57
+ return /*#__PURE__*/jsx(Root, {
58
+ children: /*#__PURE__*/jsx(ContextBridge, {
59
+ children: children
60
+ })
61
+ });
62
+ }, [fiber, ContextBridge]);
63
+ }
64
+ function Block({
65
+ set
66
+ }) {
67
+ useIsomorphicLayoutEffect(() => {
68
+ set(new Promise(() => null));
69
+ return () => set(false);
70
+ }, [set]);
71
+ return null;
72
+ }
46
73
 
47
- // Throw if an object or literal was passed for args
48
- if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
74
+ // NOTE: static members get down-level transpiled to mutations which break tree-shaking
75
+ const ErrorBoundary = /* @__PURE__ */(_ErrorBoundary => (_ErrorBoundary = class ErrorBoundary extends React.Component {
76
+ constructor(...args) {
77
+ super(...args);
78
+ this.state = {
79
+ error: false
80
+ };
81
+ }
82
+ componentDidCatch(err) {
83
+ this.props.set(err);
84
+ }
85
+ render() {
86
+ return this.state.error ? null : this.props.children;
87
+ }
88
+ }, _ErrorBoundary.getDerivedStateFromError = () => ({
89
+ error: true
90
+ }), _ErrorBoundary))();
91
+ function calculateDpr(dpr) {
92
+ var _window$devicePixelRa;
93
+ // Err on the side of progress by assuming 2x dpr if we can't detect it
94
+ // This will happen in workers where window is defined but dpr isn't.
95
+ const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
96
+ return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
49
97
  }
50
- function createInstance(type, props, root) {
51
- var _props$object;
52
- validateInstance(type, props);
53
98
 
54
- // Regenerate the R3F instance for primitives to simulate a new object
55
- if (type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
56
- return prepare(props.object, root, type, props);
99
+ /**
100
+ * Returns instance root state
101
+ */
102
+ function getRootState(obj) {
103
+ var _r3f;
104
+ return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
57
105
  }
58
- function hideInstance(instance) {
59
- if (!instance.isHidden) {
60
- var _instance$parent;
61
- if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
62
- detach(instance.parent, instance);
63
- } else if (isObject3D(instance.object)) {
64
- instance.object.visible = false;
106
+ // A collection of compare functions
107
+ const is = {
108
+ obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
109
+ fun: a => typeof a === 'function',
110
+ str: a => typeof a === 'string',
111
+ num: a => typeof a === 'number',
112
+ boo: a => typeof a === 'boolean',
113
+ und: a => a === void 0,
114
+ nul: a => a === null,
115
+ arr: a => Array.isArray(a),
116
+ equ(a, b, {
117
+ arrays = 'shallow',
118
+ objects = 'reference',
119
+ strict = true
120
+ } = {}) {
121
+ // Wrong type or one of the two undefined, doesn't match
122
+ if (typeof a !== typeof b || !!a !== !!b) return false;
123
+ // Atomic, just compare a against b
124
+ if (is.str(a) || is.num(a) || is.boo(a)) return a === b;
125
+ const isObj = is.obj(a);
126
+ if (isObj && objects === 'reference') return a === b;
127
+ const isArr = is.arr(a);
128
+ if (isArr && arrays === 'reference') return a === b;
129
+ // Array or Object, shallow compare first to see if it's a match
130
+ if ((isArr || isObj) && a === b) return true;
131
+ // Last resort, go through keys
132
+ let i;
133
+ // Check if a has all the keys of b
134
+ for (i in a) if (!(i in b)) return false;
135
+ // Check if values between keys match
136
+ if (isObj && arrays === 'shallow' && objects === 'shallow') {
137
+ for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
138
+ strict,
139
+ objects: 'reference'
140
+ })) return false;
141
+ } else {
142
+ for (i in strict ? b : a) if (a[i] !== b[i]) return false;
65
143
  }
66
- instance.isHidden = true;
67
- invalidateInstance(instance);
144
+ // If i is undefined
145
+ if (is.und(i)) {
146
+ // If both arrays are empty we consider them equal
147
+ if (isArr && a.length === 0 && b.length === 0) return true;
148
+ // If both objects are empty we consider them equal
149
+ if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
150
+ // Otherwise match them by value
151
+ if (a !== b) return false;
152
+ }
153
+ return true;
154
+ }
155
+ };
156
+
157
+ // Collects nodes and materials from a THREE.Object3D
158
+ function buildGraph(object) {
159
+ const data = {
160
+ nodes: {},
161
+ materials: {}
162
+ };
163
+ if (object) {
164
+ object.traverse(obj => {
165
+ if (obj.name) data.nodes[obj.name] = obj;
166
+ if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
167
+ });
68
168
  }
169
+ return data;
69
170
  }
70
- function unhideInstance(instance) {
71
- if (instance.isHidden) {
72
- var _instance$parent2;
73
- if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
74
- attach(instance.parent, instance);
75
- } else if (isObject3D(instance.object) && instance.props.visible !== false) {
76
- instance.object.visible = true;
77
- }
78
- instance.isHidden = false;
79
- invalidateInstance(instance);
171
+ // Disposes an object and all its properties
172
+ function dispose(obj) {
173
+ if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
174
+ for (const p in obj) {
175
+ const prop = obj[p];
176
+ if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
80
177
  }
81
178
  }
179
+ const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
82
180
 
83
- // https://github.com/facebook/react/issues/20271
84
- // This will make sure events and attach are only handled once when trees are complete
85
- function handleContainerEffects(parent, child, beforeChild) {
86
- // Bail if tree isn't mounted or parent is not a container.
87
- // This ensures that the tree is finalized and React won't discard results to Suspense
88
- const state = child.root.getState();
89
- if (!parent.parent && parent.object !== state.scene) return;
90
-
91
- // Create & link object on first run
92
- if (!child.object) {
93
- var _child$props$object, _child$props$args;
94
- // Get target from catalogue
95
- const name = `${child.type[0].toUpperCase()}${child.type.slice(1)}`;
96
- const target = catalogue[name];
181
+ // Gets only instance props from reconciler fibers
182
+ function getInstanceProps(queue) {
183
+ const props = {};
184
+ for (const key in queue) {
185
+ if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
186
+ }
187
+ return props;
188
+ }
97
189
 
98
- // Create object
99
- child.object = (_child$props$object = child.props.object) != null ? _child$props$object : new target(...((_child$props$args = child.props.args) != null ? _child$props$args : []));
100
- child.object.__r3f = child;
190
+ // Each object in the scene carries a small LocalState descriptor
191
+ function prepare(target, root, type, props) {
192
+ const object = target;
101
193
 
102
- // Set initial props
103
- applyProps(child.object, child.props);
194
+ // Create instance descriptor
195
+ let instance = object == null ? void 0 : object.__r3f;
196
+ if (!instance) {
197
+ instance = {
198
+ root,
199
+ type,
200
+ parent: null,
201
+ children: [],
202
+ props: getInstanceProps(props),
203
+ object,
204
+ eventCount: 0,
205
+ handlers: {},
206
+ isHidden: false
207
+ };
208
+ if (object) object.__r3f = instance;
104
209
  }
210
+ return instance;
211
+ }
212
+ function resolve(root, key) {
213
+ var _target;
214
+ let target = root[key];
215
+ if (!key.includes('-')) return {
216
+ root,
217
+ key,
218
+ target
219
+ };
105
220
 
106
- // Append instance
107
- if (child.props.attach) {
108
- attach(parent, child);
109
- } else if (isObject3D(child.object) && isObject3D(parent.object)) {
110
- const childIndex = parent.object.children.indexOf(beforeChild == null ? void 0 : beforeChild.object);
111
- if (beforeChild && childIndex !== -1) {
112
- child.object.parent = parent.object;
113
- parent.object.children.splice(childIndex, 0, child.object);
114
- child.object.dispatchEvent({
115
- type: 'added'
116
- });
117
- parent.object.dispatchEvent({
118
- type: 'childadded',
119
- child: child.object
120
- });
121
- } else {
122
- parent.object.add(child.object);
123
- }
124
- }
125
-
126
- // Link subtree
127
- for (const childInstance of child.children) handleContainerEffects(child, childInstance);
221
+ // Resolve pierced target
222
+ const chain = key.split('-');
223
+ target = chain.reduce((acc, key) => acc[key], root);
224
+ key = chain.pop();
128
225
 
129
- // Tree was updated, request a frame
130
- invalidateInstance(child);
226
+ // Switch root if atomic
227
+ if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
228
+ return {
229
+ root,
230
+ key,
231
+ target
232
+ };
131
233
  }
132
- function appendChild(parent, child) {
133
- if (!child) return;
134
-
135
- // Link instances
136
- child.parent = parent;
137
- parent.children.push(child);
138
234
 
139
- // Attach tree once complete
140
- handleContainerEffects(parent, child);
235
+ // Checks if a dash-cased string ends with an integer
236
+ const INDEX_REGEX = /-\d+$/;
237
+ function attach(parent, child) {
238
+ if (is.str(child.props.attach)) {
239
+ // If attaching into an array (foo-0), create one
240
+ if (INDEX_REGEX.test(child.props.attach)) {
241
+ const index = child.props.attach.replace(INDEX_REGEX, '');
242
+ const {
243
+ root,
244
+ key
245
+ } = resolve(parent.object, index);
246
+ if (!Array.isArray(root[key])) root[key] = [];
247
+ }
248
+ const {
249
+ root,
250
+ key
251
+ } = resolve(parent.object, child.props.attach);
252
+ child.previousAttach = root[key];
253
+ root[key] = child.object;
254
+ } else if (is.fun(child.props.attach)) {
255
+ child.previousAttach = child.props.attach(parent.object, child.object);
256
+ }
141
257
  }
142
- function insertBefore(parent, child, beforeChild) {
143
- if (!child || !beforeChild) return;
144
-
145
- // Link instances
146
- child.parent = parent;
147
- const childIndex = parent.children.indexOf(beforeChild);
148
- if (childIndex !== -1) parent.children.splice(childIndex, 0, child);else parent.children.push(child);
149
-
150
- // Attach tree once complete
151
- handleContainerEffects(parent, child, beforeChild);
258
+ function detach(parent, child) {
259
+ if (is.str(child.props.attach)) {
260
+ const {
261
+ root,
262
+ key
263
+ } = resolve(parent.object, child.props.attach);
264
+ const previous = child.previousAttach;
265
+ // When the previous value was undefined, it means the value was never set to begin with
266
+ if (previous === undefined) delete root[key];
267
+ // Otherwise set the previous value
268
+ else root[key] = previous;
269
+ } else {
270
+ child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
271
+ }
272
+ delete child.previousAttach;
152
273
  }
153
- function disposeOnIdle(object) {
154
- if (typeof object.dispose === 'function') {
155
- const handleDispose = () => {
156
- try {
157
- object.dispose();
158
- } catch {
159
- // no-op
160
- }
161
- };
162
-
163
- // In a testing environment, cleanup immediately
164
- if (typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined') handleDispose();
165
- // Otherwise, using a real GPU so schedule cleanup to prevent stalls
166
- else unstable_scheduleCallback(unstable_IdlePriority, handleDispose);
274
+ const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
275
+ // Instance props
276
+ 'args', 'dispose', 'attach', 'object', 'onUpdate',
277
+ // Behavior flags
278
+ 'dispose'];
279
+ const MEMOIZED_PROTOTYPES = new Map();
280
+ function getMemoizedPrototype(root) {
281
+ let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
282
+ try {
283
+ if (!ctor) {
284
+ ctor = new root.constructor();
285
+ MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
286
+ }
287
+ } catch (e) {
288
+ // ...
167
289
  }
290
+ return ctor;
168
291
  }
169
- function removeChild(parent, child, dispose) {
170
- if (!child) return;
171
292
 
172
- // Unlink instances
173
- child.parent = null;
174
- const childIndex = parent.children.indexOf(child);
175
- if (childIndex !== -1) parent.children.splice(childIndex, 1);
293
+ // This function prepares a set of changes to be applied to the instance
294
+ function diffProps(instance, newProps) {
295
+ const changedProps = {};
176
296
 
177
- // Eagerly tear down tree
178
- if (child.props.attach) {
179
- detach(parent, child);
180
- } else if (isObject3D(child.object) && isObject3D(parent.object)) {
181
- parent.object.remove(child.object);
182
- removeInteractivity(findInitialRoot(child), child.object);
183
- }
297
+ // Sort through props
298
+ for (const prop in newProps) {
299
+ // Skip reserved keys
300
+ if (RESERVED_PROPS.includes(prop)) continue;
301
+ // Skip if props match
302
+ if (is.equ(newProps[prop], instance.props[prop])) continue;
184
303
 
185
- // Allow objects to bail out of unmount disposal with dispose={null}
186
- const shouldDispose = child.props.dispose !== null && dispose !== false;
304
+ // Props changed, add them
305
+ changedProps[prop] = newProps[prop];
187
306
 
188
- // Recursively remove instance children
189
- for (let i = child.children.length - 1; i >= 0; i--) {
190
- const node = child.children[i];
191
- removeChild(child, node, shouldDispose);
307
+ // Reset pierced props
308
+ for (const other in newProps) {
309
+ if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
310
+ }
192
311
  }
193
- child.children.length = 0;
194
312
 
195
- // Unlink instance object
196
- delete child.object.__r3f;
197
-
198
- // Dispose object whenever the reconciler feels like it.
199
- // Never dispose of primitives because their state may be kept outside of React!
200
- // In order for an object to be able to dispose it
201
- // - has a dispose method
202
- // - cannot be a <primitive object={...} />
203
- // - cannot be a THREE.Scene, because three has broken its own API
204
- if (shouldDispose && child.type !== 'primitive' && child.object.type !== 'Scene') {
205
- disposeOnIdle(child.object);
206
- }
313
+ // Reset removed props for HMR
314
+ for (const prop in instance.props) {
315
+ if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
316
+ const {
317
+ root,
318
+ key
319
+ } = resolve(instance.object, prop);
207
320
 
208
- // Tree was updated, request a frame for top-level instance
209
- if (dispose === undefined) invalidateInstance(child);
210
- }
211
- function setFiberRef(fiber, publicInstance) {
212
- for (const _fiber of [fiber, fiber.alternate]) {
213
- if (_fiber !== null) {
214
- if (typeof _fiber.ref === 'function') {
215
- _fiber.refCleanup == null ? void 0 : _fiber.refCleanup();
216
- const cleanup = _fiber.ref(publicInstance);
217
- if (typeof cleanup === 'function') _fiber.refCleanup = cleanup;
218
- } else if (_fiber.ref) {
219
- _fiber.ref.current = publicInstance;
220
- }
321
+ // https://github.com/mrdoob/three.js/issues/21209
322
+ // HMR/fast-refresh relies on the ability to cancel out props, but threejs
323
+ // has no means to do this. Hence we curate a small collection of value-classes
324
+ // with their respective constructor/set arguments
325
+ // For removed props, try to set default values, if possible
326
+ if (root.constructor && root.constructor.length === 0) {
327
+ // create a blank slate of the instance and copy the particular parameter.
328
+ const ctor = getMemoizedPrototype(root);
329
+ if (!is.und(ctor)) changedProps[key] = ctor[key];
330
+ } else {
331
+ // instance does not have constructor, just set it to 0
332
+ changedProps[key] = 0;
221
333
  }
222
334
  }
335
+ return changedProps;
223
336
  }
224
- const reconstructed = [];
225
- function swapInstances() {
226
- // Detach instance
227
- for (const [instance] of reconstructed) {
228
- const parent = instance.parent;
229
- if (parent) {
230
- if (instance.props.attach) {
231
- detach(parent, instance);
232
- } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
233
- parent.object.remove(instance.object);
234
- }
235
- for (const child of instance.children) {
236
- if (child.props.attach) {
237
- detach(instance, child);
238
- } else if (isObject3D(child.object) && isObject3D(instance.object)) {
239
- instance.object.remove(child.object);
240
- }
241
- }
242
- }
243
337
 
244
- // If the old instance is hidden, we need to unhide it.
245
- // React assumes it can discard instances since they're pure for DOM.
246
- // This isn't true for us since our lifetimes are impure and longliving.
247
- // So, we manually check if an instance was hidden and unhide it.
248
- if (instance.isHidden) unhideInstance(instance);
338
+ // https://github.com/mrdoob/three.js/pull/27042
339
+ // https://github.com/mrdoob/three.js/pull/22748
340
+ const colorMaps = ['map', 'emissiveMap', 'sheenColorMap', 'specularColorMap', 'envMap'];
341
+ const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
342
+ // This function applies a set of changes to the instance
343
+ function applyProps(object, props) {
344
+ var _instance$object;
345
+ const instance = object.__r3f;
346
+ const rootState = instance && findInitialRoot(instance).getState();
347
+ const prevHandlers = instance == null ? void 0 : instance.eventCount;
348
+ for (const prop in props) {
349
+ let value = props[prop];
249
350
 
250
- // Dispose of old object if able
251
- if (instance.object.__r3f) delete instance.object.__r3f;
252
- if (instance.type !== 'primitive') disposeOnIdle(instance.object);
253
- }
351
+ // Don't mutate reserved keys
352
+ if (RESERVED_PROPS.includes(prop)) continue;
254
353
 
255
- // Update instance
256
- for (const [instance, props, fiber] of reconstructed) {
257
- instance.props = props;
258
- const parent = instance.parent;
259
- if (parent) {
260
- var _instance$props$objec, _instance$props$args;
261
- // Get target from catalogue
262
- const name = `${instance.type[0].toUpperCase()}${instance.type.slice(1)}`;
263
- const target = catalogue[name];
354
+ // Deal with pointer events, including removing them if undefined
355
+ if (instance && EVENT_REGEX.test(prop)) {
356
+ if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
357
+ instance.eventCount = Object.keys(instance.handlers).length;
358
+ continue;
359
+ }
264
360
 
265
- // Create object
266
- instance.object = (_instance$props$objec = instance.props.object) != null ? _instance$props$objec : new target(...((_instance$props$args = instance.props.args) != null ? _instance$props$args : []));
267
- instance.object.__r3f = instance;
268
- setFiberRef(fiber, instance.object);
361
+ // Ignore setting undefined props
362
+ // https://github.com/pmndrs/react-three-fiber/issues/274
363
+ if (value === undefined) continue;
364
+ let {
365
+ root,
366
+ key,
367
+ target
368
+ } = resolve(object, prop);
269
369
 
270
- // Set initial props
271
- applyProps(instance.object, instance.props);
272
- if (instance.props.attach) {
273
- attach(parent, instance);
274
- } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
275
- parent.object.add(instance.object);
276
- }
277
- for (const child of instance.children) {
278
- if (child.props.attach) {
279
- attach(instance, child);
280
- } else if (isObject3D(child.object) && isObject3D(instance.object)) {
281
- instance.object.add(child.object);
282
- }
283
- }
284
-
285
- // Tree was updated, request a frame
286
- invalidateInstance(instance);
287
- }
288
- }
289
- reconstructed.length = 0;
290
- }
291
-
292
- // Don't handle text instances, make it no-op
293
- const handleTextInstance = () => {};
294
- const NO_CONTEXT = {};
295
- let currentUpdatePriority = NoEventPriority;
296
-
297
- // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberFlags.js
298
- const NoFlags = 0;
299
- const Update = 4;
300
- const reconciler = createReconciler({
301
- isPrimaryRenderer: false,
302
- warnsIfNotActing: false,
303
- supportsMutation: true,
304
- supportsPersistence: false,
305
- supportsHydration: false,
306
- createInstance,
307
- removeChild,
308
- appendChild,
309
- appendInitialChild: appendChild,
310
- insertBefore,
311
- appendChildToContainer(container, child) {
312
- const scene = container.getState().scene.__r3f;
313
- if (!child || !scene) return;
314
- appendChild(scene, child);
315
- },
316
- removeChildFromContainer(container, child) {
317
- const scene = container.getState().scene.__r3f;
318
- if (!child || !scene) return;
319
- removeChild(scene, child);
320
- },
321
- insertInContainerBefore(container, child, beforeChild) {
322
- const scene = container.getState().scene.__r3f;
323
- if (!child || !beforeChild || !scene) return;
324
- insertBefore(scene, child, beforeChild);
325
- },
326
- getRootHostContext: () => NO_CONTEXT,
327
- getChildHostContext: () => NO_CONTEXT,
328
- commitUpdate(instance, type, oldProps, newProps, fiber) {
329
- var _newProps$args, _oldProps$args, _newProps$args2;
330
- validateInstance(type, newProps);
331
- let reconstruct = false;
332
-
333
- // Reconstruct primitives if object prop changes
334
- if (instance.type === 'primitive' && oldProps.object !== newProps.object) reconstruct = true;
335
- // Reconstruct instance if args were added or removed
336
- else if (((_newProps$args = newProps.args) == null ? void 0 : _newProps$args.length) !== ((_oldProps$args = oldProps.args) == null ? void 0 : _oldProps$args.length)) reconstruct = true;
337
- // Reconstruct instance if args were changed
338
- else if ((_newProps$args2 = newProps.args) != null && _newProps$args2.some((value, index) => {
339
- var _oldProps$args2;
340
- return value !== ((_oldProps$args2 = oldProps.args) == null ? void 0 : _oldProps$args2[index]);
341
- })) reconstruct = true;
342
-
343
- // Reconstruct when args or <primitive object={...} have changes
344
- if (reconstruct) {
345
- reconstructed.push([instance, {
346
- ...newProps
347
- }, fiber]);
348
- } else {
349
- // Create a diff-set, flag if there are any changes
350
- const changedProps = diffProps(instance, newProps);
351
- if (Object.keys(changedProps).length) {
352
- Object.assign(instance.props, changedProps);
353
- applyProps(instance.object, changedProps);
354
- }
355
- }
356
-
357
- // Flush reconstructed siblings when we hit the last updated child in a sequence
358
- const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
359
- if (isTailSibling) swapInstances();
360
- },
361
- finalizeInitialChildren: () => false,
362
- commitMount() {},
363
- getPublicInstance: instance => instance == null ? void 0 : instance.object,
364
- prepareForCommit: () => null,
365
- preparePortalMount: container => prepare(container.getState().scene, container, '', {}),
366
- resetAfterCommit: () => {},
367
- shouldSetTextContent: () => false,
368
- clearContainer: () => false,
369
- hideInstance,
370
- unhideInstance,
371
- createTextInstance: handleTextInstance,
372
- hideTextInstance: handleTextInstance,
373
- unhideTextInstance: handleTextInstance,
374
- scheduleTimeout: typeof setTimeout === 'function' ? setTimeout : undefined,
375
- cancelTimeout: typeof clearTimeout === 'function' ? clearTimeout : undefined,
376
- noTimeout: -1,
377
- getInstanceFromNode: () => null,
378
- beforeActiveInstanceBlur() {},
379
- afterActiveInstanceBlur() {},
380
- detachDeletedInstance() {},
381
- prepareScopeUpdate() {},
382
- getInstanceFromScope: () => null,
383
- shouldAttemptEagerTransition: () => false,
384
- trackSchedulerEvent: () => {},
385
- resolveEventType: () => null,
386
- resolveEventTimeStamp: () => -1.1,
387
- requestPostPaintCallback() {},
388
- maySuspendCommit: () => false,
389
- preloadInstance: () => true,
390
- // true indicates already loaded
391
- startSuspendingCommit() {},
392
- suspendInstance() {},
393
- waitForCommitToBeReady: () => null,
394
- NotPendingTransition: null,
395
- setCurrentUpdatePriority(newPriority) {
396
- currentUpdatePriority = newPriority;
397
- },
398
- getCurrentUpdatePriority() {
399
- return currentUpdatePriority;
400
- },
401
- resolveUpdatePriority() {
402
- var _window$event;
403
- if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
404
- switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
405
- case 'click':
406
- case 'contextmenu':
407
- case 'dblclick':
408
- case 'pointercancel':
409
- case 'pointerdown':
410
- case 'pointerup':
411
- return DiscreteEventPriority;
412
- case 'pointermove':
413
- case 'pointerout':
414
- case 'pointerover':
415
- case 'pointerenter':
416
- case 'pointerleave':
417
- case 'wheel':
418
- return ContinuousEventPriority;
419
- default:
420
- return DefaultEventPriority;
421
- }
422
- },
423
- resetFormInstance() {}
424
- });
425
-
426
- var _window$document, _window$navigator;
427
- /**
428
- * Returns the instance's initial (outmost) root.
429
- */
430
- function findInitialRoot(instance) {
431
- let root = instance.root;
432
- while (root.getState().previousRoot) root = root.getState().previousRoot;
433
- return root;
434
- }
435
-
436
- /**
437
- * Returns `true` with correct TS type inference if an object has a configurable color space (since r152).
438
- */
439
- const hasColorSpace = object => 'colorSpace' in object || 'outputColorSpace' in object;
440
- /**
441
- * The current THREE.ColorManagement instance, if present.
442
- */
443
- const getColorManagement = () => {
444
- var _ColorManagement;
445
- return (_ColorManagement = catalogue.ColorManagement) != null ? _ColorManagement : null;
446
- };
447
- /**
448
- * Safely flush async effects when testing, simulating a legacy root.
449
- */
450
- const act = React.act;
451
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
452
- const isRef = obj => obj && obj.hasOwnProperty('current');
453
-
454
- /**
455
- * An SSR-friendly useLayoutEffect.
456
- *
457
- * React currently throws a warning when using useLayoutEffect on the server.
458
- * To get around it, we can conditionally useEffect on the server (no-op) and
459
- * useLayoutEffect elsewhere.
460
- *
461
- * @see https://github.com/facebook/react/issues/14927
462
- */
463
- const useIsomorphicLayoutEffect = typeof window !== 'undefined' && ((_window$document = window.document) != null && _window$document.createElement || ((_window$navigator = window.navigator) == null ? void 0 : _window$navigator.product) === 'ReactNative') ? React.useLayoutEffect : React.useEffect;
464
- function useMutableCallback(fn) {
465
- const ref = React.useRef(fn);
466
- useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
467
- return ref;
468
- }
469
- /**
470
- * Bridges renderer Context and StrictMode from a primary renderer.
471
- */
472
- function useBridge() {
473
- const fiber = useFiber();
474
- const ContextBridge = useContextBridge();
475
- return React.useMemo(() => ({
476
- children
477
- }) => {
478
- const strict = !!traverseFiber(fiber, true, node => node.type === React.StrictMode);
479
- const Root = strict ? React.StrictMode : React.Fragment;
480
- return /*#__PURE__*/jsx(Root, {
481
- children: /*#__PURE__*/jsx(ContextBridge, {
482
- children: children
483
- })
484
- });
485
- }, [fiber, ContextBridge]);
486
- }
487
- function Block({
488
- set
489
- }) {
490
- useIsomorphicLayoutEffect(() => {
491
- set(new Promise(() => null));
492
- return () => set(false);
493
- }, [set]);
494
- return null;
495
- }
496
- class ErrorBoundary extends React.Component {
497
- constructor(...args) {
498
- super(...args);
499
- this.state = {
500
- error: false
501
- };
502
- }
503
- componentDidCatch(err) {
504
- this.props.set(err);
505
- }
506
- render() {
507
- return this.state.error ? null : this.props.children;
508
- }
509
- }
510
- ErrorBoundary.getDerivedStateFromError = () => ({
511
- error: true
512
- });
513
- function calculateDpr(dpr) {
514
- var _window$devicePixelRa;
515
- // Err on the side of progress by assuming 2x dpr if we can't detect it
516
- // This will happen in workers where window is defined but dpr isn't.
517
- const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
518
- return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
519
- }
520
-
521
- /**
522
- * Returns instance root state
523
- */
524
- function getRootState(obj) {
525
- var _r3f;
526
- return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
527
- }
528
- // A collection of compare functions
529
- const is = {
530
- obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
531
- fun: a => typeof a === 'function',
532
- str: a => typeof a === 'string',
533
- num: a => typeof a === 'number',
534
- boo: a => typeof a === 'boolean',
535
- und: a => a === void 0,
536
- arr: a => Array.isArray(a),
537
- equ(a, b, {
538
- arrays = 'shallow',
539
- objects = 'reference',
540
- strict = true
541
- } = {}) {
542
- // Wrong type or one of the two undefined, doesn't match
543
- if (typeof a !== typeof b || !!a !== !!b) return false;
544
- // Atomic, just compare a against b
545
- if (is.str(a) || is.num(a) || is.boo(a)) return a === b;
546
- const isObj = is.obj(a);
547
- if (isObj && objects === 'reference') return a === b;
548
- const isArr = is.arr(a);
549
- if (isArr && arrays === 'reference') return a === b;
550
- // Array or Object, shallow compare first to see if it's a match
551
- if ((isArr || isObj) && a === b) return true;
552
- // Last resort, go through keys
553
- let i;
554
- // Check if a has all the keys of b
555
- for (i in a) if (!(i in b)) return false;
556
- // Check if values between keys match
557
- if (isObj && arrays === 'shallow' && objects === 'shallow') {
558
- for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
559
- strict,
560
- objects: 'reference'
561
- })) return false;
562
- } else {
563
- for (i in strict ? b : a) if (a[i] !== b[i]) return false;
564
- }
565
- // If i is undefined
566
- if (is.und(i)) {
567
- // If both arrays are empty we consider them equal
568
- if (isArr && a.length === 0 && b.length === 0) return true;
569
- // If both objects are empty we consider them equal
570
- if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
571
- // Otherwise match them by value
572
- if (a !== b) return false;
573
- }
574
- return true;
575
- }
576
- };
577
-
578
- // Collects nodes and materials from a THREE.Object3D
579
- function buildGraph(object) {
580
- const data = {
581
- nodes: {},
582
- materials: {}
583
- };
584
- if (object) {
585
- object.traverse(obj => {
586
- if (obj.name) data.nodes[obj.name] = obj;
587
- if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
588
- });
589
- }
590
- return data;
591
- }
592
- // Disposes an object and all its properties
593
- function dispose(obj) {
594
- if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
595
- for (const p in obj) {
596
- const prop = obj[p];
597
- if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
598
- }
599
- }
600
- const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
601
-
602
- // Gets only instance props from reconciler fibers
603
- function getInstanceProps(queue) {
604
- const props = {};
605
- for (const key in queue) {
606
- if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
607
- }
608
- return props;
609
- }
610
-
611
- // Each object in the scene carries a small LocalState descriptor
612
- function prepare(target, root, type, props) {
613
- const object = target;
614
-
615
- // Create instance descriptor
616
- let instance = object == null ? void 0 : object.__r3f;
617
- if (!instance) {
618
- instance = {
619
- root,
620
- type,
621
- parent: null,
622
- children: [],
623
- props: getInstanceProps(props),
624
- object,
625
- eventCount: 0,
626
- handlers: {},
627
- isHidden: false
628
- };
629
- if (object) {
630
- object.__r3f = instance;
631
- if (type) applyProps(object, instance.props);
632
- }
633
- }
634
- return instance;
635
- }
636
- function resolve(root, key) {
637
- var _target;
638
- let target = root[key];
639
- if (!key.includes('-')) return {
640
- root,
641
- key,
642
- target
643
- };
644
-
645
- // Resolve pierced target
646
- const chain = key.split('-');
647
- target = chain.reduce((acc, key) => acc[key], root);
648
- key = chain.pop();
649
-
650
- // Switch root if atomic
651
- if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
652
- return {
653
- root,
654
- key,
655
- target
656
- };
657
- }
658
-
659
- // Checks if a dash-cased string ends with an integer
660
- const INDEX_REGEX = /-\d+$/;
661
- function attach(parent, child) {
662
- if (is.str(child.props.attach)) {
663
- // If attaching into an array (foo-0), create one
664
- if (INDEX_REGEX.test(child.props.attach)) {
665
- const index = child.props.attach.replace(INDEX_REGEX, '');
666
- const {
667
- root,
668
- key
669
- } = resolve(parent.object, index);
670
- if (!Array.isArray(root[key])) root[key] = [];
671
- }
672
- const {
673
- root,
674
- key
675
- } = resolve(parent.object, child.props.attach);
676
- child.previousAttach = root[key];
677
- root[key] = child.object;
678
- } else if (is.fun(child.props.attach)) {
679
- child.previousAttach = child.props.attach(parent.object, child.object);
680
- }
681
- }
682
- function detach(parent, child) {
683
- if (is.str(child.props.attach)) {
684
- const {
685
- root,
686
- key
687
- } = resolve(parent.object, child.props.attach);
688
- const previous = child.previousAttach;
689
- // When the previous value was undefined, it means the value was never set to begin with
690
- if (previous === undefined) delete root[key];
691
- // Otherwise set the previous value
692
- else root[key] = previous;
693
- } else {
694
- child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
695
- }
696
- delete child.previousAttach;
697
- }
698
- const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
699
- // Instance props
700
- 'args', 'dispose', 'attach', 'object', 'onUpdate',
701
- // Behavior flags
702
- 'dispose'];
703
- const MEMOIZED_PROTOTYPES = new Map();
704
-
705
- // This function prepares a set of changes to be applied to the instance
706
- function diffProps(instance, newProps) {
707
- const changedProps = {};
708
-
709
- // Sort through props
710
- for (const prop in newProps) {
711
- // Skip reserved keys
712
- if (RESERVED_PROPS.includes(prop)) continue;
713
- // Skip if props match
714
- if (is.equ(newProps[prop], instance.props[prop])) continue;
715
-
716
- // Props changed, add them
717
- changedProps[prop] = newProps[prop];
718
-
719
- // Reset pierced props
720
- for (const other in newProps) {
721
- if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
722
- }
723
- }
724
-
725
- // Reset removed props for HMR
726
- for (const prop in instance.props) {
727
- if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
728
- const {
729
- root,
730
- key
731
- } = resolve(instance.object, prop);
732
-
733
- // https://github.com/mrdoob/three.js/issues/21209
734
- // HMR/fast-refresh relies on the ability to cancel out props, but threejs
735
- // has no means to do this. Hence we curate a small collection of value-classes
736
- // with their respective constructor/set arguments
737
- // For removed props, try to set default values, if possible
738
- if (root.constructor && root.constructor.length === 0) {
739
- // create a blank slate of the instance and copy the particular parameter.
740
- let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
741
- if (!ctor) {
742
- ctor = new root.constructor();
743
- MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
744
- }
745
- changedProps[key] = ctor[key];
746
- } else {
747
- // instance does not have constructor, just set it to 0
748
- changedProps[key] = 0;
749
- }
750
- }
751
- return changedProps;
752
- }
753
- typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';
754
-
755
- // const LinearEncoding = 3000
756
- const sRGBEncoding = 3001;
757
- const SRGBColorSpace = 'srgb';
758
- const LinearSRGBColorSpace = 'srgb-linear';
759
-
760
- // https://github.com/mrdoob/three.js/pull/27042
761
- // https://github.com/mrdoob/three.js/pull/22748
762
- const colorMaps = ['map', 'emissiveMap', 'sheenTintMap',
763
- // <r134
764
- 'sheenColorMap', 'specularTintMap',
765
- // <r134
766
- 'specularColorMap', 'envMap'];
767
- const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
768
-
769
- // This function applies a set of changes to the instance
770
- function applyProps(object, props) {
771
- const instance = object.__r3f;
772
- const rootState = instance && findInitialRoot(instance).getState();
773
- const prevHandlers = instance == null ? void 0 : instance.eventCount;
774
- for (const prop in props) {
775
- let value = props[prop];
776
-
777
- // Don't mutate reserved keys
778
- if (RESERVED_PROPS.includes(prop)) continue;
779
-
780
- // Deal with pointer events, including removing them if undefined
781
- if (instance && EVENT_REGEX.test(prop)) {
782
- if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
783
- instance.eventCount = Object.keys(instance.handlers).length;
784
- }
785
-
786
- // Ignore setting undefined props
787
- // https://github.com/pmndrs/react-three-fiber/issues/274
788
- if (value === undefined) continue;
789
- let {
790
- root,
791
- key,
792
- target
793
- } = resolve(object, prop);
794
-
795
- // Alias (output)encoding => (output)colorSpace (since r152)
796
- // https://github.com/pmndrs/react-three-fiber/pull/2829
797
- if (hasColorSpace(root)) {
798
- if (key === 'encoding') {
799
- key = 'colorSpace';
800
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
801
- } else if (key === 'outputEncoding') {
802
- key = 'outputColorSpace';
803
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
804
- }
370
+ // Layers must be written to the mask property
371
+ if (target instanceof THREE.Layers && value instanceof THREE.Layers) {
372
+ target.mask = value.mask;
373
+ } else if (target instanceof THREE.Color && isColorRepresentation(value)) {
374
+ target.set(value);
805
375
  }
806
-
807
- // Copy if properties match signatures
808
- if (typeof (target == null ? void 0 : target.copy) === 'function' && target.copy === value.copy) {
376
+ // Copy if properties match signatures and implement math interface (likely read-only)
377
+ else if (target && typeof target.set === 'function' && typeof target.copy === 'function' && value != null && value.constructor && target.constructor === value.constructor) {
809
378
  target.copy(value);
810
379
  }
811
- // Layers have no copy function, we must therefore copy the mask property
812
- else if (target instanceof THREE.Layers && value instanceof THREE.Layers) {
813
- target.mask = value.mask;
814
- }
815
380
  // Set array types
816
- else if (target != null && target.set && Array.isArray(value)) {
817
- if (target.fromArray) target.fromArray(value);else target.set(...value);
381
+ else if (target && typeof target.set === 'function' && Array.isArray(value)) {
382
+ if (typeof target.fromArray === 'function') target.fromArray(value);else target.set(...value);
818
383
  }
819
384
  // Set literal types
820
- else if (target != null && target.set && typeof value !== 'object') {
821
- const isColor = target instanceof THREE.Color;
385
+ else if (target && typeof target.set === 'function' && typeof value === 'number') {
822
386
  // Allow setting array scalars
823
- if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
387
+ if (typeof target.setScalar === 'function') target.setScalar(value);
824
388
  // Otherwise just set single value
825
389
  else target.set(value);
826
-
827
- // Emulate THREE.ColorManagement for older three.js versions
828
- // https://github.com/pmndrs/react-three-fiber/issues/344
829
- if (!getColorManagement() && !(rootState != null && rootState.linear) && isColor) target.convertSRGBToLinear();
830
390
  }
831
391
  // Else, just overwrite the value
832
392
  else {
393
+ var _root$key;
833
394
  root[key] = value;
834
395
 
835
396
  // Auto-convert sRGB texture parameters for built-in materials
836
397
  // https://github.com/pmndrs/react-three-fiber/issues/344
837
398
  // https://github.com/mrdoob/three.js/pull/25857
838
- if (rootState && !rootState.linear && colorMaps.includes(key) && root[key] instanceof THREE.Texture &&
399
+ if (rootState && !rootState.linear && colorMaps.includes(key) && (_root$key = root[key]) != null && _root$key.isTexture &&
839
400
  // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
840
401
  root[key].format === THREE.RGBAFormat && root[key].type === THREE.UnsignedByteType) {
841
402
  // NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
842
- if (hasColorSpace(root[key])) root[key].colorSpace = 'srgb';else root[key].encoding = sRGBEncoding;
403
+ root[key].colorSpace = THREE.SRGBColorSpace;
843
404
  }
844
405
  }
845
406
  }
846
407
 
847
408
  // Register event handlers
848
- if (instance != null && instance.parent && rootState != null && rootState.internal && instance.object instanceof THREE.Object3D && prevHandlers !== instance.eventCount) {
409
+ if (instance != null && instance.parent && rootState != null && rootState.internal && (_instance$object = instance.object) != null && _instance$object.isObject3D && prevHandlers !== instance.eventCount) {
410
+ const object = instance.object;
849
411
  // Pre-emptively remove the instance from the interaction manager
850
- const index = rootState.internal.interaction.indexOf(instance.object);
412
+ const index = rootState.internal.interaction.indexOf(object);
851
413
  if (index > -1) rootState.internal.interaction.splice(index, 1);
852
414
  // Add the instance to the interaction manager only when it has handlers
853
- if (instance.eventCount && instance.object.raycast !== null && instance.object instanceof THREE.Object3D) {
854
- rootState.internal.interaction.push(instance.object);
415
+ if (instance.eventCount && object.raycast !== null) {
416
+ rootState.internal.interaction.push(object);
855
417
  }
856
418
  }
857
419
 
858
420
  // Auto-attach geometries and materials
859
421
  if (instance && instance.props.attach === undefined) {
860
- if (instance.object instanceof THREE.BufferGeometry) instance.props.attach = 'geometry';else if (instance.object instanceof THREE.Material) instance.props.attach = 'material';
422
+ if (instance.object.isBufferGeometry) instance.props.attach = 'geometry';else if (instance.object.isMaterial) instance.props.attach = 'material';
861
423
  }
862
424
 
863
425
  // Instance was updated, request a frame
@@ -1029,7 +591,19 @@ function createEvents(store) {
1029
591
  stopped: false
1030
592
  };
1031
593
  for (const hit of intersections) {
1032
- const state = getRootState(hit.object);
594
+ let state = getRootState(hit.object);
595
+
596
+ // If the object is not managed by R3F, it might be parented to an element which is.
597
+ // Traverse upwards until we find a managed parent and use its state instead.
598
+ if (!state) {
599
+ hit.object.traverseAncestors(obj => {
600
+ const parentState = getRootState(obj);
601
+ if (parentState) {
602
+ state = parentState;
603
+ return false;
604
+ }
605
+ });
606
+ }
1033
607
  if (state) {
1034
608
  const {
1035
609
  raycaster,
@@ -1290,7 +864,7 @@ function createEvents(store) {
1290
864
  }
1291
865
 
1292
866
  const isRenderer = def => !!(def != null && def.render);
1293
- const context = /*#__PURE__*/React.createContext(null);
867
+ const context = /* @__PURE__ */React.createContext(null);
1294
868
  const createStore = (invalidate, advance) => {
1295
869
  const rootStore = createWithEqualityFn((set, get) => {
1296
870
  const position = new THREE.Vector3();
@@ -1304,7 +878,7 @@ const createStore = (invalidate, advance) => {
1304
878
  left
1305
879
  } = size;
1306
880
  const aspect = width / height;
1307
- if (target instanceof THREE.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
881
+ if (target.isVector3) tempTarget.copy(target);else tempTarget.set(...target);
1308
882
  const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1309
883
  if (isOrthographicCamera(camera)) {
1310
884
  return {
@@ -1506,161 +1080,576 @@ const createStore = (invalidate, advance) => {
1506
1080
  oldDpr = viewport.dpr;
1507
1081
  // Update camera & renderer
1508
1082
  updateCamera(camera, size);
1509
- gl.setPixelRatio(viewport.dpr);
1083
+ if (viewport.dpr > 0) gl.setPixelRatio(viewport.dpr);
1510
1084
  const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
1511
1085
  gl.setSize(size.width, size.height, updateStyle);
1512
1086
  }
1513
-
1514
- // Update viewport once the camera changes
1515
- if (camera !== oldCamera) {
1516
- oldCamera = camera;
1517
- // Update viewport
1518
- set(state => ({
1519
- viewport: {
1520
- ...state.viewport,
1521
- ...state.viewport.getCurrentViewport(camera)
1522
- }
1523
- }));
1087
+
1088
+ // Update viewport once the camera changes
1089
+ if (camera !== oldCamera) {
1090
+ oldCamera = camera;
1091
+ // Update viewport
1092
+ set(state => ({
1093
+ viewport: {
1094
+ ...state.viewport,
1095
+ ...state.viewport.getCurrentViewport(camera)
1096
+ }
1097
+ }));
1098
+ }
1099
+ });
1100
+
1101
+ // Invalidate on any change
1102
+ rootStore.subscribe(state => invalidate(state));
1103
+
1104
+ // Return root state
1105
+ return rootStore;
1106
+ };
1107
+
1108
+ /**
1109
+ * Exposes an object's {@link Instance}.
1110
+ * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle
1111
+ *
1112
+ * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1113
+ */
1114
+ function useInstanceHandle(ref) {
1115
+ const instance = React.useRef(null);
1116
+ React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1117
+ return instance;
1118
+ }
1119
+
1120
+ /**
1121
+ * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1122
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1123
+ */
1124
+ function useStore() {
1125
+ const store = React.useContext(context);
1126
+ if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1127
+ return store;
1128
+ }
1129
+
1130
+ /**
1131
+ * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1132
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1133
+ */
1134
+ function useThree(selector = state => state, equalityFn) {
1135
+ return useStore()(selector, equalityFn);
1136
+ }
1137
+
1138
+ /**
1139
+ * Executes a callback before render in a shared frame loop.
1140
+ * Can order effects with render priority or manually render with a positive priority.
1141
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1142
+ */
1143
+ function useFrame(callback, renderPriority = 0) {
1144
+ const store = useStore();
1145
+ const subscribe = store.getState().internal.subscribe;
1146
+ // Memoize ref
1147
+ const ref = useMutableCallback(callback);
1148
+ // Subscribe on mount, unsubscribe on unmount
1149
+ useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1150
+ return null;
1151
+ }
1152
+
1153
+ /**
1154
+ * Returns a node graph of an object with named nodes & materials.
1155
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1156
+ */
1157
+ function useGraph(object) {
1158
+ return React.useMemo(() => buildGraph(object), [object]);
1159
+ }
1160
+ const memoizedLoaders = new WeakMap();
1161
+ const isConstructor$1 = value => {
1162
+ var _value$prototype;
1163
+ return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1164
+ };
1165
+ function loadingFn(extensions, onProgress) {
1166
+ return function (Proto, ...input) {
1167
+ let loader;
1168
+
1169
+ // Construct and cache loader if constructor was passed
1170
+ if (isConstructor$1(Proto)) {
1171
+ loader = memoizedLoaders.get(Proto);
1172
+ if (!loader) {
1173
+ loader = new Proto();
1174
+ memoizedLoaders.set(Proto, loader);
1175
+ }
1176
+ } else {
1177
+ loader = Proto;
1178
+ }
1179
+
1180
+ // Apply loader extensions
1181
+ if (extensions) extensions(loader);
1182
+
1183
+ // Go through the urls and load them
1184
+ return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => {
1185
+ if (isObject3D(data == null ? void 0 : data.scene)) Object.assign(data, buildGraph(data.scene));
1186
+ res(data);
1187
+ }, onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
1188
+ };
1189
+ }
1190
+
1191
+ /**
1192
+ * Synchronously loads and caches assets with a three loader.
1193
+ *
1194
+ * Note: this hook's caller must be wrapped with `React.Suspense`
1195
+ * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1196
+ */
1197
+ function useLoader(loader, input, extensions, onProgress) {
1198
+ // Use suspense to load async assets
1199
+ const keys = Array.isArray(input) ? input : [input];
1200
+ const results = suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1201
+ equal: is.equ
1202
+ });
1203
+ // Return the object(s)
1204
+ return Array.isArray(input) ? results : results[0];
1205
+ }
1206
+
1207
+ /**
1208
+ * Preloads an asset into cache as a side-effect.
1209
+ */
1210
+ useLoader.preload = function (loader, input, extensions) {
1211
+ const keys = Array.isArray(input) ? input : [input];
1212
+ return preload(loadingFn(extensions), [loader, ...keys]);
1213
+ };
1214
+
1215
+ /**
1216
+ * Removes a loaded asset from cache.
1217
+ */
1218
+ useLoader.clear = function (loader, input) {
1219
+ const keys = Array.isArray(input) ? input : [input];
1220
+ return clear([loader, ...keys]);
1221
+ };
1222
+
1223
+ // TODO: upstream to DefinitelyTyped for React 19
1224
+ // https://github.com/facebook/react/issues/28956
1225
+
1226
+ function createReconciler(config) {
1227
+ const reconciler = Reconciler(config);
1228
+ reconciler.injectIntoDevTools({
1229
+ bundleType: typeof process !== 'undefined' && process.env.NODE_ENV !== 'production' ? 1 : 0,
1230
+ rendererPackageName: '@react-three/fiber',
1231
+ version: React.version
1232
+ });
1233
+ return reconciler;
1234
+ }
1235
+ const NoEventPriority = 0;
1236
+
1237
+ // TODO: handle constructor overloads
1238
+ // https://github.com/pmndrs/react-three-fiber/pull/2931
1239
+ // https://github.com/microsoft/TypeScript/issues/37079
1240
+
1241
+ const catalogue = {};
1242
+ const PREFIX_REGEX = /^three(?=[A-Z])/;
1243
+ const toPascalCase = type => `${type[0].toUpperCase()}${type.slice(1)}`;
1244
+ let i = 0;
1245
+ const isConstructor = object => typeof object === 'function';
1246
+ function extend(objects) {
1247
+ if (isConstructor(objects)) {
1248
+ const Component = `${i++}`;
1249
+ catalogue[Component] = objects;
1250
+ return Component;
1251
+ } else {
1252
+ Object.assign(catalogue, objects);
1253
+ }
1254
+ }
1255
+ function validateInstance(type, props) {
1256
+ // Get target from catalogue
1257
+ const name = `${type[0].toUpperCase()}${type.slice(1)}`;
1258
+ const target = catalogue[name];
1259
+
1260
+ // Validate element target
1261
+ if (type !== 'primitive' && !target) throw new Error(`R3F: ${name} is not part of the THREE namespace! Did you forget to extend? See: https://docs.pmnd.rs/react-three-fiber/api/objects#using-3rd-party-objects-declaratively`);
1262
+
1263
+ // Validate primitives
1264
+ if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
1265
+
1266
+ // Throw if an object or literal was passed for args
1267
+ if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
1268
+ }
1269
+ function createInstance(type, props, root) {
1270
+ var _props$object;
1271
+ // Remove three* prefix from elements
1272
+ type = type.replace(PREFIX_REGEX, '');
1273
+ validateInstance(type, props);
1274
+
1275
+ // Regenerate the R3F instance for primitives to simulate a new object
1276
+ if (type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
1277
+ return prepare(props.object, root, type, props);
1278
+ }
1279
+ function hideInstance(instance) {
1280
+ if (!instance.isHidden) {
1281
+ var _instance$parent;
1282
+ if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
1283
+ detach(instance.parent, instance);
1284
+ } else if (isObject3D(instance.object)) {
1285
+ instance.object.visible = false;
1286
+ }
1287
+ instance.isHidden = true;
1288
+ invalidateInstance(instance);
1289
+ }
1290
+ }
1291
+ function unhideInstance(instance) {
1292
+ if (instance.isHidden) {
1293
+ var _instance$parent2;
1294
+ if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
1295
+ attach(instance.parent, instance);
1296
+ } else if (isObject3D(instance.object) && instance.props.visible !== false) {
1297
+ instance.object.visible = true;
1524
1298
  }
1525
- });
1299
+ instance.isHidden = false;
1300
+ invalidateInstance(instance);
1301
+ }
1302
+ }
1526
1303
 
1527
- // Invalidate on any change
1528
- rootStore.subscribe(state => invalidate(state));
1304
+ // https://github.com/facebook/react/issues/20271
1305
+ // This will make sure events and attach are only handled once when trees are complete
1306
+ function handleContainerEffects(parent, child, beforeChild) {
1307
+ // Bail if tree isn't mounted or parent is not a container.
1308
+ // This ensures that the tree is finalized and React won't discard results to Suspense
1309
+ const state = child.root.getState();
1310
+ if (!parent.parent && parent.object !== state.scene) return;
1529
1311
 
1530
- // Return root state
1531
- return rootStore;
1532
- };
1312
+ // Create & link object on first run
1313
+ if (!child.object) {
1314
+ var _child$props$object, _child$props$args;
1315
+ // Get target from catalogue
1316
+ const target = catalogue[toPascalCase(child.type)];
1533
1317
 
1534
- /**
1535
- * Exposes an object's {@link Instance}.
1536
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
1537
- *
1538
- * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1539
- */
1540
- function useInstanceHandle(ref) {
1541
- const instance = React.useRef(null);
1542
- React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1543
- return instance;
1318
+ // Create object
1319
+ child.object = (_child$props$object = child.props.object) != null ? _child$props$object : new target(...((_child$props$args = child.props.args) != null ? _child$props$args : []));
1320
+ child.object.__r3f = child;
1321
+ }
1322
+
1323
+ // Set initial props
1324
+ applyProps(child.object, child.props);
1325
+
1326
+ // Append instance
1327
+ if (child.props.attach) {
1328
+ attach(parent, child);
1329
+ } else if (isObject3D(child.object) && isObject3D(parent.object)) {
1330
+ const childIndex = parent.object.children.indexOf(beforeChild == null ? void 0 : beforeChild.object);
1331
+ if (beforeChild && childIndex !== -1) {
1332
+ child.object.parent = parent.object;
1333
+ parent.object.children.splice(childIndex, 0, child.object);
1334
+ child.object.dispatchEvent({
1335
+ type: 'added'
1336
+ });
1337
+ parent.object.dispatchEvent({
1338
+ type: 'childadded',
1339
+ child: child.object
1340
+ });
1341
+ } else {
1342
+ parent.object.add(child.object);
1343
+ }
1344
+ }
1345
+
1346
+ // Link subtree
1347
+ for (const childInstance of child.children) handleContainerEffects(child, childInstance);
1348
+
1349
+ // Tree was updated, request a frame
1350
+ invalidateInstance(child);
1544
1351
  }
1352
+ function appendChild(parent, child) {
1353
+ if (!child) return;
1545
1354
 
1546
- /**
1547
- * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1548
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1549
- */
1550
- function useStore() {
1551
- const store = React.useContext(context);
1552
- if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1553
- return store;
1355
+ // Link instances
1356
+ child.parent = parent;
1357
+ parent.children.push(child);
1358
+
1359
+ // Attach tree once complete
1360
+ handleContainerEffects(parent, child);
1554
1361
  }
1362
+ function insertBefore(parent, child, beforeChild) {
1363
+ if (!child || !beforeChild) return;
1555
1364
 
1556
- /**
1557
- * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1558
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1559
- */
1560
- function useThree(selector = state => state, equalityFn) {
1561
- return useStore()(selector, equalityFn);
1365
+ // Link instances
1366
+ child.parent = parent;
1367
+ const childIndex = parent.children.indexOf(beforeChild);
1368
+ if (childIndex !== -1) parent.children.splice(childIndex, 0, child);else parent.children.push(child);
1369
+
1370
+ // Attach tree once complete
1371
+ handleContainerEffects(parent, child, beforeChild);
1562
1372
  }
1373
+ function disposeOnIdle(object) {
1374
+ if (typeof object.dispose === 'function') {
1375
+ const handleDispose = () => {
1376
+ try {
1377
+ object.dispose();
1378
+ } catch {
1379
+ // no-op
1380
+ }
1381
+ };
1563
1382
 
1564
- /**
1565
- * Executes a callback before render in a shared frame loop.
1566
- * Can order effects with render priority or manually render with a positive priority.
1567
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1568
- */
1569
- function useFrame(callback, renderPriority = 0) {
1570
- const store = useStore();
1571
- const subscribe = store.getState().internal.subscribe;
1572
- // Memoize ref
1573
- const ref = useMutableCallback(callback);
1574
- // Subscribe on mount, unsubscribe on unmount
1575
- useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1576
- return null;
1383
+ // In a testing environment, cleanup immediately
1384
+ if (typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined') handleDispose();
1385
+ // Otherwise, using a real GPU so schedule cleanup to prevent stalls
1386
+ else unstable_scheduleCallback(unstable_IdlePriority, handleDispose);
1387
+ }
1577
1388
  }
1389
+ function removeChild(parent, child, dispose) {
1390
+ if (!child) return;
1578
1391
 
1579
- /**
1580
- * Returns a node graph of an object with named nodes & materials.
1581
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1582
- */
1583
- function useGraph(object) {
1584
- return React.useMemo(() => buildGraph(object), [object]);
1392
+ // Unlink instances
1393
+ child.parent = null;
1394
+ const childIndex = parent.children.indexOf(child);
1395
+ if (childIndex !== -1) parent.children.splice(childIndex, 1);
1396
+
1397
+ // Eagerly tear down tree
1398
+ if (child.props.attach) {
1399
+ detach(parent, child);
1400
+ } else if (isObject3D(child.object) && isObject3D(parent.object)) {
1401
+ parent.object.remove(child.object);
1402
+ removeInteractivity(findInitialRoot(child), child.object);
1403
+ }
1404
+
1405
+ // Allow objects to bail out of unmount disposal with dispose={null}
1406
+ const shouldDispose = child.props.dispose !== null && dispose !== false;
1407
+
1408
+ // Recursively remove instance children
1409
+ for (let i = child.children.length - 1; i >= 0; i--) {
1410
+ const node = child.children[i];
1411
+ removeChild(child, node, shouldDispose);
1412
+ }
1413
+ child.children.length = 0;
1414
+
1415
+ // Unlink instance object
1416
+ delete child.object.__r3f;
1417
+
1418
+ // Dispose object whenever the reconciler feels like it.
1419
+ // Never dispose of primitives because their state may be kept outside of React!
1420
+ // In order for an object to be able to dispose it
1421
+ // - has a dispose method
1422
+ // - cannot be a <primitive object={...} />
1423
+ // - cannot be a THREE.Scene, because three has broken its own API
1424
+ if (shouldDispose && child.type !== 'primitive' && child.object.type !== 'Scene') {
1425
+ disposeOnIdle(child.object);
1426
+ }
1427
+
1428
+ // Tree was updated, request a frame for top-level instance
1429
+ if (dispose === undefined) invalidateInstance(child);
1585
1430
  }
1586
- const memoizedLoaders = new WeakMap();
1587
- const isConstructor = value => {
1588
- var _value$prototype;
1589
- return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1590
- };
1591
- function loadingFn(extensions, onProgress) {
1592
- return async function (Proto, ...input) {
1593
- let loader;
1431
+ function setFiberRef(fiber, publicInstance) {
1432
+ for (const _fiber of [fiber, fiber.alternate]) {
1433
+ if (_fiber !== null) {
1434
+ if (typeof _fiber.ref === 'function') {
1435
+ _fiber.refCleanup == null ? void 0 : _fiber.refCleanup();
1436
+ const cleanup = _fiber.ref(publicInstance);
1437
+ if (typeof cleanup === 'function') _fiber.refCleanup = cleanup;
1438
+ } else if (_fiber.ref) {
1439
+ _fiber.ref.current = publicInstance;
1440
+ }
1441
+ }
1442
+ }
1443
+ }
1444
+ const reconstructed = [];
1445
+ function swapInstances() {
1446
+ // Detach instance
1447
+ for (const [instance] of reconstructed) {
1448
+ const parent = instance.parent;
1449
+ if (parent) {
1450
+ if (instance.props.attach) {
1451
+ detach(parent, instance);
1452
+ } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
1453
+ parent.object.remove(instance.object);
1454
+ }
1455
+ for (const child of instance.children) {
1456
+ if (child.props.attach) {
1457
+ detach(instance, child);
1458
+ } else if (isObject3D(child.object) && isObject3D(instance.object)) {
1459
+ instance.object.remove(child.object);
1460
+ }
1461
+ }
1462
+ }
1594
1463
 
1595
- // Construct and cache loader if constructor was passed
1596
- if (isConstructor(Proto)) {
1597
- loader = memoizedLoaders.get(Proto);
1598
- if (!loader) {
1599
- loader = new Proto();
1600
- memoizedLoaders.set(Proto, loader);
1464
+ // If the old instance is hidden, we need to unhide it.
1465
+ // React assumes it can discard instances since they're pure for DOM.
1466
+ // This isn't true for us since our lifetimes are impure and longliving.
1467
+ // So, we manually check if an instance was hidden and unhide it.
1468
+ if (instance.isHidden) unhideInstance(instance);
1469
+
1470
+ // Dispose of old object if able
1471
+ if (instance.object.__r3f) delete instance.object.__r3f;
1472
+ if (instance.type !== 'primitive') disposeOnIdle(instance.object);
1473
+ }
1474
+
1475
+ // Update instance
1476
+ for (const [instance, props, fiber] of reconstructed) {
1477
+ instance.props = props;
1478
+ const parent = instance.parent;
1479
+ if (parent) {
1480
+ var _instance$props$objec, _instance$props$args;
1481
+ // Get target from catalogue
1482
+ const target = catalogue[toPascalCase(instance.type)];
1483
+
1484
+ // Create object
1485
+ instance.object = (_instance$props$objec = instance.props.object) != null ? _instance$props$objec : new target(...((_instance$props$args = instance.props.args) != null ? _instance$props$args : []));
1486
+ instance.object.__r3f = instance;
1487
+ setFiberRef(fiber, instance.object);
1488
+
1489
+ // Set initial props
1490
+ applyProps(instance.object, instance.props);
1491
+ if (instance.props.attach) {
1492
+ attach(parent, instance);
1493
+ } else if (isObject3D(instance.object) && isObject3D(parent.object)) {
1494
+ parent.object.add(instance.object);
1601
1495
  }
1602
- } else {
1603
- loader = Proto;
1496
+ for (const child of instance.children) {
1497
+ if (child.props.attach) {
1498
+ attach(instance, child);
1499
+ } else if (isObject3D(child.object) && isObject3D(instance.object)) {
1500
+ instance.object.add(child.object);
1501
+ }
1502
+ }
1503
+
1504
+ // Tree was updated, request a frame
1505
+ invalidateInstance(instance);
1604
1506
  }
1507
+ }
1508
+ reconstructed.length = 0;
1509
+ }
1605
1510
 
1606
- // Apply loader extensions
1607
- if (extensions) extensions(loader);
1511
+ // Don't handle text instances, make it no-op
1512
+ const handleTextInstance = () => {};
1513
+ const NO_CONTEXT = {};
1514
+ let currentUpdatePriority = NoEventPriority;
1608
1515
 
1609
- // Go through the urls and load them
1610
- return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => res(isObject3D(data == null ? void 0 : data.scene) ? Object.assign(data, buildGraph(data.scene)) : data), onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
1611
- };
1612
- }
1516
+ // https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberFlags.js
1517
+ const NoFlags = 0;
1518
+ const Update = 4;
1519
+ const reconciler = /* @__PURE__ */createReconciler({
1520
+ isPrimaryRenderer: false,
1521
+ warnsIfNotActing: false,
1522
+ supportsMutation: true,
1523
+ supportsPersistence: false,
1524
+ supportsHydration: false,
1525
+ createInstance,
1526
+ removeChild,
1527
+ appendChild,
1528
+ appendInitialChild: appendChild,
1529
+ insertBefore,
1530
+ appendChildToContainer(container, child) {
1531
+ const scene = container.getState().scene.__r3f;
1532
+ if (!child || !scene) return;
1533
+ appendChild(scene, child);
1534
+ },
1535
+ removeChildFromContainer(container, child) {
1536
+ const scene = container.getState().scene.__r3f;
1537
+ if (!child || !scene) return;
1538
+ removeChild(scene, child);
1539
+ },
1540
+ insertInContainerBefore(container, child, beforeChild) {
1541
+ const scene = container.getState().scene.__r3f;
1542
+ if (!child || !beforeChild || !scene) return;
1543
+ insertBefore(scene, child, beforeChild);
1544
+ },
1545
+ getRootHostContext: () => NO_CONTEXT,
1546
+ getChildHostContext: () => NO_CONTEXT,
1547
+ commitUpdate(instance, type, oldProps, newProps, fiber) {
1548
+ var _newProps$args, _oldProps$args, _newProps$args2;
1549
+ validateInstance(type, newProps);
1550
+ let reconstruct = false;
1613
1551
 
1614
- /**
1615
- * Synchronously loads and caches assets with a three loader.
1616
- *
1617
- * Note: this hook's caller must be wrapped with `React.Suspense`
1618
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1619
- */
1620
- function useLoader(loader, input, extensions, onProgress) {
1621
- // Use suspense to load async assets
1622
- const keys = Array.isArray(input) ? input : [input];
1623
- const results = suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1624
- equal: is.equ
1625
- });
1626
- // Return the object(s)
1627
- return Array.isArray(input) ? results : results[0];
1628
- }
1552
+ // Reconstruct primitives if object prop changes
1553
+ if (instance.type === 'primitive' && oldProps.object !== newProps.object) reconstruct = true;
1554
+ // Reconstruct instance if args were added or removed
1555
+ else if (((_newProps$args = newProps.args) == null ? void 0 : _newProps$args.length) !== ((_oldProps$args = oldProps.args) == null ? void 0 : _oldProps$args.length)) reconstruct = true;
1556
+ // Reconstruct instance if args were changed
1557
+ else if ((_newProps$args2 = newProps.args) != null && _newProps$args2.some((value, index) => {
1558
+ var _oldProps$args2;
1559
+ return value !== ((_oldProps$args2 = oldProps.args) == null ? void 0 : _oldProps$args2[index]);
1560
+ })) reconstruct = true;
1629
1561
 
1630
- /**
1631
- * Preloads an asset into cache as a side-effect.
1632
- */
1633
- useLoader.preload = function (loader, input, extensions) {
1634
- const keys = Array.isArray(input) ? input : [input];
1635
- return preload(loadingFn(extensions), [loader, ...keys]);
1636
- };
1562
+ // Reconstruct when args or <primitive object={...} have changes
1563
+ if (reconstruct) {
1564
+ reconstructed.push([instance, {
1565
+ ...newProps
1566
+ }, fiber]);
1567
+ } else {
1568
+ // Create a diff-set, flag if there are any changes
1569
+ const changedProps = diffProps(instance, newProps);
1570
+ if (Object.keys(changedProps).length) {
1571
+ Object.assign(instance.props, changedProps);
1572
+ applyProps(instance.object, changedProps);
1573
+ }
1574
+ }
1637
1575
 
1638
- /**
1639
- * Removes a loaded asset from cache.
1640
- */
1641
- useLoader.clear = function (loader, input) {
1642
- const keys = Array.isArray(input) ? input : [input];
1643
- return clear([loader, ...keys]);
1644
- };
1576
+ // Flush reconstructed siblings when we hit the last updated child in a sequence
1577
+ const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
1578
+ if (isTailSibling) swapInstances();
1579
+ },
1580
+ finalizeInitialChildren: () => false,
1581
+ commitMount() {},
1582
+ getPublicInstance: instance => instance == null ? void 0 : instance.object,
1583
+ prepareForCommit: () => null,
1584
+ preparePortalMount: container => prepare(container.getState().scene, container, '', {}),
1585
+ resetAfterCommit: () => {},
1586
+ shouldSetTextContent: () => false,
1587
+ clearContainer: () => false,
1588
+ hideInstance,
1589
+ unhideInstance,
1590
+ createTextInstance: handleTextInstance,
1591
+ hideTextInstance: handleTextInstance,
1592
+ unhideTextInstance: handleTextInstance,
1593
+ scheduleTimeout: typeof setTimeout === 'function' ? setTimeout : undefined,
1594
+ cancelTimeout: typeof clearTimeout === 'function' ? clearTimeout : undefined,
1595
+ noTimeout: -1,
1596
+ getInstanceFromNode: () => null,
1597
+ beforeActiveInstanceBlur() {},
1598
+ afterActiveInstanceBlur() {},
1599
+ detachDeletedInstance() {},
1600
+ prepareScopeUpdate() {},
1601
+ getInstanceFromScope: () => null,
1602
+ shouldAttemptEagerTransition: () => false,
1603
+ trackSchedulerEvent: () => {},
1604
+ resolveEventType: () => null,
1605
+ resolveEventTimeStamp: () => -1.1,
1606
+ requestPostPaintCallback() {},
1607
+ maySuspendCommit: () => false,
1608
+ preloadInstance: () => true,
1609
+ // true indicates already loaded
1610
+ startSuspendingCommit() {},
1611
+ suspendInstance() {},
1612
+ waitForCommitToBeReady: () => null,
1613
+ NotPendingTransition: null,
1614
+ HostTransitionContext: /* @__PURE__ */React.createContext(null),
1615
+ setCurrentUpdatePriority(newPriority) {
1616
+ currentUpdatePriority = newPriority;
1617
+ },
1618
+ getCurrentUpdatePriority() {
1619
+ return currentUpdatePriority;
1620
+ },
1621
+ resolveUpdatePriority() {
1622
+ var _window$event;
1623
+ if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
1624
+ switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
1625
+ case 'click':
1626
+ case 'contextmenu':
1627
+ case 'dblclick':
1628
+ case 'pointercancel':
1629
+ case 'pointerdown':
1630
+ case 'pointerup':
1631
+ return DiscreteEventPriority;
1632
+ case 'pointermove':
1633
+ case 'pointerout':
1634
+ case 'pointerover':
1635
+ case 'pointerenter':
1636
+ case 'pointerleave':
1637
+ case 'wheel':
1638
+ return ContinuousEventPriority;
1639
+ default:
1640
+ return DefaultEventPriority;
1641
+ }
1642
+ },
1643
+ resetFormInstance() {}
1644
+ });
1645
1645
 
1646
1646
  const _roots = new Map();
1647
1647
  const shallowLoose = {
1648
1648
  objects: 'shallow',
1649
1649
  strict: false
1650
1650
  };
1651
- const createRendererInstance = (gl, canvas) => {
1652
- const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1653
- if (isRenderer(customRenderer)) return customRenderer;
1654
- return new THREE.WebGLRenderer({
1655
- powerPreference: 'high-performance',
1656
- canvas: canvas,
1657
- antialias: true,
1658
- alpha: true,
1659
- ...gl
1660
- });
1661
- };
1662
1651
  function computeInitialSize(canvas, size) {
1663
- if (!size && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1652
+ if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1664
1653
  const {
1665
1654
  width,
1666
1655
  height,
@@ -1736,10 +1725,13 @@ function createRoot(canvas) {
1736
1725
 
1737
1726
  // Locals
1738
1727
  let onCreated;
1739
- let configured = false;
1740
1728
  let lastCamera;
1729
+ let configured = false;
1730
+ let pending = null;
1741
1731
  return {
1742
- configure(props = {}) {
1732
+ async configure(props = {}) {
1733
+ let resolve;
1734
+ pending = new Promise(_resolve => resolve = _resolve);
1743
1735
  let {
1744
1736
  gl: glConfig,
1745
1737
  size: propsSize,
@@ -1762,9 +1754,26 @@ function createRoot(canvas) {
1762
1754
 
1763
1755
  // Set up renderer (one time only!)
1764
1756
  let gl = state.gl;
1765
- if (!state.gl) state.set({
1766
- gl: gl = createRendererInstance(glConfig, canvas)
1767
- });
1757
+ if (!state.gl) {
1758
+ const defaultProps = {
1759
+ canvas: canvas,
1760
+ powerPreference: 'high-performance',
1761
+ antialias: true,
1762
+ alpha: true
1763
+ };
1764
+ const customRenderer = typeof glConfig === 'function' ? await glConfig(defaultProps) : glConfig;
1765
+ if (isRenderer(customRenderer)) {
1766
+ gl = customRenderer;
1767
+ } else {
1768
+ gl = new THREE.WebGLRenderer({
1769
+ ...defaultProps,
1770
+ ...glConfig
1771
+ });
1772
+ }
1773
+ state.set({
1774
+ gl
1775
+ });
1776
+ }
1768
1777
 
1769
1778
  // Set up raycaster (one time only!)
1770
1779
  let raycaster = state.raycaster;
@@ -1790,7 +1799,7 @@ function createRoot(canvas) {
1790
1799
  // Create default camera, don't overwrite any user-set state
1791
1800
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
1792
1801
  lastCamera = cameraOptions;
1793
- const isCamera = cameraOptions instanceof THREE.Camera;
1802
+ const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
1794
1803
  const camera = isCamera ? cameraOptions : orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
1795
1804
  if (!isCamera) {
1796
1805
  camera.position.z = 5;
@@ -1820,7 +1829,7 @@ function createRoot(canvas) {
1820
1829
  // Set up scene (one time only!)
1821
1830
  if (!state.scene) {
1822
1831
  let scene;
1823
- if (sceneOptions instanceof THREE.Scene) {
1832
+ if (sceneOptions != null && sceneOptions.isScene) {
1824
1833
  scene = sceneOptions;
1825
1834
  prepare(scene, store, '', {});
1826
1835
  } else {
@@ -1833,8 +1842,34 @@ function createRoot(canvas) {
1833
1842
  });
1834
1843
  }
1835
1844
 
1845
+ // Store events internally
1846
+ if (events && !state.events.handlers) state.set({
1847
+ events: events(store)
1848
+ });
1849
+ // Check size, allow it to take on container bounds initially
1850
+ const size = computeInitialSize(canvas, propsSize);
1851
+ if (!is.equ(size, state.size, shallowLoose)) {
1852
+ state.setSize(size.width, size.height, size.top, size.left);
1853
+ }
1854
+ // Check pixelratio
1855
+ if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1856
+ // Check frameloop
1857
+ if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
1858
+ // Check pointer missed
1859
+ if (!state.onPointerMissed) state.set({
1860
+ onPointerMissed
1861
+ });
1862
+ // Check performance
1863
+ if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1864
+ performance: {
1865
+ ...state.performance,
1866
+ ...performance
1867
+ }
1868
+ }));
1869
+
1836
1870
  // Set up XR (one time only!)
1837
1871
  if (!state.xr) {
1872
+ var _gl$xr;
1838
1873
  // Handle frame behavior in WebXR
1839
1874
  const handleXRFrame = (timestamp, frame) => {
1840
1875
  const state = store.getState();
@@ -1865,7 +1900,7 @@ function createRoot(canvas) {
1865
1900
  };
1866
1901
 
1867
1902
  // Subscribe to WebXR session events
1868
- if (gl.xr) xr.connect();
1903
+ if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
1869
1904
  state.set({
1870
1905
  xr
1871
1906
  });
@@ -1892,22 +1927,12 @@ function createRoot(canvas) {
1892
1927
  }
1893
1928
  if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
1894
1929
  }
1895
-
1896
- // Safely set color management if available.
1897
- // Avoid accessing THREE.ColorManagement to play nice with older versions
1898
- const ColorManagement = getColorManagement();
1899
- if (ColorManagement) {
1900
- if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
1901
- }
1930
+ THREE.ColorManagement.enabled = !legacy;
1902
1931
 
1903
1932
  // Set color space and tonemapping preferences
1904
1933
  if (!configured) {
1905
- const LinearEncoding = 3000;
1906
- const sRGBEncoding = 3001;
1907
- applyProps(gl, {
1908
- outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1909
- toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping
1910
- });
1934
+ gl.outputColorSpace = linear ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace;
1935
+ gl.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
1911
1936
  }
1912
1937
 
1913
1938
  // Update color management state
@@ -1923,45 +1948,24 @@ function createRoot(canvas) {
1923
1948
 
1924
1949
  // Set gl props
1925
1950
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
1926
- // Store events internally
1927
- if (events && !state.events.handlers) state.set({
1928
- events: events(store)
1929
- });
1930
- // Check size, allow it to take on container bounds initially
1931
- const size = computeInitialSize(canvas, propsSize);
1932
- if (!is.equ(size, state.size, shallowLoose)) {
1933
- state.setSize(size.width, size.height, size.top, size.left);
1934
- }
1935
- // Check pixelratio
1936
- if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1937
- // Check frameloop
1938
- if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
1939
- // Check pointer missed
1940
- if (!state.onPointerMissed) state.set({
1941
- onPointerMissed
1942
- });
1943
- // Check performance
1944
- if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1945
- performance: {
1946
- ...state.performance,
1947
- ...performance
1948
- }
1949
- }));
1950
1951
 
1951
1952
  // Set locals
1952
1953
  onCreated = onCreatedCallback;
1953
1954
  configured = true;
1955
+ resolve();
1954
1956
  return this;
1955
1957
  },
1956
1958
  render(children) {
1957
1959
  // The root has to be configured before it can be rendered
1958
- if (!configured) this.configure();
1959
- reconciler.updateContainer( /*#__PURE__*/jsx(Provider, {
1960
- store: store,
1961
- children: children,
1962
- onCreated: onCreated,
1963
- rootElement: canvas
1964
- }), fiber, null, () => undefined);
1960
+ if (!configured && !pending) this.configure();
1961
+ pending.then(() => {
1962
+ reconciler.updateContainer( /*#__PURE__*/jsx(Provider, {
1963
+ store: store,
1964
+ children: children,
1965
+ onCreated: onCreated,
1966
+ rootElement: canvas
1967
+ }), fiber, null, () => undefined);
1968
+ });
1965
1969
  return store;
1966
1970
  },
1967
1971
  unmount() {
@@ -1969,12 +1973,6 @@ function createRoot(canvas) {
1969
1973
  }
1970
1974
  };
1971
1975
  }
1972
- function render(children, canvas, config) {
1973
- console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
1974
- const root = createRoot(canvas);
1975
- root.configure(config);
1976
- return root.render(children);
1977
- }
1978
1976
  function Provider({
1979
1977
  store,
1980
1978
  children,
@@ -2112,18 +2110,17 @@ function Portal({
2112
2110
  return store;
2113
2111
  // eslint-disable-next-line react-hooks/exhaustive-deps
2114
2112
  }, [previousRoot, container]);
2115
- return /*#__PURE__*/jsx(Fragment, {
2116
- children: reconciler.createPortal( /*#__PURE__*/jsx(context.Provider, {
2117
- value: usePortalStore,
2118
- children: children
2119
- }), usePortalStore, null)
2120
- });
2113
+ return (
2114
+ /*#__PURE__*/
2115
+ // @ts-ignore, reconciler types are not maintained
2116
+ jsx(Fragment, {
2117
+ children: reconciler.createPortal( /*#__PURE__*/jsx(context.Provider, {
2118
+ value: usePortalStore,
2119
+ children: children
2120
+ }), usePortalStore, null)
2121
+ })
2122
+ );
2121
2123
  }
2122
- reconciler.injectIntoDevTools({
2123
- bundleType: process.env.NODE_ENV === 'production' ? 0 : 1,
2124
- rendererPackageName: '@react-three/fiber',
2125
- version: React.version
2126
- });
2127
2124
 
2128
2125
  function createSubs(callback, subs) {
2129
2126
  const sub = {
@@ -2222,7 +2219,7 @@ function loop(timestamp) {
2222
2219
  repeat += update(timestamp, state);
2223
2220
  }
2224
2221
  }
2225
- useFrameInProgress = true;
2222
+ useFrameInProgress = false;
2226
2223
 
2227
2224
  // Run after-effects
2228
2225
  flushGlobalEffects('after', timestamp);
@@ -2363,4 +2360,4 @@ function createPointerEvents(store) {
2363
2360
  };
2364
2361
  }
2365
2362
 
2366
- export { useStore as A, Block as B, useThree as C, useFrame as D, ErrorBoundary as E, useGraph as F, useLoader as G, _roots as _, useMutableCallback as a, useIsomorphicLayoutEffect as b, createRoot as c, unmountComponentAtNode as d, extend as e, createPointerEvents as f, createEvents as g, flushGlobalEffects as h, isRef as i, addEffect as j, addAfterEffect as k, addTail as l, invalidate as m, advance as n, render as o, createPortal as p, context as q, reconciler as r, applyProps as s, threeTypes as t, useBridge as u, getRootState as v, dispose as w, act as x, buildGraph as y, useInstanceHandle as z };
2363
+ export { useThree as A, Block as B, useFrame as C, useGraph as D, ErrorBoundary as E, useLoader as F, _roots as _, useMutableCallback as a, useIsomorphicLayoutEffect as b, createRoot as c, unmountComponentAtNode as d, extend as e, createPointerEvents as f, createEvents as g, flushGlobalEffects as h, isRef as i, addEffect as j, addAfterEffect as k, addTail as l, invalidate as m, advance as n, createPortal as o, context as p, applyProps as q, reconciler as r, getRootState as s, threeTypes as t, useBridge as u, dispose as v, act as w, buildGraph as x, useInstanceHandle as y, useStore as z };