@react-three/drei 9.0.0-beta.3 → 9.0.0-beta.4

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.
Files changed (107) hide show
  1. package/README.md +293 -26
  2. package/core/ArcballControls.cjs.js +1 -1
  3. package/core/ArcballControls.js +13 -25
  4. package/core/Bounds.cjs.js +1 -1
  5. package/core/Bounds.d.ts +2 -1
  6. package/core/Bounds.js +25 -14
  7. package/core/Clone.cjs.js +1 -0
  8. package/core/Clone.d.ts +11 -0
  9. package/core/Clone.js +92 -0
  10. package/core/Cloud.cjs.js +1 -1
  11. package/core/Cloud.d.ts +3 -1
  12. package/core/Cloud.js +4 -1
  13. package/core/ComputedAttribute.cjs.js +1 -0
  14. package/core/ComputedAttribute.d.ts +9 -0
  15. package/core/ComputedAttribute.js +34 -0
  16. package/core/ContactShadows.cjs.js +1 -1
  17. package/core/ContactShadows.d.ts +3 -1
  18. package/core/ContactShadows.js +15 -2
  19. package/core/CubeCamera.cjs.js +1 -1
  20. package/core/CubeCamera.js +6 -6
  21. package/core/DeviceOrientationControls.cjs.js +1 -1
  22. package/core/DeviceOrientationControls.js +2 -6
  23. package/core/Effects.cjs.js +1 -1
  24. package/core/Effects.d.ts +3 -2
  25. package/core/Effects.js +2 -2
  26. package/core/Environment.cjs.js +1 -1
  27. package/core/Environment.d.ts +13 -3
  28. package/core/Environment.js +96 -33
  29. package/core/FirstPersonControls.cjs.js +1 -1
  30. package/core/FirstPersonControls.js +2 -6
  31. package/core/Float.cjs.js +1 -1
  32. package/core/Float.d.ts +1 -1
  33. package/core/Float.js +5 -4
  34. package/core/GizmoHelper.cjs.js +1 -1
  35. package/core/GizmoHelper.d.ts +2 -1
  36. package/core/GizmoHelper.js +31 -41
  37. package/core/Image.cjs.js +1 -1
  38. package/core/Image.d.ts +5 -3
  39. package/core/Image.js +6 -1
  40. package/core/Instances.cjs.js +1 -1
  41. package/core/Instances.d.ts +1 -1
  42. package/core/Instances.js +5 -11
  43. package/core/Lightformer.cjs.js +1 -0
  44. package/core/Lightformer.d.ts +41 -0
  45. package/core/Lightformer.js +51 -0
  46. package/core/MeshReflectorMaterial.cjs.js +1 -1
  47. package/core/MeshReflectorMaterial.d.ts +1 -2
  48. package/core/MeshReflectorMaterial.js +4 -5
  49. package/core/OrbitControls.cjs.js +1 -1
  50. package/core/OrbitControls.js +18 -31
  51. package/core/PointMaterial.cjs.js +1 -1
  52. package/core/PointMaterial.d.ts +5 -8
  53. package/core/PointMaterial.js +16 -34
  54. package/core/PointerLockControls.cjs.js +1 -1
  55. package/core/PointerLockControls.d.ts +3 -1
  56. package/core/PointerLockControls.js +36 -22
  57. package/core/Points.cjs.js +1 -1
  58. package/core/Points.d.ts +3 -3
  59. package/core/Points.js +11 -30
  60. package/core/Reflector.d.ts +1 -1
  61. package/core/Sampler.cjs.js +1 -0
  62. package/core/Sampler.d.ts +21 -0
  63. package/core/Sampler.js +65 -0
  64. package/core/SpotLight.d.ts +1 -1
  65. package/core/Stage.cjs.js +1 -1
  66. package/core/Stage.js +1 -2
  67. package/core/Stars.cjs.js +1 -1
  68. package/core/Stars.js +3 -0
  69. package/core/Stats.cjs.js +1 -1
  70. package/core/Stats.js +1 -1
  71. package/core/Text.cjs.js +1 -1
  72. package/core/Text.d.ts +2 -1
  73. package/core/Text.js +3 -2
  74. package/core/TrackballControls.cjs.js +1 -1
  75. package/core/TrackballControls.js +7 -5
  76. package/core/Trail.cjs.js +1 -0
  77. package/core/Trail.d.ts +21 -0
  78. package/core/Trail.js +160 -0
  79. package/core/TransformControls.cjs.js +1 -1
  80. package/core/TransformControls.js +7 -13
  81. package/core/index.cjs.js +1 -1
  82. package/core/index.d.ts +6 -0
  83. package/core/index.js +9 -2
  84. package/core/useBoxProjectedEnv.cjs.js +1 -0
  85. package/core/useBoxProjectedEnv.d.ts +8 -0
  86. package/core/useBoxProjectedEnv.js +98 -0
  87. package/core/useFBO.cjs.js +1 -1
  88. package/core/useFBO.d.ts +1 -1
  89. package/core/useFBO.js +22 -24
  90. package/core/useGLTF.d.ts +1 -1
  91. package/core/useHelper.d.ts +3 -1
  92. package/index.cjs.js +1 -1
  93. package/index.js +7 -1
  94. package/materials/ConvolutionMaterial.cjs.js +1 -1
  95. package/materials/ConvolutionMaterial.js +2 -0
  96. package/materials/MeshReflectorMaterial.cjs.js +1 -1
  97. package/materials/MeshReflectorMaterial.d.ts +0 -3
  98. package/materials/MeshReflectorMaterial.js +8 -34
  99. package/materials/SpotLightMaterial.cjs.js +1 -1
  100. package/materials/SpotLightMaterial.js +3 -0
  101. package/native/index.cjs.js +1 -1
  102. package/native/index.js +9 -2
  103. package/package.json +6 -5
  104. package/web/ScrollControls.cjs.js +1 -1
  105. package/web/ScrollControls.js +2 -2
  106. package/web/index.cjs.js +1 -1
  107. package/web/index.js +9 -2
package/README.md CHANGED
@@ -65,6 +65,10 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
65
65
  <li><a href="#effects">Effects</a></li>
66
66
  <li><a href="#gradienttexture">GradientTexture</a></li>
67
67
  <li><a href="#edges">Edges</a></li>
68
+ <li><a href="#trail">Trail</a></li>
69
+ <li><a href="#sampler">Sampler</a></li>
70
+ <li><a href="#computedattribute">Computed Attribute</a></li>
71
+ <li><a href="#clone">Clone</a></li>
68
72
  <li><a href="#useanimations">useAnimations</a></li>
