@react-three/fiber 9.0.0-rc.0 → 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,867 +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() {
384
- return false;
385
- },
386
- requestPostPaintCallback() {},
387
- maySuspendCommit() {
388
- return false;
389
- },
390
- preloadInstance() {
391
- return true; // true indicates already loaded
392
- },
393
- startSuspendingCommit() {},
394
- suspendInstance() {},
395
- waitForCommitToBeReady() {
396
- return null;
397
- },
398
- NotPendingTransition: null,
399
- setCurrentUpdatePriority(newPriority) {
400
- currentUpdatePriority = newPriority;
401
- },
402
- getCurrentUpdatePriority() {
403
- return currentUpdatePriority;
404
- },
405
- resolveUpdatePriority() {
406
- var _window$event;
407
- if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
408
- switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
409
- case 'click':
410
- case 'contextmenu':
411
- case 'dblclick':
412
- case 'pointercancel':
413
- case 'pointerdown':
414
- case 'pointerup':
415
- return DiscreteEventPriority;
416
- case 'pointermove':
417
- case 'pointerout':
418
- case 'pointerover':
419
- case 'pointerenter':
420
- case 'pointerleave':
421
- case 'wheel':
422
- return ContinuousEventPriority;
423
- default:
424
- return DefaultEventPriority;
425
- }
426
- },
427
- resetFormInstance() {}
428
- });
429
-
430
- var _window$document, _window$navigator;
431
- /**
432
- * Returns the instance's initial (outmost) root.
433
- */
434
- function findInitialRoot(instance) {
435
- let root = instance.root;
436
- while (root.getState().previousRoot) root = root.getState().previousRoot;
437
- return root;
438
- }
439
-
440
- /**
441
- * Returns `true` with correct TS type inference if an object has a configurable color space (since r152).
442
- */
443
- const hasColorSpace = object => 'colorSpace' in object || 'outputColorSpace' in object;
444
- /**
445
- * The current THREE.ColorManagement instance, if present.
446
- */
447
- const getColorManagement = () => {
448
- var _ColorManagement;
449
- return (_ColorManagement = catalogue.ColorManagement) != null ? _ColorManagement : null;
450
- };
451
- /**
452
- * Safely flush async effects when testing, simulating a legacy root.
453
- */
454
- const act = React.act;
455
- const isOrthographicCamera = def => def && def.isOrthographicCamera;
456
- const isRef = obj => obj && obj.hasOwnProperty('current');
457
-
458
- /**
459
- * An SSR-friendly useLayoutEffect.
460
- *
461
- * React currently throws a warning when using useLayoutEffect on the server.
462
- * To get around it, we can conditionally useEffect on the server (no-op) and
463
- * useLayoutEffect elsewhere.
464
- *
465
- * @see https://github.com/facebook/react/issues/14927
466
- */
467
- 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;
468
- function useMutableCallback(fn) {
469
- const ref = React.useRef(fn);
470
- useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
471
- return ref;
472
- }
473
- /**
474
- * Bridges renderer Context and StrictMode from a primary renderer.
475
- */
476
- function useBridge() {
477
- const fiber = useFiber();
478
- const ContextBridge = useContextBridge();
479
- return React.useMemo(() => ({
480
- children
481
- }) => {
482
- const strict = !!traverseFiber(fiber, true, node => node.type === React.StrictMode);
483
- const Root = strict ? React.StrictMode : React.Fragment;
484
- return /*#__PURE__*/jsx(Root, {
485
- children: /*#__PURE__*/jsx(ContextBridge, {
486
- children: children
487
- })
488
- });
489
- }, [fiber, ContextBridge]);
490
- }
491
- function Block({
492
- set
493
- }) {
494
- useIsomorphicLayoutEffect(() => {
495
- set(new Promise(() => null));
496
- return () => set(false);
497
- }, [set]);
498
- return null;
499
- }
500
- class ErrorBoundary extends React.Component {
501
- constructor(...args) {
502
- super(...args);
503
- this.state = {
504
- error: false
505
- };
506
- }
507
- componentDidCatch(err) {
508
- this.props.set(err);
509
- }
510
- render() {
511
- return this.state.error ? null : this.props.children;
512
- }
513
- }
514
- ErrorBoundary.getDerivedStateFromError = () => ({
515
- error: true
516
- });
517
- function calculateDpr(dpr) {
518
- var _window$devicePixelRa;
519
- // Err on the side of progress by assuming 2x dpr if we can't detect it
520
- // This will happen in workers where window is defined but dpr isn't.
521
- const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
522
- return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
523
- }
524
-
525
- /**
526
- * Returns instance root state
527
- */
528
- function getRootState(obj) {
529
- var _r3f;
530
- return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
531
- }
532
- // A collection of compare functions
533
- const is = {
534
- obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
535
- fun: a => typeof a === 'function',
536
- str: a => typeof a === 'string',
537
- num: a => typeof a === 'number',
538
- boo: a => typeof a === 'boolean',
539
- und: a => a === void 0,
540
- arr: a => Array.isArray(a),
541
- equ(a, b, {
542
- arrays = 'shallow',
543
- objects = 'reference',
544
- strict = true
545
- } = {}) {
546
- // Wrong type or one of the two undefined, doesn't match
547
- if (typeof a !== typeof b || !!a !== !!b) return false;
548
- // Atomic, just compare a against b
549
- if (is.str(a) || is.num(a) || is.boo(a)) return a === b;
550
- const isObj = is.obj(a);
551
- if (isObj && objects === 'reference') return a === b;
552
- const isArr = is.arr(a);
553
- if (isArr && arrays === 'reference') return a === b;
554
- // Array or Object, shallow compare first to see if it's a match
555
- if ((isArr || isObj) && a === b) return true;
556
- // Last resort, go through keys
557
- let i;
558
- // Check if a has all the keys of b
559
- for (i in a) if (!(i in b)) return false;
560
- // Check if values between keys match
561
- if (isObj && arrays === 'shallow' && objects === 'shallow') {
562
- for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
563
- strict,
564
- objects: 'reference'
565
- })) return false;
566
- } else {
567
- for (i in strict ? b : a) if (a[i] !== b[i]) return false;
568
- }
569
- // If i is undefined
570
- if (is.und(i)) {
571
- // If both arrays are empty we consider them equal
572
- if (isArr && a.length === 0 && b.length === 0) return true;
573
- // If both objects are empty we consider them equal
574
- if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
575
- // Otherwise match them by value
576
- if (a !== b) return false;
577
- }
578
- return true;
579
- }
580
- };
581
-
582
- // Collects nodes and materials from a THREE.Object3D
583
- function buildGraph(object) {
584
- const data = {
585
- nodes: {},
586
- materials: {}
587
- };
588
- if (object) {
589
- object.traverse(obj => {
590
- if (obj.name) data.nodes[obj.name] = obj;
591
- if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
592
- });
593
- }
594
- return data;
595
- }
596
- // Disposes an object and all its properties
597
- function dispose(obj) {
598
- if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
599
- for (const p in obj) {
600
- const prop = obj[p];
601
- if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
602
- }
603
- }
604
- const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
605
-
606
- // Gets only instance props from reconciler fibers
607
- function getInstanceProps(queue) {
608
- const props = {};
609
- for (const key in queue) {
610
- if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
611
- }
612
- return props;
613
- }
614
-
615
- // Each object in the scene carries a small LocalState descriptor
616
- function prepare(target, root, type, props) {
617
- const object = target;
618
-
619
- // Create instance descriptor
620
- let instance = object == null ? void 0 : object.__r3f;
621
- if (!instance) {
622
- instance = {
623
- root,
624
- type,
625
- parent: null,
626
- children: [],
627
- props: getInstanceProps(props),
628
- object,
629
- eventCount: 0,
630
- handlers: {},
631
- isHidden: false
632
- };
633
- if (object) {
634
- object.__r3f = instance;
635
- if (type) applyProps(object, instance.props);
636
- }
637
- }
638
- return instance;
639
- }
640
- function resolve(root, key) {
641
- var _target;
642
- let target = root[key];
643
- if (!key.includes('-')) return {
644
- root,
645
- key,
646
- target
647
- };
648
-
649
- // Resolve pierced target
650
- const chain = key.split('-');
651
- target = chain.reduce((acc, key) => acc[key], root);
652
- key = chain.pop();
653
-
654
- // Switch root if atomic
655
- if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
656
- return {
657
- root,
658
- key,
659
- target
660
- };
661
- }
662
-
663
- // Checks if a dash-cased string ends with an integer
664
- const INDEX_REGEX = /-\d+$/;
665
- function attach(parent, child) {
666
- if (is.str(child.props.attach)) {
667
- // If attaching into an array (foo-0), create one
668
- if (INDEX_REGEX.test(child.props.attach)) {
669
- const index = child.props.attach.replace(INDEX_REGEX, '');
670
- const {
671
- root,
672
- key
673
- } = resolve(parent.object, index);
674
- if (!Array.isArray(root[key])) root[key] = [];
675
- }
676
- const {
677
- root,
678
- key
679
- } = resolve(parent.object, child.props.attach);
680
- child.previousAttach = root[key];
681
- root[key] = child.object;
682
- } else if (is.fun(child.props.attach)) {
683
- child.previousAttach = child.props.attach(parent.object, child.object);
684
- }
685
- }
686
- function detach(parent, child) {
687
- if (is.str(child.props.attach)) {
688
- const {
689
- root,
690
- key
691
- } = resolve(parent.object, child.props.attach);
692
- const previous = child.previousAttach;
693
- // When the previous value was undefined, it means the value was never set to begin with
694
- if (previous === undefined) delete root[key];
695
- // Otherwise set the previous value
696
- else root[key] = previous;
697
- } else {
698
- child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
699
- }
700
- delete child.previousAttach;
701
- }
702
- const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
703
- // Instance props
704
- 'args', 'dispose', 'attach', 'object', 'onUpdate',
705
- // Behavior flags
706
- 'dispose'];
707
- const MEMOIZED_PROTOTYPES = new Map();
708
-
709
- // This function prepares a set of changes to be applied to the instance
710
- function diffProps(instance, newProps) {
711
- const changedProps = {};
712
-
713
- // Sort through props
714
- for (const prop in newProps) {
715
- // Skip reserved keys
716
- if (RESERVED_PROPS.includes(prop)) continue;
717
- // Skip if props match
718
- if (is.equ(newProps[prop], instance.props[prop])) continue;
719
-
720
- // Props changed, add them
721
- changedProps[prop] = newProps[prop];
722
-
723
- // Reset pierced props
724
- for (const other in newProps) {
725
- if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
726
- }
727
- }
728
-
729
- // Reset removed props for HMR
730
- for (const prop in instance.props) {
731
- if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
732
- const {
733
- root,
734
- key
735
- } = resolve(instance.object, prop);
736
-
737
- // https://github.com/mrdoob/three.js/issues/21209
738
- // HMR/fast-refresh relies on the ability to cancel out props, but threejs
739
- // has no means to do this. Hence we curate a small collection of value-classes
740
- // with their respective constructor/set arguments
741
- // For removed props, try to set default values, if possible
742
- if (root.constructor && root.constructor.length === 0) {
743
- // create a blank slate of the instance and copy the particular parameter.
744
- let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
745
- if (!ctor) {
746
- ctor = new root.constructor();
747
- MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
748
- }
749
- changedProps[key] = ctor[key];
750
- } else {
751
- // instance does not have constructor, just set it to 0
752
- changedProps[key] = 0;
753
- }
754
- }
755
- return changedProps;
756
- }
757
- typeof process !== 'undefined' && process.env.NODE_ENV !== 'production';
758
-
759
- // const LinearEncoding = 3000
760
- const sRGBEncoding = 3001;
761
- const SRGBColorSpace = 'srgb';
762
- const LinearSRGBColorSpace = 'srgb-linear';
763
-
764
- // https://github.com/mrdoob/three.js/pull/27042
765
- // https://github.com/mrdoob/three.js/pull/22748
766
- const colorMaps = ['map', 'emissiveMap', 'sheenTintMap',
767
- // <r134
768
- 'sheenColorMap', 'specularTintMap',
769
- // <r134
770
- 'specularColorMap', 'envMap'];
771
- const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
772
-
773
- // This function applies a set of changes to the instance
774
- function applyProps(object, props) {
775
- const instance = object.__r3f;
776
- const rootState = instance && findInitialRoot(instance).getState();
777
- const prevHandlers = instance == null ? void 0 : instance.eventCount;
778
- for (const prop in props) {
779
- let value = props[prop];
780
-
781
- // Don't mutate reserved keys
782
- if (RESERVED_PROPS.includes(prop)) continue;
783
-
784
- // Deal with pointer events, including removing them if undefined
785
- if (instance && EVENT_REGEX.test(prop)) {
786
- if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
787
- instance.eventCount = Object.keys(instance.handlers).length;
788
- }
789
-
790
- // Ignore setting undefined props
791
- // https://github.com/pmndrs/react-three-fiber/issues/274
792
- if (value === undefined) continue;
793
- let {
794
- root,
795
- key,
796
- target
797
- } = resolve(object, prop);
798
-
799
- // Alias (output)encoding => (output)colorSpace (since r152)
800
- // https://github.com/pmndrs/react-three-fiber/pull/2829
801
- if (hasColorSpace(root)) {
802
- if (key === 'encoding') {
803
- key = 'colorSpace';
804
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
805
- } else if (key === 'outputEncoding') {
806
- key = 'outputColorSpace';
807
- value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
808
- }
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);
809
375
  }
810
-
811
- // Copy if properties match signatures
812
- 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) {
813
378
  target.copy(value);
814
379
  }
