@react-three/fiber 9.0.0-alpha.4 → 9.0.0-alpha.5
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 +6 -0
- package/dist/declarations/src/core/hooks.d.ts +7 -3
- package/dist/declarations/src/core/index.d.ts +1 -1
- package/dist/declarations/src/core/reconciler.d.ts +1 -0
- package/dist/declarations/src/core/store.d.ts +16 -2
- package/dist/{loop-7f1fb121.cjs.dev.js → loop-02cefb27.cjs.dev.js} +173 -32
- package/dist/{loop-7224f71f.cjs.prod.js → loop-1fad3b6f.cjs.prod.js} +173 -32
- package/dist/{loop-bdf826ba.esm.js → loop-d73c6316.esm.js} +173 -32
- package/dist/react-three-fiber.cjs.dev.js +1 -1
- package/dist/react-three-fiber.cjs.prod.js +1 -1
- package/dist/react-three-fiber.esm.js +2 -2
- package/native/dist/react-three-fiber-native.cjs.dev.js +1 -1
- package/native/dist/react-three-fiber-native.cjs.prod.js +1 -1
- package/native/dist/react-three-fiber-native.esm.js +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,11 +5,15 @@ import { ObjectMap } from "./utils.js";
|
|
|
5
5
|
import type { Instance } from "./reconciler.js";
|
|
6
6
|
/**
|
|
7
7
|
* Exposes an object's {@link Instance}.
|
|
8
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#
|
|
8
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
|
|
9
9
|
*
|
|
10
10
|
* **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
|
|
11
11
|
*/
|
|
12
|
-
export declare function useInstanceHandle<
|
|
12
|
+
export declare function useInstanceHandle<T>(ref: React.RefObject<T>): React.RefObject<Instance<T>>;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
|
|
15
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
|
|
16
|
+
*/
|
|
13
17
|
export declare function useStore(): RootStore;
|
|
14
18
|
/**
|
|
15
19
|
* Accesses R3F's internal state, containing renderer, canvas, scene, etc.
|
|
@@ -24,7 +28,7 @@ export declare function useThree<T = RootState>(selector?: (state: RootState) =>
|
|
|
24
28
|
export declare function useFrame(callback: RenderCallback, renderPriority?: number): null;
|
|
25
29
|
/**
|
|
26
30
|
* Executes a callback in a given update stage.
|
|
27
|
-
* Uses the stage instance to
|
|
31
|
+
* Uses the stage instance to identify which stage to target in the lifecycle.
|
|
28
32
|
*/
|
|
29
33
|
export declare function useUpdate(callback: UpdateCallback, stage?: StageTypes): void;
|
|
30
34
|
/**
|
|
@@ -9,7 +9,7 @@ export type { ReconcilerRoot, GLProps, CameraProps, RenderProps, InjectState } f
|
|
|
9
9
|
export { _roots, render, createRoot, unmountComponentAtNode, createPortal } from "./renderer.js";
|
|
10
10
|
export type { UpdateSubscription } from "./stages.js";
|
|
11
11
|
export { Stage, FixedStage, Stages } from "./stages.js";
|
|
12
|
-
export type { Subscription, Dpr, Size, RenderCallback, UpdateCallback, LegacyAlways, FrameloopMode, FrameloopRender, FrameloopLegacy, Frameloop, Performance, Renderer, StageTypes, XRManager, RootState, RootStore, } from "./store.js";
|
|
12
|
+
export type { Subscription, Dpr, Size, Viewport, RenderCallback, UpdateCallback, LegacyAlways, FrameloopMode, FrameloopRender, FrameloopLegacy, Frameloop, Performance, Renderer, StageTypes, XRManager, RootState, RootStore, } from "./store.js";
|
|
13
13
|
export { context } from "./store.js";
|
|
14
14
|
export type { ObjectMap, Camera, Disposable, Act } from "./utils.js";
|
|
15
15
|
export { applyProps, getRootState, dispose, act, buildGraph } from "./utils.js";
|
|
@@ -21,6 +21,18 @@ export interface Size {
|
|
|
21
21
|
top: number;
|
|
22
22
|
left: number;
|
|
23
23
|
}
|
|
24
|
+
export interface Viewport extends Size {
|
|
25
|
+
/** The initial pixel ratio */
|
|
26
|
+
initialDpr: number;
|
|
27
|
+
/** Current pixel ratio */
|
|
28
|
+
dpr: number;
|
|
29
|
+
/** size.width / viewport.width */
|
|
30
|
+
factor: number;
|
|
31
|
+
/** Camera distance */
|
|
32
|
+
distance: number;
|
|
33
|
+
/** Camera aspect ratio: width / height */
|
|
34
|
+
aspect: number;
|
|
35
|
+
}
|
|
24
36
|
export type RenderCallback = (state: RootState, delta: number, frame?: XRFrame) => void;
|
|
25
37
|
export type UpdateCallback = RenderCallback;
|
|
26
38
|
export type LegacyAlways = 'always';
|
|
@@ -106,10 +118,12 @@ export interface RootState {
|
|
|
106
118
|
frameloop: FrameloopLegacy;
|
|
107
119
|
/** Adaptive performance interface */
|
|
108
120
|
performance: Performance;
|
|
109
|
-
/** The current pixel ratio */
|
|
110
|
-
dpr: number;
|
|
111
121
|
/** Reactive pixel-size of the canvas */
|
|
112
122
|
size: Size;
|
|
123
|
+
/** Reactive size of the viewport in threejs units */
|
|
124
|
+
viewport: Viewport & {
|
|
125
|
+
getCurrentViewport: (camera?: Camera, target?: THREE.Vector3 | Parameters<THREE.Vector3['set']>, size?: Size) => Omit<Viewport, 'dpr' | 'initialDpr'>;
|
|
126
|
+
};
|
|
113
127
|
/** Flags the canvas for render, but doesn't render in itself */
|
|
114
128
|
invalidate: (frames?: number) => void;
|
|
115
129
|
/** Advance (render) one step */
|
|
@@ -60,7 +60,8 @@ const extend = objects => {
|
|
|
60
60
|
return void Object.assign(catalogue, objects);
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
-
function createInstance(type, props, root) {
|
|
63
|
+
function createInstance(type, props, root, flushPrimitive = true) {
|
|
64
|
+
var _props$object;
|
|
64
65
|
// Get target from catalogue
|
|
65
66
|
const name = `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
66
67
|
const target = catalogue[name];
|
|
@@ -74,10 +75,37 @@ function createInstance(type, props, root) {
|
|
|
74
75
|
// Throw if an object or literal was passed for args
|
|
75
76
|
if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
|
|
76
77
|
|
|
78
|
+
// Regenerate the R3F instance for primitives to simulate a new object
|
|
79
|
+
if (flushPrimitive && type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
|
|
80
|
+
|
|
77
81
|
// Create instance
|
|
78
82
|
const instance = prepare(props.object, root, type, props);
|
|
79
83
|
return instance;
|
|
80
84
|
}
|
|
85
|
+
function hideInstance(instance) {
|
|
86
|
+
if (!instance.isHidden) {
|
|
87
|
+
var _instance$parent;
|
|
88
|
+
if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
|
|
89
|
+
detach(instance.parent, instance);
|
|
90
|
+
} else if (isObject3D(instance.object)) {
|
|
91
|
+
instance.object.visible = false;
|
|
92
|
+
}
|
|
93
|
+
instance.isHidden = true;
|
|
94
|
+
invalidateInstance(instance);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function unhideInstance(instance) {
|
|
98
|
+
if (instance.isHidden) {
|
|
99
|
+
var _instance$parent2;
|
|
100
|
+
if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
|
|
101
|
+
attach(instance.parent, instance);
|
|
102
|
+
} else if (isObject3D(instance.object) && instance.props.visible !== false) {
|
|
103
|
+
instance.object.visible = true;
|
|
104
|
+
}
|
|
105
|
+
instance.isHidden = false;
|
|
106
|
+
invalidateInstance(instance);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
81
109
|
|
|
82
110
|
// https://github.com/facebook/react/issues/20271
|
|
83
111
|
// This will make sure events and attach are only handled once when trees are complete
|
|
@@ -213,8 +241,19 @@ function setFiberInstance(fiber, instance) {
|
|
|
213
241
|
}
|
|
214
242
|
}
|
|
215
243
|
function switchInstance(oldInstance, type, props, fiber) {
|
|
244
|
+
// If the old instance is hidden, we need to unhide it.
|
|
245
|
+
// React assumes it can discard instances since they're pure for DOM.
|
|
246
|
+
// This isn't true for us since our lifetimes are impure and longliving.
|
|
247
|
+
// So, we manually check if an instance was hidden and unhide it.
|
|
248
|
+
if (oldInstance.isHidden) unhideInstance(oldInstance);
|
|
249
|
+
|
|
216
250
|
// Create a new instance
|
|
217
|
-
const newInstance = createInstance(type, props, oldInstance.root);
|
|
251
|
+
const newInstance = createInstance(type, props, oldInstance.root, false);
|
|
252
|
+
|
|
253
|
+
// Update attach props for primitives since we don't flush them
|
|
254
|
+
if (type === 'primitive') {
|
|
255
|
+
newInstance.props.attach = props.attach;
|
|
256
|
+
}
|
|
218
257
|
|
|
219
258
|
// Move children to new instance
|
|
220
259
|
for (const child of oldInstance.children) {
|
|
@@ -262,7 +301,9 @@ const reconciler = Reconciler__default["default"]({
|
|
|
262
301
|
supportsMutation: true,
|
|
263
302
|
supportsPersistence: false,
|
|
264
303
|
supportsHydration: false,
|
|
265
|
-
createInstance,
|
|
304
|
+
createInstance(type, props, root) {
|
|
305
|
+
return createInstance(type, props, root);
|
|
306
|
+
},
|
|
266
307
|
removeChild,
|
|
267
308
|
appendChild,
|
|
268
309
|
appendInitialChild: appendChild,
|
|
@@ -319,8 +360,8 @@ const reconciler = Reconciler__default["default"]({
|
|
|
319
360
|
resetAfterCommit: () => {},
|
|
320
361
|
shouldSetTextContent: () => false,
|
|
321
362
|
clearContainer: () => false,
|
|
322
|
-
hideInstance
|
|
323
|
-
unhideInstance
|
|
363
|
+
hideInstance,
|
|
364
|
+
unhideInstance,
|
|
324
365
|
createTextInstance: handleTextInstance,
|
|
325
366
|
hideTextInstance: handleTextInstance,
|
|
326
367
|
unhideTextInstance: handleTextInstance,
|
|
@@ -575,7 +616,8 @@ function prepare(target, root, type, props) {
|
|
|
575
616
|
props: getInstanceProps(props),
|
|
576
617
|
object,
|
|
577
618
|
eventCount: 0,
|
|
578
|
-
handlers: {}
|
|
619
|
+
handlers: {},
|
|
620
|
+
isHidden: false
|
|
579
621
|
};
|
|
580
622
|
if (object) {
|
|
581
623
|
object.__r3f = instance;
|
|
@@ -1247,6 +1289,44 @@ const isRenderer = def => !!(def != null && def.render);
|
|
|
1247
1289
|
const context = /*#__PURE__*/React__namespace.createContext(null);
|
|
1248
1290
|
const createStore = (invalidate, advance) => {
|
|
1249
1291
|
const rootStore = traditional.createWithEqualityFn((set, get) => {
|
|
1292
|
+
const position = new THREE__namespace.Vector3();
|
|
1293
|
+
const defaultTarget = new THREE__namespace.Vector3();
|
|
1294
|
+
const tempTarget = new THREE__namespace.Vector3();
|
|
1295
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
1296
|
+
const {
|
|
1297
|
+
width,
|
|
1298
|
+
height,
|
|
1299
|
+
top,
|
|
1300
|
+
left
|
|
1301
|
+
} = size;
|
|
1302
|
+
const aspect = width / height;
|
|
1303
|
+
if (target instanceof THREE__namespace.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
|
|
1304
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
1305
|
+
if (isOrthographicCamera(camera)) {
|
|
1306
|
+
return {
|
|
1307
|
+
width: width / camera.zoom,
|
|
1308
|
+
height: height / camera.zoom,
|
|
1309
|
+
top,
|
|
1310
|
+
left,
|
|
1311
|
+
factor: 1,
|
|
1312
|
+
distance,
|
|
1313
|
+
aspect
|
|
1314
|
+
};
|
|
1315
|
+
} else {
|
|
1316
|
+
const fov = camera.fov * Math.PI / 180; // convert vertical fov to radians
|
|
1317
|
+
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
1318
|
+
const w = h * (width / height);
|
|
1319
|
+
return {
|
|
1320
|
+
width: w,
|
|
1321
|
+
height: h,
|
|
1322
|
+
top,
|
|
1323
|
+
left,
|
|
1324
|
+
factor: width / w,
|
|
1325
|
+
distance,
|
|
1326
|
+
aspect
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1250
1330
|
let performanceTimeout = undefined;
|
|
1251
1331
|
const setPerformanceCurrent = current => set(state => ({
|
|
1252
1332
|
performance: {
|
|
@@ -1295,13 +1375,24 @@ const createStore = (invalidate, advance) => {
|
|
|
1295
1375
|
performanceTimeout = setTimeout(() => setPerformanceCurrent(get().performance.max), state.performance.debounce);
|
|
1296
1376
|
}
|
|
1297
1377
|
},
|
|
1298
|
-
dpr: 1,
|
|
1299
1378
|
size: {
|
|
1300
1379
|
width: 0,
|
|
1301
1380
|
height: 0,
|
|
1302
1381
|
top: 0,
|
|
1303
1382
|
left: 0
|
|
1304
1383
|
},
|
|
1384
|
+
viewport: {
|
|
1385
|
+
initialDpr: 0,
|
|
1386
|
+
dpr: 0,
|
|
1387
|
+
width: 0,
|
|
1388
|
+
height: 0,
|
|
1389
|
+
top: 0,
|
|
1390
|
+
left: 0,
|
|
1391
|
+
aspect: 0,
|
|
1392
|
+
distance: 0,
|
|
1393
|
+
factor: 0,
|
|
1394
|
+
getCurrentViewport
|
|
1395
|
+
},
|
|
1305
1396
|
setEvents: events => set(state => ({
|
|
1306
1397
|
...state,
|
|
1307
1398
|
events: {
|
|
@@ -1310,17 +1401,30 @@ const createStore = (invalidate, advance) => {
|
|
|
1310
1401
|
}
|
|
1311
1402
|
})),
|
|
1312
1403
|
setSize: (width, height, top = 0, left = 0) => {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1404
|
+
const camera = get().camera;
|
|
1405
|
+
const size = {
|
|
1406
|
+
width,
|
|
1407
|
+
height,
|
|
1408
|
+
top,
|
|
1409
|
+
left
|
|
1410
|
+
};
|
|
1411
|
+
set(state => ({
|
|
1412
|
+
size,
|
|
1413
|
+
viewport: {
|
|
1414
|
+
...state.viewport,
|
|
1415
|
+
...getCurrentViewport(camera, defaultTarget, size)
|
|
1319
1416
|
}
|
|
1320
|
-
});
|
|
1417
|
+
}));
|
|
1321
1418
|
},
|
|
1322
|
-
setDpr: dpr => set({
|
|
1323
|
-
|
|
1419
|
+
setDpr: dpr => set(state => {
|
|
1420
|
+
const resolved = calculateDpr(dpr);
|
|
1421
|
+
return {
|
|
1422
|
+
viewport: {
|
|
1423
|
+
...state.viewport,
|
|
1424
|
+
dpr: resolved,
|
|
1425
|
+
initialDpr: state.viewport.initialDpr || resolved
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1324
1428
|
}),
|
|
1325
1429
|
setFrameloop: frameloop => {
|
|
1326
1430
|
var _frameloop$mode, _frameloop$render, _frameloop$maxDelta;
|
|
@@ -1409,23 +1513,39 @@ const createStore = (invalidate, advance) => {
|
|
|
1409
1513
|
});
|
|
1410
1514
|
const state = rootStore.getState();
|
|
1411
1515
|
let oldSize = state.size;
|
|
1412
|
-
let oldDpr = state.dpr;
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1516
|
+
let oldDpr = state.viewport.dpr;
|
|
1517
|
+
let oldCamera = state.camera;
|
|
1518
|
+
rootStore.subscribe(() => {
|
|
1519
|
+
const {
|
|
1520
|
+
camera,
|
|
1521
|
+
size,
|
|
1522
|
+
viewport,
|
|
1523
|
+
gl,
|
|
1524
|
+
set
|
|
1525
|
+
} = rootStore.getState();
|
|
1526
|
+
|
|
1419
1527
|
// Resize camera and renderer on changes to size and pixelratio
|
|
1420
|
-
if (size !== oldSize || dpr !== oldDpr) {
|
|
1528
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1421
1529
|
oldSize = size;
|
|
1422
|
-
oldDpr = dpr;
|
|
1530
|
+
oldDpr = viewport.dpr;
|
|
1423
1531
|
// Update camera & renderer
|
|
1424
1532
|
updateCamera(camera, size);
|
|
1425
|
-
gl.setPixelRatio(dpr);
|
|
1533
|
+
gl.setPixelRatio(viewport.dpr);
|
|
1426
1534
|
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
1427
1535
|
gl.setSize(size.width, size.height, updateStyle);
|
|
1428
1536
|
}
|
|
1537
|
+
|
|
1538
|
+
// Update viewport once the camera changes
|
|
1539
|
+
if (camera !== oldCamera) {
|
|
1540
|
+
oldCamera = camera;
|
|
1541
|
+
// Update viewport
|
|
1542
|
+
set(state => ({
|
|
1543
|
+
viewport: {
|
|
1544
|
+
...state.viewport,
|
|
1545
|
+
...state.viewport.getCurrentViewport(camera)
|
|
1546
|
+
}
|
|
1547
|
+
}));
|
|
1548
|
+
}
|
|
1429
1549
|
});
|
|
1430
1550
|
|
|
1431
1551
|
// Invalidate on any change
|
|
@@ -1570,15 +1690,20 @@ const Lifecycle = [Early, Fixed, Update, Late, Render, After];
|
|
|
1570
1690
|
|
|
1571
1691
|
/**
|
|
1572
1692
|
* Exposes an object's {@link Instance}.
|
|
1573
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#
|
|
1693
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
|
|
1574
1694
|
*
|
|
1575
1695
|
* **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
|
|
1576
1696
|
*/
|
|
1577
1697
|
function useInstanceHandle(ref) {
|
|
1578
1698
|
const instance = React__namespace.useRef(null);
|
|
1579
|
-
|
|
1699
|
+
React__namespace.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
|
|
1580
1700
|
return instance;
|
|
1581
1701
|
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
|
|
1705
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
|
|
1706
|
+
*/
|
|
1582
1707
|
function useStore() {
|
|
1583
1708
|
const store = React__namespace.useContext(context);
|
|
1584
1709
|
if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
|
|
@@ -1610,7 +1735,7 @@ function useFrame(callback, renderPriority = 0) {
|
|
|
1610
1735
|
|
|
1611
1736
|
/**
|
|
1612
1737
|
* Executes a callback in a given update stage.
|
|
1613
|
-
* Uses the stage instance to
|
|
1738
|
+
* Uses the stage instance to identify which stage to target in the lifecycle.
|
|
1614
1739
|
*/
|
|
1615
1740
|
function useUpdate(callback, stage = Stages.Update) {
|
|
1616
1741
|
const store = useStore();
|
|
@@ -1657,6 +1782,7 @@ function loadingFn(extensions, onProgress) {
|
|
|
1657
1782
|
return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => res(isObject3D(data == null ? void 0 : data.scene) ? Object.assign(data, buildGraph(data.scene)) : data), onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
|
|
1658
1783
|
};
|
|
1659
1784
|
}
|
|
1785
|
+
|
|
1660
1786
|
/**
|
|
1661
1787
|
* Synchronously loads and caches assets with a three loader.
|
|
1662
1788
|
*
|
|
@@ -1880,7 +2006,15 @@ function createRoot(canvas) {
|
|
|
1880
2006
|
const camera = isCamera ? cameraOptions : orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
|
|
1881
2007
|
if (!isCamera) {
|
|
1882
2008
|
camera.position.z = 5;
|
|
1883
|
-
if (cameraOptions)
|
|
2009
|
+
if (cameraOptions) {
|
|
2010
|
+
applyProps(camera, cameraOptions);
|
|
2011
|
+
// Preserve user-defined frustum if possible
|
|
2012
|
+
// https://github.com/pmndrs/react-three-fiber/issues/3160
|
|
2013
|
+
if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
|
|
2014
|
+
camera.manual = true;
|
|
2015
|
+
camera.updateProjectionMatrix();
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
1884
2018
|
// Always look at center by default
|
|
1885
2019
|
if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
|
|
1886
2020
|
}
|
|
@@ -2009,7 +2143,7 @@ function createRoot(canvas) {
|
|
|
2009
2143
|
state.setSize(size.width, size.height, size.top, size.left);
|
|
2010
2144
|
}
|
|
2011
2145
|
// Check pixelratio
|
|
2012
|
-
if (dpr && state.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
2146
|
+
if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
2013
2147
|
// Check frameloop
|
|
2014
2148
|
if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
|
|
2015
2149
|
// Check pointer missed
|
|
@@ -2132,8 +2266,11 @@ function Portal({
|
|
|
2132
2266
|
const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
|
|
2133
2267
|
const [pointer] = React__namespace.useState(() => new THREE__namespace.Vector2());
|
|
2134
2268
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
2269
|
+
let viewport;
|
|
2135
2270
|
if (injectState.camera && size) {
|
|
2136
2271
|
const camera = injectState.camera;
|
|
2272
|
+
// Calculate the override viewport, if present
|
|
2273
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size);
|
|
2137
2274
|
// Update the portal camera, if it differs from the previous layer
|
|
2138
2275
|
if (camera !== rootState.camera) updateCamera(camera, size);
|
|
2139
2276
|
}
|
|
@@ -2149,7 +2286,7 @@ function Portal({
|
|
|
2149
2286
|
mouse: pointer,
|
|
2150
2287
|
// Their previous root is the layer before it
|
|
2151
2288
|
previousRoot,
|
|
2152
|
-
// Events and
|
|
2289
|
+
// Events, size and viewport can be overridden by the inject layer
|
|
2153
2290
|
events: {
|
|
2154
2291
|
...rootState.events,
|
|
2155
2292
|
...injectState.events,
|
|
@@ -2159,6 +2296,10 @@ function Portal({
|
|
|
2159
2296
|
...rootState.size,
|
|
2160
2297
|
...size
|
|
2161
2298
|
},
|
|
2299
|
+
viewport: {
|
|
2300
|
+
...rootState.viewport,
|
|
2301
|
+
...viewport
|
|
2302
|
+
},
|
|
2162
2303
|
// Layers are allowed to override events
|
|
2163
2304
|
setEvents: events => injectState.set(state => ({
|
|
2164
2305
|
...state,
|
|
@@ -60,7 +60,8 @@ const extend = objects => {
|
|
|
60
60
|
return void Object.assign(catalogue, objects);
|
|
61
61
|
}
|
|
62
62
|
};
|
|
63
|
-
function createInstance(type, props, root) {
|
|
63
|
+
function createInstance(type, props, root, flushPrimitive = true) {
|
|
64
|
+
var _props$object;
|
|
64
65
|
// Get target from catalogue
|
|
65
66
|
const name = `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
66
67
|
const target = catalogue[name];
|
|
@@ -74,10 +75,37 @@ function createInstance(type, props, root) {
|
|
|
74
75
|
// Throw if an object or literal was passed for args
|
|
75
76
|
if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
|
|
76
77
|
|
|
78
|
+
// Regenerate the R3F instance for primitives to simulate a new object
|
|
79
|
+
if (flushPrimitive && type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
|
|
80
|
+
|
|
77
81
|
// Create instance
|
|
78
82
|
const instance = prepare(props.object, root, type, props);
|
|
79
83
|
return instance;
|
|
80
84
|
}
|
|
85
|
+
function hideInstance(instance) {
|
|
86
|
+
if (!instance.isHidden) {
|
|
87
|
+
var _instance$parent;
|
|
88
|
+
if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
|
|
89
|
+
detach(instance.parent, instance);
|
|
90
|
+
} else if (isObject3D(instance.object)) {
|
|
91
|
+
instance.object.visible = false;
|
|
92
|
+
}
|
|
93
|
+
instance.isHidden = true;
|
|
94
|
+
invalidateInstance(instance);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function unhideInstance(instance) {
|
|
98
|
+
if (instance.isHidden) {
|
|
99
|
+
var _instance$parent2;
|
|
100
|
+
if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
|
|
101
|
+
attach(instance.parent, instance);
|
|
102
|
+
} else if (isObject3D(instance.object) && instance.props.visible !== false) {
|
|
103
|
+
instance.object.visible = true;
|
|
104
|
+
}
|
|
105
|
+
instance.isHidden = false;
|
|
106
|
+
invalidateInstance(instance);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
81
109
|
|
|
82
110
|
// https://github.com/facebook/react/issues/20271
|
|
83
111
|
// This will make sure events and attach are only handled once when trees are complete
|
|
@@ -213,8 +241,19 @@ function setFiberInstance(fiber, instance) {
|
|
|
213
241
|
}
|
|
214
242
|
}
|
|
215
243
|
function switchInstance(oldInstance, type, props, fiber) {
|
|
244
|
+
// If the old instance is hidden, we need to unhide it.
|
|
245
|
+
// React assumes it can discard instances since they're pure for DOM.
|
|
246
|
+
// This isn't true for us since our lifetimes are impure and longliving.
|
|
247
|
+
// So, we manually check if an instance was hidden and unhide it.
|
|
248
|
+
if (oldInstance.isHidden) unhideInstance(oldInstance);
|
|
249
|
+
|
|
216
250
|
// Create a new instance
|
|
217
|
-
const newInstance = createInstance(type, props, oldInstance.root);
|
|
251
|
+
const newInstance = createInstance(type, props, oldInstance.root, false);
|
|
252
|
+
|
|
253
|
+
// Update attach props for primitives since we don't flush them
|
|
254
|
+
if (type === 'primitive') {
|
|
255
|
+
newInstance.props.attach = props.attach;
|
|
256
|
+
}
|
|
218
257
|
|
|
219
258
|
// Move children to new instance
|
|
220
259
|
for (const child of oldInstance.children) {
|
|
@@ -262,7 +301,9 @@ const reconciler = Reconciler__default["default"]({
|
|
|
262
301
|
supportsMutation: true,
|
|
263
302
|
supportsPersistence: false,
|
|
264
303
|
supportsHydration: false,
|
|
265
|
-
createInstance,
|
|
304
|
+
createInstance(type, props, root) {
|
|
305
|
+
return createInstance(type, props, root);
|
|
306
|
+
},
|
|
266
307
|
removeChild,
|
|
267
308
|
appendChild,
|
|
268
309
|
appendInitialChild: appendChild,
|
|
@@ -319,8 +360,8 @@ const reconciler = Reconciler__default["default"]({
|
|
|
319
360
|
resetAfterCommit: () => {},
|
|
320
361
|
shouldSetTextContent: () => false,
|
|
321
362
|
clearContainer: () => false,
|
|
322
|
-
hideInstance
|
|
323
|
-
unhideInstance
|
|
363
|
+
hideInstance,
|
|
364
|
+
unhideInstance,
|
|
324
365
|
createTextInstance: handleTextInstance,
|
|
325
366
|
hideTextInstance: handleTextInstance,
|
|
326
367
|
unhideTextInstance: handleTextInstance,
|
|
@@ -575,7 +616,8 @@ function prepare(target, root, type, props) {
|
|
|
575
616
|
props: getInstanceProps(props),
|
|
576
617
|
object,
|
|
577
618
|
eventCount: 0,
|
|
578
|
-
handlers: {}
|
|
619
|
+
handlers: {},
|
|
620
|
+
isHidden: false
|
|
579
621
|
};
|
|
580
622
|
if (object) {
|
|
581
623
|
object.__r3f = instance;
|
|
@@ -1247,6 +1289,44 @@ const isRenderer = def => !!(def != null && def.render);
|
|
|
1247
1289
|
const context = /*#__PURE__*/React__namespace.createContext(null);
|
|
1248
1290
|
const createStore = (invalidate, advance) => {
|
|
1249
1291
|
const rootStore = traditional.createWithEqualityFn((set, get) => {
|
|
1292
|
+
const position = new THREE__namespace.Vector3();
|
|
1293
|
+
const defaultTarget = new THREE__namespace.Vector3();
|
|
1294
|
+
const tempTarget = new THREE__namespace.Vector3();
|
|
1295
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
1296
|
+
const {
|
|
1297
|
+
width,
|
|
1298
|
+
height,
|
|
1299
|
+
top,
|
|
1300
|
+
left
|
|
1301
|
+
} = size;
|
|
1302
|
+
const aspect = width / height;
|
|
1303
|
+
if (target instanceof THREE__namespace.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
|
|
1304
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
1305
|
+
if (isOrthographicCamera(camera)) {
|
|
1306
|
+
return {
|
|
1307
|
+
width: width / camera.zoom,
|
|
1308
|
+
height: height / camera.zoom,
|
|
1309
|
+
top,
|
|
1310
|
+
left,
|
|
1311
|
+
factor: 1,
|
|
1312
|
+
distance,
|
|
1313
|
+
aspect
|
|
1314
|
+
};
|
|
1315
|
+
} else {
|
|
1316
|
+
const fov = camera.fov * Math.PI / 180; // convert vertical fov to radians
|
|
1317
|
+
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
1318
|
+
const w = h * (width / height);
|
|
1319
|
+
return {
|
|
1320
|
+
width: w,
|
|
1321
|
+
height: h,
|
|
1322
|
+
top,
|
|
1323
|
+
left,
|
|
1324
|
+
factor: width / w,
|
|
1325
|
+
distance,
|
|
1326
|
+
aspect
|
|
1327
|
+
};
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1250
1330
|
let performanceTimeout = undefined;
|
|
1251
1331
|
const setPerformanceCurrent = current => set(state => ({
|
|
1252
1332
|
performance: {
|
|
@@ -1295,13 +1375,24 @@ const createStore = (invalidate, advance) => {
|
|
|
1295
1375
|
performanceTimeout = setTimeout(() => setPerformanceCurrent(get().performance.max), state.performance.debounce);
|
|
1296
1376
|
}
|
|
1297
1377
|
},
|
|
1298
|
-
dpr: 1,
|
|
1299
1378
|
size: {
|
|
1300
1379
|
width: 0,
|
|
1301
1380
|
height: 0,
|
|
1302
1381
|
top: 0,
|
|
1303
1382
|
left: 0
|
|
1304
1383
|
},
|
|
1384
|
+
viewport: {
|
|
1385
|
+
initialDpr: 0,
|
|
1386
|
+
dpr: 0,
|
|
1387
|
+
width: 0,
|
|
1388
|
+
height: 0,
|
|
1389
|
+
top: 0,
|
|
1390
|
+
left: 0,
|
|
1391
|
+
aspect: 0,
|
|
1392
|
+
distance: 0,
|
|
1393
|
+
factor: 0,
|
|
1394
|
+
getCurrentViewport
|
|
1395
|
+
},
|
|
1305
1396
|
setEvents: events => set(state => ({
|
|
1306
1397
|
...state,
|
|
1307
1398
|
events: {
|
|
@@ -1310,17 +1401,30 @@ const createStore = (invalidate, advance) => {
|
|
|
1310
1401
|
}
|
|
1311
1402
|
})),
|
|
1312
1403
|
setSize: (width, height, top = 0, left = 0) => {
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1404
|
+
const camera = get().camera;
|
|
1405
|
+
const size = {
|
|
1406
|
+
width,
|
|
1407
|
+
height,
|
|
1408
|
+
top,
|
|
1409
|
+
left
|
|
1410
|
+
};
|
|
1411
|
+
set(state => ({
|
|
1412
|
+
size,
|
|
1413
|
+
viewport: {
|
|
1414
|
+
...state.viewport,
|
|
1415
|
+
...getCurrentViewport(camera, defaultTarget, size)
|
|
1319
1416
|
}
|
|
1320
|
-
});
|
|
1417
|
+
}));
|
|
1321
1418
|
},
|
|
1322
|
-
setDpr: dpr => set({
|
|
1323
|
-
|
|
1419
|
+
setDpr: dpr => set(state => {
|
|
1420
|
+
const resolved = calculateDpr(dpr);
|
|
1421
|
+
return {
|
|
1422
|
+
viewport: {
|
|
1423
|
+
...state.viewport,
|
|
1424
|
+
dpr: resolved,
|
|
1425
|
+
initialDpr: state.viewport.initialDpr || resolved
|
|
1426
|
+
}
|
|
1427
|
+
};
|
|
1324
1428
|
}),
|
|
1325
1429
|
setFrameloop: frameloop => {
|
|
1326
1430
|
var _frameloop$mode, _frameloop$render, _frameloop$maxDelta;
|
|
@@ -1409,23 +1513,39 @@ const createStore = (invalidate, advance) => {
|
|
|
1409
1513
|
});
|
|
1410
1514
|
const state = rootStore.getState();
|
|
1411
1515
|
let oldSize = state.size;
|
|
1412
|
-
let oldDpr = state.dpr;
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1516
|
+
let oldDpr = state.viewport.dpr;
|
|
1517
|
+
let oldCamera = state.camera;
|
|
1518
|
+
rootStore.subscribe(() => {
|
|
1519
|
+
const {
|
|
1520
|
+
camera,
|
|
1521
|
+
size,
|
|
1522
|
+
viewport,
|
|
1523
|
+
gl,
|
|
1524
|
+
set
|
|
1525
|
+
} = rootStore.getState();
|
|
1526
|
+
|
|
1419
1527
|
// Resize camera and renderer on changes to size and pixelratio
|
|
1420
|
-
if (size !== oldSize || dpr !== oldDpr) {
|
|
1528
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1421
1529
|
oldSize = size;
|
|
1422
|
-
oldDpr = dpr;
|
|
1530
|
+
oldDpr = viewport.dpr;
|
|
1423
1531
|
// Update camera & renderer
|
|
1424
1532
|
updateCamera(camera, size);
|
|
1425
|
-
gl.setPixelRatio(dpr);
|
|
1533
|
+
gl.setPixelRatio(viewport.dpr);
|
|
1426
1534
|
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
1427
1535
|
gl.setSize(size.width, size.height, updateStyle);
|
|
1428
1536
|
}
|
|
1537
|
+
|
|
1538
|
+
// Update viewport once the camera changes
|
|
1539
|
+
if (camera !== oldCamera) {
|
|
1540
|
+
oldCamera = camera;
|
|
1541
|
+
// Update viewport
|
|
1542
|
+
set(state => ({
|
|
1543
|
+
viewport: {
|
|
1544
|
+
...state.viewport,
|
|
1545
|
+
...state.viewport.getCurrentViewport(camera)
|
|
1546
|
+
}
|
|
1547
|
+
}));
|
|
1548
|
+
}
|
|
1429
1549
|
});
|
|
1430
1550
|
|
|
1431
1551
|
// Invalidate on any change
|
|
@@ -1570,15 +1690,20 @@ const Lifecycle = [Early, Fixed, Update, Late, Render, After];
|
|
|
1570
1690
|
|
|
1571
1691
|
/**
|
|
1572
1692
|
* Exposes an object's {@link Instance}.
|
|
1573
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#
|
|
1693
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
|
|
1574
1694
|
*
|
|
1575
1695
|
* **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
|
|
1576
1696
|
*/
|
|
1577
1697
|
function useInstanceHandle(ref) {
|
|
1578
1698
|
const instance = React__namespace.useRef(null);
|
|
1579
|
-
|
|
1699
|
+
React__namespace.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
|
|
1580
1700
|
return instance;
|
|
1581
1701
|
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
|
|
1705
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
|
|
1706
|
+
*/
|
|
1582
1707
|
function useStore() {
|
|
1583
1708
|
const store = React__namespace.useContext(context);
|
|
1584
1709
|
if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
|
|
@@ -1610,7 +1735,7 @@ function useFrame(callback, renderPriority = 0) {
|
|
|
1610
1735
|
|
|
1611
1736
|
/**
|
|
1612
1737
|
* Executes a callback in a given update stage.
|
|
1613
|
-
* Uses the stage instance to
|
|
1738
|
+
* Uses the stage instance to identify which stage to target in the lifecycle.
|
|
1614
1739
|
*/
|
|
1615
1740
|
function useUpdate(callback, stage = Stages.Update) {
|
|
1616
1741
|
const store = useStore();
|
|
@@ -1657,6 +1782,7 @@ function loadingFn(extensions, onProgress) {
|
|
|
1657
1782
|
return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => res(isObject3D(data == null ? void 0 : data.scene) ? Object.assign(data, buildGraph(data.scene)) : data), onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
|
|
1658
1783
|
};
|
|
1659
1784
|
}
|
|
1785
|
+
|
|
1660
1786
|
/**
|
|
1661
1787
|
* Synchronously loads and caches assets with a three loader.
|
|
1662
1788
|
*
|
|
@@ -1880,7 +2006,15 @@ function createRoot(canvas) {
|
|
|
1880
2006
|
const camera = isCamera ? cameraOptions : orthographic ? new THREE__namespace.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE__namespace.PerspectiveCamera(75, 0, 0.1, 1000);
|
|
1881
2007
|
if (!isCamera) {
|
|
1882
2008
|
camera.position.z = 5;
|
|
1883
|
-
if (cameraOptions)
|
|
2009
|
+
if (cameraOptions) {
|
|
2010
|
+
applyProps(camera, cameraOptions);
|
|
2011
|
+
// Preserve user-defined frustum if possible
|
|
2012
|
+
// https://github.com/pmndrs/react-three-fiber/issues/3160
|
|
2013
|
+
if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
|
|
2014
|
+
camera.manual = true;
|
|
2015
|
+
camera.updateProjectionMatrix();
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
1884
2018
|
// Always look at center by default
|
|
1885
2019
|
if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
|
|
1886
2020
|
}
|
|
@@ -2009,7 +2143,7 @@ function createRoot(canvas) {
|
|
|
2009
2143
|
state.setSize(size.width, size.height, size.top, size.left);
|
|
2010
2144
|
}
|
|
2011
2145
|
// Check pixelratio
|
|
2012
|
-
if (dpr && state.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
2146
|
+
if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
2013
2147
|
// Check frameloop
|
|
2014
2148
|
if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
|
|
2015
2149
|
// Check pointer missed
|
|
@@ -2132,8 +2266,11 @@ function Portal({
|
|
|
2132
2266
|
const [raycaster] = React__namespace.useState(() => new THREE__namespace.Raycaster());
|
|
2133
2267
|
const [pointer] = React__namespace.useState(() => new THREE__namespace.Vector2());
|
|
2134
2268
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
2269
|
+
let viewport;
|
|
2135
2270
|
if (injectState.camera && size) {
|
|
2136
2271
|
const camera = injectState.camera;
|
|
2272
|
+
// Calculate the override viewport, if present
|
|
2273
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new THREE__namespace.Vector3(), size);
|
|
2137
2274
|
// Update the portal camera, if it differs from the previous layer
|
|
2138
2275
|
if (camera !== rootState.camera) updateCamera(camera, size);
|
|
2139
2276
|
}
|
|
@@ -2149,7 +2286,7 @@ function Portal({
|
|
|
2149
2286
|
mouse: pointer,
|
|
2150
2287
|
// Their previous root is the layer before it
|
|
2151
2288
|
previousRoot,
|
|
2152
|
-
// Events and
|
|
2289
|
+
// Events, size and viewport can be overridden by the inject layer
|
|
2153
2290
|
events: {
|
|
2154
2291
|
...rootState.events,
|
|
2155
2292
|
...injectState.events,
|
|
@@ -2159,6 +2296,10 @@ function Portal({
|
|
|
2159
2296
|
...rootState.size,
|
|
2160
2297
|
...size
|
|
2161
2298
|
},
|
|
2299
|
+
viewport: {
|
|
2300
|
+
...rootState.viewport,
|
|
2301
|
+
...viewport
|
|
2302
|
+
},
|
|
2162
2303
|
// Layers are allowed to override events
|
|
2163
2304
|
setEvents: events => injectState.set(state => ({
|
|
2164
2305
|
...state,
|
|
@@ -34,7 +34,8 @@ const extend = objects => {
|
|
|
34
34
|
return void Object.assign(catalogue, objects);
|
|
35
35
|
}
|
|
36
36
|
};
|
|
37
|
-
function createInstance(type, props, root) {
|
|
37
|
+
function createInstance(type, props, root, flushPrimitive = true) {
|
|
38
|
+
var _props$object;
|
|
38
39
|
// Get target from catalogue
|
|
39
40
|
const name = `${type[0].toUpperCase()}${type.slice(1)}`;
|
|
40
41
|
const target = catalogue[name];
|
|
@@ -48,10 +49,37 @@ function createInstance(type, props, root) {
|
|
|
48
49
|
// Throw if an object or literal was passed for args
|
|
49
50
|
if (props.args !== undefined && !Array.isArray(props.args)) throw new Error('R3F: The args prop must be an array!');
|
|
50
51
|
|
|
52
|
+
// Regenerate the R3F instance for primitives to simulate a new object
|
|
53
|
+
if (flushPrimitive && type === 'primitive' && (_props$object = props.object) != null && _props$object.__r3f) delete props.object.__r3f;
|
|
54
|
+
|
|
51
55
|
// Create instance
|
|
52
56
|
const instance = prepare(props.object, root, type, props);
|
|
53
57
|
return instance;
|
|
54
58
|
}
|
|
59
|
+
function hideInstance(instance) {
|
|
60
|
+
if (!instance.isHidden) {
|
|
61
|
+
var _instance$parent;
|
|
62
|
+
if (instance.props.attach && (_instance$parent = instance.parent) != null && _instance$parent.object) {
|
|
63
|
+
detach(instance.parent, instance);
|
|
64
|
+
} else if (isObject3D(instance.object)) {
|
|
65
|
+
instance.object.visible = false;
|
|
66
|
+
}
|
|
67
|
+
instance.isHidden = true;
|
|
68
|
+
invalidateInstance(instance);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function unhideInstance(instance) {
|
|
72
|
+
if (instance.isHidden) {
|
|
73
|
+
var _instance$parent2;
|
|
74
|
+
if (instance.props.attach && (_instance$parent2 = instance.parent) != null && _instance$parent2.object) {
|
|
75
|
+
attach(instance.parent, instance);
|
|
76
|
+
} else if (isObject3D(instance.object) && instance.props.visible !== false) {
|
|
77
|
+
instance.object.visible = true;
|
|
78
|
+
}
|
|
79
|
+
instance.isHidden = false;
|
|
80
|
+
invalidateInstance(instance);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
55
83
|
|
|
56
84
|
// https://github.com/facebook/react/issues/20271
|
|
57
85
|
// This will make sure events and attach are only handled once when trees are complete
|
|
@@ -187,8 +215,19 @@ function setFiberInstance(fiber, instance) {
|
|
|
187
215
|
}
|
|
188
216
|
}
|
|
189
217
|
function switchInstance(oldInstance, type, props, fiber) {
|
|
218
|
+
// If the old instance is hidden, we need to unhide it.
|
|
219
|
+
// React assumes it can discard instances since they're pure for DOM.
|
|
220
|
+
// This isn't true for us since our lifetimes are impure and longliving.
|
|
221
|
+
// So, we manually check if an instance was hidden and unhide it.
|
|
222
|
+
if (oldInstance.isHidden) unhideInstance(oldInstance);
|
|
223
|
+
|
|
190
224
|
// Create a new instance
|
|
191
|
-
const newInstance = createInstance(type, props, oldInstance.root);
|
|
225
|
+
const newInstance = createInstance(type, props, oldInstance.root, false);
|
|
226
|
+
|
|
227
|
+
// Update attach props for primitives since we don't flush them
|
|
228
|
+
if (type === 'primitive') {
|
|
229
|
+
newInstance.props.attach = props.attach;
|
|
230
|
+
}
|
|
192
231
|
|
|
193
232
|
// Move children to new instance
|
|
194
233
|
for (const child of oldInstance.children) {
|
|
@@ -236,7 +275,9 @@ const reconciler = Reconciler({
|
|
|
236
275
|
supportsMutation: true,
|
|
237
276
|
supportsPersistence: false,
|
|
238
277
|
supportsHydration: false,
|
|
239
|
-
createInstance,
|
|
278
|
+
createInstance(type, props, root) {
|
|
279
|
+
return createInstance(type, props, root);
|
|
280
|
+
},
|
|
240
281
|
removeChild,
|
|
241
282
|
appendChild,
|
|
242
283
|
appendInitialChild: appendChild,
|
|
@@ -293,8 +334,8 @@ const reconciler = Reconciler({
|
|
|
293
334
|
resetAfterCommit: () => {},
|
|
294
335
|
shouldSetTextContent: () => false,
|
|
295
336
|
clearContainer: () => false,
|
|
296
|
-
hideInstance
|
|
297
|
-
unhideInstance
|
|
337
|
+
hideInstance,
|
|
338
|
+
unhideInstance,
|
|
298
339
|
createTextInstance: handleTextInstance,
|
|
299
340
|
hideTextInstance: handleTextInstance,
|
|
300
341
|
unhideTextInstance: handleTextInstance,
|
|
@@ -549,7 +590,8 @@ function prepare(target, root, type, props) {
|
|
|
549
590
|
props: getInstanceProps(props),
|
|
550
591
|
object,
|
|
551
592
|
eventCount: 0,
|
|
552
|
-
handlers: {}
|
|
593
|
+
handlers: {},
|
|
594
|
+
isHidden: false
|
|
553
595
|
};
|
|
554
596
|
if (object) {
|
|
555
597
|
object.__r3f = instance;
|
|
@@ -1221,6 +1263,44 @@ const isRenderer = def => !!(def != null && def.render);
|
|
|
1221
1263
|
const context = /*#__PURE__*/React.createContext(null);
|
|
1222
1264
|
const createStore = (invalidate, advance) => {
|
|
1223
1265
|
const rootStore = createWithEqualityFn((set, get) => {
|
|
1266
|
+
const position = new THREE.Vector3();
|
|
1267
|
+
const defaultTarget = new THREE.Vector3();
|
|
1268
|
+
const tempTarget = new THREE.Vector3();
|
|
1269
|
+
function getCurrentViewport(camera = get().camera, target = defaultTarget, size = get().size) {
|
|
1270
|
+
const {
|
|
1271
|
+
width,
|
|
1272
|
+
height,
|
|
1273
|
+
top,
|
|
1274
|
+
left
|
|
1275
|
+
} = size;
|
|
1276
|
+
const aspect = width / height;
|
|
1277
|
+
if (target instanceof THREE.Vector3) tempTarget.copy(target);else tempTarget.set(...target);
|
|
1278
|
+
const distance = camera.getWorldPosition(position).distanceTo(tempTarget);
|
|
1279
|
+
if (isOrthographicCamera(camera)) {
|
|
1280
|
+
return {
|
|
1281
|
+
width: width / camera.zoom,
|
|
1282
|
+
height: height / camera.zoom,
|
|
1283
|
+
top,
|
|
1284
|
+
left,
|
|
1285
|
+
factor: 1,
|
|
1286
|
+
distance,
|
|
1287
|
+
aspect
|
|
1288
|
+
};
|
|
1289
|
+
} else {
|
|
1290
|
+
const fov = camera.fov * Math.PI / 180; // convert vertical fov to radians
|
|
1291
|
+
const h = 2 * Math.tan(fov / 2) * distance; // visible height
|
|
1292
|
+
const w = h * (width / height);
|
|
1293
|
+
return {
|
|
1294
|
+
width: w,
|
|
1295
|
+
height: h,
|
|
1296
|
+
top,
|
|
1297
|
+
left,
|
|
1298
|
+
factor: width / w,
|
|
1299
|
+
distance,
|
|
1300
|
+
aspect
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1224
1304
|
let performanceTimeout = undefined;
|
|
1225
1305
|
const setPerformanceCurrent = current => set(state => ({
|
|
1226
1306
|
performance: {
|
|
@@ -1269,13 +1349,24 @@ const createStore = (invalidate, advance) => {
|
|
|
1269
1349
|
performanceTimeout = setTimeout(() => setPerformanceCurrent(get().performance.max), state.performance.debounce);
|
|
1270
1350
|
}
|
|
1271
1351
|
},
|
|
1272
|
-
dpr: 1,
|
|
1273
1352
|
size: {
|
|
1274
1353
|
width: 0,
|
|
1275
1354
|
height: 0,
|
|
1276
1355
|
top: 0,
|
|
1277
1356
|
left: 0
|
|
1278
1357
|
},
|
|
1358
|
+
viewport: {
|
|
1359
|
+
initialDpr: 0,
|
|
1360
|
+
dpr: 0,
|
|
1361
|
+
width: 0,
|
|
1362
|
+
height: 0,
|
|
1363
|
+
top: 0,
|
|
1364
|
+
left: 0,
|
|
1365
|
+
aspect: 0,
|
|
1366
|
+
distance: 0,
|
|
1367
|
+
factor: 0,
|
|
1368
|
+
getCurrentViewport
|
|
1369
|
+
},
|
|
1279
1370
|
setEvents: events => set(state => ({
|
|
1280
1371
|
...state,
|
|
1281
1372
|
events: {
|
|
@@ -1284,17 +1375,30 @@ const createStore = (invalidate, advance) => {
|
|
|
1284
1375
|
}
|
|
1285
1376
|
})),
|
|
1286
1377
|
setSize: (width, height, top = 0, left = 0) => {
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1378
|
+
const camera = get().camera;
|
|
1379
|
+
const size = {
|
|
1380
|
+
width,
|
|
1381
|
+
height,
|
|
1382
|
+
top,
|
|
1383
|
+
left
|
|
1384
|
+
};
|
|
1385
|
+
set(state => ({
|
|
1386
|
+
size,
|
|
1387
|
+
viewport: {
|
|
1388
|
+
...state.viewport,
|
|
1389
|
+
...getCurrentViewport(camera, defaultTarget, size)
|
|
1293
1390
|
}
|
|
1294
|
-
});
|
|
1391
|
+
}));
|
|
1295
1392
|
},
|
|
1296
|
-
setDpr: dpr => set({
|
|
1297
|
-
|
|
1393
|
+
setDpr: dpr => set(state => {
|
|
1394
|
+
const resolved = calculateDpr(dpr);
|
|
1395
|
+
return {
|
|
1396
|
+
viewport: {
|
|
1397
|
+
...state.viewport,
|
|
1398
|
+
dpr: resolved,
|
|
1399
|
+
initialDpr: state.viewport.initialDpr || resolved
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1298
1402
|
}),
|
|
1299
1403
|
setFrameloop: frameloop => {
|
|
1300
1404
|
var _frameloop$mode, _frameloop$render, _frameloop$maxDelta;
|
|
@@ -1383,23 +1487,39 @@ const createStore = (invalidate, advance) => {
|
|
|
1383
1487
|
});
|
|
1384
1488
|
const state = rootStore.getState();
|
|
1385
1489
|
let oldSize = state.size;
|
|
1386
|
-
let oldDpr = state.dpr;
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1490
|
+
let oldDpr = state.viewport.dpr;
|
|
1491
|
+
let oldCamera = state.camera;
|
|
1492
|
+
rootStore.subscribe(() => {
|
|
1493
|
+
const {
|
|
1494
|
+
camera,
|
|
1495
|
+
size,
|
|
1496
|
+
viewport,
|
|
1497
|
+
gl,
|
|
1498
|
+
set
|
|
1499
|
+
} = rootStore.getState();
|
|
1500
|
+
|
|
1393
1501
|
// Resize camera and renderer on changes to size and pixelratio
|
|
1394
|
-
if (size !== oldSize || dpr !== oldDpr) {
|
|
1502
|
+
if (size.width !== oldSize.width || size.height !== oldSize.height || viewport.dpr !== oldDpr) {
|
|
1395
1503
|
oldSize = size;
|
|
1396
|
-
oldDpr = dpr;
|
|
1504
|
+
oldDpr = viewport.dpr;
|
|
1397
1505
|
// Update camera & renderer
|
|
1398
1506
|
updateCamera(camera, size);
|
|
1399
|
-
gl.setPixelRatio(dpr);
|
|
1507
|
+
gl.setPixelRatio(viewport.dpr);
|
|
1400
1508
|
const updateStyle = typeof HTMLCanvasElement !== 'undefined' && gl.domElement instanceof HTMLCanvasElement;
|
|
1401
1509
|
gl.setSize(size.width, size.height, updateStyle);
|
|
1402
1510
|
}
|
|
1511
|
+
|
|
1512
|
+
// Update viewport once the camera changes
|
|
1513
|
+
if (camera !== oldCamera) {
|
|
1514
|
+
oldCamera = camera;
|
|
1515
|
+
// Update viewport
|
|
1516
|
+
set(state => ({
|
|
1517
|
+
viewport: {
|
|
1518
|
+
...state.viewport,
|
|
1519
|
+
...state.viewport.getCurrentViewport(camera)
|
|
1520
|
+
}
|
|
1521
|
+
}));
|
|
1522
|
+
}
|
|
1403
1523
|
});
|
|
1404
1524
|
|
|
1405
1525
|
// Invalidate on any change
|
|
@@ -1544,15 +1664,20 @@ const Lifecycle = [Early, Fixed, Update, Late, Render, After];
|
|
|
1544
1664
|
|
|
1545
1665
|
/**
|
|
1546
1666
|
* Exposes an object's {@link Instance}.
|
|
1547
|
-
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#
|
|
1667
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/additional-exports#useinstancehandle
|
|
1548
1668
|
*
|
|
1549
1669
|
* **Note**: this is an escape hatch to react-internal fields. Expect this to change significantly between versions.
|
|
1550
1670
|
*/
|
|
1551
1671
|
function useInstanceHandle(ref) {
|
|
1552
1672
|
const instance = React.useRef(null);
|
|
1553
|
-
|
|
1673
|
+
React.useImperativeHandle(instance, () => ref.current.__r3f, [ref]);
|
|
1554
1674
|
return instance;
|
|
1555
1675
|
}
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* Returns the R3F Canvas' Zustand store. Useful for [transient updates](https://github.com/pmndrs/zustand#transient-updates-for-often-occurring-state-changes).
|
|
1679
|
+
* @see https://docs.pmnd.rs/react-three-fiber/api/hooks#usestore
|
|
1680
|
+
*/
|
|
1556
1681
|
function useStore() {
|
|
1557
1682
|
const store = React.useContext(context);
|
|
1558
1683
|
if (!store) throw new Error('R3F: Hooks can only be used within the Canvas component!');
|
|
@@ -1584,7 +1709,7 @@ function useFrame(callback, renderPriority = 0) {
|
|
|
1584
1709
|
|
|
1585
1710
|
/**
|
|
1586
1711
|
* Executes a callback in a given update stage.
|
|
1587
|
-
* Uses the stage instance to
|
|
1712
|
+
* Uses the stage instance to identify which stage to target in the lifecycle.
|
|
1588
1713
|
*/
|
|
1589
1714
|
function useUpdate(callback, stage = Stages.Update) {
|
|
1590
1715
|
const store = useStore();
|
|
@@ -1631,6 +1756,7 @@ function loadingFn(extensions, onProgress) {
|
|
|
1631
1756
|
return Promise.all(input.map(input => new Promise((res, reject) => loader.load(input, data => res(isObject3D(data == null ? void 0 : data.scene) ? Object.assign(data, buildGraph(data.scene)) : data), onProgress, error => reject(new Error(`Could not load ${input}: ${error == null ? void 0 : error.message}`))))));
|
|
1632
1757
|
};
|
|
1633
1758
|
}
|
|
1759
|
+
|
|
1634
1760
|
/**
|
|
1635
1761
|
* Synchronously loads and caches assets with a three loader.
|
|
1636
1762
|
*
|
|
@@ -1854,7 +1980,15 @@ function createRoot(canvas) {
|
|
|
1854
1980
|
const camera = isCamera ? cameraOptions : orthographic ? new THREE.OrthographicCamera(0, 0, 0, 0, 0.1, 1000) : new THREE.PerspectiveCamera(75, 0, 0.1, 1000);
|
|
1855
1981
|
if (!isCamera) {
|
|
1856
1982
|
camera.position.z = 5;
|
|
1857
|
-
if (cameraOptions)
|
|
1983
|
+
if (cameraOptions) {
|
|
1984
|
+
applyProps(camera, cameraOptions);
|
|
1985
|
+
// Preserve user-defined frustum if possible
|
|
1986
|
+
// https://github.com/pmndrs/react-three-fiber/issues/3160
|
|
1987
|
+
if ('aspect' in cameraOptions || 'left' in cameraOptions || 'right' in cameraOptions || 'bottom' in cameraOptions || 'top' in cameraOptions) {
|
|
1988
|
+
camera.manual = true;
|
|
1989
|
+
camera.updateProjectionMatrix();
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1858
1992
|
// Always look at center by default
|
|
1859
1993
|
if (!state.camera && !(cameraOptions != null && cameraOptions.rotation)) camera.lookAt(0, 0, 0);
|
|
1860
1994
|
}
|
|
@@ -1983,7 +2117,7 @@ function createRoot(canvas) {
|
|
|
1983
2117
|
state.setSize(size.width, size.height, size.top, size.left);
|
|
1984
2118
|
}
|
|
1985
2119
|
// Check pixelratio
|
|
1986
|
-
if (dpr && state.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
2120
|
+
if (dpr && state.viewport.dpr !== calculateDpr(dpr)) state.setDpr(dpr);
|
|
1987
2121
|
// Check frameloop
|
|
1988
2122
|
if (state.frameloop !== frameloop) state.setFrameloop(frameloop);
|
|
1989
2123
|
// Check pointer missed
|
|
@@ -2106,8 +2240,11 @@ function Portal({
|
|
|
2106
2240
|
const [raycaster] = React.useState(() => new THREE.Raycaster());
|
|
2107
2241
|
const [pointer] = React.useState(() => new THREE.Vector2());
|
|
2108
2242
|
const inject = useMutableCallback((rootState, injectState) => {
|
|
2243
|
+
let viewport;
|
|
2109
2244
|
if (injectState.camera && size) {
|
|
2110
2245
|
const camera = injectState.camera;
|
|
2246
|
+
// Calculate the override viewport, if present
|
|
2247
|
+
viewport = rootState.viewport.getCurrentViewport(camera, new THREE.Vector3(), size);
|
|
2111
2248
|
// Update the portal camera, if it differs from the previous layer
|
|
2112
2249
|
if (camera !== rootState.camera) updateCamera(camera, size);
|
|
2113
2250
|
}
|
|
@@ -2123,7 +2260,7 @@ function Portal({
|
|
|
2123
2260
|
mouse: pointer,
|
|
2124
2261
|
// Their previous root is the layer before it
|
|
2125
2262
|
previousRoot,
|
|
2126
|
-
// Events and
|
|
2263
|
+
// Events, size and viewport can be overridden by the inject layer
|
|
2127
2264
|
events: {
|
|
2128
2265
|
...rootState.events,
|
|
2129
2266
|
...injectState.events,
|
|
@@ -2133,6 +2270,10 @@ function Portal({
|
|
|
2133
2270
|
...rootState.size,
|
|
2134
2271
|
...size
|
|
2135
2272
|
},
|
|
2273
|
+
viewport: {
|
|
2274
|
+
...rootState.viewport,
|
|
2275
|
+
...viewport
|
|
2276
|
+
},
|
|
2136
2277
|
// Layers are allowed to override events
|
|
2137
2278
|
setEvents: events => injectState.set(state => ({
|
|
2138
2279
|
...state,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var loop = require('./loop-
|
|
5
|
+
var loop = require('./loop-02cefb27.cjs.dev.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var loop = require('./loop-
|
|
5
|
+
var loop = require('./loop-1fad3b6f.cjs.prod.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as createEvents, e as extend, u as useBridge, a as useMutableCallback, b as useIsomorphicLayoutEffect, d as createRoot, i as isRef, E as ErrorBoundary, B as Block, f as unmountComponentAtNode } from './loop-
|
|
2
|
-
export { F as FixedStage, t as ReactThreeFiber, S as Stage, p as Stages, _ as _roots, x as act, j as addAfterEffect, h as addEffect, k as addTail, m as advance, s as applyProps, y as buildGraph, q as context, c as createEvents, o as createPortal, d as createRoot, w as dispose, e as extend, g as flushGlobalEffects, v as getRootState, l as invalidate, r as reconciler, n as render, f as unmountComponentAtNode, D as useFrame, H as useGraph, z as useInstanceHandle, I as useLoader, A as useStore, C as useThree, G as useUpdate } from './loop-
|
|
1
|
+
import { c as createEvents, e as extend, u as useBridge, a as useMutableCallback, b as useIsomorphicLayoutEffect, d as createRoot, i as isRef, E as ErrorBoundary, B as Block, f as unmountComponentAtNode } from './loop-d73c6316.esm.js';
|
|
2
|
+
export { F as FixedStage, t as ReactThreeFiber, S as Stage, p as Stages, _ as _roots, x as act, j as addAfterEffect, h as addEffect, k as addTail, m as advance, s as applyProps, y as buildGraph, q as context, c as createEvents, o as createPortal, d as createRoot, w as dispose, e as extend, g as flushGlobalEffects, v as getRootState, l as invalidate, r as reconciler, n as render, f as unmountComponentAtNode, D as useFrame, H as useGraph, z as useInstanceHandle, I as useLoader, A as useStore, C as useThree, G as useUpdate } from './loop-d73c6316.esm.js';
|
|
3
3
|
import _extends from '@babel/runtime/helpers/esm/extends';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import * as THREE from 'three';
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var loop = require('../../dist/loop-
|
|
5
|
+
var loop = require('../../dist/loop-02cefb27.cjs.dev.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
|
-
var loop = require('../../dist/loop-
|
|
5
|
+
var loop = require('../../dist/loop-1fad3b6f.cjs.prod.js');
|
|
6
6
|
var _extends = require('@babel/runtime/helpers/extends');
|
|
7
7
|
var React = require('react');
|
|
8
8
|
var THREE = require('three');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { c as createEvents, e as extend, u as useBridge, a as useMutableCallback, d as createRoot, E as ErrorBoundary, B as Block, f as unmountComponentAtNode } from '../../dist/loop-
|
|
2
|
-
export { F as FixedStage, t as ReactThreeFiber, S as Stage, p as Stages, _ as _roots, x as act, j as addAfterEffect, h as addEffect, k as addTail, m as advance, s as applyProps, y as buildGraph, q as context, c as createEvents, o as createPortal, d as createRoot, w as dispose, e as extend, g as flushGlobalEffects, v as getRootState, l as invalidate, r as reconciler, n as render, f as unmountComponentAtNode, D as useFrame, H as useGraph, z as useInstanceHandle, I as useLoader, A as useStore, C as useThree, G as useUpdate } from '../../dist/loop-
|
|
1
|
+
import { c as createEvents, e as extend, u as useBridge, a as useMutableCallback, d as createRoot, E as ErrorBoundary, B as Block, f as unmountComponentAtNode } from '../../dist/loop-d73c6316.esm.js';
|
|
2
|
+
export { F as FixedStage, t as ReactThreeFiber, S as Stage, p as Stages, _ as _roots, x as act, j as addAfterEffect, h as addEffect, k as addTail, m as advance, s as applyProps, y as buildGraph, q as context, c as createEvents, o as createPortal, d as createRoot, w as dispose, e as extend, g as flushGlobalEffects, v as getRootState, l as invalidate, r as reconciler, n as render, f as unmountComponentAtNode, D as useFrame, H as useGraph, z as useInstanceHandle, I as useLoader, A as useStore, C as useThree, G as useUpdate } from '../../dist/loop-d73c6316.esm.js';
|
|
3
3
|
import _extends from '@babel/runtime/helpers/esm/extends';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import * as THREE from 'three';
|