69
73
  </ul>
70
74
  <li><a href="#shaders">Shaders</a></li>
@@ -99,6 +103,8 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
99
103
  <li><a href="#useaspect">useAspect</a></li>
100
104
  <li><a href="#usecursor">useCursor</a></li>
101
105
  <li><a href="#useintersect">useIntersect</a></li>
106
+ <li><a href="#useboxprojectedenv">useBoxProjectedEnv</a></li>
107
+ <li><a href="#useTrail">useTrail</a></li>
102
108
  </ul>
103
109
  <li><a href="#loading">Loaders</a></li>
104
110
  <ul>
@@ -159,6 +165,7 @@ The `native` route of the library **does not** export `Html` or `Loader`. The de
159
165
  <li><a href="#stage">Stage</a></li>
160
166
  <li><a href="#backdrop">Backdrop</a></li>
161
167
  <li><a href="#environment">Environment</a></li>
168
+ <li><a href="#lightformer">Lightformer</a></li>
162
169
  <li><a href="#spotlight">SpotLight</a></li>
163
170
  <li><a href="#shadow">Shadow</a></li>
164
171
  <li><a href="#contactshadows">ContactShadows</a></li>
@@ -243,7 +250,7 @@ Drei currently exports OrbitControls [![](https://img.shields.io/badge/-storyboo
243
250
 
244
251
  All controls react to the default camera. If you have a `<PerspectiveCamera makeDefault />` in your scene, they will control it. If you need to inject an imperative camera or one that isn't the default, use the `camera` prop: `<OrbitControls camera={MyCamera} />`.
245
252
 
246
- PointerLockControls additionally supports a `selector` prop, which enables the binding of `click` event handlers for control activation to other elements than `document` (e.g. a 'Click here to play' button). All elements matching the `selector` prop will activate the controls.
253
+ PointerLockControls additionally supports a `selector` prop, which enables the binding of `click` event handlers for control activation to other elements than `document` (e.g. a 'Click here to play' button). All elements matching the `selector` prop will activate the controls. It will also center raycast events by default, so regular onPointerOver/etc events on meshes will continue to work.
247
254
 
248
255
  # TransformControls
249
256
 
@@ -364,7 +371,7 @@ Semi-OrbitControls with spring-physics, polar zoom and snap-back, for presentati
364
371
  rotation={[0, 0, 0]} // Default rotation
365
372
  polar={[0, Math.PI / 2]} // Vertical limits
366
373
  azimuth={[-Infinity, Infinity]} // Horizontal limits
367
- config = { mass: 1, tension: 170, friction: 26 } // Spring config
374
+ config={{ mass: 1, tension: 170, friction: 26 }} // Spring config
368
375
  >
369
376
  <mesh />
370
377
  </PresentationControls>
@@ -443,6 +450,14 @@ Hi-quality text rendering w/ signed distance fields (SDF) and antialiasing, usin
443
450
  </Text>
444
451
  ```
445
452
 
453
+ Text will suspend while loading the font data, but in order to completely avoid FOUC you can pass the characters it needs to render.
454
+
455
+ ```jsx
456
+ <Text font={fontUrl} characters="abcdefghijklmnopqrstuvwxyz0123456789!">
457
+ hello world!
458
+ </Text>
459
+ ```
460
+
446
461
  #### Line
447
462
 
448
463
  [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/abstractions-line--basic-line)
@@ -631,6 +646,166 @@ Abstracts [THREE.EdgesGeometry](https://threejs.org/docs/#api/en/geometries/Edge
631
646
  </mesh>
632
647
  ```
633
648
 
649
+ #### Trail
650
+
651
+ [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/misc-trail--use-trail-st)
652
+
653
+ A declarative, `three.MeshLine` based Trails implementation. You can attach it to any mesh and it will give it a beautiful trail.
654
+
655
+ Props defined bellow with their default values.
656
+
657
+ ```jsx
658
+ <Trail
659
+ width={0.2} // Width of the line
660
+ color={'hotpink'} // Color of the line
661
+ length={1} // Length of the line
662
+ decay={1} // How fast the line fades away
663
+ local={false} // Wether to use the target's world or local positions
664
+ stride={0} // Min distance between previous and current point
665
+ interval={1} // Number of frames to wait before next calculation
666
+ target={undefined} // Optional target. This object will produce the trail.
667
+ attenuation={(width) => width} // A function to define the width in each point along it.
668
+ >
669
+ {/* If `target` is not defined, Trail will use the first `Object3D` child as the target. */}
670
+ <mesh>
671
+ <sphereGeometry />
672
+ <meshBasicMaterial />
673
+ </mesh>
674
+
675
+ {/* You can optionally define a custom meshLineMaterial to use. */}
676
+ {/* <meshLineMaterial color={"red"} /> */}
677
+ </Trail>
678
+ ```
679
+
680
+ 👉 Inspired by [TheSpite's Codevember 2021 #9](https://spite.github.io/codevember-2021/9/)
681
+
682
+ #### Sampler
683
+
684
+ [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/misc-sampler--sampler-st)
685
+
686
+ <p>
687
+ <a href="https://codesandbox.io/s/ehflx3">
688
+ <img width="20%" src="https://codesandbox.io/api/v1/sandboxes/ehflx3/screenshot.png" alt="Demo"/>
689
+ </a> <br />
690
+ <small>– <a href="https://codesandbox.io/s/ehflx3">Complex Demo</a> by <a href="https://twitter.com/CantBeFaraz">@CantBeFaraz</a></small> <br />
691
+ <small>– <a href="https://codesandbox.io/s/k6rcp2">Simple Demo</a> by <a href="https://twitter.com/ggsimm">@ggsimm</a></small>
692
+ </p>
693
+
694
+ Declarative abstraction around MeshSurfaceSampler & InstancedMesh.
695
+ It samples points from the passed mesh and transforms an InstancedMesh's matrix to distribute instances on the points.
696
+
697
+ Check the demos & code for more.
698
+
699
+ You can either pass a Mesh and InstancedMesh as children:
700
+
701
+ ```tsx
702
+ // This simple example scatters 1000 spheres on the surface of the sphere mesh.
703
+ <Sampler
704
+ weight={"normal"} // the name of the attribute to be used as sampling weight
705
+ transform={transformPoint} // a function that transforms each instance given a sample. See the examples for more.
706
+ >
707
+ <mesh>
708
+ <sphereGeometry args={[2]} />
709
+ </mesh>
710
+
711
+ <instancedMesh args={[null, null, 1_000]}>
712
+ <sphereGeometry args={[0.1]}>
713
+ </instancedMesh>
714
+ </Sampler>
715
+ ```
716
+
717
+ or use refs when you can't compose declaratively:
718
+
719
+ ```tsx
720
+ const { nodes } = useGLTF('my/mesh/url')
721
+ const mesh = useRef(nodes)
722
+ const instances = useRef()
723
+
724
+ return <>
725
+ <instancedMesh args={[null, null, 1_000]}>
726
+ <sphereGeometry args={[0.1]}>
727
+ </instancedMesh>
728
+
729
+ <Sampler mesh={mesh} instances={instances}>
730
+ </>
731
+ ```
732
+
733
+ #### ComputedAttribute
734
+
735
+ [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/misc-sampler--sampler-weight-st)
736
+
737
+ Create and attach an attribute declaratively.
738
+
739
+ ```tsx
740
+ <sphereGeometry>
741
+ <ComputedAttribute
742
+ // attribute will be added to the geometry with this name
743
+ name="my-attribute-name"
744
+ compute={(geometry) => {
745
+ // ...someLogic;
746
+ return new THREE.BufferAttribute([1, 2, 3], 1)
747
+ }}
748
+ // you can pass any BufferAttribute prop to this component, eg.
749
+ usage={THREE.StaticReadUsage}
750
+ />
751
+ </sphereGeometry>
752
+ ```
753
+
754
+ #### Clone
755
+
756
+ <p>
757
+ <a href="https://codesandbox.io/s/42glz0"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/42glz0/screenshot.png" alt="Demo"/></a>
758
+ </p>
759
+
760
+ Declarative abstraction around THREE.Object3D.clone. This is useful when you want to create a shallow copy of an existing fragment (and Object3D, Groups, etc) into your scene, for instance a group from a loaded GLTF. This clone is now re-usable, but it will still refer to the original geometries and materials.
761
+
762
+ ```ts
763
+ <Clone
764
+ /** Any pre-existing THREE.Object3D (groups, meshes, ...), or an array of objects */
765
+ object: THREE.Object3D | THREE.Object3D[]
766
+ /** Children will be placed within the object, or within the group that holds arrayed objects */
767
+ children?: React.ReactNode
768
+ /** Can clone materials and/or geometries deeply (default: false) */
769
+ deep?: boolean | 'materialsOnly' | 'geometriesOnly'
770
+ /** The property keys it will shallow-clone (material, geometry, visible, ...) */
771
+ keys?: string[]
772
+ /** Can either spread over props or fill in JSX children, applies to every mesh within */
773
+ inject?: MeshProps | React.ReactNode | ((object: THREE.Object3D) => React.ReactNode)
774
+ /** Short access castShadow, applied to every mesh within */
775
+ castShadow?: boolean
776
+ /** Short access receiveShadow, applied to every mesh within */
777
+ receiveShadow?: boolean
778
+ />
779
+ ```
780
+
781
+ You create a shallow clone by passing a pre-existing object to the `object` prop.
782
+
783
+ ```jsx
784
+ const { nodes } = useGLTF(url)
785
+ return (
786
+ <Clone object={nodes.table} />
787
+ ```
788
+
789
+ Or, multiple objects:
790
+
791
+ ```jsx
792
+ <Clone object={[nodes.foo, nodes.bar]} />
793
+ ```
794
+
795
+ You can dynamically insert objects, these will apply to anything that isn't a group or a plain object3d (meshes, lines, etc):
796
+
797
+ ```jsx
798
+ <Clone object={nodes.table} inject={<meshStandardMaterial color="green" />} />
799
+ ```
800
+
801
+ Or make inserts conditional:
802
+
803
+ ```jsx
804
+ <Clone object={nodes.table} inject={
805
+ {(object) => (object.name === 'table' ? <meshStandardMaterial color="green" /> : null)}
806
+ } />
807
+ ```
808
+
634
809
  #### useAnimations
635
810
 
636
811
  [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/abstractions-useanimations--use-animations-st)
@@ -732,7 +907,7 @@ This material makes your geometry distort following simplex noise.
732
907
 
733
908
  #### PointMaterial
734
909
 
735
- An antialiased round dot that always keeps the same size.
910
+ Antialiased round dots. It takes the same props as regular THREE.PointsMaterial
736
911
 
737
912
  ```jsx
738
913
  <points>
@@ -1001,7 +1176,7 @@ return <SomethingThatNeedsADepthBuffer depthBuffer={depthBuffer} />
1001
1176
 
1002
1177
  [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/misc-usefbo--use-fbo-st)
1003
1178
 
1004
- Creates a `THREE.WebGLRenderTarget` or `THREE.WebGLMultisampleRenderTarget`.
1179
+ Creates a `THREE.WebGLRenderTarget`.
1005
1180
 
1006
1181
  ```jsx
1007
1182
  const target = useFBO({
@@ -1103,6 +1278,56 @@ const ref = useIntersect((visible) => console.log('object is visible', visible))
1103
1278
  return <mesh ref={ref} />
1104
1279
  ```
1105
1280
 
1281
+ #### useBoxProjectedEnv
1282
+
1283
+ <p>
1284
+ <a href="https://codesandbox.io/s/s006f"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/s006f/screenshot.png" alt="Demo"/></a>
1285
+ </p>
1286
+
1287
+ The cheapest possible way of getting reflections in threejs. This will box-project the current environment map onto a plane. It returns an object that you need to spread over its material. The spread object contains a ref, onBeforeCompile and customProgramCacheKey. If you combine it with drei/CubeCamera you can "film" a single frame of the environment and feed it to the material, thereby getting realistic reflections at no cost. Align it with the position and scale properties.
1288
+
1289
+ ```jsx
1290
+ const projection = useBoxProjectedEnv(
1291
+ [0, 0, 0], // Position
1292
+ [1, 1, 1] // Scale
1293
+ )
1294
+
1295
+ <CubeCamera frames={1}>
1296
+ {(texture) => (
1297
+ <mesh>
1298
+ <planeGeometry />
1299
+ <meshStandardMaterial envMap={texture} {...projection} />
1300
+ </mesh>
1301
+ )}
1302
+ </CubeCamera>
1303
+ ```
1304
+
1305
+ #### useTrail
1306
+
1307
+ [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/misc-trail--use-trail-st)
1308
+
1309
+ A hook to obtain an array of points that make up a [Trail](#trail). You can use this array to drive your own `MeshLine` or make a trail out of anything you please.
1310
+
1311
+ Note: The hook returns a ref (`MutableRefObject<Vector3[]>`) this means updates to it will not trigger a re-draw, thus keeping this cheap.
1312
+
1313
+ ```js
1314
+ const points = useTrail(
1315
+ target, // Required target object. This object will produce the trail.
1316
+ {
1317
+ length, // Length of the line
1318
+ decay, // How fast the line fades away
1319
+ local, // Wether to use the target's world or local positions
1320
+ stride, // Min distance between previous and current point
1321
+ interval, // Number of frames to wait before next calculation
1322
+ }
1323
+ )
1324
+
1325
+ // To use...
1326
+ useFrame(() => {
1327
+ meshLineRef.current.position.setPoints(points.current)
1328
+ })
1329
+ ```
1330
+
1106
1331
  # Loading
1107
1332
 
1108
1333
  #### Loader
@@ -1338,27 +1563,12 @@ A wrapper around [THREE.Points](https://threejs.org/docs/#api/en/objects/Points)
1338
1563
  limit={1000} // Optional: max amount of items (for calculating buffer size)
1339
1564
  range={1000} // Optional: draw-range
1340
1565
  >
1341
- <pointsMaterial />
1566
+ <pointsMaterial vertexColors />
1342
1567
  <Point position={[1, 2, 3]} color="red" onClick={onClick} onPointerOver={onPointerOver} ... />
1343
1568
  // As many as you want, make them conditional, mount/unmount them, lazy load them, etc ...
1344
1569
  </Points>
1345
1570
  ```
1346
1571
 
1347
- If you have a material that supports vertex colors (like drei/PointMaterial) you can have individual colors!
1348
-
1349
- ```jsx
1350
- <Points>
1351
- <PointMaterial />
1352
- <Point color="hotpink" />
1353
- ```
1354
-
1355
- Otherwise use any material you like:
1356
-
1357
- ```jsx
1358
- <Points>
1359
- <pointsMaterial vertexColors size={10} />
1360
- ```
1361
-
1362
1572
  If you just want to use buffers for position, color and size, you can use the alternative API:
1363
1573
 
1364
1574
  ```jsx
@@ -1456,7 +1666,7 @@ A very fast, but often good-enough bounds-only raycast for meshes. You can use t
1456
1666
 
1457
1667
  #### AdaptiveDpr
1458
1668
 
1459
- Drop this component into your scene and it will cut the pixel-ratio on [regress](#) according to the canvases perrformance min/max settings. This allows you to temporarily reduce visuals for more performance, for instance when the camera moves (look into drei's controls `regress` flag). Optionally you can set the canvas to a pixelated filter, which would be even faster.
1669
+ Drop this component into your scene and it will cut the pixel-ratio on regress according to the canvas's performance min/max settings. This allows you to temporarily reduce visual quality in exchange for more performance, for instance when the camera moves (look into drei's controls regress flag). Optionally, you can set the canvas to a pixelated filter, which would be even faster.
1460
1670
 
1461
1671
  ```jsx
1462
1672
  <AdaptiveDpr pixelated />
@@ -1489,7 +1699,7 @@ useBVH(mesh)
1489
1699
 
1490
1700
  [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/staging-center--default-story)
1491
1701
 
1492
- Calculates a boundary box and centers its children accordingly. `alignTop` makes adjusts it so that it's sits flush on y=0.
1702
+ Calculates a boundary box and centers its children accordingly. `alignTop` adjusts it so that it sits flush on y=0.
1493
1703
 
1494
1704
  ```jsx
1495
1705
  <Center alignTop>
@@ -1506,7 +1716,7 @@ Calculates a boundary box and centers its children accordingly. `alignTop` makes
1506
1716
  Calculates a boundary box and centers the camera accordingly. If you are using controls, make sure to pass them the `makeDefault` prop. `fit` fits the current view on first render. `clip` sets the cameras near/far planes.
1507
1717
 
1508
1718
  ```jsx
1509
- <Bounds fit clip damping={6} margin={1.2}>
1719
+ <Bounds fit clip observe damping={6} margin={1.2}>
1510
1720
  <mesh />
1511
1721
  </Bounds>
1512
1722
  ```
@@ -1639,7 +1849,7 @@ A cheap canvas-texture-based circular gradient.
1639
1849
  A [contact shadow](https://threejs.org/examples/#webgl_shadow_contact) implementation, facing upwards (positive Y) by default. `scale` can be a positive number or a 2D array `[x: number, y: number]`.
1640
1850
 
1641
1851
  ```jsx
1642
- <ContactShadows opacity={1} scale={10} blur={1} far={10} resolution={256} />
1852
+ <ContactShadows opacity={1} scale={10} blur={1} far={10} resolution={256} color="#000000" />
1643
1853
  ```
1644
1854
 
1645
1855
  Since this is a rather expensive effect you can limit the amount of frames it renders when your objects are static. For instance making it render only once:
@@ -1680,6 +1890,9 @@ function Foo() {
1680
1890
 
1681
1891
  <p>
1682
1892
  <a href="https://codesandbox.io/s/t4l0f"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/t4l0f/screenshot.png" alt="Demo"/></a>
1893
+ <a href="https://codesandbox.io/s/mih0lx"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/mih0lx/screenshot.png" alt="Demo"/></a>
1894
+ <a href="https://codesandbox.io/s/e662p3"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/e662p3/screenshot.png" alt="Demo"/></a>
1895
+ <a href="https://codesandbox.io/s/lwo219"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/lwo219/screenshot.png" alt="Demo"/></a>
1683
1896
  </p>
1684
1897
 
1685
1898
  Sets up a global cubemap, which affects the default `scene.environment`, and optionally `scene.background`, unless a custom scene has been passed. A selection of [presets](src/helpers/environment-assets.ts) from [HDRI Haven](https://hdrihaven.com/) are available for convenience. If you pass an array of files it will use THREE.CubeTextureLoader.
@@ -1688,11 +1901,11 @@ Sets up a global cubemap, which affects the default `scene.environment`, and opt
1688
1901
 
1689
1902
  ```jsx
1690
1903
  <Environment
1691
- background={false}
1904
+ background={false} // can be true, false or "only" (which only sets the background) (default: false)
1692
1905
  files={['px.png', 'nx.png', 'py.png', 'ny.png', 'pz.png', 'nz.png']}
1693
1906
  path="/"
1694
1907
  preset={null}
1695
- scene={undefined} // adds the ability to pass a custom THREE.Scene
1908
+ scene={undefined} // adds the ability to pass a custom THREE.Scene, can also be a ref
1696
1909
  />
1697
1910
  ```
1698
1911
 
@@ -1702,6 +1915,60 @@ If you provide a single string it will use THREE.RGBELoader.
1702
1915
  <Environment files="file.hdr" />
1703
1916
  ```
1704
1917
 
1918
+ If you already have a cube texture you can pass it directly:
1919
+
1920
+ ```jsx
1921
+ <CubeCamera>{(texture) => <Environment map={texture} />}</CubeCamera>
1922
+ ```
1923
+
1924
+ If you provide children you can even render a custom environment. It will render the contents into an off-buffer and film a single frame with a cube camera (whose props you can configure: near=1, far=1000, resolution=256).
1925
+
1926
+ ```jsx
1927
+ <Environment background near={1} far={1000} resolution={256}>
1928
+ <mesh scale={100}>
1929
+ <sphereGeometry args={[1, 64, 64]} />
1930
+ <meshBasicMaterial map={texture} side={THREE.BackSide} />
1931
+ </mesh>
1932
+ </Environment>
1933
+ ```
1934
+
1935
+ You can even mix a generic HDRI environment into a custom one with either the `preset` or the `files` prop.
1936
+
1937
+ ```jsx
1938
+ return (
1939
+ <Environment background near={1} far={1000} resolution={256} preset="warehouse">
1940
+ <mesh />
1941
+ ```
1942
+
1943
+ Declarative environment content can also animate with the `frames` prop, the envmap can be live. Give it a low resolution and this will happen at very little cost
1944
+
1945
+ ```jsx
1946
+ return (
1947
+ <Environment frames={Infinity} resolution={256}>
1948
+ <Float>
1949
+ <mesh />
1950
+ </Float>
1951
+ ```
1952
+
1953
+ #### Lightformer
1954
+
1955
+ <p>
1956
+ <a href="https://codesandbox.io/s/lwo219"><img width="20%" src="https://codesandbox.io/api/v1/sandboxes/lwo219/screenshot.png" alt="Demo"/></a>
1957
+ </p>
1958
+
1959
+ This component draws flat rectangles, circles or rings, mimicking the look of a light-former. You can set the output `intensity`, which will effect emissiveness once you put it into an HDRI `<Environment>`, where it mostly belong. It will act like a real light without the expense, you can have as many as you want.
1960
+
1961
+ ```jsx
1962
+ <Environment>
1963
+ <Lightformer
1964
+ form="rect" // circle | ring | rect (optional, default = rect)
1965
+ intensity={1} // power level (optional = 1)
1966
+ color="white" // (optional = white)
1967
+ scale={[10, 5]} // Scale it any way you prefer (optional = [1, 1])
1968
+ target={[0, 0, 0]} // Target position (optional = undefined)
1969
+ />
1970
+ ```
1971
+
1705
1972
  #### Sky
1706
1973
 
1707
1974
  [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.pmnd.rs/?path=/story/staging-sky--sky-st)
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),r=require("@react-three/fiber"),t=require("react"),n=require("three-stdlib");function o(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function s(e){if(e&&e.__esModule)return e;var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}var a=o(e),c=s(t);const u=t.forwardRef((({camera:e,makeDefault:o,regress:s,domElement:u,onChange:i,onStart:l,onEnd:d,...f},b)=>{const m=r.useThree((({invalidate:e})=>e)),v=r.useThree((({camera:e})=>e)),h=r.useThree((({gl:e})=>e)),p=r.useThree((({events:e})=>e)),E=r.useThree((({set:e})=>e)),g=r.useThree((({get:e})=>e)),j=r.useThree((({performance:e})=>e)),O=e||v,T=u||("boolean"!=typeof p.connected?p.connected:h.domElement),y=t.useMemo((()=>new n.ArcballControls(O)),[O]);return r.useFrame((()=>{y.enabled&&y.update()})),t.useEffect((()=>{const e=e=>{m(),s&&j.regress(),i&&i(e)};return y.connect(T),y.addEventListener("change",e),l&&y.addEventListener("start",l),d&&y.addEventListener("end",d),()=>{y.removeEventListener("change",e),l&&y.removeEventListener("start",l),d&&y.removeEventListener("end",d),y.dispose()}}),[T,i,l,d,s,y,m]),t.useEffect((()=>{if(o){const e=g().controls;return E({controls:y}),()=>E({controls:e})}}),[o,y]),c.createElement("primitive",a.default({ref:b,object:y},f))}));exports.ArcballControls=u;
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),r=require("@react-three/fiber"),t=require("react"),n=require("three-stdlib");function s(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function o(e){if(e&&e.__esModule)return e;var r=Object.create(null);return e&&Object.keys(e).forEach((function(t){if("default"!==t){var n=Object.getOwnPropertyDescriptor(e,t);Object.defineProperty(r,t,n.get?n:{enumerable:!0,get:function(){return e[t]}})}})),r.default=e,Object.freeze(r)}var a=s(e),u=o(t);const c=t.forwardRef((({camera:e,makeDefault:s,regress:o,domElement:c,onChange:i,onStart:l,onEnd:f,...d},m)=>{const v=r.useThree((e=>e.invalidate)),b=r.useThree((e=>e.camera)),h=r.useThree((e=>e.gl)),E=r.useThree((e=>e.events)),p=r.useThree((e=>e.set)),g=r.useThree((e=>e.get)),j=r.useThree((e=>e.performance)),O=e||b,T=c||E.connected||h.domElement,L=t.useMemo((()=>new n.ArcballControls(O)),[O]);return r.useFrame((()=>{L.enabled&&L.update()})),t.useEffect((()=>(L.connect(T),()=>{L.dispose()})),[T,o,L,v]),t.useEffect((()=>{const e=e=>{v(),o&&j.regress(),i&&i(e)};return L.addEventListener("change",e),l&&L.addEventListener("start",l),f&&L.addEventListener("end",f),()=>{L.removeEventListener("change",e),l&&L.removeEventListener("start",l),f&&L.removeEventListener("end",f)}}),[i,l,f]),t.useEffect((()=>{if(s){const e=g().controls;return p({controls:L}),()=>p({controls:e})}}),[s,L]),u.createElement("primitive",a.default({ref:m,object:L},d))}));exports.ArcballControls=c;
@@ -14,33 +14,23 @@ const ArcballControls = /*#__PURE__*/forwardRef(({
14
14
  onEnd,
15
15
  ...restProps
16
16
  }, ref) => {
17
- const invalidate = useThree(({
18
- invalidate
19
- }) => invalidate);
20
- const defaultCamera = useThree(({
21
- camera
22
- }) => camera);
23
- const gl = useThree(({
24
- gl
25
- }) => gl);
26
- const events = useThree(({
27
- events
28
- }) => events);
29
- const set = useThree(({
30
- set
31
- }) => set);
32
- const get = useThree(({
33
- get
34
- }) => get);
35
- const performance = useThree(({
36
- performance
37
- }) => performance);
17
+ const invalidate = useThree(state => state.invalidate);
18
+ const defaultCamera = useThree(state => state.camera);
19
+ const gl = useThree(state => state.gl);
20
+ const events = useThree(state => state.events);
21
+ const set = useThree(state => state.set);
22
+ const get = useThree(state => state.get);
23
+ const performance = useThree(state => state.performance);
38
24
  const explCamera = camera || defaultCamera;
39
- const explDomElement = domElement || (typeof events.connected !== 'boolean' ? events.connected : gl.domElement);
25
+ const explDomElement = domElement || events.connected || gl.domElement;
40
26
  const controls = useMemo(() => new ArcballControls$1(explCamera), [explCamera]);
41
27
  useFrame(() => {
42
28
  if (controls.enabled) controls.update();
43
29
  });
30
+ useEffect(() => {
31
+ controls.connect(explDomElement);
32
+ return () => void controls.dispose();
33
+ }, [explDomElement, regress, controls, invalidate]);
44
34
  useEffect(() => {
45
35
  const callback = e => {
46
36
  invalidate();
@@ -48,7 +38,6 @@ const ArcballControls = /*#__PURE__*/forwardRef(({
48
38
  if (onChange) onChange(e);
49
39
  };
50
40
 
51
- controls.connect(explDomElement);
52
41
  controls.addEventListener('change', callback);
53
42
  if (onStart) controls.addEventListener('start', onStart);
54
43
  if (onEnd) controls.addEventListener('end', onEnd);
@@ -56,9 +45,8 @@ const ArcballControls = /*#__PURE__*/forwardRef(({
56
45
  controls.removeEventListener('change', callback);
57
46
  if (onStart) controls.removeEventListener('start', onStart);
58
47
  if (onEnd) controls.removeEventListener('end', onEnd);
59
- controls.dispose();
60
48
  };
61
- }, [explDomElement, onChange, onStart, onEnd, regress, controls, invalidate]);
49
+ }, [onChange, onStart, onEnd]);
62
50
  useEffect(() => {
63
51
  if (makeDefault) {
64
52
  const old = get().controls;
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("three"),r=require("@react-three/fiber");function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var a=o(e),n=o(t);const c=e=>e&&e.isOrthographicCamera,i=a.createContext(null);exports.Bounds=function({children:e,damping:t=6,fit:o,clip:s,margin:m=1.2,eps:u=.01,onFit:f}){const p=a.useRef(null),l=r.useThree((e=>e.camera)),x=r.useThree((e=>e.controls)),h=r.useThree((e=>e.invalidate)),y=a.useRef(f);function z(e,t){return Math.abs(e.x-t.x)<u&&Math.abs(e.y-t.y)<u&&Math.abs(e.z-t.z)<u}function d(e,t,r,o){e.x=n.MathUtils.damp(e.x,t.x,r,o),e.y=n.MathUtils.damp(e.y,t.y,r,o),e.z=n.MathUtils.damp(e.z,t.z,r,o)}y.current=f;const[M]=a.useState((()=>({animating:!1,focus:new n.Vector3,camera:new n.Vector3,zoom:1}))),[g]=a.useState((()=>({focus:new n.Vector3,camera:new n.Vector3,zoom:1}))),[b]=a.useState((()=>new n.Box3)),w=a.useMemo((()=>{function e(){const e=b.getSize(new n.Vector3),t=b.getCenter(new n.Vector3),r=Math.max(e.x,e.y,e.z),o=c(l)?4*r:r/(2*Math.atan(Math.PI*l.fov/360)),a=c(l)?4*r:o/l.aspect,i=m*Math.max(o,a);return{box:b,size:e,center:t,distance:i}}return{getSize:e,refresh(t){var r;if((r=t)&&r.isObject3D?b.setFromObject(t):(e=>e&&e.isBox3)(t)?b.copy(t):p.current&&b.setFromObject(p.current),b.isEmpty()){const e=l.position.length()||10;b.setFromCenterAndSize(new n.Vector3,new n.Vector3(e,e,e))}if("OrthographicTrackballControls"===(null==x?void 0:x.constructor.name)){const{distance:t}=e(),r=l.position.clone().sub(x.target).normalize().multiplyScalar(t),o=x.target.clone().add(r);l.position.copy(o)}return this},clip(){const{distance:t}=e();return x&&(x.maxDistance=10*t),l.near=t/100,l.far=100*t,l.updateProjectionMatrix(),x&&x.update(),this},fit(){M.camera.copy(l.position),x&&M.focus.copy(x.target);const{center:r,distance:o}=e(),a=r.clone().sub(l.position).normalize().multiplyScalar(o);if(g.camera.copy(r).sub(a),g.focus.copy(r),c(l)){M.zoom=l.zoom;let e=0,o=0;const a=[new n.Vector3(b.min.x,b.min.y,b.min.z),new n.Vector3(b.min.x,b.max.y,b.min.z),new n.Vector3(b.min.x,b.min.y,b.max.z),new n.Vector3(b.min.x,b.max.y,b.max.z),new n.Vector3(b.max.x,b.max.y,b.max.z),new n.Vector3(b.max.x,b.max.y,b.min.z),new n.Vector3(b.max.x,b.min.y,b.max.z),new n.Vector3(b.max.x,b.min.y,b.min.z)];r.applyMatrix4(l.matrixWorldInverse);for(const t of a)t.applyMatrix4(l.matrixWorldInverse),e=Math.max(e,Math.abs(t.y-r.y)),o=Math.max(o,Math.abs(t.x-r.x));e*=2,o*=2;const c=(l.top-l.bottom)/e,i=(l.right-l.left)/o;g.zoom=Math.min(c,i)/m,t||(l.zoom=g.zoom,l.updateProjectionMatrix())}return t?M.animating=!0:(l.position.copy(g.camera),l.lookAt(g.focus),x&&(x.target.copy(g.focus),x.update()),h()),y.current&&y.current(this.getSize()),this}}}),[b,l,x,m,t,h]);return a.useLayoutEffect((()=>{if(w.refresh(),o&&w.fit(),s&&w.clip(),x){const e=()=>M.animating=!1;return x.addEventListener("start",e),()=>x.removeEventListener("start",e)}}),[s,o,x]),r.useFrame(((e,r)=>{if(M.animating){if(d(M.focus,g.focus,t,r),d(M.camera,g.camera,t,r),M.zoom=n.MathUtils.damp(M.zoom,g.zoom,t,r),l.position.copy(M.camera),c(l)&&(l.zoom=M.zoom,l.updateProjectionMatrix()),x?(x.target.copy(M.focus),x.update()):l.lookAt(M.focus),h(),c(l)&&!(Math.abs(M.zoom-g.zoom)<u))return;if(!c(l)&&!z(M.camera,g.camera))return;if(x&&!z(M.focus,g.focus))return;M.animating=!1}})),a.createElement("group",{ref:p},a.createElement(i.Provider,{value:w},e))},exports.useBounds=function(){return a.useContext(i)};
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("react"),t=require("three"),r=require("@react-three/fiber");function o(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var o=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,o.get?o:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var a=o(e),n=o(t);const c=e=>e&&e.isOrthographicCamera,i=a.createContext(null);exports.Bounds=function({children:e,damping:t=6,fit:o,clip:s,observe:u,margin:m=1.2,eps:f=.01,onFit:l}){const p=a.useRef(null),{camera:x,invalidate:y,size:z,controls:d}=r.useThree(),h=d,M=a.useRef(l);function g(e,t){return Math.abs(e.x-t.x)<f&&Math.abs(e.y-t.y)<f&&Math.abs(e.z-t.z)<f}function b(e,t,r,o){e.x=n.MathUtils.damp(e.x,t.x,r,o),e.y=n.MathUtils.damp(e.y,t.y,r,o),e.z=n.MathUtils.damp(e.z,t.z,r,o)}M.current=l;const[w]=a.useState((()=>({animating:!1,focus:new n.Vector3,camera:new n.Vector3,zoom:1}))),[v]=a.useState((()=>({focus:new n.Vector3,camera:new n.Vector3,zoom:1}))),[V]=a.useState((()=>new n.Box3)),j=a.useMemo((()=>{function e(){const e=V.getSize(new n.Vector3),t=V.getCenter(new n.Vector3),r=Math.max(e.x,e.y,e.z),o=c(x)?4*r:r/(2*Math.atan(Math.PI*x.fov/360)),a=c(x)?4*r:o/x.aspect,i=m*Math.max(o,a);return{box:V,size:e,center:t,distance:i}}return{getSize:e,refresh(t){if((r=t)&&r.isBox3)V.copy(t);else{const e=t||p.current;e.updateWorldMatrix(!0,!0),V.setFromObject(e)}var r;if(V.isEmpty()){const e=x.position.length()||10;V.setFromCenterAndSize(new n.Vector3,new n.Vector3(e,e,e))}if("OrthographicTrackballControls"===(null==h?void 0:h.constructor.name)){const{distance:t}=e(),r=x.position.clone().sub(h.target).normalize().multiplyScalar(t),o=h.target.clone().add(r);x.position.copy(o)}return this},clip(){const{distance:t}=e();return h&&(h.maxDistance=10*t),x.near=t/100,x.far=100*t,x.updateProjectionMatrix(),h&&h.update(),y(),this},fit(){w.camera.copy(x.position),h&&w.focus.copy(h.target);const{center:r,distance:o}=e(),a=r.clone().sub(x.position).normalize().multiplyScalar(o);if(v.camera.copy(r).sub(a),v.focus.copy(r),c(x)){w.zoom=x.zoom;let e=0,o=0;const a=[new n.Vector3(V.min.x,V.min.y,V.min.z),new n.Vector3(V.min.x,V.max.y,V.min.z),new n.Vector3(V.min.x,V.min.y,V.max.z),new n.Vector3(V.min.x,V.max.y,V.max.z),new n.Vector3(V.max.x,V.max.y,V.max.z),new n.Vector3(V.max.x,V.max.y,V.min.z),new n.Vector3(V.max.x,V.min.y,V.max.z),new n.Vector3(V.max.x,V.min.y,V.min.z)];r.applyMatrix4(x.matrixWorldInverse);for(const t of a)t.applyMatrix4(x.matrixWorldInverse),e=Math.max(e,Math.abs(t.y-r.y)),o=Math.max(o,Math.abs(t.x-r.x));e*=2,o*=2;const c=(x.top-x.bottom)/e,i=(x.right-x.left)/o;v.zoom=Math.min(c,i)/m,t||(x.zoom=v.zoom,x.updateProjectionMatrix())}return t?w.animating=!0:(x.position.copy(v.camera),x.lookAt(v.focus),h&&(h.target.copy(v.focus),h.update())),M.current&&M.current(this.getSize()),y(),this}}}),[V,x,h,m,t,y]);a.useLayoutEffect((()=>{if(h){const e=()=>w.animating=!1;return h.addEventListener("start",e),()=>h.removeEventListener("start",e)}}),[h]);const O=a.useRef(0);return a.useLayoutEffect((()=>{(u||0==O.current++)&&(j.refresh(),o&&j.fit(),s&&j.clip())}),[z,s,o,u]),r.useFrame(((e,r)=>{if(w.animating){if(b(w.focus,v.focus,t,r),b(w.camera,v.camera,t,r),w.zoom=n.MathUtils.damp(w.zoom,v.zoom,t,r),x.position.copy(w.camera),c(x)&&(x.zoom=w.zoom,x.updateProjectionMatrix()),h?(h.target.copy(w.focus),h.update()):x.lookAt(w.focus),y(),c(x)&&!(Math.abs(w.zoom-v.zoom)<f))return;if(!c(x)&&!g(w.camera,v.camera))return;if(h&&!g(w.focus,v.focus))return;w.animating=!1}})),a.createElement("group",{ref:p},a.createElement(i.Provider,{value:j},e))},exports.useBounds=function(){return a.useContext(i)};
package/core/Bounds.d.ts CHANGED
@@ -16,9 +16,10 @@ export declare type BoundsProps = JSX.IntrinsicElements['group'] & {
16
16
  damping?: number;
17
17
  fit?: boolean;
18
18
  clip?: boolean;
19
+ observe?: boolean;
19
20
  margin?: number;
20
21
  eps?: number;
21
22
  onFit?: (data: SizeProps) => void;
22
23
  };
23
- export declare function Bounds({ children, damping, fit, clip, margin, eps, onFit }: BoundsProps): JSX.Element;
24
+ export declare function Bounds({ children, damping, fit, clip, observe, margin, eps, onFit }: BoundsProps): JSX.Element;
24
25
  export declare function useBounds(): BoundsApi;
package/core/Bounds.js CHANGED
@@ -4,8 +4,6 @@ import { useThree, useFrame } from '@react-three/fiber';
4
4
 
5
5
  const isOrthographic = def => def && def.isOrthographicCamera;
6
6
 
7
- const isObject3D = def => def && def.isObject3D;
8
-
9
7
  const isBox3 = def => def && def.isBox3;
10
8
 
11
9
  const context = /*#__PURE__*/React.createContext(null);
@@ -14,15 +12,19 @@ function Bounds({
14
12
  damping = 6,
15
13
  fit,
16
14
  clip,
15
+ observe,
17
16
  margin = 1.2,
18
17
  eps = 0.01,
19
18
  onFit
20
19
  }) {
21
20
  const ref = React.useRef(null);
22
- const camera = useThree(state => state.camera); // @ts-expect-error new in @react-three/fiber@7.0.5
23
-
24
- const controls = useThree(state => state.controls);
25
- const invalidate = useThree(state => state.invalidate);
21
+ const {
22
+ camera,
23
+ invalidate,
24
+ size,
25
+ controls: controlsImpl
26
+ } = useThree();
27
+ const controls = controlsImpl;
26
28
  const onFitRef = React.useRef(onFit);
27
29
  onFitRef.current = onFit;
28
30
 
@@ -68,7 +70,11 @@ function Bounds({
68
70
  getSize,
69
71
 
70
72
  refresh(object) {
71
- if (isObject3D(object)) box.setFromObject(object);else if (isBox3(object)) box.copy(object);else if (ref.current) box.setFromObject(ref.current);
73
+ if (isBox3(object)) box.copy(object);else {
74
+ const target = object || ref.current;
75
+ target.updateWorldMatrix(true, true);
76
+ box.setFromObject(target);
77
+ }
72
78
 
73
79
  if (box.isEmpty()) {
74
80
  const max = camera.position.length() || 10;
@@ -97,6 +103,7 @@ function Bounds({
97
103
  camera.far = distance * 100;
98
104
  camera.updateProjectionMatrix();
99
105
  if (controls) controls.update();
106
+ invalidate();
100
107
  return this;
101
108
  },
102
109
 
@@ -147,21 +154,16 @@ function Bounds({
147
154
  controls.target.copy(goal.focus);
148
155
  controls.update();
149
156
  }
150
-
151
- invalidate();
152
157
  }
153
158
 
154
159
  if (onFitRef.current) onFitRef.current(this.getSize());
160
+ invalidate();
155
161
  return this;
156
162
  }
157
163
 
158
164
  };
159
165
  }, [box, camera, controls, margin, damping, invalidate]);
160
166
  React.useLayoutEffect(() => {
161
- api.refresh();
162
- if (fit) api.fit();
163
- if (clip) api.clip();
164
-
165
167
  if (controls) {
166
168
  // Try to prevent drag hijacking
167
169
  const callback = () => current.animating = false;
@@ -169,7 +171,16 @@ function Bounds({
169
171
  controls.addEventListener('start', callback);
170
172
  return () => controls.removeEventListener('start', callback);
171
173
  }
172
- }, [clip, fit, controls]);
174
+ }, [controls]); // Scale pointer on window resize
175
+
176
+ const count = React.useRef(0);
177
+ React.useLayoutEffect(() => {
178
+ if (observe || count.current++ === 0) {
179
+ api.refresh();
180
+ if (fit) api.fit();
181
+ if (clip) api.clip();
182
+ }
183
+ }, [size, clip, fit, observe]);
173
184
  useFrame((state, delta) => {
174
185
  if (current.animating) {
175
186
  damp(current.focus, goal.focus, damping, delta);
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,"__esModule",{value:!0});var e=require("@babel/runtime/helpers/extends"),t=require("react"),r=require("lodash.pick");function a(e){return e&&"object"==typeof e&&"default"in e?e:{default:e}}function n(e){if(e&&e.__esModule)return e;var t=Object.create(null);return e&&Object.keys(e).forEach((function(r){if("default"!==r){var a=Object.getOwnPropertyDescriptor(e,r);Object.defineProperty(t,r,a.get?a:{enumerable:!0,get:function(){return e[r]}})}})),t.default=e,Object.freeze(t)}var o=a(e),c=n(t),i=a(r);function l(e,{keys:t=["near","far","color","distance","decay","penumbra","angle","intensity","skeleton","visible","castShadow","receiveShadow","morphTargetDictionary","morphTargetInfluences","name","geometry","material","position","rotation","scale","up","userData"],deep:r,inject:a,castShadow:n,receiveShadow:o}){let l=i.default(e,t);return r&&(l.geometry&&"materialsOnly"!==r&&(l.geometry=l.geometry.clone()),l.material&&"geometriesOnly"!==r&&(l.material=l.material.clone())),a&&(l="function"==typeof a?{...l,children:a(e)}:c.isValidElement(a)?{...l,children:a}:{...l,...a}),"Mesh"===e.type&&(n&&(l.castShadow=!0),o&&(l.receiveShadow=!0)),l}const u=c.forwardRef((({object:e,children:t,deep:r,castShadow:a,receiveShadow:n,inject:i,keys:d,...s},f)=>{const p={keys:d,deep:r,inject:i,castShadow:a,receiveShadow:n};if(Array.isArray(e))return c.createElement("group",o.default({},s,{ref:f}),e.map((e=>c.createElement(u,o.default({key:e.uuid,object:e},p)))),t);const{children:y,...m}=l(e,p),h=e.type[0].toLowerCase()+e.type.slice(1);return c.createElement(h,o.default({},m,s,{ref:f}),(null==e?void 0:e.children).map((e=>{let t={},r=e.type[0].toLowerCase()+e.type.slice(1);return"group"===r||"object3D"===r?(r=u,t={object:e,...p}):t=l(e,p),c.createElement(r,o.default({key:e.uuid},t))})),t,y)}));exports.Clone=u;