815
- // Layers have no copy function, we must therefore copy the mask property
816
- else if (target instanceof THREE.Layers && value instanceof THREE.Layers) {
817
- target.mask = value.mask;
818
- }
819
380
  // Set array types
820
- else if (target != null && target.set && Array.isArray(value)) {
821
- 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);
822
383
  }
823
384
  // Set literal types
824
- else if (target != null && target.set && typeof value !== 'object') {
825
- const isColor = target instanceof THREE.Color;
385
+ else if (target && typeof target.set === 'function' && typeof value === 'number') {
826
386
  // Allow setting array scalars
827
- if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
387
+ if (typeof target.setScalar === 'function') target.setScalar(value);
828
388
  // Otherwise just set single value
829
389
  else target.set(value);
830
-
831
- // Emulate THREE.ColorManagement for older three.js versions
832
- // https://github.com/pmndrs/react-three-fiber/issues/344
833
- if (!getColorManagement() && !(rootState != null && rootState.linear) && isColor) target.convertSRGBToLinear();
834
390
  }
835
391
  // Else, just overwrite the value
836
392
  else {
393
+ var _root$key;
837
394
  root[key] = value;
838
395
 
839
396
  // Auto-convert sRGB texture parameters for built-in materials
840
397
  // https://github.com/pmndrs/react-three-fiber/issues/344
841
398
  // https://github.com/mrdoob/three.js/pull/25857
842
- 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 &&
843
400
  // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
844
401
  root[key].format === THREE.RGBAFormat && root[key].type === THREE.UnsignedByteType) {
845
402
  // NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
846
- if (hasColorSpace(root[key])) root[key].colorSpace = 'srgb';else root[key].encoding = sRGBEncoding;
403
+ root[key].colorSpace = THREE.SRGBColorSpace;
847
404
  }
848
405
  }
