@react-three/fiber 9.0.0-rc.1 → 9.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/declarations/src/core/hooks.d.ts +10 -12
- package/dist/declarations/src/core/reconciler.d.ts +0 -3
- package/dist/declarations/src/core/utils.d.ts +78 -31
- package/dist/{events-26d2636c.esm.js → events-89bb769e.esm.js} +1343 -1378
- package/dist/{events-cb1a9ea0.cjs.prod.js → events-bcbe26a9.cjs.prod.js} +1484 -1518
- package/dist/{events-df578889.cjs.dev.js → events-ec103e1e.cjs.dev.js} +1484 -1519
- package/dist/react-three-fiber.cjs.dev.js +2 -2
- package/dist/react-three-fiber.cjs.prod.js +2 -2
- package/dist/react-three-fiber.esm.js +3 -3
- package/native/dist/react-three-fiber-native.cjs.dev.js +2 -2
- package/native/dist/react-three-fiber-native.cjs.prod.js +2 -2
- package/native/dist/react-three-fiber-native.esm.js +3 -3
- package/package.json +6 -6
|
@@ -1,1335 +1,889 @@
|
|
|
1
1
|
import * as THREE from 'three';
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import {
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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');
|
|
43
29
|
|
|
44
|
-
|
|
45
|
-
|
|
30
|
+
/**
|
|
31
|
+
* An SSR-friendly useLayoutEffect.
|
|
32
|
+
*
|
|
33
|
+
* React currently throws a warning when using useLayoutEffect on the server.
|
|
34
|
+
* To get around it, we can conditionally useEffect on the server (no-op) and
|
|
35
|
+
* useLayoutEffect elsewhere.
|
|
36
|
+
*
|
|
37
|
+
* @see https://github.com/facebook/react/issues/14927
|
|
38
|
+
*/
|
|
39
|
+
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;
|
|
40
|
+
function useMutableCallback(fn) {
|
|
41
|
+
const ref = React.useRef(fn);
|
|
42
|
+
useIsomorphicLayoutEffect(() => void (ref.current = fn), [fn]);
|
|
43
|
+
return ref;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Bridges renderer Context and StrictMode from a primary renderer.
|
|
47
|
+
*/
|
|
48
|
+
function useBridge() {
|
|
49
|
+
const fiber = useFiber();
|
|
50
|
+
const ContextBridge = useContextBridge();
|
|
51
|
+
return React.useMemo(() => ({
|
|
52
|
+
children
|
|
53
|
+
}) => {
|
|
54
|
+
const strict = !!traverseFiber(fiber, true, node => node.type === React.StrictMode);
|
|
55
|
+
const Root = strict ? React.StrictMode : React.Fragment;
|
|
56
|
+
return /*#__PURE__*/jsx(Root, {
|
|
57
|
+
children: /*#__PURE__*/jsx(ContextBridge, {
|
|
58
|
+
children: children
|
|
59
|
+
})
|
|
60
|
+
});
|
|
61
|
+
}, [fiber, ContextBridge]);
|
|
62
|
+
}
|
|
63
|
+
function Block({
|
|
64
|
+
set
|
|
65
|
+
}) {
|
|
66
|
+
useIsomorphicLayoutEffect(() => {
|
|
67
|
+
set(new Promise(() => null));
|
|
68
|
+
return () => set(false);
|
|
69
|
+
}, [set]);
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
46
72
|
|
|
47
|
-
|
|
48
|
-
|
|
73
|
+
// NOTE: static members get down-level transpiled to mutations which break tree-shaking
|
|
74
|
+
const ErrorBoundary = /* @__PURE__ */(_ErrorBoundary => (_ErrorBoundary = class ErrorBoundary extends React.Component {
|
|
75
|
+
constructor(...args) {
|
|
76
|
+
super(...args);
|
|
77
|
+
this.state = {
|
|
78
|
+
error: false
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
componentDidCatch(err) {
|
|
82
|
+
this.props.set(err);
|
|
83
|
+
}
|
|
84
|
+
render() {
|
|
85
|
+
return this.state.error ? null : this.props.children;
|
|
86
|
+
}
|
|
87
|
+
}, _ErrorBoundary.getDerivedStateFromError = () => ({
|
|
88
|
+
error: true
|
|
89
|
+
}), _ErrorBoundary))();
|
|
90
|
+
function calculateDpr(dpr) {
|
|
91
|
+
var _window$devicePixelRa;
|
|
92
|
+
// Err on the side of progress by assuming 2x dpr if we can't detect it
|
|
93
|
+
// This will happen in workers where window is defined but dpr isn't.
|
|
94
|
+
const target = typeof window !== 'undefined' ? (_window$devicePixelRa = window.devicePixelRatio) != null ? _window$devicePixelRa : 2 : 1;
|
|
95
|
+
return Array.isArray(dpr) ? Math.min(Math.max(dpr[0], target), dpr[1]) : dpr;
|
|
49
96
|
}
|
|
50
|
-
function createInstance(type, props, root) {
|
|
51
|
-
var _props$object;
|
|
52
|
-
validateInstance(type, props);
|
|
53
97
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
98
|
+
/**
|
|
99
|
+
* Returns instance root state
|
|
100
|
+
*/
|
|
101
|
+
function getRootState(obj) {
|
|
102
|
+
var _r3f;
|
|
103
|
+
return (_r3f = obj.__r3f) == null ? void 0 : _r3f.root.getState();
|
|
57
104
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
105
|
+
// A collection of compare functions
|
|
106
|
+
const is = {
|
|
107
|
+
obj: a => a === Object(a) && !is.arr(a) && typeof a !== 'function',
|
|
108
|
+
fun: a => typeof a === 'function',
|
|
109
|
+
str: a => typeof a === 'string',
|
|
110
|
+
num: a => typeof a === 'number',
|
|
111
|
+
boo: a => typeof a === 'boolean',
|
|
112
|
+
und: a => a === void 0,
|
|
113
|
+
arr: a => Array.isArray(a),
|
|
114
|
+
equ(a, b, {
|
|
115
|
+
arrays = 'shallow',
|
|
116
|
+
objects = 'reference',
|
|
117
|
+
strict = true
|
|
118
|
+
} = {}) {
|
|
119
|
+
// Wrong type or one of the two undefined, doesn't match
|
|
120
|
+
if (typeof a !== typeof b || !!a !== !!b) return false;
|
|
121
|
+
// Atomic, just compare a against b
|
|
122
|
+
if (is.str(a) || is.num(a) || is.boo(a)) return a === b;
|
|
123
|
+
const isObj = is.obj(a);
|
|
124
|
+
if (isObj && objects === 'reference') return a === b;
|
|
125
|
+
const isArr = is.arr(a);
|
|
126
|
+
if (isArr && arrays === 'reference') return a === b;
|
|
127
|
+
// Array or Object, shallow compare first to see if it's a match
|
|
128
|
+
if ((isArr || isObj) && a === b) return true;
|
|
129
|
+
// Last resort, go through keys
|
|
130
|
+
let i;
|
|
131
|
+
// Check if a has all the keys of b
|
|
132
|
+
for (i in a) if (!(i in b)) return false;
|
|
133
|
+
// Check if values between keys match
|
|
134
|
+
if (isObj && arrays === 'shallow' && objects === 'shallow') {
|
|
135
|
+
for (i in strict ? b : a) if (!is.equ(a[i], b[i], {
|
|
136
|
+
strict,
|
|
137
|
+
objects: 'reference'
|
|
138
|
+
})) return false;
|
|
139
|
+
} else {
|
|
140
|
+
for (i in strict ? b : a) if (a[i] !== b[i]) return false;
|
|
65
141
|
}
|
|
66
|
-
|
|
67
|
-
|
|
142
|
+
// If i is undefined
|
|
143
|
+
if (is.und(i)) {
|
|
144
|
+
// If both arrays are empty we consider them equal
|
|
145
|
+
if (isArr && a.length === 0 && b.length === 0) return true;
|
|
146
|
+
// If both objects are empty we consider them equal
|
|
147
|
+
if (isObj && Object.keys(a).length === 0 && Object.keys(b).length === 0) return true;
|
|
148
|
+
// Otherwise match them by value
|
|
149
|
+
if (a !== b) return false;
|
|
150
|
+
}
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Collects nodes and materials from a THREE.Object3D
|
|
156
|
+
function buildGraph(object) {
|
|
157
|
+
const data = {
|
|
158
|
+
nodes: {},
|
|
159
|
+
materials: {}
|
|
160
|
+
};
|
|
161
|
+
if (object) {
|
|
162
|
+
object.traverse(obj => {
|
|
163
|
+
if (obj.name) data.nodes[obj.name] = obj;
|
|
164
|
+
if (obj.material && !data.materials[obj.material.name]) data.materials[obj.material.name] = obj.material;
|
|
165
|
+
});
|
|
68
166
|
}
|
|
167
|
+
return data;
|
|
69
168
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
instance.object.visible = true;
|
|
77
|
-
}
|
|
78
|
-
instance.isHidden = false;
|
|
79
|
-
invalidateInstance(instance);
|
|
169
|
+
// Disposes an object and all its properties
|
|
170
|
+
function dispose(obj) {
|
|
171
|
+
if (obj.type !== 'Scene') obj.dispose == null ? void 0 : obj.dispose();
|
|
172
|
+
for (const p in obj) {
|
|
173
|
+
const prop = obj[p];
|
|
174
|
+
if ((prop == null ? void 0 : prop.type) !== 'Scene') prop == null ? void 0 : prop.dispose == null ? void 0 : prop.dispose();
|
|
80
175
|
}
|
|
81
176
|
}
|
|
177
|
+
const REACT_INTERNAL_PROPS = ['children', 'key', 'ref'];
|
|
82
178
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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];
|
|
97
|
-
|
|
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;
|
|
101
|
-
|
|
102
|
-
// Set initial props
|
|
103
|
-
applyProps(child.object, child.props);
|
|
179
|
+
// Gets only instance props from reconciler fibers
|
|
180
|
+
function getInstanceProps(queue) {
|
|
181
|
+
const props = {};
|
|
182
|
+
for (const key in queue) {
|
|
183
|
+
if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
|
|
104
184
|
}
|
|
185
|
+
return props;
|
|
186
|
+
}
|
|
105
187
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
}
|
|
188
|
+
// Each object in the scene carries a small LocalState descriptor
|
|
189
|
+
function prepare(target, root, type, props) {
|
|
190
|
+
const object = target;
|
|
125
191
|
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
192
|
+
// Create instance descriptor
|
|
193
|
+
let instance = object == null ? void 0 : object.__r3f;
|
|
194
|
+
if (!instance) {
|
|
195
|
+
instance = {
|
|
196
|
+
root,
|
|
197
|
+
type,
|
|
198
|
+
parent: null,
|
|
199
|
+
children: [],
|
|
200
|
+
props: getInstanceProps(props),
|
|
201
|
+
object,
|
|
202
|
+
eventCount: 0,
|
|
203
|
+
handlers: {},
|
|
204
|
+
isHidden: false
|
|
205
|
+
};
|
|
206
|
+
if (object) {
|
|
207
|
+
object.__r3f = instance;
|
|
208
|
+
if (type) applyProps(object, instance.props);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return instance;
|
|
131
212
|
}
|
|
132
|
-
function
|
|
133
|
-
|
|
213
|
+
function resolve(root, key) {
|
|
214
|
+
var _target;
|
|
215
|
+
let target = root[key];
|
|
216
|
+
if (!key.includes('-')) return {
|
|
217
|
+
root,
|
|
218
|
+
key,
|
|
219
|
+
target
|
|
220
|
+
};
|
|
134
221
|
|
|
135
|
-
//
|
|
136
|
-
|
|
137
|
-
|
|
222
|
+
// Resolve pierced target
|
|
223
|
+
const chain = key.split('-');
|
|
224
|
+
target = chain.reduce((acc, key) => acc[key], root);
|
|
225
|
+
key = chain.pop();
|
|
138
226
|
|
|
139
|
-
//
|
|
140
|
-
|
|
227
|
+
// Switch root if atomic
|
|
228
|
+
if (!((_target = target) != null && _target.set)) root = chain.reduce((acc, key) => acc[key], root);
|
|
229
|
+
return {
|
|
230
|
+
root,
|
|
231
|
+
key,
|
|
232
|
+
target
|
|
233
|
+
};
|
|
141
234
|
}
|
|
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
235
|
|
|
150
|
-
|
|
151
|
-
|
|
236
|
+
// Checks if a dash-cased string ends with an integer
|
|
237
|
+
const INDEX_REGEX = /-\d+$/;
|
|
238
|
+
function attach(parent, child) {
|
|
239
|
+
if (is.str(child.props.attach)) {
|
|
240
|
+
// If attaching into an array (foo-0), create one
|
|
241
|
+
if (INDEX_REGEX.test(child.props.attach)) {
|
|
242
|
+
const index = child.props.attach.replace(INDEX_REGEX, '');
|
|
243
|
+
const {
|
|
244
|
+
root,
|
|
245
|
+
key
|
|
246
|
+
} = resolve(parent.object, index);
|
|
247
|
+
if (!Array.isArray(root[key])) root[key] = [];
|
|
248
|
+
}
|
|
249
|
+
const {
|
|
250
|
+
root,
|
|
251
|
+
key
|
|
252
|
+
} = resolve(parent.object, child.props.attach);
|
|
253
|
+
child.previousAttach = root[key];
|
|
254
|
+
root[key] = child.object;
|
|
255
|
+
} else if (is.fun(child.props.attach)) {
|
|
256
|
+
child.previousAttach = child.props.attach(parent.object, child.object);
|
|
257
|
+
}
|
|
152
258
|
}
|
|
153
|
-
function
|
|
154
|
-
if (
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
else unstable_scheduleCallback(unstable_IdlePriority, handleDispose);
|
|
259
|
+
function detach(parent, child) {
|
|
260
|
+
if (is.str(child.props.attach)) {
|
|
261
|
+
const {
|
|
262
|
+
root,
|
|
263
|
+
key
|
|
264
|
+
} = resolve(parent.object, child.props.attach);
|
|
265
|
+
const previous = child.previousAttach;
|
|
266
|
+
// When the previous value was undefined, it means the value was never set to begin with
|
|
267
|
+
if (previous === undefined) delete root[key];
|
|
268
|
+
// Otherwise set the previous value
|
|
269
|
+
else root[key] = previous;
|
|
270
|
+
} else {
|
|
271
|
+
child.previousAttach == null ? void 0 : child.previousAttach(parent.object, child.object);
|
|
167
272
|
}
|
|
273
|
+
delete child.previousAttach;
|
|
168
274
|
}
|
|
169
|
-
|
|
170
|
-
|
|
275
|
+
const RESERVED_PROPS = [...REACT_INTERNAL_PROPS,
|
|
276
|
+
// Instance props
|
|
277
|
+
'args', 'dispose', 'attach', 'object', 'onUpdate',
|
|
278
|
+
// Behavior flags
|
|
279
|
+
'dispose'];
|
|
280
|
+
const MEMOIZED_PROTOTYPES = new Map();
|
|
171
281
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
if (childIndex !== -1) parent.children.splice(childIndex, 1);
|
|
282
|
+
// This function prepares a set of changes to be applied to the instance
|
|
283
|
+
function diffProps(instance, newProps) {
|
|
284
|
+
const changedProps = {};
|
|
176
285
|
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}
|
|
286
|
+
// Sort through props
|
|
287
|
+
for (const prop in newProps) {
|
|
288
|
+
// Skip reserved keys
|
|
289
|
+
if (RESERVED_PROPS.includes(prop)) continue;
|
|
290
|
+
// Skip if props match
|
|
291
|
+
if (is.equ(newProps[prop], instance.props[prop])) continue;
|
|
184
292
|
|
|
185
|
-
|
|
186
|
-
|
|
293
|
+
// Props changed, add them
|
|
294
|
+
changedProps[prop] = newProps[prop];
|
|
187
295
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
296
|
+
// Reset pierced props
|
|
297
|
+
for (const other in newProps) {
|
|
298
|
+
if (other.startsWith(`${prop}-`)) changedProps[other] = newProps[other];
|
|
299
|
+
}
|
|
192
300
|
}
|
|
193
|
-
child.children.length = 0;
|
|
194
|
-
|
|
195
|
-
// Unlink instance object
|
|
196
|
-
delete child.object.__r3f;
|
|
197
301
|
|
|
198
|
-
//
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
disposeOnIdle(child.object);
|
|
206
|
-
}
|
|
302
|
+
// Reset removed props for HMR
|
|
303
|
+
for (const prop in instance.props) {
|
|
304
|
+
if (RESERVED_PROPS.includes(prop) || newProps.hasOwnProperty(prop)) continue;
|
|
305
|
+
const {
|
|
306
|
+
root,
|
|
307
|
+
key
|
|
308
|
+
} = resolve(instance.object, prop);
|
|
207
309
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
_fiber.ref.current = publicInstance;
|
|
310
|
+
// https://github.com/mrdoob/three.js/issues/21209
|
|
311
|
+
// HMR/fast-refresh relies on the ability to cancel out props, but threejs
|
|
312
|
+
// has no means to do this. Hence we curate a small collection of value-classes
|
|
313
|
+
// with their respective constructor/set arguments
|
|
314
|
+
// For removed props, try to set default values, if possible
|
|
315
|
+
if (root.constructor && root.constructor.length === 0) {
|
|
316
|
+
// create a blank slate of the instance and copy the particular parameter.
|
|
317
|
+
let ctor = MEMOIZED_PROTOTYPES.get(root.constructor);
|
|
318
|
+
if (!ctor) {
|
|
319
|
+
ctor = new root.constructor();
|
|
320
|
+
MEMOIZED_PROTOTYPES.set(root.constructor, ctor);
|
|
220
321
|
}
|
|
322
|
+
changedProps[key] = ctor[key];
|
|
323
|
+
} else {
|
|
324
|
+
// instance does not have constructor, just set it to 0
|
|
325
|
+
changedProps[key] = 0;
|
|
221
326
|
}
|
|
222
327
|
}
|
|
328
|
+
return changedProps;
|
|
223
329
|
}
|
|
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
330
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
if (instance.isHidden) unhideInstance(instance);
|
|
331
|
+
// https://github.com/mrdoob/three.js/pull/27042
|
|
332
|
+
// https://github.com/mrdoob/three.js/pull/22748
|
|
333
|
+
const colorMaps = ['map', 'emissiveMap', 'sheenColorMap', 'specularColorMap', 'envMap'];
|
|
334
|
+
const EVENT_REGEX = /^on(Pointer|Click|DoubleClick|ContextMenu|Wheel)/;
|
|
249
335
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
336
|
+
// This function applies a set of changes to the instance
|
|
337
|
+
function applyProps(object, props) {
|
|
338
|
+
var _instance$object;
|
|
339
|
+
const instance = object.__r3f;
|
|
340
|
+
const rootState = instance && findInitialRoot(instance).getState();
|
|
341
|
+
const prevHandlers = instance == null ? void 0 : instance.eventCount;
|
|
342
|
+
for (const prop in props) {
|
|
343
|
+
let value = props[prop];
|
|
254
344
|
|
|
255
|
-
|
|
256
|
-
|
|
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];
|
|
345
|
+
// Don't mutate reserved keys
|
|
346
|
+
if (RESERVED_PROPS.includes(prop)) continue;
|
|
264
347
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
instance.
|
|
268
|
-
|
|
348
|
+
// Deal with pointer events, including removing them if undefined
|
|
349
|
+
if (instance && EVENT_REGEX.test(prop)) {
|
|
350
|
+
if (typeof value === 'function') instance.handlers[prop] = value;else delete instance.handlers[prop];
|
|
351
|
+
instance.eventCount = Object.keys(instance.handlers).length;
|
|
352
|
+
}
|
|
269
353
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
-
}
|
|
354
|
+
// Ignore setting undefined props
|
|
355
|
+
// https://github.com/pmndrs/react-three-fiber/issues/274
|
|
356
|
+
if (value === undefined) continue;
|
|
357
|
+
let {
|
|
358
|
+
root,
|
|
359
|
+
key,
|
|
360
|
+
target
|
|
361
|
+
} = resolve(object, prop);
|
|
284
362
|
|
|
285
|
-
|
|
286
|
-
|
|
363
|
+
// Copy if properties match signatures
|
|
364
|
+
if (typeof (target == null ? void 0 : target.copy) === 'function' && target.copy === value.copy) {
|
|
365
|
+
target.copy(value);
|
|
287
366
|
}
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
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;
|
|
367
|
+
// Layers have no copy function, we must therefore copy the mask property
|
|
368
|
+
else if (target instanceof THREE.Layers && value instanceof THREE.Layers) {
|
|
369
|
+
target.mask = value.mask;
|
|
370
|
+
}
|
|
371
|
+
// Set array types
|
|
372
|
+
else if (target != null && target.set && Array.isArray(value)) {
|
|
373
|
+
if (target.fromArray) target.fromArray(value);else target.set(...value);
|
|
374
|
+
}
|
|
375
|
+
// Set literal types
|
|
376
|
+
else if (target != null && target.set && typeof value !== 'object') {
|
|
377
|
+
const isColor = target == null ? void 0 : target.isColor;
|
|
378
|
+
// Allow setting array scalars
|
|
379
|
+
if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
|
|
380
|
+
// Otherwise just set single value
|
|
381
|
+
else target.set(value);
|
|
382
|
+
}
|
|
383
|
+
// Else, just overwrite the value
|
|
384
|
+
else {
|
|
385
|
+
var _root$key;
|
|
386
|
+
root[key] = value;
|
|
342
387
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (Object.keys(changedProps).length) {
|
|
352
|
-
Object.assign(instance.props, changedProps);
|
|
353
|
-
applyProps(instance.object, changedProps);
|
|
388
|
+
// Auto-convert sRGB texture parameters for built-in materials
|
|
389
|
+
// https://github.com/pmndrs/react-three-fiber/issues/344
|
|
390
|
+
// https://github.com/mrdoob/three.js/pull/25857
|
|
391
|
+
if (rootState && !rootState.linear && colorMaps.includes(key) && (_root$key = root[key]) != null && _root$key.isTexture &&
|
|
392
|
+
// sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129
|
|
393
|
+
root[key].format === THREE.RGBAFormat && root[key].type === THREE.UnsignedByteType) {
|
|
394
|
+
// NOTE: this cannot be set from the renderer (e.g. sRGB source textures rendered to P3)
|
|
395
|
+
root[key].colorSpace = THREE.SRGBColorSpace;
|
|
354
396
|
}
|
|
355
397
|
}
|
|
398
|
+
}
|
|
356
399
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
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;
|
|
400
|
+
// Register event handlers
|
|
401
|
+
if (instance != null && instance.parent && rootState != null && rootState.internal && (_instance$object = instance.object) != null && _instance$object.isObject3D && prevHandlers !== instance.eventCount) {
|
|
402
|
+
const object = instance.object;
|
|
403
|
+
// Pre-emptively remove the instance from the interaction manager
|
|
404
|
+
const index = rootState.internal.interaction.indexOf(object);
|
|
405
|
+
if (index > -1) rootState.internal.interaction.splice(index, 1);
|
|
406
|
+
// Add the instance to the interaction manager only when it has handlers
|
|
407
|
+
if (instance.eventCount && object.raycast !== null) {
|
|
408
|
+
rootState.internal.interaction.push(object);
|
|
421
409
|
}
|
|
422
|
-
}
|
|
423
|
-
resetFormInstance() {}
|
|
424
|
-
});
|
|
410
|
+
}
|
|
425
411
|
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
function findInitialRoot(instance) {
|
|
431
|
-
let root = instance.root;
|
|
432
|
-
while (root.getState().previousRoot) root = root.getState().previousRoot;
|
|
433
|
-
return root;
|
|
434
|
-
}
|
|
412
|
+
// Auto-attach geometries and materials
|
|
413
|
+
if (instance && instance.props.attach === undefined) {
|
|
414
|
+
if (instance.object.isBufferGeometry) instance.props.attach = 'geometry';else if (instance.object.isMaterial) instance.props.attach = 'material';
|
|
415
|
+
}
|
|
435
416
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
417
|
+
// Instance was updated, request a frame
|
|
418
|
+
if (instance) invalidateInstance(instance);
|
|
419
|
+
return object;
|
|
420
|
+
}
|
|
421
|
+
function invalidateInstance(instance) {
|
|
422
|
+
var _instance$root;
|
|
423
|
+
if (!instance.parent) return;
|
|
424
|
+
instance.props.onUpdate == null ? void 0 : instance.props.onUpdate(instance.object);
|
|
425
|
+
const state = (_instance$root = instance.root) == null ? void 0 : _instance$root.getState == null ? void 0 : _instance$root.getState();
|
|
426
|
+
if (state && state.internal.frames === 0) state.invalidate();
|
|
427
|
+
}
|
|
428
|
+
function updateCamera(camera, size) {
|
|
429
|
+
// Do not mess with the camera if it belongs to the user
|
|
430
|
+
// https://github.com/pmndrs/react-three-fiber/issues/92
|
|
431
|
+
if (camera.manual) return;
|
|
432
|
+
if (isOrthographicCamera(camera)) {
|
|
433
|
+
camera.left = size.width / -2;
|
|
434
|
+
camera.right = size.width / 2;
|
|
435
|
+
camera.top = size.height / 2;
|
|
436
|
+
camera.bottom = size.height / -2;
|
|
437
|
+
} else {
|
|
438
|
+
camera.aspect = size.width / size.height;
|
|
439
|
+
}
|
|
440
|
+
camera.updateProjectionMatrix();
|
|
441
|
+
}
|
|
442
|
+
const isObject3D = object => object == null ? void 0 : object.isObject3D;
|
|
453
443
|
|
|
454
|
-
|
|
455
|
-
|
|
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;
|
|
444
|
+
function makeId(event) {
|
|
445
|
+
return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
|
|
468
446
|
}
|
|
447
|
+
|
|
469
448
|
/**
|
|
470
|
-
*
|
|
449
|
+
* Release pointer captures.
|
|
450
|
+
* This is called by releasePointerCapture in the API, and when an object is removed.
|
|
471
451
|
*/
|
|
472
|
-
function
|
|
473
|
-
const
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
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;
|
|
452
|
+
function releaseInternalPointerCapture(capturedMap, obj, captures, pointerId) {
|
|
453
|
+
const captureData = captures.get(obj);
|
|
454
|
+
if (captureData) {
|
|
455
|
+
captures.delete(obj);
|
|
456
|
+
// If this was the last capturing object for this pointer
|
|
457
|
+
if (captures.size === 0) {
|
|
458
|
+
capturedMap.delete(pointerId);
|
|
459
|
+
captureData.target.releasePointerCapture(pointerId);
|
|
460
|
+
}
|
|
508
461
|
}
|
|
509
462
|
}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
463
|
+
function removeInteractivity(store, object) {
|
|
464
|
+
const {
|
|
465
|
+
internal
|
|
466
|
+
} = store.getState();
|
|
467
|
+
// Removes every trace of an object from the data store
|
|
468
|
+
internal.interaction = internal.interaction.filter(o => o !== object);
|
|
469
|
+
internal.initialHits = internal.initialHits.filter(o => o !== object);
|
|
470
|
+
internal.hovered.forEach((value, key) => {
|
|
471
|
+
if (value.eventObject === object || value.object === object) {
|
|
472
|
+
// Clear out intersects, they are outdated by now
|
|
473
|
+
internal.hovered.delete(key);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
internal.capturedMap.forEach((captures, pointerId) => {
|
|
477
|
+
releaseInternalPointerCapture(internal.capturedMap, object, captures, pointerId);
|
|
478
|
+
});
|
|
519
479
|
}
|
|
480
|
+
function createEvents(store) {
|
|
481
|
+
/** Calculates delta */
|
|
482
|
+
function calculateDistance(event) {
|
|
483
|
+
const {
|
|
484
|
+
internal
|
|
485
|
+
} = store.getState();
|
|
486
|
+
const dx = event.offsetX - internal.initialClick[0];
|
|
487
|
+
const dy = event.offsetY - internal.initialClick[1];
|
|
488
|
+
return Math.round(Math.sqrt(dx * dx + dy * dy));
|
|
489
|
+
}
|
|
520
490
|
|
|
521
|
-
/**
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
const
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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;
|
|
491
|
+
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
492
|
+
function filterPointerEvents(objects) {
|
|
493
|
+
return objects.filter(obj => ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(name => {
|
|
494
|
+
var _r3f;
|
|
495
|
+
return (_r3f = obj.__r3f) == null ? void 0 : _r3f.handlers['onPointer' + name];
|
|
496
|
+
}));
|
|
497
|
+
}
|
|
498
|
+
function intersect(event, filter) {
|
|
499
|
+
const state = store.getState();
|
|
500
|
+
const duplicates = new Set();
|
|
501
|
+
const intersections = [];
|
|
502
|
+
// Allow callers to eliminate event objects
|
|
503
|
+
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
504
|
+
// Reset all raycaster cameras to undefined
|
|
505
|
+
for (let i = 0; i < eventsObjects.length; i++) {
|
|
506
|
+
const state = getRootState(eventsObjects[i]);
|
|
507
|
+
if (state) {
|
|
508
|
+
state.raycaster.camera = undefined;
|
|
509
|
+
}
|
|
564
510
|
}
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
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;
|
|
511
|
+
if (!state.previousRoot) {
|
|
512
|
+
// Make sure root-level pointer and ray are set up
|
|
513
|
+
state.events.compute == null ? void 0 : state.events.compute(event, state);
|
|
573
514
|
}
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
515
|
+
function handleRaycast(obj) {
|
|
516
|
+
const state = getRootState(obj);
|
|
517
|
+
// Skip event handling when noEvents is set, or when the raycasters camera is null
|
|
518
|
+
if (!state || !state.events.enabled || state.raycaster.camera === null) return [];
|
|
577
519
|
|
|
578
|
-
//
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
520
|
+
// When the camera is undefined we have to call the event layers update function
|
|
521
|
+
if (state.raycaster.camera === undefined) {
|
|
522
|
+
var _state$previousRoot;
|
|
523
|
+
state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState());
|
|
524
|
+
// If the camera is still undefined we have to skip this layer entirely
|
|
525
|
+
if (state.raycaster.camera === undefined) state.raycaster.camera = null;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Intersect object by object
|
|
529
|
+
return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// Collect events
|
|
533
|
+
let hits = eventsObjects
|
|
534
|
+
// Intersect objects
|
|
535
|
+
.flatMap(handleRaycast)
|
|
536
|
+
// Sort by event priority and distance
|
|
537
|
+
.sort((a, b) => {
|
|
538
|
+
const aState = getRootState(a.object);
|
|
539
|
+
const bState = getRootState(b.object);
|
|
540
|
+
if (!aState || !bState) return a.distance - b.distance;
|
|
541
|
+
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
542
|
+
})
|
|
543
|
+
// Filter out duplicates
|
|
544
|
+
.filter(item => {
|
|
545
|
+
const id = makeId(item);
|
|
546
|
+
if (duplicates.has(id)) return false;
|
|
547
|
+
duplicates.add(id);
|
|
548
|
+
return true;
|
|
588
549
|
});
|
|
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
550
|
|
|
602
|
-
//
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
for (const key in queue) {
|
|
606
|
-
if (!REACT_INTERNAL_PROPS.includes(key)) props[key] = queue[key];
|
|
607
|
-
}
|
|
608
|
-
return props;
|
|
609
|
-
}
|
|
551
|
+
// https://github.com/mrdoob/three.js/issues/16031
|
|
552
|
+
// Allow custom userland intersect sort order, this likely only makes sense on the root filter
|
|
553
|
+
if (state.events.filter) hits = state.events.filter(hits, state);
|
|
610
554
|
|
|
611
|
-
//
|
|
612
|
-
|
|
613
|
-
|
|
555
|
+
// Bubble up the events, find the event source (eventObject)
|
|
556
|
+
for (const hit of hits) {
|
|
557
|
+
let eventObject = hit.object;
|
|
558
|
+
// Bubble event up
|
|
559
|
+
while (eventObject) {
|
|
560
|
+
var _r3f2;
|
|
561
|
+
if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({
|
|
562
|
+
...hit,
|
|
563
|
+
eventObject
|
|
564
|
+
});
|
|
565
|
+
eventObject = eventObject.parent;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
614
568
|
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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);
|
|
569
|
+
// If the interaction is captured, make all capturing targets part of the intersect.
|
|
570
|
+
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
571
|
+
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
572
|
+
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
573
|
+
}
|
|
632
574
|
}
|
|
575
|
+
return intersections;
|
|
633
576
|
}
|
|
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
577
|
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
578
|
+
/** Handles intersections by forwarding them to handlers */
|
|
579
|
+
function handleIntersects(intersections, event, delta, callback) {
|
|
580
|
+
// If anything has been found, forward it to the event listeners
|
|
581
|
+
if (intersections.length) {
|
|
582
|
+
const localState = {
|
|
583
|
+
stopped: false
|
|
584
|
+
};
|
|
585
|
+
for (const hit of intersections) {
|
|
586
|
+
const state = getRootState(hit.object);
|
|
587
|
+
if (state) {
|
|
588
|
+
const {
|
|
589
|
+
raycaster,
|
|
590
|
+
pointer,
|
|
591
|
+
camera,
|
|
592
|
+
internal
|
|
593
|
+
} = state;
|
|
594
|
+
const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
595
|
+
const hasPointerCapture = id => {
|
|
596
|
+
var _internal$capturedMap, _internal$capturedMap2;
|
|
597
|
+
return (_internal$capturedMap = (_internal$capturedMap2 = internal.capturedMap.get(id)) == null ? void 0 : _internal$capturedMap2.has(hit.eventObject)) != null ? _internal$capturedMap : false;
|
|
598
|
+
};
|
|
599
|
+
const setPointerCapture = id => {
|
|
600
|
+
const captureData = {
|
|
601
|
+
intersection: hit,
|
|
602
|
+
target: event.target
|
|
603
|
+
};
|
|
604
|
+
if (internal.capturedMap.has(id)) {
|
|
605
|
+
// if the pointerId was previously captured, we add the hit to the
|
|
606
|
+
// event capturedMap.
|
|
607
|
+
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
608
|
+
} else {
|
|
609
|
+
// if the pointerId was not previously captured, we create a map
|
|
610
|
+
// containing the hitObject, and the hit. hitObject is used for
|
|
611
|
+
// faster access.
|
|
612
|
+
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
613
|
+
}
|
|
614
|
+
event.target.setPointerCapture(id);
|
|
615
|
+
};
|
|
616
|
+
const releasePointerCapture = id => {
|
|
617
|
+
const captures = internal.capturedMap.get(id);
|
|
618
|
+
if (captures) {
|
|
619
|
+
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
620
|
+
}
|
|
621
|
+
};
|
|
649
622
|
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
623
|
+
// Add native event props
|
|
624
|
+
let extractEventProps = {};
|
|
625
|
+
// This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
|
|
626
|
+
for (let prop in event) {
|
|
627
|
+
let property = event[prop];
|
|
628
|
+
// Only copy over atomics, leave functions alone as these should be
|
|
629
|
+
// called as event.nativeEvent.fn()
|
|
630
|
+
if (typeof property !== 'function') extractEventProps[prop] = property;
|
|
631
|
+
}
|
|
632
|
+
let raycastEvent = {
|
|
633
|
+
...hit,
|
|
634
|
+
...extractEventProps,
|
|
635
|
+
pointer,
|
|
636
|
+
intersections,
|
|
637
|
+
stopped: localState.stopped,
|
|
638
|
+
delta,
|
|
639
|
+
unprojectedPoint,
|
|
640
|
+
ray: raycaster.ray,
|
|
641
|
+
camera: camera,
|
|
642
|
+
// Hijack stopPropagation, which just sets a flag
|
|
643
|
+
stopPropagation() {
|
|
644
|
+
// https://github.com/pmndrs/react-three-fiber/issues/596
|
|
645
|
+
// Events are not allowed to stop propagation if the pointer has been captured
|
|
646
|
+
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
|
|
658
647
|
|
|
659
|
-
//
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
648
|
+
// We only authorize stopPropagation...
|
|
649
|
+
if (
|
|
650
|
+
// ...if this pointer hasn't been captured
|
|
651
|
+
!capturesForPointer ||
|
|
652
|
+
// ... or if the hit object is capturing the pointer
|
|
653
|
+
capturesForPointer.has(hit.eventObject)) {
|
|
654
|
+
raycastEvent.stopped = localState.stopped = true;
|
|
655
|
+
// Propagation is stopped, remove all other hover records
|
|
656
|
+
// An event handler is only allowed to flush other handlers if it is hovered itself
|
|
657
|
+
if (internal.hovered.size && Array.from(internal.hovered.values()).find(i => i.eventObject === hit.eventObject)) {
|
|
658
|
+
// Objects cannot flush out higher up objects that have already caught the event
|
|
659
|
+
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
660
|
+
cancelPointer([...higher, hit]);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
},
|
|
664
|
+
// there should be a distinction between target and currentTarget
|
|
665
|
+
target: {
|
|
666
|
+
hasPointerCapture,
|
|
667
|
+
setPointerCapture,
|
|
668
|
+
releasePointerCapture
|
|
669
|
+
},
|
|
670
|
+
currentTarget: {
|
|
671
|
+
hasPointerCapture,
|
|
672
|
+
setPointerCapture,
|
|
673
|
+
releasePointerCapture
|
|
674
|
+
},
|
|
675
|
+
nativeEvent: event
|
|
676
|
+
};
|
|
677
|
+
|
|
678
|
+
// Call subscribers
|
|
679
|
+
callback(raycastEvent);
|
|
680
|
+
// Event bubbling may be interrupted by stopPropagation
|
|
681
|
+
if (localState.stopped === true) break;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
671
684
|
}
|
|
672
|
-
|
|
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);
|
|
685
|
+
return intersections;
|
|
680
686
|
}
|
|
681
|
-
|
|
682
|
-
function detach(parent, child) {
|
|
683
|
-
if (is.str(child.props.attach)) {
|
|
687
|
+
function cancelPointer(intersections) {
|
|
684
688
|
const {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
689
|
+
internal
|
|
690
|
+
} = store.getState();
|
|
691
|
+
for (const hoveredObj of internal.hovered.values()) {
|
|
692
|
+
// When no objects were hit or the the hovered object wasn't found underneath the cursor
|
|
693
|
+
// we call onPointerOut and delete the object from the hovered-elements map
|
|
694
|
+
if (!intersections.length || !intersections.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
|
|
695
|
+
const eventObject = hoveredObj.eventObject;
|
|
696
|
+
const instance = eventObject.__r3f;
|
|
697
|
+
internal.hovered.delete(makeId(hoveredObj));
|
|
698
|
+
if (instance != null && instance.eventCount) {
|
|
699
|
+
const handlers = instance.handlers;
|
|
700
|
+
// Clear out intersects, they are outdated by now
|
|
701
|
+
const data = {
|
|
702
|
+
...hoveredObj,
|
|
703
|
+
intersections
|
|
704
|
+
};
|
|
705
|
+
handlers.onPointerOut == null ? void 0 : handlers.onPointerOut(data);
|
|
706
|
+
handlers.onPointerLeave == null ? void 0 : handlers.onPointerLeave(data);
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
695
710
|
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
const
|
|
699
|
-
|
|
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];
|
|
711
|
+
function pointerMissed(event, objects) {
|
|
712
|
+
for (let i = 0; i < objects.length; i++) {
|
|
713
|
+
const instance = objects[i].__r3f;
|
|
714
|
+
instance == null ? void 0 : instance.handlers.onPointerMissed == null ? void 0 : instance.handlers.onPointerMissed(event);
|
|
722
715
|
}
|
|
723
716
|
}
|
|
717
|
+
function handlePointer(name) {
|
|
718
|
+
// Deal with cancelation
|
|
719
|
+
switch (name) {
|
|
720
|
+
case 'onPointerLeave':
|
|
721
|
+
case 'onPointerCancel':
|
|
722
|
+
return () => cancelPointer([]);
|
|
723
|
+
case 'onLostPointerCapture':
|
|
724
|
+
return event => {
|
|
725
|
+
const {
|
|
726
|
+
internal
|
|
727
|
+
} = store.getState();
|
|
728
|
+
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
729
|
+
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
730
|
+
// object that's getting removed. We call it on the next frame because onLostPointerCapture
|
|
731
|
+
// fires before onPointerUp. Otherwise pointerUp would never be called if the event didn't
|
|
732
|
+
// happen in the object it originated from, leaving components in a in-between state.
|
|
733
|
+
requestAnimationFrame(() => {
|
|
734
|
+
// Only release if pointer-up didn't do it already
|
|
735
|
+
if (internal.capturedMap.has(event.pointerId)) {
|
|
736
|
+
internal.capturedMap.delete(event.pointerId);
|
|
737
|
+
cancelPointer([]);
|
|
738
|
+
}
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
}
|
|
724
743
|
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
} = resolve(instance.object, prop);
|
|
744
|
+
// Any other pointer goes here ...
|
|
745
|
+
return function handleEvent(event) {
|
|
746
|
+
const {
|
|
747
|
+
onPointerMissed,
|
|
748
|
+
internal
|
|
749
|
+
} = store.getState();
|
|
732
750
|
|
|
733
|
-
|
|
734
|
-
|
|
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
|
-
}
|
|
751
|
+
// prepareRay(event)
|
|
752
|
+
internal.lastEvent.current = event;
|
|
785
753
|
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
target
|
|
793
|
-
} = resolve(object, prop);
|
|
754
|
+
// Get fresh intersects
|
|
755
|
+
const isPointerMove = name === 'onPointerMove';
|
|
756
|
+
const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
|
|
757
|
+
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
758
|
+
const hits = intersect(event, filter);
|
|
759
|
+
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
794
760
|
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
key = 'colorSpace';
|
|
800
|
-
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
801
|
-
} else if (key === 'outputEncoding') {
|
|
802
|
-
key = 'outputColorSpace';
|
|
803
|
-
value = value === sRGBEncoding ? SRGBColorSpace : LinearSRGBColorSpace;
|
|
761
|
+
// Save initial coordinates on pointer-down
|
|
762
|
+
if (name === 'onPointerDown') {
|
|
763
|
+
internal.initialClick = [event.offsetX, event.offsetY];
|
|
764
|
+
internal.initialHits = hits.map(hit => hit.eventObject);
|
|
804
765
|
}
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
// Copy if properties match signatures
|
|
808
|
-
if (typeof (target == null ? void 0 : target.copy) === 'function' && target.copy === value.copy) {
|
|
809
|
-
target.copy(value);
|
|
810
|
-
}
|
|
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
|
-
// Set array types
|
|
816
|
-
else if (target != null && target.set && Array.isArray(value)) {
|
|
817
|
-
if (target.fromArray) target.fromArray(value);else target.set(...value);
|
|
818
|
-
}
|
|
819
|
-
// Set literal types
|
|
820
|
-
else if (target != null && target.set && typeof value !== 'object') {
|
|
821
|
-
const isColor = target instanceof THREE.Color;
|
|
822
|
-
// Allow setting array scalars
|
|
823
|
-
if (!isColor && target.setScalar && typeof value === 'number') target.setScalar(value);
|
|
824
|
-
// Otherwise just set single value
|
|
825
|
-
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
|
-
}
|
|
831
|
-
// Else, just overwrite the value
|
|
832
|
-
else {
|
|
833
|
-
root[key] = value;
|
|
834
766
|
|
|
835
|
-
//
|
|
836
|
-
//
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
if (hasColorSpace(root[key])) root[key].colorSpace = 'srgb';else root[key].encoding = sRGBEncoding;
|
|
767
|
+
// If a click yields no results, pass it back to the user as a miss
|
|
768
|
+
// Missed events have to come first in order to establish user-land side-effect clean up
|
|
769
|
+
if (isClickEvent && !hits.length) {
|
|
770
|
+
if (delta <= 2) {
|
|
771
|
+
pointerMissed(event, internal.interaction);
|
|
772
|
+
if (onPointerMissed) onPointerMissed(event);
|
|
773
|
+
}
|
|
843
774
|
}
|
|
844
|
-
|
|
845
|
-
|
|
775
|
+
// Take care of unhover
|
|
776
|
+
if (isPointerMove) cancelPointer(hits);
|
|
777
|
+
function onIntersect(data) {
|
|
778
|
+
const eventObject = data.eventObject;
|
|
779
|
+
const instance = eventObject.__r3f;
|
|
846
780
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
const index = rootState.internal.interaction.indexOf(instance.object);
|
|
851
|
-
if (index > -1) rootState.internal.interaction.splice(index, 1);
|
|
852
|
-
// 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);
|
|
855
|
-
}
|
|
856
|
-
}
|
|
781
|
+
// Check presence of handlers
|
|
782
|
+
if (!(instance != null && instance.eventCount)) return;
|
|
783
|
+
const handlers = instance.handlers;
|
|
857
784
|
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
785
|
+
/*
|
|
786
|
+
MAYBE TODO, DELETE IF NOT:
|
|
787
|
+
Check if the object is captured, captured events should not have intersects running in parallel
|
|
788
|
+
But wouldn't it be better to just replace capturedMap with a single entry?
|
|
789
|
+
Also, are we OK with straight up making picking up multiple objects impossible?
|
|
790
|
+
|
|
791
|
+
const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
|
|
792
|
+
if (pointerId !== undefined) {
|
|
793
|
+
const capturedMeshSet = internal.capturedMap.get(pointerId)
|
|
794
|
+
if (capturedMeshSet) {
|
|
795
|
+
const captured = capturedMeshSet.get(eventObject)
|
|
796
|
+
if (captured && captured.localState.stopped) return
|
|
797
|
+
}
|
|
798
|
+
}*/
|
|
862
799
|
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
800
|
+
if (isPointerMove) {
|
|
801
|
+
// Move event ...
|
|
802
|
+
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
803
|
+
// When enter or out is present take care of hover-state
|
|
804
|
+
const id = makeId(data);
|
|
805
|
+
const hoveredItem = internal.hovered.get(id);
|
|
806
|
+
if (!hoveredItem) {
|
|
807
|
+
// If the object wasn't previously hovered, book it and call its handler
|
|
808
|
+
internal.hovered.set(id, data);
|
|
809
|
+
handlers.onPointerOver == null ? void 0 : handlers.onPointerOver(data);
|
|
810
|
+
handlers.onPointerEnter == null ? void 0 : handlers.onPointerEnter(data);
|
|
811
|
+
} else if (hoveredItem.stopped) {
|
|
812
|
+
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
813
|
+
data.stopPropagation();
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// Call mouse move
|
|
817
|
+
handlers.onPointerMove == null ? void 0 : handlers.onPointerMove(data);
|
|
818
|
+
} else {
|
|
819
|
+
// All other events ...
|
|
820
|
+
const handler = handlers[name];
|
|
821
|
+
if (handler) {
|
|
822
|
+
// Forward all events back to their respective handlers with the exception of click events,
|
|
823
|
+
// which must use the initial target
|
|
824
|
+
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
825
|
+
// Missed events have to come first
|
|
826
|
+
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
827
|
+
// Now call the handler
|
|
828
|
+
handler(data);
|
|
829
|
+
}
|
|
830
|
+
} else {
|
|
831
|
+
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
832
|
+
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
833
|
+
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
handleIntersects(hits, event, delta, onIntersect);
|
|
839
|
+
};
|
|
885
840
|
}
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
function makeId(event) {
|
|
891
|
-
return (event.eventObject || event.object).uuid + '/' + event.index + event.instanceId;
|
|
841
|
+
return {
|
|
842
|
+
handlePointer
|
|
843
|
+
};
|
|
892
844
|
}
|
|
893
845
|
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
/** Returns true if an instance has a valid pointer-event registered, this excludes scroll, clicks etc */
|
|
938
|
-
function filterPointerEvents(objects) {
|
|
939
|
-
return objects.filter(obj => ['Move', 'Over', 'Enter', 'Out', 'Leave'].some(name => {
|
|
940
|
-
var _r3f;
|
|
941
|
-
return (_r3f = obj.__r3f) == null ? void 0 : _r3f.handlers['onPointer' + name];
|
|
942
|
-
}));
|
|
943
|
-
}
|
|
944
|
-
function intersect(event, filter) {
|
|
945
|
-
const state = store.getState();
|
|
946
|
-
const duplicates = new Set();
|
|
947
|
-
const intersections = [];
|
|
948
|
-
// Allow callers to eliminate event objects
|
|
949
|
-
const eventsObjects = filter ? filter(state.internal.interaction) : state.internal.interaction;
|
|
950
|
-
// Reset all raycaster cameras to undefined
|
|
951
|
-
for (let i = 0; i < eventsObjects.length; i++) {
|
|
952
|
-
const state = getRootState(eventsObjects[i]);
|
|
953
|
-
if (state) {
|
|
954
|
-
state.raycaster.camera = undefined;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
if (!state.previousRoot) {
|
|
958
|
-
// Make sure root-level pointer and ray are set up
|
|
959
|
-
state.events.compute == null ? void 0 : state.events.compute(event, state);
|
|
960
|
-
}
|
|
961
|
-
function handleRaycast(obj) {
|
|
962
|
-
const state = getRootState(obj);
|
|
963
|
-
// Skip event handling when noEvents is set, or when the raycasters camera is null
|
|
964
|
-
if (!state || !state.events.enabled || state.raycaster.camera === null) return [];
|
|
965
|
-
|
|
966
|
-
// When the camera is undefined we have to call the event layers update function
|
|
967
|
-
if (state.raycaster.camera === undefined) {
|
|
968
|
-
var _state$previousRoot;
|
|
969
|
-
state.events.compute == null ? void 0 : state.events.compute(event, state, (_state$previousRoot = state.previousRoot) == null ? void 0 : _state$previousRoot.getState());
|
|
970
|
-
// If the camera is still undefined we have to skip this layer entirely
|
|
971
|
-
if (state.raycaster.camera === undefined) state.raycaster.camera = null;
|
|
972
|
-
}
|
|
973
|
-
|
|
974
|
-
// Intersect object by object
|
|
975
|
-
return state.raycaster.camera ? state.raycaster.intersectObject(obj, true) : [];
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// Collect events
|
|
979
|
-
let hits = eventsObjects
|
|
980
|
-
// Intersect objects
|
|
981
|
-
.flatMap(handleRaycast)
|
|
982
|
-
// Sort by event priority and distance
|
|
983
|
-
.sort((a, b) => {
|
|
984
|
-
const aState = getRootState(a.object);
|
|
985
|
-
const bState = getRootState(b.object);
|
|
986
|
-
if (!aState || !bState) return a.distance - b.distance;
|
|
987
|
-
return bState.events.priority - aState.events.priority || a.distance - b.distance;
|
|
988
|
-
})
|
|
989
|
-
// Filter out duplicates
|
|
990
|
-
.filter(item => {
|
|
991
|
-
const id = makeId(item);
|
|
992
|
-
if (duplicates.has(id)) return false;
|
|
993
|
-
duplicates.add(id);
|
|
994
|
-
return true;
|
|
995
|
-
});
|
|
996
|
-
|
|
997
|
-
// https://github.com/mrdoob/three.js/issues/16031
|
|
998
|
-
// Allow custom userland intersect sort order, this likely only makes sense on the root filter
|
|
999
|
-
if (state.events.filter) hits = state.events.filter(hits, state);
|
|
1000
|
-
|
|
1001
|
-
// Bubble up the events, find the event source (eventObject)
|
|
1002
|
-
for (const hit of hits) {
|
|
1003
|
-
let eventObject = hit.object;
|
|
1004
|
-
// Bubble event up
|
|
1005
|
-
while (eventObject) {
|
|
1006
|
-
var _r3f2;
|
|
1007
|
-
if ((_r3f2 = eventObject.__r3f) != null && _r3f2.eventCount) intersections.push({
|
|
1008
|
-
...hit,
|
|
1009
|
-
eventObject
|
|
1010
|
-
});
|
|
1011
|
-
eventObject = eventObject.parent;
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
// If the interaction is captured, make all capturing targets part of the intersect.
|
|
1016
|
-
if ('pointerId' in event && state.internal.capturedMap.has(event.pointerId)) {
|
|
1017
|
-
for (let captureData of state.internal.capturedMap.get(event.pointerId).values()) {
|
|
1018
|
-
if (!duplicates.has(makeId(captureData.intersection))) intersections.push(captureData.intersection);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
return intersections;
|
|
1022
|
-
}
|
|
1023
|
-
|
|
1024
|
-
/** Handles intersections by forwarding them to handlers */
|
|
1025
|
-
function handleIntersects(intersections, event, delta, callback) {
|
|
1026
|
-
// If anything has been found, forward it to the event listeners
|
|
1027
|
-
if (intersections.length) {
|
|
1028
|
-
const localState = {
|
|
1029
|
-
stopped: false
|
|
1030
|
-
};
|
|
1031
|
-
for (const hit of intersections) {
|
|
1032
|
-
const state = getRootState(hit.object);
|
|
1033
|
-
if (state) {
|
|
1034
|
-
const {
|
|
1035
|
-
raycaster,
|
|
1036
|
-
pointer,
|
|
1037
|
-
camera,
|
|
1038
|
-
internal
|
|
1039
|
-
} = state;
|
|
1040
|
-
const unprojectedPoint = new THREE.Vector3(pointer.x, pointer.y, 0).unproject(camera);
|
|
1041
|
-
const hasPointerCapture = id => {
|
|
1042
|
-
var _internal$capturedMap, _internal$capturedMap2;
|
|
1043
|
-
return (_internal$capturedMap = (_internal$capturedMap2 = internal.capturedMap.get(id)) == null ? void 0 : _internal$capturedMap2.has(hit.eventObject)) != null ? _internal$capturedMap : false;
|
|
1044
|
-
};
|
|
1045
|
-
const setPointerCapture = id => {
|
|
1046
|
-
const captureData = {
|
|
1047
|
-
intersection: hit,
|
|
1048
|
-
target: event.target
|
|
1049
|
-
};
|
|
1050
|
-
if (internal.capturedMap.has(id)) {
|
|
1051
|
-
// if the pointerId was previously captured, we add the hit to the
|
|
1052
|
-
// event capturedMap.
|
|
1053
|
-
internal.capturedMap.get(id).set(hit.eventObject, captureData);
|
|
1054
|
-
} else {
|
|
1055
|
-
// if the pointerId was not previously captured, we create a map
|
|
1056
|
-
// containing the hitObject, and the hit. hitObject is used for
|
|
1057
|
-
// faster access.
|
|
1058
|
-
internal.capturedMap.set(id, new Map([[hit.eventObject, captureData]]));
|
|
1059
|
-
}
|
|
1060
|
-
event.target.setPointerCapture(id);
|
|
1061
|
-
};
|
|
1062
|
-
const releasePointerCapture = id => {
|
|
1063
|
-
const captures = internal.capturedMap.get(id);
|
|
1064
|
-
if (captures) {
|
|
1065
|
-
releaseInternalPointerCapture(internal.capturedMap, hit.eventObject, captures, id);
|
|
1066
|
-
}
|
|
1067
|
-
};
|
|
1068
|
-
|
|
1069
|
-
// Add native event props
|
|
1070
|
-
let extractEventProps = {};
|
|
1071
|
-
// This iterates over the event's properties including the inherited ones. Native PointerEvents have most of their props as getters which are inherited, but polyfilled PointerEvents have them all as their own properties (i.e. not inherited). We can't use Object.keys() or Object.entries() as they only return "own" properties; nor Object.getPrototypeOf(event) as that *doesn't* return "own" properties, only inherited ones.
|
|
1072
|
-
for (let prop in event) {
|
|
1073
|
-
let property = event[prop];
|
|
1074
|
-
// Only copy over atomics, leave functions alone as these should be
|
|
1075
|
-
// called as event.nativeEvent.fn()
|
|
1076
|
-
if (typeof property !== 'function') extractEventProps[prop] = property;
|
|
1077
|
-
}
|
|
1078
|
-
let raycastEvent = {
|
|
1079
|
-
...hit,
|
|
1080
|
-
...extractEventProps,
|
|
1081
|
-
pointer,
|
|
1082
|
-
intersections,
|
|
1083
|
-
stopped: localState.stopped,
|
|
1084
|
-
delta,
|
|
1085
|
-
unprojectedPoint,
|
|
1086
|
-
ray: raycaster.ray,
|
|
1087
|
-
camera: camera,
|
|
1088
|
-
// Hijack stopPropagation, which just sets a flag
|
|
1089
|
-
stopPropagation() {
|
|
1090
|
-
// https://github.com/pmndrs/react-three-fiber/issues/596
|
|
1091
|
-
// Events are not allowed to stop propagation if the pointer has been captured
|
|
1092
|
-
const capturesForPointer = 'pointerId' in event && internal.capturedMap.get(event.pointerId);
|
|
1093
|
-
|
|
1094
|
-
// We only authorize stopPropagation...
|
|
1095
|
-
if (
|
|
1096
|
-
// ...if this pointer hasn't been captured
|
|
1097
|
-
!capturesForPointer ||
|
|
1098
|
-
// ... or if the hit object is capturing the pointer
|
|
1099
|
-
capturesForPointer.has(hit.eventObject)) {
|
|
1100
|
-
raycastEvent.stopped = localState.stopped = true;
|
|
1101
|
-
// Propagation is stopped, remove all other hover records
|
|
1102
|
-
// An event handler is only allowed to flush other handlers if it is hovered itself
|
|
1103
|
-
if (internal.hovered.size && Array.from(internal.hovered.values()).find(i => i.eventObject === hit.eventObject)) {
|
|
1104
|
-
// Objects cannot flush out higher up objects that have already caught the event
|
|
1105
|
-
const higher = intersections.slice(0, intersections.indexOf(hit));
|
|
1106
|
-
cancelPointer([...higher, hit]);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
},
|
|
1110
|
-
// there should be a distinction between target and currentTarget
|
|
1111
|
-
target: {
|
|
1112
|
-
hasPointerCapture,
|
|
1113
|
-
setPointerCapture,
|
|
1114
|
-
releasePointerCapture
|
|
1115
|
-
},
|
|
1116
|
-
currentTarget: {
|
|
1117
|
-
hasPointerCapture,
|
|
1118
|
-
setPointerCapture,
|
|
1119
|
-
releasePointerCapture
|
|
1120
|
-
},
|
|
1121
|
-
nativeEvent: event
|
|
1122
|
-
};
|
|
1123
|
-
|
|
1124
|
-
// Call subscribers
|
|
1125
|
-
callback(raycastEvent);
|
|
1126
|
-
// Event bubbling may be interrupted by stopPropagation
|
|
1127
|
-
if (localState.stopped === true) break;
|
|
1128
|
-
}
|
|
1129
|
-
}
|
|
1130
|
-
}
|
|
1131
|
-
return intersections;
|
|
1132
|
-
}
|
|
1133
|
-
function cancelPointer(intersections) {
|
|
1134
|
-
const {
|
|
1135
|
-
internal
|
|
1136
|
-
} = store.getState();
|
|
1137
|
-
for (const hoveredObj of internal.hovered.values()) {
|
|
1138
|
-
// When no objects were hit or the the hovered object wasn't found underneath the cursor
|
|
1139
|
-
// we call onPointerOut and delete the object from the hovered-elements map
|
|
1140
|
-
if (!intersections.length || !intersections.find(hit => hit.object === hoveredObj.object && hit.index === hoveredObj.index && hit.instanceId === hoveredObj.instanceId)) {
|
|
1141
|
-
const eventObject = hoveredObj.eventObject;
|
|
1142
|
-
const instance = eventObject.__r3f;
|
|
1143
|
-
internal.hovered.delete(makeId(hoveredObj));
|
|
1144
|
-
if (instance != null && instance.eventCount) {
|
|
1145
|
-
const handlers = instance.handlers;
|
|
1146
|
-
// Clear out intersects, they are outdated by now
|
|
1147
|
-
const data = {
|
|
1148
|
-
...hoveredObj,
|
|
1149
|
-
intersections
|
|
1150
|
-
};
|
|
1151
|
-
handlers.onPointerOut == null ? void 0 : handlers.onPointerOut(data);
|
|
1152
|
-
handlers.onPointerLeave == null ? void 0 : handlers.onPointerLeave(data);
|
|
1153
|
-
}
|
|
1154
|
-
}
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
function pointerMissed(event, objects) {
|
|
1158
|
-
for (let i = 0; i < objects.length; i++) {
|
|
1159
|
-
const instance = objects[i].__r3f;
|
|
1160
|
-
instance == null ? void 0 : instance.handlers.onPointerMissed == null ? void 0 : instance.handlers.onPointerMissed(event);
|
|
1161
|
-
}
|
|
1162
|
-
}
|
|
1163
|
-
function handlePointer(name) {
|
|
1164
|
-
// Deal with cancelation
|
|
1165
|
-
switch (name) {
|
|
1166
|
-
case 'onPointerLeave':
|
|
1167
|
-
case 'onPointerCancel':
|
|
1168
|
-
return () => cancelPointer([]);
|
|
1169
|
-
case 'onLostPointerCapture':
|
|
1170
|
-
return event => {
|
|
1171
|
-
const {
|
|
1172
|
-
internal
|
|
1173
|
-
} = store.getState();
|
|
1174
|
-
if ('pointerId' in event && internal.capturedMap.has(event.pointerId)) {
|
|
1175
|
-
// If the object event interface had onLostPointerCapture, we'd call it here on every
|
|
1176
|
-
// object that's getting removed. We call it on the next frame because onLostPointerCapture
|
|
1177
|
-
// fires before onPointerUp. Otherwise pointerUp would never be called if the event didn't
|
|
1178
|
-
// happen in the object it originated from, leaving components in a in-between state.
|
|
1179
|
-
requestAnimationFrame(() => {
|
|
1180
|
-
// Only release if pointer-up didn't do it already
|
|
1181
|
-
if (internal.capturedMap.has(event.pointerId)) {
|
|
1182
|
-
internal.capturedMap.delete(event.pointerId);
|
|
1183
|
-
cancelPointer([]);
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
};
|
|
1188
|
-
}
|
|
1189
|
-
|
|
1190
|
-
// Any other pointer goes here ...
|
|
1191
|
-
return function handleEvent(event) {
|
|
1192
|
-
const {
|
|
1193
|
-
onPointerMissed,
|
|
1194
|
-
internal
|
|
1195
|
-
} = store.getState();
|
|
1196
|
-
|
|
1197
|
-
// prepareRay(event)
|
|
1198
|
-
internal.lastEvent.current = event;
|
|
1199
|
-
|
|
1200
|
-
// Get fresh intersects
|
|
1201
|
-
const isPointerMove = name === 'onPointerMove';
|
|
1202
|
-
const isClickEvent = name === 'onClick' || name === 'onContextMenu' || name === 'onDoubleClick';
|
|
1203
|
-
const filter = isPointerMove ? filterPointerEvents : undefined;
|
|
1204
|
-
const hits = intersect(event, filter);
|
|
1205
|
-
const delta = isClickEvent ? calculateDistance(event) : 0;
|
|
1206
|
-
|
|
1207
|
-
// Save initial coordinates on pointer-down
|
|
1208
|
-
if (name === 'onPointerDown') {
|
|
1209
|
-
internal.initialClick = [event.offsetX, event.offsetY];
|
|
1210
|
-
internal.initialHits = hits.map(hit => hit.eventObject);
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
// If a click yields no results, pass it back to the user as a miss
|
|
1214
|
-
// Missed events have to come first in order to establish user-land side-effect clean up
|
|
1215
|
-
if (isClickEvent && !hits.length) {
|
|
1216
|
-
if (delta <= 2) {
|
|
1217
|
-
pointerMissed(event, internal.interaction);
|
|
1218
|
-
if (onPointerMissed) onPointerMissed(event);
|
|
1219
|
-
}
|
|
1220
|
-
}
|
|
1221
|
-
// Take care of unhover
|
|
1222
|
-
if (isPointerMove) cancelPointer(hits);
|
|
1223
|
-
function onIntersect(data) {
|
|
1224
|
-
const eventObject = data.eventObject;
|
|
1225
|
-
const instance = eventObject.__r3f;
|
|
1226
|
-
|
|
1227
|
-
// Check presence of handlers
|
|
1228
|
-
if (!(instance != null && instance.eventCount)) return;
|
|
1229
|
-
const handlers = instance.handlers;
|
|
1230
|
-
|
|
1231
|
-
/*
|
|
1232
|
-
MAYBE TODO, DELETE IF NOT:
|
|
1233
|
-
Check if the object is captured, captured events should not have intersects running in parallel
|
|
1234
|
-
But wouldn't it be better to just replace capturedMap with a single entry?
|
|
1235
|
-
Also, are we OK with straight up making picking up multiple objects impossible?
|
|
1236
|
-
|
|
1237
|
-
const pointerId = (data as ThreeEvent<PointerEvent>).pointerId
|
|
1238
|
-
if (pointerId !== undefined) {
|
|
1239
|
-
const capturedMeshSet = internal.capturedMap.get(pointerId)
|
|
1240
|
-
if (capturedMeshSet) {
|
|
1241
|
-
const captured = capturedMeshSet.get(eventObject)
|
|
1242
|
-
if (captured && captured.localState.stopped) return
|
|
1243
|
-
}
|
|
1244
|
-
}*/
|
|
1245
|
-
|
|
1246
|
-
if (isPointerMove) {
|
|
1247
|
-
// Move event ...
|
|
1248
|
-
if (handlers.onPointerOver || handlers.onPointerEnter || handlers.onPointerOut || handlers.onPointerLeave) {
|
|
1249
|
-
// When enter or out is present take care of hover-state
|
|
1250
|
-
const id = makeId(data);
|
|
1251
|
-
const hoveredItem = internal.hovered.get(id);
|
|
1252
|
-
if (!hoveredItem) {
|
|
1253
|
-
// If the object wasn't previously hovered, book it and call its handler
|
|
1254
|
-
internal.hovered.set(id, data);
|
|
1255
|
-
handlers.onPointerOver == null ? void 0 : handlers.onPointerOver(data);
|
|
1256
|
-
handlers.onPointerEnter == null ? void 0 : handlers.onPointerEnter(data);
|
|
1257
|
-
} else if (hoveredItem.stopped) {
|
|
1258
|
-
// If the object was previously hovered and stopped, we shouldn't allow other items to proceed
|
|
1259
|
-
data.stopPropagation();
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
// Call mouse move
|
|
1263
|
-
handlers.onPointerMove == null ? void 0 : handlers.onPointerMove(data);
|
|
1264
|
-
} else {
|
|
1265
|
-
// All other events ...
|
|
1266
|
-
const handler = handlers[name];
|
|
1267
|
-
if (handler) {
|
|
1268
|
-
// Forward all events back to their respective handlers with the exception of click events,
|
|
1269
|
-
// which must use the initial target
|
|
1270
|
-
if (!isClickEvent || internal.initialHits.includes(eventObject)) {
|
|
1271
|
-
// Missed events have to come first
|
|
1272
|
-
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
1273
|
-
// Now call the handler
|
|
1274
|
-
handler(data);
|
|
1275
|
-
}
|
|
1276
|
-
} else {
|
|
1277
|
-
// Trigger onPointerMissed on all elements that have pointer over/out handlers, but not click and weren't hit
|
|
1278
|
-
if (isClickEvent && internal.initialHits.includes(eventObject)) {
|
|
1279
|
-
pointerMissed(event, internal.interaction.filter(object => !internal.initialHits.includes(object)));
|
|
1280
|
-
}
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
handleIntersects(hits, event, delta, onIntersect);
|
|
1285
|
-
};
|
|
1286
|
-
}
|
|
1287
|
-
return {
|
|
1288
|
-
handlePointer
|
|
1289
|
-
};
|
|
1290
|
-
}
|
|
1291
|
-
|
|
1292
|
-
const isRenderer = def => !!(def != null && def.render);
|
|
1293
|
-
const context = /*#__PURE__*/React.createContext(null);
|
|
1294
|
-
const createStore = (invalidate, advance) => {
|
|
1295
|
-
const rootStore = createWithEqualityFn((set, get) => {
|
|
1296
|
-
const position = new THREE.Vector3();
|
|
1297
|
-
const defaultTarget = new THREE.Vector3();
|
|
1298
|
-
const tempTarget = new THREE.Vector3();
|
|
1299
|
-
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
1300
|
-
const {
|
|
1301
|
-
width,
|
|
1302
|
-
height,
|
|
1303
|
-
top,
|
|
1304
|
-
left
|
|
1305
|
-
} = size;
|
|
1306
|
-
const aspect = width / height;
|
|
1307
|
-
if (target instanceof THREE.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
|
|
1308
|
-
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
1309
|
-
if (isOrthographicCamera(camera)) {
|
|
1310
|
-
return {
|
|
1311
|
-
width: width / camera.zoom,
|
|
1312
|
-
height: height / camera.zoom,
|
|
1313
|
-
top,
|
|
1314
|
-
left,
|
|
1315
|
-
factor: 1,
|
|
1316
|
-
distance,
|
|
1317
|
-
aspect
|
|
1318
|
-
};
|
|
1319
|
-
} else {
|
|
1320
|
-
const fov = camera.fov * Math.PI / 180; // convert vertical fov to radians
|
|
1321
|
-
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
1322
|
-
const w = h * (width / height);
|
|
1323
|
-
return {
|
|
1324
|
-
width: w,
|
|
1325
|
-
height: h,
|
|
1326
|
-
top,
|
|
1327
|
-
left,
|
|
1328
|
-
factor: width / w,
|
|
1329
|
-
distance,
|
|
1330
|
-
aspect
|
|
1331
|
-
};
|
|
1332
|
-
}
|
|
846
|
+
const isRenderer = def => !!(def != null && def.render);
|
|
847
|
+
const context = /* @__PURE__ */React.createContext(null);
|
|
848
|
+
const createStore = (invalidate, advance) => {
|
|
849
|
+
const rootStore = createWithEqualityFn((set, get) => {
|
|
850
|
+
const position = new THREE.Vector3();
|
|
851
|
+
const defaultTarget = new THREE.Vector3();
|
|
852
|
+
const tempTarget = new THREE.Vector3();
|
|
853
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
854
|
+
const {
|
|
855
|
+
width,
|
|
856
|
+
height,
|
|
857
|
+
top,
|
|
858
|
+
left
|
|
859
|
+
} = size;
|
|
860
|
+
const aspect = width / height;
|
|
861
|
+
if (target.isVector3) tempTarget.copy(target);else tempTarget.set(...target);
|
|
862
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
863
|
+
if (isOrthographicCamera(camera)) {
|
|
864
|
+
return {
|
|
865
|
+
width: width / camera.zoom,
|
|
866
|
+
height: height / camera.zoom,
|
|
867
|
+
top,
|
|
868
|
+
left,
|
|
869
|
+
factor: 1,
|
|
870
|
+
distance,
|
|
871
|
+
aspect
|
|
872
|
+
};
|
|
873
|
+
} else {
|
|
874
|
+
const fov = camera.fov * Math.PI / 180; // convert vertical fov to radians
|
|
875
|
+
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
876
|
+
const w = h * (width / height);
|
|
877
|
+
return {
|
|
878
|
+
width: w,
|
|
879
|
+
height: h,
|
|
880
|
+
top,
|
|
881
|
+
left,
|
|
882
|
+
factor: width / w,
|
|
883
|
+
distance,
|
|
884
|
+
aspect
|
|
885
|
+
};
|
|
886
|
+
}
|
|
1333
887
|
}
|
|
1334
888
|
let performanceTimeout = undefined;
|
|
1335
889
|
const setPerformanceCurrent = current => set(state => ({
|
|
@@ -1500,148 +1054,573 @@ const createStore = (invalidate, advance) => {
|
|
|
1500
1054
|
set
|
|
1501
1055
|
} = rootStore.getState();
|
|
1502
1056
|
|
|
1503
|
-
// Resize camera and renderer on changes to size and pixelratio
|
|
1504
|
-
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1505
|
-
oldSize = size;
|
|
1506
|
-
oldDpr = viewport.dpr;
|
|
1507
|
-
// Update camera & renderer
|
|
1508
|
-
updateCamera(camera, size);
|
|
1509
|
-
gl.setPixelRatio(viewport.dpr);
|
|
1510
|
-
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
1511
|
-
gl.setSize(size.width, size.height, updateStyle);
|
|
1057
|
+
// Resize camera and renderer on changes to size and pixelratio
|
|
1058
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1059
|
+
oldSize = size;
|
|
1060
|
+
oldDpr = viewport.dpr;
|
|
1061
|
+
// Update camera & renderer
|
|
1062
|
+
updateCamera(camera, size);
|
|
1063
|
+
gl.setPixelRatio(viewport.dpr);
|
|
1064
|
+
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
1065
|
+
gl.setSize(size.width, size.height, updateStyle);
|
|
1066
|
+
}
|
|
1067
|
+
|
|
1068
|
+
// Update viewport once the camera changes
|
|
1069
|
+
if (camera !== oldCamera) {
|
|
1070
|
+
oldCamera = camera;
|
|
1071
|
+
// Update viewport
|
|
1072
|
+
set(state => ({
|
|
1073
|
+
viewport: {
|
|
1074
|
+
...state.viewport,
|
|
1075
|
+
...state.viewport.getCurrentViewport(camera)
|
|
1076
|
+
}
|
|
1077
|
+
}));
|
|
1078
|
+
}
|
|
1079
|
+
});
|
|
1080
|
+
|
|
1081
|
+
// Invalidate on any change
|
|
1082
|
+
rootStore.subscribe(state => invalidate(state));
|
|
1083
|
+
|
|
1084
|
+
// Return root state
|
|
1085
|
+
return rootStore;
|
|
1086
|
+
};
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* Exposes an object's {@link Instance}.
|
|
1090
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useInstanceHandle
|
|
1091
|
+
*
|
|
1092
|
+
* **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
|
|
1093
|
+
*/
|
|
1094
|
+
function useInstanceHandle(ref) {
|
|
1095
|
+
const instance = React.useRef(null);
|
|
1096
|
+
React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
|
|
1097
|
+
return instance;
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
|
|
1102
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
|
|
1103
|
+
*/
|
|
1104
|
+
function useStore() {
|
|
1105
|
+
const store = React.useContext(context);
|
|
1106
|
+
if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
|
|
1107
|
+
return store;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
/**
|
|
1111
|
+
* Accesses R3F's internal state, containing renderer, canvas, scene, etc.
|
|
1112
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usethree
|
|
1113
|
+
*/
|
|
1114
|
+
function useThree(selector = state => state, equalityFn) {
|
|
1115
|
+
return useStore()(selector, equalityFn);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
/**
|
|
1119
|
+
* Executes a callback before render in a shared frame loop.
|
|
1120
|
+
* Can order effects with render priority or manually render with a positive priority.
|
|
1121
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useframe
|
|
1122
|
+
*/
|
|
1123
|
+
function useFrame(callback, renderPriority = 0) {
|
|
1124
|
+
const store = useStore();
|
|
1125
|
+
const subscribe = store.getState().internal.subscribe;
|
|
1126
|
+
// Memoize ref
|
|
1127
|
+
const ref = useMutableCallback(callback);
|
|
1128
|
+
// Subscribe on mount, unsubscribe on unmount
|
|
1129
|
+
useIsomorphicLayoutEffect(() => subscribe(ref, renderPriority, store), [renderPriority, subscribe, store]);
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
/**
|
|
1134
|
+
* Returns a node graph of an object with named nodes & materials.
|
|
1135
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usegraph
|
|
1136
|
+
*/
|
|
1137
|
+
function useGraph(object) {
|
|
1138
|
+
return React.useMemo(() => buildGraph(object), [object]);
|
|
1139
|
+
}
|
|
1140
|
+
const memoizedLoaders = new WeakMap();
|
|
1141
|
+
const isConstructor$1 = value => {
|
|
1142
|
+
var _value$prototype;
|
|
1143
|
+
return typeof value === 'function' && (value == null ? void 0 : (_value$prototype = value.prototype) == null ? void 0 : _value$prototype.constructor) === value;
|
|
1144
|
+
};
|
|
1145
|
+
function loadingFn(extensions, onProgress) {
|
|
1146
|
+
return function (Proto, ...input) {
|
|
1147
|
+
let loader;
|
|
1148
|
+
|
|
1149
|
+
// Construct and cache loader if constructor was passed
|
|
1150
|
+
if (isConstructor$1(Proto)) {
|
|
1151
|
+
loader = memoizedLoaders.get(Proto);
|
|
1152
|
+
if (!loader) {
|
|
1153
|
+
loader = new Proto();
|
|
1154
|
+
memoizedLoaders.set(Proto, loader);
|
|
1155
|
+
}
|
|
1156
|
+
} else {
|
|
1157
|
+
loader = Proto;
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Apply loader extensions
|
|
1161
|
+
if (extensions) extensions(loader);
|
|
1162
|
+
|
|
1163
|
+
// Go through the urls and load them
|
|
1164
|
+
return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => {
|
|
1165
|
+
if (isObject3D(data == null ? void 0 : data.scene)) Object.assign(data, buildGraph(data.scene));
|
|
1166
|
+
res(data);
|
|
1167
|
+
}, onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/**
|
|
1172
|
+
* Synchronously loads and caches assets with a three loader.
|
|
1173
|
+
*
|
|
1174
|
+
* Note: this hook's caller must be wrapped with `React.Suspense`
|
|
1175
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#useloader
|
|
1176
|
+
*/
|
|
1177
|
+
function useLoader(loader, input, extensions, onProgress) {
|
|
1178
|
+
// Use suspense to load async assets
|
|
1179
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
1180
|
+
const results = suspend(loadingFn(extensions, onProgress), [loader, ...keys], {
|
|
1181
|
+
equal: is.equ
|
|
1182
|
+
});
|
|
1183
|
+
// Return the object(s)
|
|
1184
|
+
return Array.isArray(input) ? results : results[0];
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Preloads an asset into cache as a side-effect.
|
|
1189
|
+
*/
|
|
1190
|
+
useLoader.preload = function (loader, input, extensions) {
|
|
1191
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
1192
|
+
return preload(loadingFn(extensions), [loader, ...keys]);
|
|
1193
|
+
};
|
|
1194
|
+
|
|
1195
|
+
/**
|
|
1196
|
+
* Removes a loaded asset from cache.
|
|
1197
|
+
*/
|
|
1198
|
+
useLoader.clear = function (loader, input) {
|
|
1199
|
+
const keys = Array.isArray(input) ? input : [input];
|
|
1200
|
+
return clear([loader, ...keys]);
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
// TODO: upstream to DefinitelyTyped for React 19
|
|
1204
|
+
// https://github.com/facebook/react/issues/28956
|
|
1205
|
+
|
|
1206
|
+
function createReconciler(config) {
|
|
1207
|
+
const reconciler = Reconciler(config);
|
|
1208
|
+
reconciler.injectIntoDevTools({
|
|
1209
|
+
bundleType: typeof process !== 'undefined' && process.env.NODE_ENV !== 'production' ? 1 : 0,
|
|
1210
|
+
rendererPackageName: '@react-three/fiber',
|
|
1211
|
+
version: React.version
|
|
1212
|
+
});
|
|
1213
|
+
return reconciler;
|
|
1214
|
+
}
|
|
1215
|
+
const NoEventPriority = 0;
|
|
1216
|
+
|
|
1217
|
+
// TODO: handle constructor overloads
|
|
1218
|
+
// https://github.com/pmndrs/react-three-fiber/pull/2931
|
|
1219
|
+
// https://github.com/microsoft/TypeScript/issues/37079
|
|
1220
|
+
|
|
1221
|
+
const catalogue = {};
|
|
1222
|
+
let i = 0;
|
|
1223
|
+
const isConstructor = object => typeof object === 'function';
|
|
1224
|
+
function extend(objects) {
|
|
1225
|
+
if (isConstructor(objects)) {
|
|
1226
|
+
const Component = `${i++}`;
|
|
1227
|
+
catalogue[Component] = objects;
|
|
1228
|
+
return Component;
|
|
1229
|
+
} else {
|
|
1230
|
+
Object.assign(catalogue, objects);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
function validateInstance(type, props) {
|
|
1234
|
+
// Get target from catalogue
|
|
1235
|
+
const name = `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
1236
|
+
const target = catalogue[name];
|
|
1237
|
+
|
|
1238
|
+
// Validate element target
|
|
1239
|
+
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`);
|
|
1240
|
+
|
|
1241
|
+
// Validate primitives
|
|
1242
|
+
if (type === 'primitive' && !props.object) throw new Error(`R3F: Primitives without 'object' are invalid!`);
|
|
1243
|
+
|
|
1244
|
+
// Throw if an object or literal was passed for args
|
|
1245
|
+
if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
|
|
1246
|
+
}
|
|
1247
|
+
function createInstance(type, props, root) {
|
|
1248
|
+
var _props$object;
|
|
1249
|
+
validateInstance(type, props);
|
|
1250
|
+
|
|
1251
|
+
// Regenerate the R3F instance for primitives to simulate a new object
|
|
1252
|
+
if (type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
|
|
1253
|
+
return prepare(props.object, root, type, props);
|
|
1254
|
+
}
|
|
1255
|
+
function hideInstance(instance) {
|
|
1256
|
+
if (!instance.isHidden) {
|
|
1257
|
+
var _instance$parent;
|
|
1258
|
+
if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
|
|
1259
|
+
detach(instance.parent, instance);
|
|
1260
|
+
} else if (isObject3D(instance.object)) {
|
|
1261
|
+
instance.object.visible = false;
|
|
1262
|
+
}
|
|
1263
|
+
instance.isHidden = true;
|
|
1264
|
+
invalidateInstance(instance);
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
function unhideInstance(instance) {
|
|
1268
|
+
if (instance.isHidden) {
|
|
1269
|
+
var _instance$parent2;
|
|
1270
|
+
if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
|
|
1271
|
+
attach(instance.parent, instance);
|
|
1272
|
+
} else if (isObject3D(instance.object) && instance.props.visible !== false) {
|
|
1273
|
+
instance.object.visible = true;
|
|
1512
1274
|
}
|
|
1275
|
+
instance.isHidden = false;
|
|
1276
|
+
invalidateInstance(instance);
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1513
1279
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1280
|
+
// https://github.com/facebook/react/issues/20271
|
|
1281
|
+
// This will make sure events and attach are only handled once when trees are complete
|
|
1282
|
+
function handleContainerEffects(parent, child, beforeChild) {
|
|
1283
|
+
// Bail if tree isn't mounted or parent is not a container.
|
|
1284
|
+
// This ensures that the tree is finalized and React won't discard results to Suspense
|
|
1285
|
+
const state = child.root.getState();
|
|
1286
|
+
if (!parent.parent && parent.object !== state.scene) return;
|
|
1287
|
+
|
|
1288
|
+
// Create & link object on first run
|
|
1289
|
+
if (!child.object) {
|
|
1290
|
+
var _child$props$object, _child$props$args;
|
|
1291
|
+
// Get target from catalogue
|
|
1292
|
+
const name = `${child.type[0].toUpperCase()}${child.type.slice(1)}`;
|
|
1293
|
+
const target = catalogue[name];
|
|
1294
|
+
|
|
1295
|
+
// Create object
|
|
1296
|
+
child.object = (_child$props$object = child.props.object) != null ? _child$props$object : new target(...((_child$props$args = child.props.args) != null ? _child$props$args : []));
|
|
1297
|
+
child.object.__r3f = child;
|
|
1298
|
+
|
|
1299
|
+
// Set initial props
|
|
1300
|
+
applyProps(child.object, child.props);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
// Append instance
|
|
1304
|
+
if (child.props.attach) {
|
|
1305
|
+
attach(parent, child);
|
|
1306
|
+
} else if (isObject3D(child.object) && isObject3D(parent.object)) {
|
|
1307
|
+
const childIndex = parent.object.children.indexOf(beforeChild == null ? void 0 : beforeChild.object);
|
|
1308
|
+
if (beforeChild && childIndex !== -1) {
|
|
1309
|
+
child.object.parent = parent.object;
|
|
1310
|
+
parent.object.children.splice(childIndex, 0, child.object);
|
|
1311
|
+
child.object.dispatchEvent({
|
|
1312
|
+
type: 'added'
|
|
1313
|
+
});
|
|
1314
|
+
// @ts-expect-error https://github.com/mrdoob/three.js/pull/16934
|
|
1315
|
+
parent.object.dispatchEvent({
|
|
1316
|
+
type: 'childadded',
|
|
1317
|
+
child: child.object
|
|
1318
|
+
});
|
|
1319
|
+
} else {
|
|
1320
|
+
parent.object.add(child.object);
|
|
1524
1321
|
}
|
|
1525
|
-
}
|
|
1322
|
+
}
|
|
1526
1323
|
|
|
1527
|
-
//
|
|
1528
|
-
|
|
1324
|
+
// Link subtree
|
|
1325
|
+
for (const childInstance of child.children) handleContainerEffects(child, childInstance);
|
|
1529
1326
|
|
|
1530
|
-
//
|
|
1531
|
-
|
|
1532
|
-
}
|
|
1327
|
+
// Tree was updated, request a frame
|
|
1328
|
+
invalidateInstance(child);
|
|
1329
|
+
}
|
|
1330
|
+
function appendChild(parent, child) {
|
|
1331
|
+
if (!child) return;
|
|
1533
1332
|
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
function useInstanceHandle(ref) {
|
|
1541
|
-
const instance = React.useRef(null);
|
|
1542
|
-
React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
|
|
1543
|
-
return instance;
|
|
1333
|
+
// Link instances
|
|
1334
|
+
child.parent = parent;
|
|
1335
|
+
parent.children.push(child);
|
|
1336
|
+
|
|
1337
|
+
// Attach tree once complete
|
|
1338
|
+
handleContainerEffects(parent, child);
|
|
1544
1339
|
}
|
|
1340
|
+
function insertBefore(parent, child, beforeChild) {
|
|
1341
|
+
if (!child || !beforeChild) return;
|
|
1545
1342
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1343
|
+
// Link instances
|
|
1344
|
+
child.parent = parent;
|
|
1345
|
+
const childIndex = parent.children.indexOf(beforeChild);
|
|
1346
|
+
if (childIndex !== -1) parent.children.splice(childIndex, 0, child);else parent.children.push(child);
|
|
1347
|
+
|
|
1348
|
+
// Attach tree once complete
|
|
1349
|
+
handleContainerEffects(parent, child, beforeChild);
|
|
1350
|
+
}
|
|
1351
|
+
function disposeOnIdle(object) {
|
|
1352
|
+
if (typeof object.dispose === 'function') {
|
|
1353
|
+
const handleDispose = () => {
|
|
1354
|
+
try {
|
|
1355
|
+
object.dispose();
|
|
1356
|
+
} catch {
|
|
1357
|
+
// no-op
|
|
1358
|
+
}
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
// In a testing environment, cleanup immediately
|
|
1362
|
+
if (typeof IS_REACT_ACT_ENVIRONMENT !== 'undefined') handleDispose();
|
|
1363
|
+
// Otherwise, using a real GPU so schedule cleanup to prevent stalls
|
|
1364
|
+
else unstable_scheduleCallback(unstable_IdlePriority, handleDispose);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
function removeChild(parent, child, dispose) {
|
|
1368
|
+
if (!child) return;
|
|
1369
|
+
|
|
1370
|
+
// Unlink instances
|
|
1371
|
+
child.parent = null;
|
|
1372
|
+
const childIndex = parent.children.indexOf(child);
|
|
1373
|
+
if (childIndex !== -1) parent.children.splice(childIndex, 1);
|
|
1374
|
+
|
|
1375
|
+
// Eagerly tear down tree
|
|
1376
|
+
if (child.props.attach) {
|
|
1377
|
+
detach(parent, child);
|
|
1378
|
+
} else if (isObject3D(child.object) && isObject3D(parent.object)) {
|
|
1379
|
+
parent.object.remove(child.object);
|
|
1380
|
+
removeInteractivity(findInitialRoot(child), child.object);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
// Allow objects to bail out of unmount disposal with dispose={null}
|
|
1384
|
+
const shouldDispose = child.props.dispose !== null && dispose !== false;
|
|
1385
|
+
|
|
1386
|
+
// Recursively remove instance children
|
|
1387
|
+
for (let i = child.children.length - 1; i >= 0; i--) {
|
|
1388
|
+
const node = child.children[i];
|
|
1389
|
+
removeChild(child, node, shouldDispose);
|
|
1390
|
+
}
|
|
1391
|
+
child.children.length = 0;
|
|
1392
|
+
|
|
1393
|
+
// Unlink instance object
|
|
1394
|
+
delete child.object.__r3f;
|
|
1395
|
+
|
|
1396
|
+
// Dispose object whenever the reconciler feels like it.
|
|
1397
|
+
// Never dispose of primitives because their state may be kept outside of React!
|
|
1398
|
+
// In order for an object to be able to dispose it
|
|
1399
|
+
// - has a dispose method
|
|
1400
|
+
// - cannot be a <primitive object={...} />
|
|
1401
|
+
// - cannot be a THREE.Scene, because three has broken its own API
|
|
1402
|
+
if (shouldDispose && child.type !== 'primitive' && child.object.type !== 'Scene') {
|
|
1403
|
+
disposeOnIdle(child.object);
|
|
1404
|
+
}
|
|
1405
|
+
|
|
1406
|
+
// Tree was updated, request a frame for top-level instance
|
|
1407
|
+
if (dispose === undefined) invalidateInstance(child);
|
|
1408
|
+
}
|
|
1409
|
+
function setFiberRef(fiber, publicInstance) {
|
|
1410
|
+
for (const _fiber of [fiber, fiber.alternate]) {
|
|
1411
|
+
if (_fiber !== null) {
|
|
1412
|
+
if (typeof _fiber.ref === 'function') {
|
|
1413
|
+
_fiber.refCleanup == null ? void 0 : _fiber.refCleanup();
|
|
1414
|
+
const cleanup = _fiber.ref(publicInstance);
|
|
1415
|
+
if (typeof cleanup === 'function') _fiber.refCleanup = cleanup;
|
|
1416
|
+
} else if (_fiber.ref) {
|
|
1417
|
+
_fiber.ref.current = publicInstance;
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1554
1421
|
}
|
|
1422
|
+
const reconstructed = [];
|
|
1423
|
+
function swapInstances() {
|
|
1424
|
+
// Detach instance
|
|
1425
|
+
for (const [instance] of reconstructed) {
|
|
1426
|
+
const parent = instance.parent;
|
|
1427
|
+
if (parent) {
|
|
1428
|
+
if (instance.props.attach) {
|
|
1429
|
+
detach(parent, instance);
|
|
1430
|
+
} else if (isObject3D(instance.object) && isObject3D(parent.object)) {
|
|
1431
|
+
parent.object.remove(instance.object);
|
|
1432
|
+
}
|
|
1433
|
+
for (const child of instance.children) {
|
|
1434
|
+
if (child.props.attach) {
|
|
1435
|
+
detach(instance, child);
|
|
1436
|
+
} else if (isObject3D(child.object) && isObject3D(instance.object)) {
|
|
1437
|
+
instance.object.remove(child.object);
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1555
1441
|
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
return useStore()(selector, equalityFn);
|
|
1562
|
-
}
|
|
1442
|
+
// If the old instance is hidden, we need to unhide it.
|
|
1443
|
+
// React assumes it can discard instances since they're pure for DOM.
|
|
1444
|
+
// This isn't true for us since our lifetimes are impure and longliving.
|
|
1445
|
+
// So, we manually check if an instance was hidden and unhide it.
|
|
1446
|
+
if (instance.isHidden) unhideInstance(instance);
|
|
1563
1447
|
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
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;
|
|
1577
|
-
}
|
|
1448
|
+
// Dispose of old object if able
|
|
1449
|
+
if (instance.object.__r3f) delete instance.object.__r3f;
|
|
1450
|
+
if (instance.type !== 'primitive') disposeOnIdle(instance.object);
|
|
1451
|
+
}
|
|
1578
1452
|
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
const
|
|
1587
|
-
const
|
|
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;
|
|
1453
|
+
// Update instance
|
|
1454
|
+
for (const [instance, props, fiber] of reconstructed) {
|
|
1455
|
+
instance.props = props;
|
|
1456
|
+
const parent = instance.parent;
|
|
1457
|
+
if (parent) {
|
|
1458
|
+
var _instance$props$objec, _instance$props$args;
|
|
1459
|
+
// Get target from catalogue
|
|
1460
|
+
const name = `${instance.type[0].toUpperCase()}${instance.type.slice(1)}`;
|
|
1461
|
+
const target = catalogue[name];
|
|
1594
1462
|
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1463
|
+
// Create object
|
|
1464
|
+
instance.object = (_instance$props$objec = instance.props.object) != null ? _instance$props$objec : new target(...((_instance$props$args = instance.props.args) != null ? _instance$props$args : []));
|
|
1465
|
+
instance.object.__r3f = instance;
|
|
1466
|
+
setFiberRef(fiber, instance.object);
|
|
1467
|
+
|
|
1468
|
+
// Set initial props
|
|
1469
|
+
applyProps(instance.object, instance.props);
|
|
1470
|
+
if (instance.props.attach) {
|
|
1471
|
+
attach(parent, instance);
|
|
1472
|
+
} else if (isObject3D(instance.object) && isObject3D(parent.object)) {
|
|
1473
|
+
parent.object.add(instance.object);
|
|
1601
1474
|
}
|
|
1602
|
-
|
|
1603
|
-
|
|
1475
|
+
for (const child of instance.children) {
|
|
1476
|
+
if (child.props.attach) {
|
|
1477
|
+
attach(instance, child);
|
|
1478
|
+
} else if (isObject3D(child.object) && isObject3D(instance.object)) {
|
|
1479
|
+
instance.object.add(child.object);
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// Tree was updated, request a frame
|
|
1484
|
+
invalidateInstance(instance);
|
|
1604
1485
|
}
|
|
1486
|
+
}
|
|
1487
|
+
reconstructed.length = 0;
|
|
1488
|
+
}
|
|
1605
1489
|
|
|
1606
|
-
|
|
1607
|
-
|
|
1490
|
+
// Don't handle text instances, make it no-op
|
|
1491
|
+
const handleTextInstance = () => {};
|
|
1492
|
+
const NO_CONTEXT = {};
|
|
1493
|
+
let currentUpdatePriority = NoEventPriority;
|
|
1608
1494
|
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1495
|
+
// https://github.com/facebook/react/blob/main/packages/react-reconciler/src/ReactFiberFlags.js
|
|
1496
|
+
const NoFlags = 0;
|
|
1497
|
+
const Update = 4;
|
|
1498
|
+
const reconciler = /* @__PURE__ */createReconciler({
|
|
1499
|
+
isPrimaryRenderer: false,
|
|
1500
|
+
warnsIfNotActing: false,
|
|
1501
|
+
supportsMutation: true,
|
|
1502
|
+
supportsPersistence: false,
|
|
1503
|
+
supportsHydration: false,
|
|
1504
|
+
createInstance,
|
|
1505
|
+
removeChild,
|
|
1506
|
+
appendChild,
|
|
1507
|
+
appendInitialChild: appendChild,
|
|
1508
|
+
insertBefore,
|
|
1509
|
+
appendChildToContainer(container, child) {
|
|
1510
|
+
const scene = container.getState().scene.__r3f;
|
|
1511
|
+
if (!child || !scene) return;
|
|
1512
|
+
appendChild(scene, child);
|
|
1513
|
+
},
|
|
1514
|
+
removeChildFromContainer(container, child) {
|
|
1515
|
+
const scene = container.getState().scene.__r3f;
|
|
1516
|
+
if (!child || !scene) return;
|
|
1517
|
+
removeChild(scene, child);
|
|
1518
|
+
},
|
|
1519
|
+
insertInContainerBefore(container, child, beforeChild) {
|
|
1520
|
+
const scene = container.getState().scene.__r3f;
|
|
1521
|
+
if (!child || !beforeChild || !scene) return;
|
|
1522
|
+
insertBefore(scene, child, beforeChild);
|
|
1523
|
+
},
|
|
1524
|
+
getRootHostContext: () => NO_CONTEXT,
|
|
1525
|
+
getChildHostContext: () => NO_CONTEXT,
|
|
1526
|
+
commitUpdate(instance, type, oldProps, newProps, fiber) {
|
|
1527
|
+
var _newProps$args, _oldProps$args, _newProps$args2;
|
|
1528
|
+
validateInstance(type, newProps);
|
|
1529
|
+
let reconstruct = false;
|
|
1613
1530
|
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
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
|
-
}
|
|
1531
|
+
// Reconstruct primitives if object prop changes
|
|
1532
|
+
if (instance.type === 'primitive' && oldProps.object !== newProps.object) reconstruct = true;
|
|
1533
|
+
// Reconstruct instance if args were added or removed
|
|
1534
|
+
else if (((_newProps$args = newProps.args) == null ? void 0 : _newProps$args.length) !== ((_oldProps$args = oldProps.args) == null ? void 0 : _oldProps$args.length)) reconstruct = true;
|
|
1535
|
+
// Reconstruct instance if args were changed
|
|
1536
|
+
else if ((_newProps$args2 = newProps.args) != null && _newProps$args2.some((value, index) => {
|
|
1537
|
+
var _oldProps$args2;
|
|
1538
|
+
return value !== ((_oldProps$args2 = oldProps.args) == null ? void 0 : _oldProps$args2[index]);
|
|
1539
|
+
})) reconstruct = true;
|
|
1629
1540
|
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1541
|
+
// Reconstruct when args or <primitive object={...} have changes
|
|
1542
|
+
if (reconstruct) {
|
|
1543
|
+
reconstructed.push([instance, {
|
|
1544
|
+
...newProps
|
|
1545
|
+
}, fiber]);
|
|
1546
|
+
} else {
|
|
1547
|
+
// Create a diff-set, flag if there are any changes
|
|
1548
|
+
const changedProps = diffProps(instance, newProps);
|
|
1549
|
+
if (Object.keys(changedProps).length) {
|
|
1550
|
+
Object.assign(instance.props, changedProps);
|
|
1551
|
+
applyProps(instance.object, changedProps);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1637
1554
|
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1555
|
+
// Flush reconstructed siblings when we hit the last updated child in a sequence
|
|
1556
|
+
const isTailSibling = fiber.sibling === null || (fiber.flags & Update) === NoFlags;
|
|
1557
|
+
if (isTailSibling) swapInstances();
|
|
1558
|
+
},
|
|
1559
|
+
finalizeInitialChildren: () => false,
|
|
1560
|
+
commitMount() {},
|
|
1561
|
+
getPublicInstance: instance => instance == null ? void 0 : instance.object,
|
|
1562
|
+
prepareForCommit: () => null,
|
|
1563
|
+
preparePortalMount: container => prepare(container.getState().scene, container, '', {}),
|
|
1564
|
+
resetAfterCommit: () => {},
|
|
1565
|
+
shouldSetTextContent: () => false,
|
|
1566
|
+
clearContainer: () => false,
|
|
1567
|
+
hideInstance,
|
|
1568
|
+
unhideInstance,
|
|
1569
|
+
createTextInstance: handleTextInstance,
|
|
1570
|
+
hideTextInstance: handleTextInstance,
|
|
1571
|
+
unhideTextInstance: handleTextInstance,
|
|
1572
|
+
scheduleTimeout: typeof setTimeout === 'function' ? setTimeout : undefined,
|
|
1573
|
+
cancelTimeout: typeof clearTimeout === 'function' ? clearTimeout : undefined,
|
|
1574
|
+
noTimeout: -1,
|
|
1575
|
+
getInstanceFromNode: () => null,
|
|
1576
|
+
beforeActiveInstanceBlur() {},
|
|
1577
|
+
afterActiveInstanceBlur() {},
|
|
1578
|
+
detachDeletedInstance() {},
|
|
1579
|
+
prepareScopeUpdate() {},
|
|
1580
|
+
getInstanceFromScope: () => null,
|
|
1581
|
+
shouldAttemptEagerTransition: () => false,
|
|
1582
|
+
trackSchedulerEvent: () => {},
|
|
1583
|
+
resolveEventType: () => null,
|
|
1584
|
+
resolveEventTimeStamp: () => -1.1,
|
|
1585
|
+
requestPostPaintCallback() {},
|
|
1586
|
+
maySuspendCommit: () => false,
|
|
1587
|
+
preloadInstance: () => true,
|
|
1588
|
+
// true indicates already loaded
|
|
1589
|
+
startSuspendingCommit() {},
|
|
1590
|
+
suspendInstance() {},
|
|
1591
|
+
waitForCommitToBeReady: () => null,
|
|
1592
|
+
NotPendingTransition: null,
|
|
1593
|
+
HostTransitionContext: /* @__PURE__ */React.createContext(null),
|
|
1594
|
+
setCurrentUpdatePriority(newPriority) {
|
|
1595
|
+
currentUpdatePriority = newPriority;
|
|
1596
|
+
},
|
|
1597
|
+
getCurrentUpdatePriority() {
|
|
1598
|
+
return currentUpdatePriority;
|
|
1599
|
+
},
|
|
1600
|
+
resolveUpdatePriority() {
|
|
1601
|
+
var _window$event;
|
|
1602
|
+
if (currentUpdatePriority !== NoEventPriority) return currentUpdatePriority;
|
|
1603
|
+
switch (typeof window !== 'undefined' && ((_window$event = window.event) == null ? void 0 : _window$event.type)) {
|
|
1604
|
+
case 'click':
|
|
1605
|
+
case 'contextmenu':
|
|
1606
|
+
case 'dblclick':
|
|
1607
|
+
case 'pointercancel':
|
|
1608
|
+
case 'pointerdown':
|
|
1609
|
+
case 'pointerup':
|
|
1610
|
+
return DiscreteEventPriority;
|
|
1611
|
+
case 'pointermove':
|
|
1612
|
+
case 'pointerout':
|
|
1613
|
+
case 'pointerover':
|
|
1614
|
+
case 'pointerenter':
|
|
1615
|
+
case 'pointerleave':
|
|
1616
|
+
case 'wheel':
|
|
1617
|
+
return ContinuousEventPriority;
|
|
1618
|
+
default:
|
|
1619
|
+
return DefaultEventPriority;
|
|
1620
|
+
}
|
|
1621
|
+
},
|
|
1622
|
+
resetFormInstance() {}
|
|
1623
|
+
});
|
|
1645
1624
|
|
|
1646
1625
|
const _roots = new Map();
|
|
1647
1626
|
const shallowLoose = {
|
|
@@ -1660,7 +1639,7 @@ const createRendererInstance = (gl, canvas) => {
|
|
|
1660
1639
|
});
|
|
1661
1640
|
};
|
|
1662
1641
|
function computeInitialSize(canvas, size) {
|
|
1663
|
-
if (!size && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
|
|
1642
|
+
if (!size && typeof HTMLCanvasElement !== 'undefined' && canvas instanceof HTMLCanvasElement && canvas.parentElement) {
|
|
1664
1643
|
const {
|
|
1665
1644
|
width,
|
|
1666
1645
|
height,
|
|
@@ -1790,7 +1769,7 @@ function createRoot(canvas) {
|
|
|
1790
1769
|
// Create default camera, don't overwrite any user-set state
|
|
1791
1770
|
if (!state.camera || state.camera === lastCamera && !is.equ(lastCamera, cameraOptions, shallowLoose)) {
|
|
1792
1771
|
lastCamera = cameraOptions;
|
|
1793
|
-
const isCamera = cameraOptions
|
|
1772
|
+
const isCamera = cameraOptions == null ? void 0 : cameraOptions.isCamera;
|
|
1794
1773
|
const camera = isCamera ? cameraOptions : orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
|
|
1795
1774
|
if (!isCamera) {
|
|
1796
1775
|
camera.position.z = 5;
|
|
@@ -1820,7 +1799,7 @@ function createRoot(canvas) {
|
|
|
1820
1799
|
// Set up scene (one time only!)
|
|
1821
1800
|
if (!state.scene) {
|
|
1822
1801
|
let scene;
|
|
1823
|
-
if (sceneOptions
|
|
1802
|
+
if (sceneOptions != null && sceneOptions.isScene) {
|
|
1824
1803
|
scene = sceneOptions;
|
|
1825
1804
|
prepare(scene, store, '', {});
|
|
1826
1805
|
} else {
|
|
@@ -1835,6 +1814,7 @@ function createRoot(canvas) {
|
|
|
1835
1814
|
|
|
1836
1815
|
// Set up XR (one time only!)
|
|
1837
1816
|
if (!state.xr) {
|
|
1817
|
+
var _gl$xr;
|
|
1838
1818
|
// Handle frame behavior in WebXR
|
|
1839
1819
|
const handleXRFrame = (timestamp, frame) => {
|
|
1840
1820
|
const state = store.getState();
|
|
@@ -1865,7 +1845,7 @@ function createRoot(canvas) {
|
|
|
1865
1845
|
};
|
|
1866
1846
|
|
|
1867
1847
|
// Subscribe to WebXR session events
|
|
1868
|
-
if (gl.xr) xr.connect();
|
|
1848
|
+
if (typeof ((_gl$xr = gl.xr) == null ? void 0 : _gl$xr.addEventListener) === 'function') xr.connect();
|
|
1869
1849
|
state.set({
|
|
1870
1850
|
xr
|
|
1871
1851
|
});
|
|
@@ -1892,22 +1872,12 @@ function createRoot(canvas) {
|
|
|
1892
1872
|
}
|
|
1893
1873
|
if (oldEnabled !== gl.shadowMap.enabled || oldType !== gl.shadowMap.type) gl.shadowMap.needsUpdate = true;
|
|
1894
1874
|
}
|
|
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
|
-
}
|
|
1875
|
+
THREE.ColorManagement.enabled = !legacy;
|
|
1902
1876
|
|
|
1903
1877
|
// Set color space and tonemapping preferences
|
|
1904
1878
|
if (!configured) {
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
applyProps(gl, {
|
|
1908
|
-
outputEncoding: linear ? LinearEncoding : sRGBEncoding,
|
|
1909
|
-
toneMapping: flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping
|
|
1910
|
-
});
|
|
1879
|
+
gl.outputColorSpace = linear ? THREE.LinearSRGBColorSpace : THREE.SRGBColorSpace;
|
|
1880
|
+
gl.toneMapping = flat ? THREE.NoToneMapping : THREE.ACESFilmicToneMapping;
|
|
1911
1881
|
}
|
|
1912
1882
|
|
|
1913
1883
|
// Update color management state
|
|
@@ -2119,11 +2089,6 @@ function Portal({
|
|
|
2119
2089
|
}), usePortalStore, null)
|
|
2120
2090
|
});
|
|
2121
2091
|
}
|
|
2122
|
-
reconciler.injectIntoDevTools({
|
|
2123
|
-
bundleType: process.env.NODE_ENV === 'production' ? 0 : 1,
|
|
2124
|
-
rendererPackageName: '@react-three/fiber',
|
|
2125
|
-
version: React.version
|
|
2126
|
-
});
|
|
2127
2092
|
|
|
2128
2093
|
function createSubs(callback, subs) {
|
|
2129
2094
|
const sub = {
|