849
406
  }
850
407
 
851
408
  // Register event handlers
852
- 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;
853
411
  // Pre-emptively remove the instance from the interaction manager
854
- const index = rootState.internal.interaction.indexOf(instance.object);
412
+ const index = rootState.internal.interaction.indexOf(object);
855
413
  if (index > -1) rootState.internal.interaction.splice(index, 1);
856
414
  // Add the instance to the interaction manager only when it has handlers
857
- if (instance.eventCount && instance.object.raycast !== null && instance.object instanceof THREE.Object3D) {
858
- rootState.internal.interaction.push(instance.object);
415
+ if (instance.eventCount && object.raycast !== null) {
416
+ rootState.internal.interaction.push(object);
859
417
  }
860
418
  }
861
419
 
862
420
  // Auto-attach geometries and materials
863
421
  if (instance && instance.props.attach === undefined) {
864
- 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';
865
423
  }
866
424
 
867
425
  // Instance was updated, request a frame
@@ -1033,7 +591,19 @@ function createEvents(store) {
1033
591
  stopped: false
1034
592
  };
1035
593
  for (const hit of intersections) {
1036
- 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
+ }
1037
607
  if (state) {
1038
608
  const {
1039
609
  raycaster,
@@ -1294,7 +864,7 @@ function createEvents(store) {
1294
864
  }
1295
865
 
1296
866
  const isRenderer = def => !!(def != null && def.render);
1297
- const context = /*#__PURE__*/React.createContext(null);
867
+ const context = /* @__PURE__ */React.createContext(null);
1298
868
  const createStore = (invalidate, advance) => {
1299
869
  const rootStore = createWithEqualityFn((set, get) => {
1300
870
  const position = new THREE.Vector3();
@@ -1308,7 +878,7 @@ const createStore = (invalidate, advance) => {
1308
878
  left
1309
879
  } = size;
1310
880
  const aspect = width / height;
1311
- if (target instanceof THREE.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
881
+ if (target.isVector3) tempTarget.copy(target);else tempTarget.set(...target);
1312
882
  const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
1313
883
  if (isOrthographicCamera(camera)) {
1314
884
  return {
@@ -1510,161 +1080,576 @@ const createStore = (invalidate, advance) => {
1510
1080
  oldDpr = viewport.dpr;
1511
1081
  // Update camera & renderer
1512
1082
  updateCamera(camera, size);
1513
- gl.setPixelRatio(viewport.dpr);
1083
+ if (viewport.dpr > 0) gl.setPixelRatio(viewport.dpr);
1514
1084
  const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
1515
1085
  gl.setSize(size.width, size.height, updateStyle);
1516
1086
  }
1517
1087
 
1518
- // Update viewport once the camera changes
1519
- if (camera !== oldCamera) {
1520
- oldCamera = camera;
1521
- // Update viewport
1522
- set(state => ({
1523
- viewport: {
1524
- ...state.viewport,
1525
- ...state.viewport.getCurrentViewport(camera)
1526
- }
1527
- }));
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;
1528
1286
  }
1529
- });
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;
1298
+ }
1299
+ instance.isHidden = false;
1300
+ invalidateInstance(instance);
1301
+ }
1302
+ }
1530
1303
 
1531
- // Invalidate on any change
1532
- 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;
1533
1311
 
1534
- // Return root state
1535
- return rootStore;
1536
- };
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)];
1537
1317
 
1538
- /**
1539
- * Exposes an object's {@link Instance}.
1540
- * @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
1541
- *
1542
- * **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
1543
- */
1544
- function useInstanceHandle(ref) {
1545
- const instance = React.useRef(null);
1546
- React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
1547
- 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);
1548
1351
  }
1352
+ function appendChild(parent, child) {
1353
+ if (!child) return;
1549
1354
 
1550
- /**
1551
- * Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
1552
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
1553
- */
1554
- function useStore() {
1555
- const store = React.useContext(context);
1556
- if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
1557
- return store;
1355
+ // Link instances
1356
+ child.parent = parent;
1357
+ parent.children.push(child);
1358
+
1359
+ // Attach tree once complete
1360
+ handleContainerEffects(parent, child);
1558
1361
  }
1362
+ function insertBefore(parent, child, beforeChild) {
1363
+ if (!child || !beforeChild) return;
1559
1364
 
1560
- /**
1561
- * Accesses R3F's internal state, containing renderer, canvas, scene, etc.
1562
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
1563
- */
1564
- function useThree(selector = state => state, equalityFn) {
1565
- 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);
1566
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
+ };
1567
1382
 
1568
- /**
1569
- * Executes a callback before render in a shared frame loop.
1570
- * Can order effects with render priority or manually render with a positive priority.
1571
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
1572
- */
1573
- function useFrame(callback, renderPriority = 0) {
1574
- const store = useStore();
1575
- const subscribe = store.getState().internal.subscribe;
1576
- // Memoize ref
1577
- const ref = useMutableCallback(callback);
1578
- // Subscribe on mount, unsubscribe on unmount
1579
- useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
1580
- 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
+ }
1581
1388
  }
1389
+ function removeChild(parent, child, dispose) {
1390
+ if (!child) return;
1582
1391
 
1583
- /**
1584
- * Returns a node graph of an object with named nodes & materials.
1585
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
1586
- */
1587
- function useGraph(object) {
1588
- 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);
1589
1430
  }
1590
- const memoizedLoaders = new WeakMap();
1591
- const isConstructor = value => {
1592
- var _value$prototype;
1593
- return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
1594
- };
1595
- function loadingFn(extensions, onProgress) {
1596
- return async function (Proto, ...input) {
1597
- 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
+ }
1463
+
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);
1598
1488
 
1599
- // Construct and cache loader if constructor was passed
1600
- if (isConstructor(Proto)) {
1601
- loader = memoizedLoaders.get(Proto);
1602
- if (!loader) {
1603
- loader = new Proto();
1604
- memoizedLoaders.set(Proto, loader);
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);
1605
1495
  }
1606
- } else {
1607
- 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);
1608
1506
  }
1507
+ }
1508
+ reconstructed.length = 0;
1509
+ }
1609
1510
 
1610
- // Apply loader extensions
1611
- 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;
1612
1515
 
1613
- // Go through the urls and load them
1614
- 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}`))))));
1615
- };
1616
- }
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;
1617
1551
 
1618
- /**
1619
- * Synchronously loads and caches assets with a three loader.
1620
- *
1621
- * Note: this hook's caller must be wrapped with `React.Suspense`
1622
- * @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
1623
- */
1624
- function useLoader(loader, input, extensions, onProgress) {
1625
- // Use suspense to load async assets
1626
- const keys = Array.isArray(input) ? input : [input];
1627
- const results = suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
1628
- equal: is.equ
1629
- });
1630
- // Return the object(s)
1631
- return Array.isArray(input) ? results : results[0];
1632
- }
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;
1633
1561
 
1634
- /**
1635
- * Preloads an asset into cache as a side-effect.
1636
- */
1637
- useLoader.preload = function (loader, input, extensions) {
1638
- const keys = Array.isArray(input) ? input : [input];
1639
- return preload(loadingFn(extensions), [loader, ...keys]);
1640
- };
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
+ }
1641
1575
 
1642
- /**
1643
- * Removes a loaded asset from cache.
1644
- */
1645
- useLoader.clear = function (loader, input) {
1646
- const keys = Array.isArray(input) ? input : [input];
1647
- return clear([loader, ...keys]);
1648
- };
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
+ });
1649
1645
 
1650
1646
  const _roots = new Map();
1651
1647
  const shallowLoose = {
1652
1648
  objects: 'shallow',
1653
1649
  strict: false
1654
1650
  };
1655
- const createRendererInstance = (gl, canvas) => {
1656
- const customRenderer = typeof gl === 'function' ? gl(canvas) : gl;
1657
- if (isRenderer(customRenderer)) return customRenderer;
1658
- return new THREE.WebGLRenderer({
1659
- powerPreference: 'high-performance',
1660
- canvas: canvas,
1661
- antialias: true,
1662
- alpha: true,
1663
- ...gl
1664
- });
1665
- };
1666
1651
  function computeInitialSize(canvas, size) {
1667
- if (!size && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1652
+ if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
1668
1653
  const {
1669
1654
  width,
1670
1655
  height,
@@ -1740,10 +1725,13 @@ function createRoot(canvas) {
1740
1725
 
1741
1726
  // Locals
1742
1727
  let onCreated;
1743
- let configured = false;
1744
1728
  let lastCamera;
1729
+ let configured = false;
1730
+ let pending = null;
1745
1731
  return {
1746
- configure(props = {}) {
1732
+ async configure(props = {}) {
1733
+ let resolve;
1734
+ pending = new Promise(_resolve => resolve = _resolve);
1747
1735
  let {
1748
1736
  gl: glConfig,
1749
1737
  size: propsSize,
@@ -1766,9 +1754,26 @@ function createRoot(canvas) {
1766
1754
 
1767
1755
  // Set up renderer (one time only!)
1768
1756
  let gl = state.gl;
1769
- if (!state.gl) state.set({
1770
- gl: gl = createRendererInstance(glConfig, canvas)
1771
- });
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
+ }
1772
1777
 
1773
1778
  // Set up raycaster (one time only!)
1774
1779
  let raycaster = state.raycaster;
@@ -1794,7 +1799,7 @@ function createRoot(canvas) {
1794
1799
  // Create default camera, don't overwrite any user-set state
1795
1800
  if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
1796
1801
  lastCamera = cameraOptions;
1797
- const isCamera = cameraOptions instanceof THREE.Camera;
1802
+ const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
1798
1803
  const camera = isCamera ? cameraOptions : orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
1799
1804
  if (!isCamera) {
1800
1805
  camera.position.z = 5;
@@ -1824,7 +1829,7 @@ function createRoot(canvas) {
1824
1829
  // Set up scene (one time only!)
1825
1830
  if (!state.scene) {
1826
1831
  let scene;
1827
- if (sceneOptions instanceof THREE.Scene) {
1832
+ if (sceneOptions != null && sceneOptions.isScene) {
1828
1833
  scene = sceneOptions;
1829
1834
  prepare(scene, store, '', {});
1830
1835
  } else {
@@ -1837,8 +1842,34 @@ function createRoot(canvas) {
1837
1842
  });
1838
1843
  }
1839
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
+
1840
1870
  // Set up XR (one time only!)
1841
1871
  if (!state.xr) {
1872
+ var _gl$xr;
1842
1873
  // Handle frame behavior in WebXR
1843
1874
  const handleXRFrame = (timestamp, frame) => {
1844
1875
  const state = store.getState();
@@ -1869,7 +1900,7 @@ function createRoot(canvas) {
1869
1900
  };
1870
1901
 
1871
1902
  // Subscribe to WebXR session events
1872
- if (gl.xr) xr.connect();
1903
+ if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
1873
1904
  state.set({
1874
1905
  xr
1875
1906
  });
@@ -1896,22 +1927,12 @@ function createRoot(canvas) {
1896
1927
  }
1897
1928
  if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
1898
1929
  }
1899
-
1900
- // Safely set color management if available.
1901
- // Avoid accessing THREE.ColorManagement to play nice with older versions
1902
- const ColorManagement = getColorManagement();
1903
- if (ColorManagement) {
1904
- if ('enabled' in ColorManagement) ColorManagement.enabled = !legacy;else if ('legacyMode' in ColorManagement) ColorManagement.legacyMode = legacy;
1905
- }
1930
+ THREE.ColorManagement.enabled = !legacy;
1906
1931
 
1907
1932
  // Set color space and tonemapping preferences
1908
1933
  if (!configured) {
1909
- const LinearEncoding = 3000;
1910
- const sRGBEncoding = 3001;
1911
- applyProps(gl, {
1912
- outputEncoding: linear ? LinearEncoding : sRGBEncoding,
1913
- toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping
1914
- });
1934
+ gl.outputColorSpace = linear ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace;
1935
+ gl.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
1915
1936
  }
1916
1937
 
1917
1938
  // Update color management state
@@ -1927,45 +1948,24 @@ function createRoot(canvas) {
1927
1948
 
1928
1949
  // Set gl props
1929
1950
  if (glConfig && !is.fun(glConfig) && !isRenderer(glConfig) && !is.equ(glConfig, gl, shallowLoose)) applyProps(gl, glConfig);
1930
- // Store events internally
1931
- if (events && !state.events.handlers) state.set({
1932
- events: events(store)
1933
- });
1934
- // Check size, allow it to take on container bounds initially
1935
- const size = computeInitialSize(canvas, propsSize);
1936
- if (!is.equ(size, state.size, shallowLoose)) {
1937
- state.setSize(size.width, size.height, size.top, size.left);
1938
- }
1939
- // Check pixelratio
1940
- if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
1941
- // Check frameloop
1942
- if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
1943
- // Check pointer missed
1944
- if (!state.onPointerMissed) state.set({
1945
- onPointerMissed
1946
- });
1947
- // Check performance
1948
- if (performance && !is.equ(performance, state.performance, shallowLoose)) state.set(state => ({
1949
- performance: {
1950
- ...state.performance,
1951
- ...performance
1952
- }
1953
- }));
1954
1951
 
1955
1952
  // Set locals
1956
1953
  onCreated = onCreatedCallback;
1957
1954
  configured = true;
1955
+ resolve();
1958
1956
  return this;
1959
1957
  },
1960
1958
  render(children) {
1961
1959
  // The root has to be configured before it can be rendered
1962
- if (!configured) this.configure();
1963
- reconciler.updateContainer( /*#__PURE__*/jsx(Provider, {
1964
- store: store,
1965
- children: children,
1966
- onCreated: onCreated,
1967
- rootElement: canvas
1968
- }), 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
+ });
1969
1969
  return store;
1970
1970
  },
1971
1971
  unmount() {
@@ -1973,12 +1973,6 @@ function createRoot(canvas) {
1973
1973
  }
1974
1974
  };
1975
1975
  }
1976
- function render(children, canvas, config) {
1977
- console.warn('R3F.render is no longer supported in React 18. Use createRoot instead!');
1978
- const root = createRoot(canvas);
1979
- root.configure(config);
1980
- return root.render(children);
1981
- }
1982
1976
  function Provider({
1983
1977
  store,
1984
1978
  children,
@@ -2116,18 +2110,17 @@ function Portal({
2116
2110
  return store;
2117
2111
  // eslint-disable-next-line react-hooks/exhaustive-deps
2118
2112
  }, [previousRoot, container]);
2119
- return /*#__PURE__*/jsx(Fragment, {
2120
- children: reconciler.createPortal( /*#__PURE__*/jsx(context.Provider, {
2121
- value: usePortalStore,
2122
- children: children
2123
- }), usePortalStore, null)
2124
- });
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
+ );
2125
2123
  }
2126
- reconciler.injectIntoDevTools({
2127
- bundleType: process.env.NODE_ENV === 'production' ? 0 : 1,
2128
- rendererPackageName: '@react-three/fiber',
2129
- version: React.version
2130
- });
2131
2124
 
2132
2125
  function createSubs(callback, subs) {
2133
2126
  const sub = {
@@ -2226,7 +2219,7 @@ function loop(timestamp) {
2226
2219
  repeat += update(timestamp, state);
2227
2220
  }
2228
2221
  }
2229
- useFrameInProgress = true;
2222
+ useFrameInProgress = false;
2230
2223
 
2231
2224
  // Run after-effects
2232
2225
  flushGlobalEffects('after', timestamp);
@@ -2367,4 +2360,4 @@ function createPointerEvents(store) {
2367
2360
  };
2368
2361
  }
2369
2362
 
2370
- 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 };