diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json index 217fa53fc..c81521fa3 100644 --- a/.codesandbox/ci.json +++ b/.codesandbox/ci.json @@ -1,5 +1,5 @@ { "sandboxes": ["4nc1u", "bfplr", "1wh6f"], "packages": ["dist"], - "node": "18" + "node": "20" } diff --git a/.storybook/stories/AccumulativeShadows.stories.tsx b/.storybook/stories/AccumulativeShadows.stories.tsx index eb71abacc..6aa968cb9 100644 --- a/.storybook/stories/AccumulativeShadows.stories.tsx +++ b/.storybook/stories/AccumulativeShadows.stories.tsx @@ -1,7 +1,7 @@ import * as THREE from 'three' import * as React from 'react' import { ComponentProps } from 'react' -import { FlakesTexture } from 'three/examples/jsm/textures/FlakesTexture' +import { FlakesTexture } from 'three-stdlib' import { Meta, StoryObj } from '@storybook/react' import { Setup } from '../Setup' @@ -48,7 +48,7 @@ function Suzi(props: ComponentProps<'group'>) { material.color.set('orange') material.roughness = 0 material.normalMap = new THREE.CanvasTexture( - new FlakesTexture(), + new FlakesTexture() as HTMLCanvasElement, THREE.UVMapping, THREE.RepeatWrapping, THREE.RepeatWrapping diff --git a/.storybook/stories/ArcballControls.stories.tsx b/.storybook/stories/ArcballControls.stories.tsx index 10b61c250..550ce2d66 100644 --- a/.storybook/stories/ArcballControls.stories.tsx +++ b/.storybook/stories/ArcballControls.stories.tsx @@ -47,7 +47,7 @@ const CustomCamera = ({ ...props }: ComponentProps) => { * we will render our scene in a render target and use it as a map. */ const fbo = useFBO(400, 400) - const virtualCamera = useRef() + const virtualCamera = useRef(null!) const [virtualScene] = useState(() => new Scene()) useFrame(({ gl }) => { diff --git a/.storybook/stories/BBAnchor.stories.tsx b/.storybook/stories/BBAnchor.stories.tsx index c2ceb5e01..a3f6aad7d 100644 --- a/.storybook/stories/BBAnchor.stories.tsx +++ b/.storybook/stories/BBAnchor.stories.tsx @@ -30,7 +30,7 @@ function BBAnchorScene({ drawBoundingBox: boolean children?: React.ReactNode }) { - const ref = React.useRef(null) + const ref = React.useRef>(null!) useHelper(drawBoundingBox && ref, BoxHelper, 'cyan') diff --git a/.storybook/stories/Bvh.stories.tsx b/.storybook/stories/Bvh.stories.tsx index 57d793278..e0aa1f650 100644 --- a/.storybook/stories/Bvh.stories.tsx +++ b/.storybook/stories/Bvh.stories.tsx @@ -23,7 +23,7 @@ export default { type Story = StoryObj function TorusBVH({ z = 0, ...props }: { z: number } & React.ComponentProps) { - const mesh = React.useRef>(null!) + const mesh = React.useRef>(null!) useHelper(mesh, MeshBVHHelper) @@ -122,7 +122,7 @@ const DebugRayCast = ({ grp }) => { } function Scene(props: React.ComponentProps) { - const grp = React.useRef>(null) + const grp = React.useRef>(null) const { raycaster } = useThree() raycaster.firstHitOnly = true diff --git a/.storybook/stories/CameraControls.stories.tsx b/.storybook/stories/CameraControls.stories.tsx index c093e8c78..40ec47c48 100644 --- a/.storybook/stories/CameraControls.stories.tsx +++ b/.storybook/stories/CameraControls.stories.tsx @@ -1,6 +1,6 @@ import { createPortal, useFrame } from '@react-three/fiber' import React, { ComponentProps, useRef, useState } from 'react' -import { Scene } from 'three' +import * as THREE from 'three' import { Meta, StoryObj } from '@storybook/react' import { Setup } from '../Setup' @@ -47,9 +47,9 @@ const CameraControlsScene2 = (props: ComponentProps) => { * we will render our scene in a render target and use it as a map. */ const fbo = useFBO(400, 400) - const virtualCamera = useRef() - const [virtualScene] = useState(() => new Scene()) - const cameraControlRef = useRef(null) + const virtualCamera = useRef(null!) + const [virtualScene] = useState(() => new THREE.Scene()) + const cameraControlRef = useRef(null!) useFrame(({ gl }) => { if (virtualCamera.current) { diff --git a/.storybook/stories/CubeCamera.stories.tsx b/.storybook/stories/CubeCamera.stories.tsx index 55537eaa8..10addadf1 100644 --- a/.storybook/stories/CubeCamera.stories.tsx +++ b/.storybook/stories/CubeCamera.stories.tsx @@ -22,18 +22,16 @@ export default { type Story = StoryObj -declare global { - namespace JSX { - interface IntrinsicElements { - axisHelper: object - } +declare module '@react-three/fiber' { + interface ThreeElements { + axisHelper: object } } function Sphere({ offset = 0, ...props }: ComponentProps & { offset?: number }) { - const ref = React.useRef>(null) + const ref = React.useRef(null!) useFrame(({ clock }) => { - ref.current!.position.y = Math.sin(offset + clock.elapsedTime) * 5 + ref.current.position.y = Math.sin(offset + clock.elapsedTime) * 5 }) return ( diff --git a/.storybook/stories/CurveModifier.stories.tsx b/.storybook/stories/CurveModifier.stories.tsx index 78bc8ec4b..a2ecf50cc 100644 --- a/.storybook/stories/CurveModifier.stories.tsx +++ b/.storybook/stories/CurveModifier.stories.tsx @@ -9,15 +9,13 @@ import { CurveModifier, CurveModifierRef } from '../../src' extend({ StdText: TextGeometry }) -type TextGeometryImpl = JSX.IntrinsicElements['extrudeGeometry'] & { +type TextGeometryImpl = ThreeElements['extrudeGeometry'] & { args: [string, TextGeometryParameters] } -declare global { - namespace JSX { - interface IntrinsicElements { - stdText: TextGeometryImpl - } +declare module '@react-three/fiber' { + interface ThreeElements { + stdText: TextGeometryImpl } } diff --git a/.storybook/stories/Extrude.stories.tsx b/.storybook/stories/Extrude.stories.tsx index a13488d5a..a3d26fcf0 100644 --- a/.storybook/stories/Extrude.stories.tsx +++ b/.storybook/stories/Extrude.stories.tsx @@ -22,7 +22,7 @@ export default { type Story = StoryObj function ExtrudeScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( <> diff --git a/.storybook/stories/FaceControls.stories.tsx b/.storybook/stories/FaceControls.stories.tsx index 9855aa21e..436701a2f 100644 --- a/.storybook/stories/FaceControls.stories.tsx +++ b/.storybook/stories/FaceControls.stories.tsx @@ -7,7 +7,7 @@ import * as easing from 'maath/easing' import { Setup } from '../Setup' import { FaceLandmarker, FaceControls, Box, WebcamVideoTexture } from '../../src' -import { ComponentProps, ElementRef, useRef, useState } from 'react' +import { ComponentProps, ComponentRef, useRef, useState } from 'react' import { FaceLandmarkerResult } from '@mediapipe/tasks-vision' import { useFrame, useThree } from '@react-three/fiber' @@ -55,8 +55,8 @@ export const FaceControlsSt = { // function FaceControlsScene2(props: ComponentProps) { - const faceLandmarkerRef = useRef>(null) - const videoTextureRef = useRef>(null) + const faceLandmarkerRef = useRef>(null) + const videoTextureRef = useRef>(null) const [faceLandmarkerResult, setFaceLandmarkerResult] = useState() @@ -99,7 +99,7 @@ export const FaceControlsSt2 = { // function FaceControlsScene3(props: ComponentProps) { - const faceControlsRef = useRef>(null) + const faceControlsRef = useRef>(null) const camera = useThree((state) => state.camera) const [current] = useState(() => new THREE.Object3D()) diff --git a/.storybook/stories/Fbo.stories.tsx b/.storybook/stories/Fbo.stories.tsx index d49cb8ee2..03b6118a1 100644 --- a/.storybook/stories/Fbo.stories.tsx +++ b/.storybook/stories/Fbo.stories.tsx @@ -22,7 +22,7 @@ export default { type Story = StoryObj function SpinningThing() { - const mesh = React.useRef>(null!) + const mesh = React.useRef>(null!) useFrame(() => { mesh.current.rotation.x = mesh.current.rotation.y = mesh.current.rotation.z += 0.01 @@ -40,7 +40,7 @@ function FboScene(props: React.ComponentProps) { } function TargetWrapper({ target }: { target: THREE.WebGLRenderTarget }) { - const cam = React.useRef>(null!) + const cam = React.useRef>(null!) const scene = React.useMemo(() => { const scene = new THREE.Scene() diff --git a/.storybook/stories/HTML.stories.tsx b/.storybook/stories/HTML.stories.tsx index 348f016d2..cabc5addc 100644 --- a/.storybook/stories/HTML.stories.tsx +++ b/.storybook/stories/HTML.stories.tsx @@ -31,7 +31,7 @@ function HTMLScene({ color?: React.ComponentProps<'meshBasicMaterial'>['color'] children?: React.ReactNode } & HtmlProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( @@ -164,8 +164,8 @@ export const HTMLCalculatePositionSt = { // function HTMLOccluderScene(props: HtmlProps) { - const turntableRef = useTurntable>() - const occluderRef = React.useRef>(null) + const turntableRef = useTurntable>() + const occluderRef = React.useRef>(null) return ( <> diff --git a/.storybook/stories/Lathe.stories.tsx b/.storybook/stories/Lathe.stories.tsx index 762cb5020..13b5d141d 100644 --- a/.storybook/stories/Lathe.stories.tsx +++ b/.storybook/stories/Lathe.stories.tsx @@ -22,7 +22,7 @@ export default { type Story = StoryObj function LatheScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/MarchingCubes.stories.tsx b/.storybook/stories/MarchingCubes.stories.tsx index be0d03add..2a1b4a511 100644 --- a/.storybook/stories/MarchingCubes.stories.tsx +++ b/.storybook/stories/MarchingCubes.stories.tsx @@ -35,8 +35,8 @@ function MarchingCubesScene({ planeY?: boolean planeZ?: boolean } & React.ComponentProps) { - const cubeRefOne = React.useRef>(null) - const cubeRefTwo = React.useRef>(null) + const cubeRefOne = React.useRef>(null) + const cubeRefTwo = React.useRef>(null) useFrame(({ clock }) => { if (!cubeRefOne.current || !cubeRefTwo.current) return diff --git a/.storybook/stories/MeshDistortMaterial.stories.tsx b/.storybook/stories/MeshDistortMaterial.stories.tsx index ddd8db5cd..7a1fa508c 100644 --- a/.storybook/stories/MeshDistortMaterial.stories.tsx +++ b/.storybook/stories/MeshDistortMaterial.stories.tsx @@ -47,7 +47,7 @@ export const MeshDistortMaterialSt = { // function MeshDistortMaterialRefScene(props: React.ComponentProps) { - const material = React.useRef>(null!) + const material = React.useRef>(null!) useFrame(({ clock }) => { material.current.distort = Math.sin(clock.getElapsedTime()) diff --git a/.storybook/stories/MeshRefractionMaterial.stories.tsx b/.storybook/stories/MeshRefractionMaterial.stories.tsx index 5c5982c74..f7517aea4 100644 --- a/.storybook/stories/MeshRefractionMaterial.stories.tsx +++ b/.storybook/stories/MeshRefractionMaterial.stories.tsx @@ -39,7 +39,7 @@ function Diamond({ rotation: React.ComponentProps<'mesh'>['rotation'] position: React.ComponentProps<'mesh'>['position'] } & React.ComponentProps) { - const ref = React.useRef>(null) + const ref = React.useRef>(null) const { nodes } = useGLTF('/dflat.glb') as any // Use a custom envmap/scene-backdrop for the diamond material // This way we can have a clear BG while cube-cam can still film other objects diff --git a/.storybook/stories/MeshWobbleMaterial.stories.tsx b/.storybook/stories/MeshWobbleMaterial.stories.tsx index af1de9481..12af140d8 100644 --- a/.storybook/stories/MeshWobbleMaterial.stories.tsx +++ b/.storybook/stories/MeshWobbleMaterial.stories.tsx @@ -45,7 +45,7 @@ export const MeshWobbleMaterialSt = { // function MeshWobbleMaterialRefScene(props: React.ComponentProps) { - const material = React.useRef>(null) + const material = React.useRef>(null) useFrame(({ clock }) => { if (material.current === null) return diff --git a/.storybook/stories/OrbitControls.stories.tsx b/.storybook/stories/OrbitControls.stories.tsx index 32158e07f..fed9e42dd 100644 --- a/.storybook/stories/OrbitControls.stories.tsx +++ b/.storybook/stories/OrbitControls.stories.tsx @@ -50,7 +50,7 @@ const CustomCamera = (props: OrbitControlsProps) => { * we will render our scene in a render target and use it as a map. */ const fbo = useFBO(400, 400) - const virtualCamera = useRef | null>(null) + const virtualCamera = useRef | null>(null) const [virtualScene] = useState(() => new Scene()) useFrame(({ gl }) => { diff --git a/.storybook/stories/Reflector.stories.tsx b/.storybook/stories/Reflector.stories.tsx index 4788aef36..b604a3646 100644 --- a/.storybook/stories/Reflector.stories.tsx +++ b/.storybook/stories/Reflector.stories.tsx @@ -42,7 +42,7 @@ function ReflectorScene({ const roughnessMap = useTexture('roughness_floor.jpeg') const normalMap = useTexture('NORM.jpg') const distortionMap = useTexture('dist_map.jpeg') - const $box = React.useRef>(null!) + const $box = React.useRef>(null!) React.useEffect(() => { distortionMap.wrapS = distortionMap.wrapT = RepeatWrapping diff --git a/.storybook/stories/RoundedBox.stories.tsx b/.storybook/stories/RoundedBox.stories.tsx index 83bbb55cf..23bc13f7f 100644 --- a/.storybook/stories/RoundedBox.stories.tsx +++ b/.storybook/stories/RoundedBox.stories.tsx @@ -22,7 +22,7 @@ export default { type Story = StoryObj function RoundedBoxScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( @@ -44,7 +44,7 @@ export const RoundedBoxSt = { // function RoundedBoxScene2(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( <> diff --git a/.storybook/stories/Sampler.stories.tsx b/.storybook/stories/Sampler.stories.tsx index 1d22a9776..22b2a5160 100644 --- a/.storybook/stories/Sampler.stories.tsx +++ b/.storybook/stories/Sampler.stories.tsx @@ -4,7 +4,7 @@ import { Meta, StoryObj } from '@storybook/react' import { Setup } from '../Setup' import { Sampler, ComputedAttribute, TransformFn } from '../../src' -import { BufferAttribute, Vector3 } from 'three' +import { BufferAttribute, InstancedMesh, Mesh, Vector3 } from 'three' export default { title: 'Misc/Sampler', @@ -49,8 +49,8 @@ export const SamplerSt = { // function RefAPIScene(props: React.ComponentProps) { - const meshRef = React.useRef(null) - const instancesRef = React.useRef(null) + const meshRef = React.useRef(null!) + const instancesRef = React.useRef(null!) return ( <> diff --git a/.storybook/stories/ScreenQuad.stories.tsx b/.storybook/stories/ScreenQuad.stories.tsx index c2b837920..b475bcaa8 100644 --- a/.storybook/stories/ScreenQuad.stories.tsx +++ b/.storybook/stories/ScreenQuad.stories.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as THREE from 'three' -import { extend, useThree, useFrame } from '@react-three/fiber' +import { extend, useThree, useFrame, ThreeElements } from '@react-three/fiber' import { Meta, StoryObj } from '@storybook/react' import { Setup } from '../Setup' @@ -47,22 +47,18 @@ const ColorShiftMaterial = shaderMaterial( extend({ ColorShiftMaterial }) -type ColorShiftMaterialImpl = { - time: number - resolution: number[] -} & JSX.IntrinsicElements['shaderMaterial'] - -declare global { - namespace JSX { - interface IntrinsicElements { - colorShiftMaterial: ColorShiftMaterialImpl +declare module '@react-three/fiber' { + interface ThreeElements { + colorShiftMaterial: ThreeElements['shaderMaterial'] & { + time: number + resolution: number[] } } } function ScreenQuadScene(props: React.ComponentProps) { const size = useThree((state) => state.size) - const ref = React.useRef>(null!) + const ref = React.useRef(null!) useFrame((state) => { if (ref.current.uniforms) { diff --git a/.storybook/stories/Segments.stories.tsx b/.storybook/stories/Segments.stories.tsx index 49578d292..5016b3a20 100644 --- a/.storybook/stories/Segments.stories.tsx +++ b/.storybook/stories/Segments.stories.tsx @@ -49,7 +49,7 @@ export const BasicSegmentsSt = { // function AnimatedSegments(props: React.ComponentProps) { - const ref = React.useRef[]>([]) + const ref = React.useRef[]>([]) useFrame(({ clock }) => { ref.current.forEach((r, i) => { const time = clock.elapsedTime @@ -64,7 +64,16 @@ function AnimatedSegments(props: React.ComponentProps) { return ( {Array.from({ length: 10000 }).map((_, i) => ( - (ref.current[i] = r!)} color="orange" start={[0, 0, 0]} end={[0, 0, 0]} /> + { + ref.current[i] = r! + return () => void (ref.current[i] = null!) + }} + color="orange" + start={[0, 0, 0]} + end={[0, 0, 0]} + /> ))} ) diff --git a/.storybook/stories/Shadow.stories.tsx b/.storybook/stories/Shadow.stories.tsx index d306af4a8..54a87ad52 100644 --- a/.storybook/stories/Shadow.stories.tsx +++ b/.storybook/stories/Shadow.stories.tsx @@ -21,8 +21,8 @@ export default { type Story = StoryObj function ShadowScene(props: React.ComponentProps) { - const shadow = React.useRef>(null!) - const mesh = React.useRef>(null!) + const shadow = React.useRef>(null!) + const mesh = React.useRef>(null!) useFrame(({ clock }) => { shadow.current.scale.x = Math.sin(clock.getElapsedTime()) + 3 diff --git a/.storybook/stories/Shapes.Box.stories.tsx b/.storybook/stories/Shapes.Box.stories.tsx index 76e9c89a4..992e4c368 100644 --- a/.storybook/stories/Shapes.Box.stories.tsx +++ b/.storybook/stories/Shapes.Box.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function BoxScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Circle.stories.tsx b/.storybook/stories/Shapes.Circle.stories.tsx index 9e2d98d98..352bec23e 100644 --- a/.storybook/stories/Shapes.Circle.stories.tsx +++ b/.storybook/stories/Shapes.Circle.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function CircleScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Cone.stories.tsx b/.storybook/stories/Shapes.Cone.stories.tsx index 333f79e82..72db32e06 100644 --- a/.storybook/stories/Shapes.Cone.stories.tsx +++ b/.storybook/stories/Shapes.Cone.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function ConeScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Cylinder.stories.tsx b/.storybook/stories/Shapes.Cylinder.stories.tsx index 39fb0c31f..02ad662f0 100644 --- a/.storybook/stories/Shapes.Cylinder.stories.tsx +++ b/.storybook/stories/Shapes.Cylinder.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function CylinderScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Dodecahedron.stories.tsx b/.storybook/stories/Shapes.Dodecahedron.stories.tsx index 3734e3336..ed6352dc8 100644 --- a/.storybook/stories/Shapes.Dodecahedron.stories.tsx +++ b/.storybook/stories/Shapes.Dodecahedron.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function DodecahedronScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Icosahedron.stories.tsx b/.storybook/stories/Shapes.Icosahedron.stories.tsx index b32780934..9ee719b14 100644 --- a/.storybook/stories/Shapes.Icosahedron.stories.tsx +++ b/.storybook/stories/Shapes.Icosahedron.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function IcosahedronScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Octahedron.stories.tsx b/.storybook/stories/Shapes.Octahedron.stories.tsx index fbeb952f7..184149e29 100644 --- a/.storybook/stories/Shapes.Octahedron.stories.tsx +++ b/.storybook/stories/Shapes.Octahedron.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function OctahedronScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Plane.stories.tsx b/.storybook/stories/Shapes.Plane.stories.tsx index 36c28d483..d5fdbac69 100644 --- a/.storybook/stories/Shapes.Plane.stories.tsx +++ b/.storybook/stories/Shapes.Plane.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function PlaneScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Polyhedron.stories.tsx b/.storybook/stories/Shapes.Polyhedron.stories.tsx index 22cdc7fdf..449966eb1 100644 --- a/.storybook/stories/Shapes.Polyhedron.stories.tsx +++ b/.storybook/stories/Shapes.Polyhedron.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function PolyhedronScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Ring.stories.tsx b/.storybook/stories/Shapes.Ring.stories.tsx index 15cc66c4a..0ba83d74c 100644 --- a/.storybook/stories/Shapes.Ring.stories.tsx +++ b/.storybook/stories/Shapes.Ring.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function RingScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Sphere.stories.tsx b/.storybook/stories/Shapes.Sphere.stories.tsx index aa8acbf7a..26aafc119 100644 --- a/.storybook/stories/Shapes.Sphere.stories.tsx +++ b/.storybook/stories/Shapes.Sphere.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function SphereScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Tetrahedron.stories.tsx b/.storybook/stories/Shapes.Tetrahedron.stories.tsx index a3cfe3b91..14122bc8d 100644 --- a/.storybook/stories/Shapes.Tetrahedron.stories.tsx +++ b/.storybook/stories/Shapes.Tetrahedron.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function TetrahedronScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.Torus.stories.tsx b/.storybook/stories/Shapes.Torus.stories.tsx index 827850332..e79d8942a 100644 --- a/.storybook/stories/Shapes.Torus.stories.tsx +++ b/.storybook/stories/Shapes.Torus.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function TorusScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Shapes.TorusKnot.stories.tsx b/.storybook/stories/Shapes.TorusKnot.stories.tsx index abf3e605a..44031bfba 100644 --- a/.storybook/stories/Shapes.TorusKnot.stories.tsx +++ b/.storybook/stories/Shapes.TorusKnot.stories.tsx @@ -21,7 +21,7 @@ export default { type Story = StoryObj function TorusKnotScene(props: React.ComponentProps) { - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/Texture.stories.tsx b/.storybook/stories/Texture.stories.tsx index 62f6c4225..e06492bdd 100644 --- a/.storybook/stories/Texture.stories.tsx +++ b/.storybook/stories/Texture.stories.tsx @@ -51,7 +51,7 @@ function TextureScene2(props: React.ComponentProps) { {(textures) => ( - + )} diff --git a/.storybook/stories/Tube.stories.tsx b/.storybook/stories/Tube.stories.tsx index 7d8edf404..8e9b64f88 100644 --- a/.storybook/stories/Tube.stories.tsx +++ b/.storybook/stories/Tube.stories.tsx @@ -45,7 +45,7 @@ function TubeScene(props: React.ComponentProps) { return new CustomSinCurve(10) }, []) - const ref = useTurntable>() + const ref = useTurntable>() return ( diff --git a/.storybook/stories/meshBounds.stories.tsx b/.storybook/stories/meshBounds.stories.tsx index b854f4fbf..621c144c2 100644 --- a/.storybook/stories/meshBounds.stories.tsx +++ b/.storybook/stories/meshBounds.stories.tsx @@ -8,7 +8,7 @@ import { useTurntable } from '../useTurntable' import { meshBounds } from '../../src' function MeshBounds(props: React.ComponentProps<'mesh'>) { - const mesh = useTurntable>() + const mesh = useTurntable>() const [hovered, setHover] = React.useState(false) diff --git a/.storybook/stories/shaderMaterial.stories.tsx b/.storybook/stories/shaderMaterial.stories.tsx index 95325b873..5cefec892 100644 --- a/.storybook/stories/shaderMaterial.stories.tsx +++ b/.storybook/stories/shaderMaterial.stories.tsx @@ -8,54 +8,49 @@ import { Box, shaderMaterial, useTexture } from '../../src' const MyMaterial = shaderMaterial( { map: new Texture(), repeats: 1 }, - ` -varying vec2 vUv; + /* glsl */ ` + varying vec2 vUv; -void main() { - vUv = uv; - - gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.); -} -`, - ` -varying vec2 vUv; -uniform float repeats; -uniform sampler2D map; + void main() { + vUv = uv; + gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1); + } + `, + /* glsl */ ` + varying vec2 vUv; + uniform float repeats; + uniform sampler2D map; + + float random (vec2 st) { + return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); + } -float random (vec2 st) { - return fract(sin(dot(st.xy, vec2(12.9898,78.233))) * 43758.5453123); -} + void main(){ + vec2 uv = vUv; -void main(){ - vec2 uv = vUv; + uv *= repeats; + uv = fract(uv); - uv *= repeats; - uv = fract(uv); + vec3 color = vec3( + texture2D(map, uv).r, + texture2D(map, uv + vec2(0.01, 0.01)).g, + texture2D(map, uv - vec2(0.01, 0.01)).b + ); - vec3 color = vec3( - texture2D(map, uv).r, - texture2D(map, uv + vec2(0.01,0.01)).g, - texture2D(map, uv - vec2(0.01,0.01)).b - ); - - gl_FragColor = vec4(color,1.0); + gl_FragColor = vec4(color, 1.0); - #include - #include -} -` + #include + #include + } + ` ) extend({ MyMaterial }) -type MyMaterialImpl = { - repeats: number - map: Texture | Texture[] -} & JSX.IntrinsicElements['shaderMaterial'] - -declare global { - namespace JSX { - interface IntrinsicElements { - myMaterial: MyMaterialImpl +declare module '@react-three/fiber' { + interface ThreeElements { + myMaterial: ThreeElements['shaderMaterial'] & { + repeats: number + map: Texture | Texture[] } } } diff --git a/.storybook/stories/useAnimations.stories.tsx b/.storybook/stories/useAnimations.stories.tsx index 05c45a216..f828d759f 100644 --- a/.storybook/stories/useAnimations.stories.tsx +++ b/.storybook/stories/useAnimations.stories.tsx @@ -41,7 +41,7 @@ type Story = StoryObj useGLTF.preload('ybot.glb') function UseAnimationsScene(props: React.ComponentProps) { - const root = React.useRef>(null) + const root = React.useRef>(null) const { nodes, animations } = useGLTF('ybot.glb') as GLTF & { nodes: { YB_Body: THREE.SkinnedMesh diff --git a/.storybook/stories/useCamera.stories.tsx b/.storybook/stories/useCamera.stories.tsx index 2b800cc14..188964442 100644 --- a/.storybook/stories/useCamera.stories.tsx +++ b/.storybook/stories/useCamera.stories.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { useFrame, useThree, createPortal } from '@react-three/fiber' +import { useFrame, useThree, createPortal, ThreeEvent } from '@react-three/fiber' import * as THREE from 'three' import { Meta, StoryObj } from '@storybook/react' @@ -22,8 +22,8 @@ export default { type Story = StoryObj function UseCameraScene() { - const virtualCam = React.useRef>(null!) - const ref = React.useRef>(null) + const virtualCam = React.useRef(null!) + const ref = React.useRef(null) const [hover, setHover] = React.useState(null) @@ -51,7 +51,7 @@ function UseCameraScene() { }, 1) const handlePointerOut = () => setHover(null) - const handlePointerMove = (e: THREE.Event) => setHover(Math.floor(e.faceIndex ?? 0 / 2)) + const handlePointerMove = (e: ThreeEvent) => setHover(Math.floor(e.faceIndex ?? 0 / 2)) return createPortal( <> diff --git a/docs/abstractions/image.mdx b/docs/abstractions/image.mdx index b5d1f6fd5..a22d78f30 100644 --- a/docs/abstractions/image.mdx +++ b/docs/abstractions/image.mdx @@ -24,7 +24,7 @@ sourcecode: src/core/Image.tsx A shader-based image component with auto-cover (similar to css/background: cover). ```tsx -export type ImageProps = Omit & { +export type ImageProps = Omit & { segments?: number scale?: number | [number, number] color?: Color diff --git a/docs/abstractions/outlines.mdx b/docs/abstractions/outlines.mdx index 942ee35f1..816f39d05 100644 --- a/docs/abstractions/outlines.mdx +++ b/docs/abstractions/outlines.mdx @@ -12,7 +12,7 @@ sourcecode: src/core/Outlines.tsx An ornamental component that extracts the geometry from its parent and displays an [inverted-hull outline](https://bnpr.gitbook.io/bnpr/outline/inverse-hull-method). Supported parents are ``, `` and ``. ```tsx -type OutlinesProps = JSX.IntrinsicElements['group'] & { +type OutlinesProps = ThreeElements['group'] & { /** Outline color, default: black */ color: ReactThreeFiber.Color /** Line thickness is independent of zoom, default: false */ diff --git a/docs/abstractions/splat.mdx b/docs/abstractions/splat.mdx index c0ba96cb9..65026c8e5 100644 --- a/docs/abstractions/splat.mdx +++ b/docs/abstractions/splat.mdx @@ -23,7 +23,7 @@ type SplatProps = { alphaHash?: boolean /** Chunk size for lazy loading, prevents chokings the worker, default: 25000 (25kb) */ chunkSize?: number -} & JSX.IntrinsicElements['mesh'] +} & ThreeElements['mesh'] ``` ```jsx diff --git a/docs/cameras/cube-camera.mdx b/docs/cameras/cube-camera.mdx index ac2371d23..215847ab4 100644 --- a/docs/cameras/cube-camera.mdx +++ b/docs/cameras/cube-camera.mdx @@ -8,7 +8,7 @@ sourcecode: src/core/CubeCamera.tsx A [THREE.CubeCamera](https://threejs.org/docs/#api/en/cameras/CubeCamera) that returns its texture as a render-prop. It makes children invisible while rendering to the internal buffer so that they are not included in the reflection. ```tsx -type Props = JSX.IntrinsicElements['group'] & { +type Props = ThreeElements['group'] & { /** Number of frames to render, Infinity */ frames?: number /** Resolution of the FBO, 256 */ diff --git a/docs/cameras/perspective-camera.mdx b/docs/cameras/perspective-camera.mdx index 2627b13e9..9b919e937 100644 --- a/docs/cameras/perspective-camera.mdx +++ b/docs/cameras/perspective-camera.mdx @@ -6,7 +6,7 @@ sourcecode: src/core/PerspectiveCamera.tsx [![](https://img.shields.io/badge/-storybook-%23ff69b4)](https://drei.vercel.app/?path=/story/camera-perspectivecamera--perspective-camera-scene-st) ```tsx -type Props = Omit & { +type Props = Omit & { /** Registers the camera as the system default, fiber will start rendering with it */ makeDefault?: boolean /** Making it manual will stop responsiveness and you have to calculate aspect ratio yourself. */ diff --git a/docs/controls/motion-path-controls.mdx b/docs/controls/motion-path-controls.mdx index 60e1e59a2..97da04bb5 100644 --- a/docs/controls/motion-path-controls.mdx +++ b/docs/controls/motion-path-controls.mdx @@ -12,15 +12,15 @@ sourcecode: src/core/MotionPathControls.tsx Motion path controls, it takes a path of bezier curves or catmull-rom curves as input and animates the passed `object` along that path. It can be configured to look upon an external object for staging or presentation purposes by adding a `focusObject` property (ref). ```tsx -type MotionPathProps = JSX.IntrinsicElements['group'] & { +type MotionPathProps = ThreeElements['group'] & { /** An optional array of THREE curves */ curves?: THREE.Curve[] /** Show debug helpers */ debug?: boolean /** The target object that is moved, default: null (the default camera) */ - object?: React.MutableRefObject + object?: React.RefObject /** An object where the target looks towards, can also be a vector, default: null */ - focus?: [x: number, y: number, z: number] | React.MutableRefObject + focus?: [x: number, y: number, z: number] | React.RefObject /** Position between 0 (start) and end (1), if this is not set useMotion().current must be used, default: null */ offset?: number /** Optionally smooth the curve, default: false */ @@ -83,9 +83,9 @@ type MotionState = { /** The combined curve */ path: THREE.CurvePath /** The focus object */ - focus: React.MutableRefObject> | [x: number, y: number, z: number] | undefined + focus: React.RefObject> | [x: number, y: number, z: number] | undefined /** The target object that is moved along the curve */ - object: React.MutableRefObject> + object: React.RefObject> /** The automated, 0-1 normalised and damped current goal position along curve */ offset: number /** The current point on the curve */ diff --git a/docs/gizmos/transform-controls.mdx b/docs/gizmos/transform-controls.mdx index 0b8fe9507..5b7fac3f6 100644 --- a/docs/gizmos/transform-controls.mdx +++ b/docs/gizmos/transform-controls.mdx @@ -21,7 +21,7 @@ You can wrap objects which then receive a transform gizmo. ``` -You could also reference the object which might make it easier to exchange the target. Now the object does not have to be part of the same sub-graph. References can be plain objects or React.MutableRefObjects. +You could also reference the object which might make it easier to exchange the target. Now the object does not have to be part of the same sub-graph. References can be plain objects or React.RefObjects. ```jsx diff --git a/docs/misc/face-landmarker.mdx b/docs/misc/face-landmarker.mdx index 6278b4067..a2b6c3023 100644 --- a/docs/misc/face-landmarker.mdx +++ b/docs/misc/face-landmarker.mdx @@ -52,7 +52,7 @@ faceLandmarkerOptions.baseOptions.modelAssetPath = modelAssetPath; You can get the FaceLandmarker instance through `ref`: ```tsx -const faceLandmarkerRef = useRef>(null) +const faceLandmarkerRef = useRef>(null) {/* ... */} diff --git a/docs/misc/trail-use-trail.mdx b/docs/misc/trail-use-trail.mdx index 9f9afa2a1..8b2980e2c 100644 --- a/docs/misc/trail-use-trail.mdx +++ b/docs/misc/trail-use-trail.mdx @@ -7,7 +7,7 @@ sourcecode: src/core/Trail.tsx 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. -Note: The hook returns a ref (`MutableRefObject`) this means updates to it will not trigger a re-draw, thus keeping this cheap. +Note: The hook returns a ref (`RefObject`) this means updates to it will not trigger a re-draw, thus keeping this cheap. ```js const points = useTrail( diff --git a/docs/performances/bvh.mdx b/docs/performances/bvh.mdx index 19c5a1989..c5ef2c232 100644 --- a/docs/performances/bvh.mdx +++ b/docs/performances/bvh.mdx @@ -30,7 +30,7 @@ export interface BVHOptions { } export type BvhProps = BVHOptions & - JSX.IntrinsicElements['group'] & { + ThreeElements['group'] & { /**Enabled, default: true */ enabled?: boolean /** Use .raycastFirst to retrieve hits which is generally faster, default: false */ diff --git a/docs/portals/fisheye.mdx b/docs/portals/fisheye.mdx index 700ce86db..00022dd44 100644 --- a/docs/portals/fisheye.mdx +++ b/docs/portals/fisheye.mdx @@ -10,7 +10,7 @@ sourcecode: src/core/Fisheye.tsx ```tsx -export type FisheyeProps = JSX.IntrinsicElements['mesh'] & { +export type FisheyeProps = ThreeElements['mesh'] & { /** Zoom factor, 0..1, 0 */ zoom?: number /** Number of segments, 64 */ diff --git a/docs/portals/mesh-portal-material.mdx b/docs/portals/mesh-portal-material.mdx index d74ae4e18..c4064d0ef 100644 --- a/docs/portals/mesh-portal-material.mdx +++ b/docs/portals/mesh-portal-material.mdx @@ -19,7 +19,7 @@ sourcecode: src/core/MeshPortalMaterial.tsx ```tsx -export type PortalProps = JSX.IntrinsicElements['shaderMaterial'] & { +export type PortalProps = ThreeElements['shaderMaterial'] & { /** Mix the portals own scene with the world scene, 0 = world scene render, * 0.5 = both scenes render, 1 = portal scene renders, defaults to 0 */ blend?: number diff --git a/docs/portals/render-cube-texture.mdx b/docs/portals/render-cube-texture.mdx index f7ea8de4d..2b850c046 100644 --- a/docs/portals/render-cube-texture.mdx +++ b/docs/portals/render-cube-texture.mdx @@ -6,7 +6,7 @@ sourcecode: src/core/RenderCubeTexture.tsx This component allows you to render a live scene into a cubetexture which you can then apply to a material, for instance as an environment map (via the envMap property). The contents of it run inside a portal and are separate from the rest of the canvas, therefore you can have events in there, environment maps, etc. ```tsx -export type RenderCubeTextureProps = Omit & { +export type RenderCubeTextureProps = Omit & { /** Optional stencil buffer, defaults to false */ stencilBuffer?: boolean /** Optional depth buffer, defaults to true */ diff --git a/docs/portals/render-texture.mdx b/docs/portals/render-texture.mdx index 3bdf53db1..4b84a23c9 100644 --- a/docs/portals/render-texture.mdx +++ b/docs/portals/render-texture.mdx @@ -12,7 +12,7 @@ sourcecode: src/core/RenderTexture.tsx This component allows you to render a live scene into a texture which you can then apply to a material. The contents of it run inside a portal and are separate from the rest of the canvas, therefore you can have events in there, environment maps, etc. ```tsx -type Props = JSX.IntrinsicElements['texture'] & { +type Props = ThreeElements['texture'] & { /** Optional width of the texture, defaults to viewport bounds */ width?: number /** Optional height of the texture, defaults to viewport bounds */ diff --git a/docs/portals/view.mdx b/docs/portals/view.mdx index 0fdf7b18b..b4d0c7dd5 100644 --- a/docs/portals/view.mdx +++ b/docs/portals/view.mdx @@ -49,7 +49,7 @@ export type ViewProps = { /** The tracking element, the view will be cut according to its whereabouts * @deprecated You can use inline Views now, see: https://github.com/pmndrs/drei/pull/1784 */ - track?: React.MutableRefObject + track?: React.RefObject } export type ViewportProps = { Port: () => React.ReactNode } & React.ForwardRefExoticComponent< diff --git a/docs/shaders/mesh-refraction-material.mdx b/docs/shaders/mesh-refraction-material.mdx index e23b3c348..37bc96c91 100644 --- a/docs/shaders/mesh-refraction-material.mdx +++ b/docs/shaders/mesh-refraction-material.mdx @@ -12,7 +12,7 @@ sourcecode: src/core/MeshRefractionMaterial.tsx A convincing Glass/Diamond refraction material. ```tsx -type MeshRefractionMaterialProps = JSX.IntrinsicElements['shaderMaterial'] & { +type MeshRefractionMaterialProps = ThreeElements['shaderMaterial'] & { /** Environment map */ envMap: THREE.CubeTexture | THREE.Texture /** Number of ray-cast bounces, it can be expensive to have too many, 2 */ diff --git a/docs/shaders/mesh-transmission-material.mdx b/docs/shaders/mesh-transmission-material.mdx index ef1f7b745..733959680 100644 --- a/docs/shaders/mesh-transmission-material.mdx +++ b/docs/shaders/mesh-transmission-material.mdx @@ -16,7 +16,7 @@ Although it should be faster than MPM keep in mind that it can still be expensiv For performance and visual reasons the host mesh gets removed from the render-stack temporarily. If you have other objects that you don't want to see reflected in the material just add them to the parent mesh as children. ```tsx -type MeshTransmissionMaterialProps = JSX.IntrinsicElements['meshPhysicalMaterial'] & { +type MeshTransmissionMaterialProps = ThreeElements['meshPhysicalMaterial'] & { /* Transmission, default: 1 */ transmission?: number /* Thickness (refraction), default: 0 */ diff --git a/docs/staging/accumulative-shadows.mdx b/docs/staging/accumulative-shadows.mdx index 2b9cb62c8..689fcdfdb 100644 --- a/docs/staging/accumulative-shadows.mdx +++ b/docs/staging/accumulative-shadows.mdx @@ -14,7 +14,7 @@ A planar, Y-up oriented shadow-catcher that can accumulate into soft shadows and You must pair it with lightsources (and scene objects!) that cast shadows, which go into the children slot. Best use it with the `RandomizedLight` component, which jiggles a set of lights around, creating realistic raycast-like shadows and ambient occlusion. ```tsx -type AccumulativeShadowsProps = JSX.IntrinsicElements['group'] & { +type AccumulativeShadowsProps = ThreeElements['group'] & { /** How many frames it can render, more yields cleaner results but takes more time, 40 */ frames?: number /** If frames === Infinity blend controls the refresh ratio, 100 */ diff --git a/docs/staging/caustics.mdx b/docs/staging/caustics.mdx index 77eceb3ab..b9e8f6c5e 100644 --- a/docs/staging/caustics.mdx +++ b/docs/staging/caustics.mdx @@ -15,7 +15,7 @@ sourcecode: src/core/Caustics.tsx Caustics are swirls of light that appear when light passes through transmissive surfaces. This component uses a raymarching technique to project caustics onto a catcher plane. It is based on [github/N8python/caustics](https://github.com/N8python/caustics). ```tsx -type CausticsProps = JSX.IntrinsicElements['group'] & { +type CausticsProps = ThreeElements['group'] & { /** How many frames it will render, set it to Infinity for runtime, default: 1 */ frames?: number /** Enables visual cues to help you stage your scene, default: false */ @@ -37,7 +37,7 @@ type CausticsProps = JSX.IntrinsicElements['group'] & { /** Buffer resolution, default: 2048 */ resolution?: number /** Camera position, it will point towards the contents bounds center, default: [5, 5, 5] */ - lightSource?: [x: number, y: number, z: number] | React.MutableRefObject + lightSource?: [x: number, y: number, z: number] | React.RefObject } ``` diff --git a/docs/staging/center.mdx b/docs/staging/center.mdx index 1a59d03c1..e24be8fec 100644 --- a/docs/staging/center.mdx +++ b/docs/staging/center.mdx @@ -17,7 +17,7 @@ sourcecode: src/core/Center.tsx Calculates a boundary box and centers its children accordingly. ```tsx -export type Props = JSX.IntrinsicElements['group'] & { +export type Props = ThreeElements['group'] & { top?: boolean right?: boolean bottom?: boolean diff --git a/docs/staging/cloud.mdx b/docs/staging/cloud.mdx index 38bec3065..91c6ac5cc 100644 --- a/docs/staging/cloud.mdx +++ b/docs/staging/cloud.mdx @@ -19,7 +19,7 @@ sourcecode: src/core/Cloud.tsx Particle based cloud. ```tsx -type CloudsProps = JSX.IntrinsicElements['group'] & { +type CloudsProps = ThreeElements['group'] & { /** Optional cloud texture, points to a default hosted on rawcdn.githack */ texture?: string /** Maximum number of segments, default: 200 (make this tight to save memory!) */ @@ -32,7 +32,7 @@ type CloudsProps = JSX.IntrinsicElements['group'] & { frustumCulled?: boolean } -type CloudProps = JSX.IntrinsicElements['group'] & { +type CloudProps = ThreeElements['group'] & { /** A seeded random will show the same cloud consistently, default: Math.random() */ seed?: number /** How many segments or particles the cloud will have, default: 20 */ diff --git a/docs/staging/randomized-light.mdx b/docs/staging/randomized-light.mdx index c2e7cf39a..8d4aa0951 100644 --- a/docs/staging/randomized-light.mdx +++ b/docs/staging/randomized-light.mdx @@ -5,7 +5,7 @@ title: RandomizedLight A randomized light that internally runs multiple lights and jiggles them. See below, you would normally pair it with `AccumulativeShadows`. This component is context aware, paired with AccumulativeShadows it will take the number of frames from its parent. ```tsx -type RandomizedLightProps = JSX.IntrinsicElements['group'] & { +type RandomizedLightProps = ThreeElements['group'] & { /** How many frames it will jiggle the lights, 1. * Frames is context aware, if a provider like AccumulativeShadows exists, frames will be taken from there! */ frames?: number diff --git a/docs/staging/resize.mdx b/docs/staging/resize.mdx index 1dc225b0b..d50022bed 100644 --- a/docs/staging/resize.mdx +++ b/docs/staging/resize.mdx @@ -14,7 +14,7 @@ sourcecode: src/core/Resize.tsx Calculates a boundary box and scales its children so the highest dimension is constrained by 1. NB: proportions are preserved. ```tsx -export type ResizeProps = JSX.IntrinsicElements['group'] & { +export type ResizeProps = ThreeElements['group'] & { /** constrained by width dimension (x axis), undefined */ width?: boolean /** constrained by height dimension (y axis), undefined */ diff --git a/package.json b/package.json index 3d46cd924..a13340d51 100644 --- a/package.json +++ b/package.json @@ -48,7 +48,6 @@ "@babel/runtime": "^7.26.0", "@mediapipe/tasks-vision": "0.10.17", "@monogrid/gainmap-js": "^3.0.6", - "@react-spring/three": "~9.7.5", "@use-gesture/react": "^10.3.1", "camera-controls": "^2.9.0", "cross-env": "^7.0.3", @@ -57,14 +56,14 @@ "hls.js": "^1.5.17", "maath": "^0.10.8", "meshline": "^3.3.1", - "react-composer": "^5.0.3", "stats-gl": "^2.2.8", "stats.js": "^0.17.0", "suspend-react": "^0.1.3", - "three-mesh-bvh": "^0.7.8", + "three-mesh-bvh": "^0.8.3", "three-stdlib": "^2.35.6", "troika-three-text": "^0.52.0", "tunnel-rat": "^0.1.2", + "use-sync-external-store": "^1.4.0", "utility-types": "^3.11.0", "zustand": "^5.0.1" }, @@ -82,7 +81,7 @@ "@eslint/eslintrc": "^3.2.0", "@eslint/js": "^9.15.0", "@playwright/test": "^1.45.2", - "@react-three/fiber": "^8.0.8", + "@react-three/fiber": "9.0.0-rc.4", "@rollup/plugin-babel": "^5.3.0", "@rollup/plugin-commonjs": "^19.0.0", "@rollup/plugin-json": "^4.1.0", @@ -95,9 +94,9 @@ "@storybook/react": "^8.4.4", "@storybook/react-vite": "^8.4.4", "@storybook/theming": "^8.4.4", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@types/three": "^0.151.0", + "@types/react": "^19.0.2", + "@types/react-dom": "^19.0.2", + "@types/three": "^0.159.0", "@typescript-eslint/eslint-plugin": "^8.15.0", "@typescript-eslint/parser": "^8.15.0", "@vitejs/plugin-react": "^4.3.3", @@ -113,8 +112,8 @@ "json": "^11.0.0", "prettier": "^3.3.3", "pretty-quick": "^4.0.0", - "react": "^18.0.0", - "react-dom": "^18.0.0", + "react": "^19.0.0", + "react-dom": "^19.0.0", "rimraf": "^6.0.1", "rollup": "^2.79.2", "rollup-plugin-glslify": "^1.3.0", @@ -123,17 +122,17 @@ "semantic-release": "^24.2.0", "serve": "^14.2.4", "storybook": "^8.4.4", - "three": "^0.151.0", + "three": "^0.159.0", "ts-node": "^10.9.2", "typescript": "^5.6.3", "vite": "^5.4.11", "vite-plugin-glslify": "^2.1.0" }, "peerDependencies": { - "@react-three/fiber": "^8", - "react": "^18", - "react-dom": "^18", - "three": ">=0.137" + "@react-three/fiber": "9.0.0-rc.4", + "react": "^19", + "react-dom": "^19", + "three": ">=0.159" }, "peerDependenciesMeta": { "react-dom": { diff --git a/src/core/AccumulativeShadows.tsx b/src/core/AccumulativeShadows.tsx index 5d7786d37..33d3ea158 100644 --- a/src/core/AccumulativeShadows.tsx +++ b/src/core/AccumulativeShadows.tsx @@ -1,6 +1,6 @@ import * as THREE from 'three' import * as React from 'react' -import { extend, ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { extend, ReactThreeFiber, ThreeElements, useFrame, useThree } from '@react-three/fiber' import { shaderMaterial } from './shaderMaterial' import { DiscardMaterial } from '../materials/DiscardMaterial' import { ForwardRefComponent } from '../helpers/ts-utils' @@ -14,7 +14,7 @@ function isGeometry(object: any): object is THREE.Mesh { return !!object.geometry } -export type AccumulativeShadowsProps = { +export type AccumulativeShadowsProps = Omit & { /** How many frames it can render, more yields cleaner results but takes more time, 40 */ frames?: number /** If frames === Infinity blend controls the refresh ratio, 100 */ @@ -65,11 +65,9 @@ type SoftShadowMaterialProps = { blend?: number } -declare global { - namespace JSX { - interface IntrinsicElements { - softShadowMaterial: JSX.IntrinsicElements['shaderMaterial'] & SoftShadowMaterialProps - } +declare module '@react-three/fiber' { + interface ThreeElements { + softShadowMaterial: ThreeElements['shaderMaterial'] & SoftShadowMaterialProps } } @@ -104,128 +102,128 @@ const SoftShadowMaterial = /* @__PURE__ */ shaderMaterial( }` ) -export const AccumulativeShadows: ForwardRefComponent< - JSX.IntrinsicElements['group'] & AccumulativeShadowsProps, - AccumulativeContext -> = /* @__PURE__ */ React.forwardRef( - ( - { - children, - temporal, - frames = 40, - limit = Infinity, - blend = 20, - scale = 10, - opacity = 1, - alphaTest = 0.75, - color = 'black', - colorBlend = 2, - resolution = 1024, - toneMapped = true, - ...props - }: JSX.IntrinsicElements['group'] & AccumulativeShadowsProps, - forwardRef: React.ForwardedRef - ) => { - extend({ SoftShadowMaterial }) - - const gl = useThree((state) => state.gl) - const scene = useThree((state) => state.scene) - const camera = useThree((state) => state.camera) - const invalidate = useThree((state) => state.invalidate) - const gPlane = React.useRef>(null!) - const gLights = React.useRef(null!) - - const [plm] = React.useState(() => new ProgressiveLightMap(gl, scene, resolution)) - React.useLayoutEffect(() => { - plm.configure(gPlane.current) - }, []) - - const api = React.useMemo( - () => ({ - lights: new Map(), - temporal: !!temporal, - frames: Math.max(2, frames), - blend: Math.max(2, frames === Infinity ? blend : frames), - count: 0, - getMesh: () => gPlane.current, - reset: () => { - // Clear buffers, reset opacities, set frame count to 0 - plm.clear() - const material = gPlane.current.material - material.opacity = 0 - material.alphaTest = 0 - api.count = 0 - }, - update: (frames = 1) => { - // Adapt the opacity-blend ratio to the number of frames - const material = gPlane.current.material - if (!api.temporal) { - material.opacity = opacity - material.alphaTest = alphaTest - } else { - material.opacity = Math.min(opacity, material.opacity + opacity / api.blend) - material.alphaTest = Math.min(alphaTest, material.alphaTest + alphaTest / api.blend) - } - - // Switch accumulative lights on - gLights.current.visible = true - // Collect scene lights and meshes - plm.prepare() - - // Update the lightmap and the accumulative lights - for (let i = 0; i < frames; i++) { - api.lights.forEach((light) => light.update()) - plm.update(camera, api.blend) - } - // Switch lights off - gLights.current.visible = false - // Restore lights and meshes - plm.finish() - }, - }), - [plm, camera, scene, temporal, frames, blend, opacity, alphaTest] - ) - - React.useLayoutEffect(() => { - // Reset internals, buffers, ... - api.reset() - // Update lightmap - if (!api.temporal && api.frames !== Infinity) api.update(api.blend) - }) - - // Expose api, allow children to set itself as the main light source - React.useImperativeHandle(forwardRef, () => api, [api]) - - useFrame(() => { - if ((api.temporal || api.frames === Infinity) && api.count < api.frames && api.count < limit) { - invalidate() - api.update() - api.count++ - } - }) - - return ( - - null} ref={gLights}> - {children} +export const AccumulativeShadows: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef( + ( + { + children, + temporal, + frames = 40, + limit = Infinity, + blend = 20, + scale = 10, + opacity = 1, + alphaTest = 0.75, + color = 'black', + colorBlend = 2, + resolution = 1024, + toneMapped = true, + ...props + }, + forwardRef + ) => { + extend({ SoftShadowMaterial }) + + const gl = useThree((state) => state.gl) + const scene = useThree((state) => state.scene) + const camera = useThree((state) => state.camera) + const invalidate = useThree((state) => state.invalidate) + const gPlane = React.useRef>( + null! + ) + const gLights = React.useRef(null!) + + const [plm] = React.useState(() => new ProgressiveLightMap(gl, scene, resolution)) + React.useLayoutEffect(() => { + plm.configure(gPlane.current) + }, []) + + const api = React.useMemo( + () => ({ + lights: new Map(), + temporal: !!temporal, + frames: Math.max(2, frames), + blend: Math.max(2, frames === Infinity ? blend : frames), + count: 0, + getMesh: () => gPlane.current, + reset: () => { + // Clear buffers, reset opacities, set frame count to 0 + plm.clear() + const material = gPlane.current.material + material.opacity = 0 + material.alphaTest = 0 + api.count = 0 + }, + update: (frames = 1) => { + // Adapt the opacity-blend ratio to the number of frames + const material = gPlane.current.material + if (!api.temporal) { + material.opacity = opacity + material.alphaTest = alphaTest + } else { + material.opacity = Math.min(opacity, material.opacity + opacity / api.blend) + material.alphaTest = Math.min(alphaTest, material.alphaTest + alphaTest / api.blend) + } + + // Switch accumulative lights on + gLights.current.visible = true + // Collect scene lights and meshes + plm.prepare() + + // Update the lightmap and the accumulative lights + for (let i = 0; i < frames; i++) { + api.lights.forEach((light) => light.update()) + plm.update(camera, api.blend) + } + // Switch lights off + gLights.current.visible = false + // Restore lights and meshes + plm.finish() + }, + }), + [plm, camera, scene, temporal, frames, blend, opacity, alphaTest] + ) + + React.useLayoutEffect(() => { + // Reset internals, buffers, ... + api.reset() + // Update lightmap + if (!api.temporal && api.frames !== Infinity) api.update(api.blend) + }) + + // Expose api, allow children to set itself as the main light source + React.useImperativeHandle(forwardRef, () => api, [api]) + + useFrame(() => { + if ((api.temporal || api.frames === Infinity) && api.count < api.frames && api.count < limit) { + invalidate() + api.update() + api.count++ + } + }) + + return ( + + null} ref={gLights}> + {children} + + + + + - - - - - - ) - } -) + ) + } + ) -export type RandomizedLightProps = { +export type RandomizedLightProps = Omit & { /** How many frames it will jiggle the lights, 1. * Frames is context aware, if a provider like AccumulativeShadows exists, frames will be taken from there! */ frames?: number @@ -253,81 +251,79 @@ export type RandomizedLightProps = { far?: number } -export const RandomizedLight: ForwardRefComponent< - JSX.IntrinsicElements['group'] & RandomizedLightProps, - AccumulativeLightContext -> = /* @__PURE__ */ React.forwardRef( - ( - { - castShadow = true, - bias = 0.001, - mapSize = 512, - size = 5, - near = 0.5, - far = 500, - frames = 1, - position = [0, 0, 0], - radius = 1, - amount = 8, - intensity = version >= 155 ? Math.PI : 1, - ambient = 0.5, - ...props - }: JSX.IntrinsicElements['group'] & RandomizedLightProps, - forwardRef: React.ForwardedRef - ) => { - const gLights = React.useRef(null!) - const length = new THREE.Vector3(...position).length() - const parent = React.useContext(accumulativeContext) - - const update = React.useCallback(() => { - let light: THREE.Object3D | undefined - if (gLights.current) { - for (let l = 0; l < gLights.current.children.length; l++) { - light = gLights.current.children[l] - if (Math.random() > ambient) { - light.position.set( - position[0] + THREE.MathUtils.randFloatSpread(radius), - position[1] + THREE.MathUtils.randFloatSpread(radius), - position[2] + THREE.MathUtils.randFloatSpread(radius) - ) - } else { - let lambda = Math.acos(2 * Math.random() - 1) - Math.PI / 2.0 - let phi = 2 * Math.PI * Math.random() - light.position.set( - Math.cos(lambda) * Math.cos(phi) * length, - Math.abs(Math.cos(lambda) * Math.sin(phi) * length), - Math.sin(lambda) * length - ) +export const RandomizedLight: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef( + ( + { + castShadow = true, + bias = 0.001, + mapSize = 512, + size = 5, + near = 0.5, + far = 500, + frames = 1, + position = [0, 0, 0], + radius = 1, + amount = 8, + intensity = version >= 155 ? Math.PI : 1, + ambient = 0.5, + ...props + }, + forwardRef + ) => { + const gLights = React.useRef(null!) + const length = new THREE.Vector3(...position).length() + const parent = React.useContext(accumulativeContext) + + const update = React.useCallback(() => { + let light: THREE.Object3D | undefined + if (gLights.current) { + for (let l = 0; l < gLights.current.children.length; l++) { + light = gLights.current.children[l] + if (Math.random() > ambient) { + light.position.set( + position[0] + THREE.MathUtils.randFloatSpread(radius), + position[1] + THREE.MathUtils.randFloatSpread(radius), + position[2] + THREE.MathUtils.randFloatSpread(radius) + ) + } else { + let lambda = Math.acos(2 * Math.random() - 1) - Math.PI / 2.0 + let phi = 2 * Math.PI * Math.random() + light.position.set( + Math.cos(lambda) * Math.cos(phi) * length, + Math.abs(Math.cos(lambda) * Math.sin(phi) * length), + Math.sin(lambda) * length + ) + } } } - } - }, [radius, ambient, length, ...position]) - - const api: AccumulativeLightContext = React.useMemo(() => ({ update }), [update]) - React.useImperativeHandle(forwardRef, () => api, [api]) - React.useLayoutEffect(() => { - const group = gLights.current - if (parent) parent.lights?.set(group.uuid, api) - return () => void parent?.lights?.delete(group.uuid) - }, [parent, api]) - - return ( - - {Array.from({ length: amount }, (_, index) => ( - - - - ))} - - ) - } -) + }, [radius, ambient, length, ...position]) + + const api: AccumulativeLightContext = React.useMemo(() => ({ update }), [update]) + React.useImperativeHandle(forwardRef, () => api, [api]) + React.useLayoutEffect(() => { + const group = gLights.current + if (parent) parent.lights?.set(group.uuid, api) + return () => void parent?.lights?.delete(group.uuid) + }, [parent, api]) + + return ( + + {Array.from({ length: amount }, (_, index) => ( + + + + ))} + + ) + } + ) // Based on "Progressive Light Map Accumulator", by [zalo](https://github.com/zalo/) class ProgressiveLightMap { diff --git a/src/core/ArcballControls.tsx b/src/core/ArcballControls.tsx index 98254c1aa..71f25fa40 100644 --- a/src/core/ArcballControls.tsx +++ b/src/core/ArcballControls.tsx @@ -1,14 +1,14 @@ -import { EventManager, ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { EventManager, ReactThreeFiber, ThreeElement, useFrame, useThree } from '@react-three/fiber' import * as React from 'react' import { forwardRef, useEffect, useMemo } from 'react' import { ArcballControls as ArcballControlsImpl } from 'three-stdlib' import type { Event, OrthographicCamera, PerspectiveCamera } from 'three' -import { ForwardRefComponent } from '../helpers/ts-utils' +import { ForwardRefComponent, Overwrite } from '../helpers/ts-utils' export type ArcballControlsProps = Omit< - ReactThreeFiber.Overwrite< - ReactThreeFiber.Object3DNode, + Overwrite< + ThreeElement, { target?: ReactThreeFiber.Vector3 camera?: OrthographicCamera | PerspectiveCamera diff --git a/src/core/AsciiRenderer.tsx b/src/core/AsciiRenderer.tsx index c6d811441..faa90a178 100644 --- a/src/core/AsciiRenderer.tsx +++ b/src/core/AsciiRenderer.tsx @@ -2,7 +2,7 @@ import * as React from 'react' import { useFrame, useThree } from '@react-three/fiber' import { AsciiEffect } from 'three-stdlib' -type AsciiRendererProps = { +export type AsciiRendererProps = { /** Render index, default: 1 */ renderIndex?: number /** CSS background color (can be "transparent"), default: black */ diff --git a/src/core/BBAnchor.tsx b/src/core/BBAnchor.tsx index a1bcb6e42..e8346bb78 100644 --- a/src/core/BBAnchor.tsx +++ b/src/core/BBAnchor.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import * as THREE from 'three' -import { useFrame, GroupProps } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' const boundingBox = /* @__PURE__ */ new THREE.Box3() const boundingBoxSize = /* @__PURE__ */ new THREE.Vector3() -export interface BBAnchorProps extends GroupProps { +export type BBAnchorProps = ThreeElements['group'] & { anchor: THREE.Vector3 | [number, number, number] } diff --git a/src/core/Backdrop.tsx b/src/core/Backdrop.tsx index 7306fcea8..acc9172b6 100644 --- a/src/core/Backdrop.tsx +++ b/src/core/Backdrop.tsx @@ -1,9 +1,10 @@ +import { ThreeElements } from '@react-three/fiber' import * as React from 'react' import { PlaneGeometry, BufferAttribute } from 'three' const easeInExpo = (x: number) => (x === 0 ? 0 : Math.pow(2, 10 * x - 10)) -export type BackdropProps = JSX.IntrinsicElements['group'] & { +export type BackdropProps = ThreeElements['group'] & { floor?: number segments?: number receiveShadow?: boolean diff --git a/src/core/Billboard.tsx b/src/core/Billboard.tsx index 2e3512560..db0bdcba6 100644 --- a/src/core/Billboard.tsx +++ b/src/core/Billboard.tsx @@ -1,14 +1,14 @@ import * as React from 'react' import { Group, Quaternion } from 'three' -import { useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -export type BillboardProps = { +export type BillboardProps = Omit & { follow?: boolean lockX?: boolean lockY?: boolean lockZ?: boolean -} & JSX.IntrinsicElements['group'] +} /** * Wraps children in a billboarded group. Sample usage: diff --git a/src/core/Bounds.tsx b/src/core/Bounds.tsx index 2ab6f7845..a6c37c2e9 100644 --- a/src/core/Bounds.tsx +++ b/src/core/Bounds.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import * as THREE from 'three' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' export type SizeProps = { box: THREE.Box3 @@ -27,7 +27,7 @@ export type BoundsApi = { clip(): BoundsApi } -export type BoundsProps = JSX.IntrinsicElements['group'] & { +export type BoundsProps = Omit & { maxDuration?: number margin?: number observe?: boolean diff --git a/src/core/Bvh.tsx b/src/core/Bvh.tsx index a6d0654b9..c26e084cf 100644 --- a/src/core/Bvh.tsx +++ b/src/core/Bvh.tsx @@ -1,4 +1,4 @@ -import { useThree } from '@react-three/fiber' +import { ThreeElements, useThree } from '@react-three/fiber' import * as React from 'react' import { Mesh, Group } from 'three' import { acceleratedRaycast, computeBoundsTree, disposeBoundsTree, SAH, SplitStrategy } from 'three-mesh-bvh' @@ -27,7 +27,7 @@ export interface BVHOptions { } export type BvhProps = BVHOptions & - JSX.IntrinsicElements['group'] & { + ThreeElements['group'] & { /**Enabled, default: true */ enabled?: boolean /** Use .raycastFirst to retrieve hits which is generally faster, default: false */ @@ -39,7 +39,7 @@ const isMesh = (child: any): child is Mesh => child.isMesh /** * @deprecated Use the Bvh component instead */ -export function useBVH(mesh: React.MutableRefObject, options?: BVHOptions) { +export function useBVH(mesh: React.RefObject, options?: BVHOptions) { options = { strategy: SAH, verbose: false, diff --git a/src/core/CameraControls.tsx b/src/core/CameraControls.tsx index 082b96749..40eb1561b 100644 --- a/src/core/CameraControls.tsx +++ b/src/core/CameraControls.tsx @@ -16,14 +16,14 @@ import { import * as React from 'react' import { forwardRef, useMemo, useEffect } from 'react' -import { extend, useFrame, useThree, ReactThreeFiber, EventManager } from '@react-three/fiber' +import { extend, useFrame, useThree, ReactThreeFiber, EventManager, ThreeElement } from '@react-three/fiber' import CameraControlsImpl from 'camera-controls' -import { ForwardRefComponent } from '../helpers/ts-utils' +import { ForwardRefComponent, Overwrite } from '../helpers/ts-utils' export type CameraControlsProps = Omit< - ReactThreeFiber.Overwrite< - ReactThreeFiber.Node, + Overwrite< + ThreeElement, { camera?: PerspectiveCamera | OrthographicCamera domElement?: HTMLElement @@ -35,7 +35,7 @@ export type CameraControlsProps = Omit< regress?: boolean } >, - 'ref' | keyof EventDispatcher + 'ref' | 'args' | keyof EventDispatcher > export const CameraControls: ForwardRefComponent = /* @__PURE__ */ forwardRef< diff --git a/src/core/CatmullRomLine.tsx b/src/core/CatmullRomLine.tsx index 1c3040327..c7f61d029 100644 --- a/src/core/CatmullRomLine.tsx +++ b/src/core/CatmullRomLine.tsx @@ -4,50 +4,51 @@ import { Line2 } from 'three-stdlib' import { Line, LineProps } from './Line' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = Omit & { +export type CatmullRomLineProps = Omit & { closed?: boolean curveType?: 'centripetal' | 'chordal' | 'catmullrom' tension?: number segments?: number } -export const CatmullRomLine: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - function CatmullRomLine( - { points, closed = false, curveType = 'centripetal', tension = 0.5, segments = 20, vertexColors, ...rest }, - ref - ) { - const curve = React.useMemo(() => { - const mappedPoints = points.map((pt) => - pt instanceof Vector3 ? pt : new Vector3(...(pt as [number, number, number])) - ) - - return new CatmullRomCurve3(mappedPoints, closed, curveType, tension) - }, [points, closed, curveType, tension]) - - const segmentedPoints = React.useMemo(() => curve.getPoints(segments), [curve, segments]) - - const interpolatedVertexColors = React.useMemo(() => { - if (!vertexColors || vertexColors.length < 2) return undefined - - if (vertexColors.length === segments + 1) return vertexColors - - const mappedColors = vertexColors.map((color) => - color instanceof Color ? color : new Color(...(color as [number, number, number])) - ) - if (closed) mappedColors.push(mappedColors[0].clone()) - - const iColors: Color[] = [mappedColors[0]] - const divisions = segments / (mappedColors.length - 1) - for (let i = 1; i < segments; i++) { - const alpha = (i % divisions) / divisions - const colorIndex = Math.floor(i / divisions) - iColors.push(mappedColors[colorIndex].clone().lerp(mappedColors[colorIndex + 1], alpha)) - } - iColors.push(mappedColors[mappedColors.length - 1]) - - return iColors - }, [vertexColors, segments]) - - return - } -) +export const CatmullRomLine: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< + Line2, + CatmullRomLineProps +>(function CatmullRomLine( + { points, closed = false, curveType = 'centripetal', tension = 0.5, segments = 20, vertexColors, ...rest }, + ref +) { + const curve = React.useMemo(() => { + const mappedPoints = points.map((pt) => + pt instanceof Vector3 ? pt : new Vector3(...(pt as [number, number, number])) + ) + + return new CatmullRomCurve3(mappedPoints, closed, curveType, tension) + }, [points, closed, curveType, tension]) + + const segmentedPoints = React.useMemo(() => curve.getPoints(segments), [curve, segments]) + + const interpolatedVertexColors = React.useMemo(() => { + if (!vertexColors || vertexColors.length < 2) return undefined + + if (vertexColors.length === segments + 1) return vertexColors + + const mappedColors = vertexColors.map((color) => + color instanceof Color ? color : new Color(...(color as [number, number, number])) + ) + if (closed) mappedColors.push(mappedColors[0].clone()) + + const iColors: Color[] = [mappedColors[0]] + const divisions = segments / (mappedColors.length - 1) + for (let i = 1; i < segments; i++) { + const alpha = (i % divisions) / divisions + const colorIndex = Math.floor(i / divisions) + iColors.push(mappedColors[colorIndex].clone().lerp(mappedColors[colorIndex + 1], alpha)) + } + iColors.push(mappedColors[mappedColors.length - 1]) + + return iColors + }, [vertexColors, segments]) + + return +}) diff --git a/src/core/Caustics.tsx b/src/core/Caustics.tsx index a5b6d46f0..2dfabb0ed 100644 --- a/src/core/Caustics.tsx +++ b/src/core/Caustics.tsx @@ -1,10 +1,13 @@ +// TODO: ESLint thinks Caustics is executed in a loop. +/* eslint-disable react-hooks/rules-of-hooks */ + /** Author: @N8Programs https://github.com/N8python * https://github.com/N8python/caustics */ import * as THREE from 'three' import * as React from 'react' -import { extend, ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { extend, ReactThreeFiber, ThreeElements, useFrame, useThree } from '@react-three/fiber' import { useFBO } from './Fbo' import { useHelper } from './Helper' import { shaderMaterial } from './shaderMaterial' @@ -41,7 +44,7 @@ type CausticsProjectionMaterialType = THREE.MeshNormalMaterial & { lightViewMatrix?: THREE.Matrix4 } -type CausticsProps = JSX.IntrinsicElements['group'] & { +export type CausticsProps = Omit & { /** How many frames it will render, set it to Infinity for runtime, default: 1 */ frames?: number /** Enables visual cues to help you stage your scene, default: false */ @@ -63,20 +66,18 @@ type CausticsProps = JSX.IntrinsicElements['group'] & { /** Buffer resolution, default: 2048 */ resolution?: number /** Camera position, it will point towards the contents bounds center, default: [5, 5, 5] */ - lightSource?: [x: number, y: number, z: number] | React.MutableRefObject + lightSource?: [x: number, y: number, z: number] | React.RefObject } -declare global { - namespace JSX { - interface IntrinsicElements { - causticsProjectionMaterial: ReactThreeFiber.MeshNormalMaterialProps & { - viewMatrix?: { value: THREE.Matrix4 } - color?: ReactThreeFiber.Color - causticsTexture?: THREE.Texture - causticsTextureB?: THREE.Texture - lightProjMatrix?: THREE.Matrix4 - lightViewMatrix?: THREE.Matrix4 - } +declare module '@react-three/fiber' { + interface ThreeElements { + causticsProjectionMaterial: ThreeElements['meshNormalMaterial'] & { + viewMatrix?: { value: THREE.Matrix4 } + color?: ReactThreeFiber.Color + causticsTexture?: THREE.Texture + causticsTextureB?: THREE.Texture + lightProjMatrix?: THREE.Matrix4 + lightViewMatrix?: THREE.Matrix4 } } } diff --git a/src/core/Center.tsx b/src/core/Center.tsx index c3c1936ee..d21f2676c 100644 --- a/src/core/Center.tsx +++ b/src/core/Center.tsx @@ -1,6 +1,6 @@ import { Box3, Vector3, Sphere, Group, Object3D } from 'three' import * as React from 'react' -import { useThree } from '@react-three/fiber' +import { ThreeElements, useThree } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' export type OnCenterCallbackProps = { @@ -19,7 +19,7 @@ export type OnCenterCallbackProps = { depthAlignment: number } -export type CenterProps = { +export type CenterProps = Omit & { top?: boolean right?: boolean bottom?: boolean @@ -42,8 +42,8 @@ export type CenterProps = { cacheKey?: any } -export const Center: ForwardRefComponent = - /* @__PURE__ */ React.forwardRef(function Center( +export const Center: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( + function Center( { children, disable, @@ -113,4 +113,5 @@ export const Center: ForwardRefComponent ) - }) + } +) diff --git a/src/core/Clone.tsx b/src/core/Clone.tsx index b617322ab..091431fa7 100644 --- a/src/core/Clone.tsx +++ b/src/core/Clone.tsx @@ -1,10 +1,10 @@ import * as THREE from 'three' import * as React from 'react' -import { MeshProps } from '@react-three/fiber' import { SkeletonUtils } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' +import { ThreeElements } from '@react-three/fiber' -export type CloneProps = { +export type CloneProps = Omit & { /** Any pre-existing THREE.Object3D (groups, meshes, ...), or an array of objects */ object: THREE.Object3D | THREE.Object3D[] /** Children will be placed within the object, or within the group that holds arrayed objects */ @@ -14,7 +14,7 @@ export type CloneProps = { /** The property keys it will shallow-clone (material, geometry, visible, ...) */ keys?: string[] /** Can either spread over props or fill in JSX children, applies to every mesh within */ - inject?: MeshProps | React.ReactNode | ((object: THREE.Object3D) => React.ReactNode) + inject?: ThreeElements['mesh'] | React.ReactNode | ((object: THREE.Object3D) => React.ReactNode) /** Short access castShadow, applied to every mesh within */ castShadow?: boolean /** Short access receiveShadow, applied to every mesh within */ @@ -57,7 +57,7 @@ function createSpread( inject, castShadow, receiveShadow, - }: Omit & Partial + } ) { let spread: Record<(typeof keys)[number], any> = {} for (const key of keys) { @@ -81,59 +81,45 @@ function createSpread( return spread } -export const Clone: ForwardRefComponent & CloneProps, THREE.Group> = - /* @__PURE__ */ React.forwardRef( - ( - { - isChild = false, - object, - children, - deep, - castShadow, - receiveShadow, - inject, - keys, - ...props - }: Omit & CloneProps, - forwardRef: React.Ref - ) => { - const config = { keys, deep, inject, castShadow, receiveShadow } - object = React.useMemo(() => { - if (isChild === false && !Array.isArray(object)) { - let isSkinned = false - object.traverse((object) => { - if ((object as any).isSkinnedMesh) isSkinned = true - }) - if (isSkinned) return SkeletonUtils.clone(object) - } - return object - }, [object, isChild]) - - // Deal with arrayed clones - if (Array.isArray(object)) { - return ( - - {object.map((o) => ( - - ))} - {children} - - ) +export const Clone: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( + ({ isChild = false, object, children, deep, castShadow, receiveShadow, inject, keys, ...props }, forwardRef) => { + const config = { keys, deep, inject, castShadow, receiveShadow } + object = React.useMemo(() => { + if (isChild === false && !Array.isArray(object)) { + let isSkinned = false + object.traverse((object) => { + if ((object as any).isSkinnedMesh) isSkinned = true + }) + if (isSkinned) return SkeletonUtils.clone(object) } + return object + }, [object, isChild]) - // Singleton clones - const { children: injectChildren, ...spread } = createSpread(object, config) - const Element = (object.type[0].toLowerCase() + object.type.slice(1)) as unknown as React.ExoticComponent - + // Deal with arrayed clones + if (Array.isArray(object)) { return ( - - {object.children.map((child) => { - if (child.type === 'Bone') return - return - })} + + {object.map((o) => ( + + ))} {children} - {injectChildren} - + ) } - ) + + // Singleton clones + const { children: injectChildren, ...spread } = createSpread(object, config) + const Element = (object.type[0].toLowerCase() + object.type.slice(1)) as unknown as React.ExoticComponent + + return ( + + {object.children.map((child) => { + if (child.type === 'Bone') return + return + })} + {children} + {injectChildren} + + ) + } +) diff --git a/src/core/Cloud.tsx b/src/core/Cloud.tsx index e6038f61d..f16e84f22 100644 --- a/src/core/Cloud.tsx +++ b/src/core/Cloud.tsx @@ -13,15 +13,13 @@ import { Quaternion, BufferAttribute, } from 'three' -import { MaterialNode, extend, applyProps, useFrame, ReactThreeFiber } from '@react-three/fiber' +import { extend, applyProps, useFrame, ReactThreeFiber, ThreeElement, ThreeElements } from '@react-three/fiber' import { useTexture } from './Texture' import { setUpdateRange } from '../helpers/deprecated' -declare global { - namespace JSX { - interface IntrinsicElements { - cloudMaterial: MaterialNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + cloudMaterial: ThreeElement } } @@ -37,7 +35,7 @@ type CloudState = { position: Vector3 volume: number length: number - ref: React.MutableRefObject + ref: React.RefObject speed: number growth: number opacity: number @@ -48,7 +46,7 @@ type CloudState = { color: Color } -type CloudsProps = JSX.IntrinsicElements['group'] & { +export type CloudsProps = Omit & { /** Optional cloud texture, points to a default hosted on rawcdn.githack */ texture?: string /** Maximum number of segments, default: 200 (make this tight to save memory!) */ @@ -61,7 +59,7 @@ type CloudsProps = JSX.IntrinsicElements['group'] & { frustumCulled?: boolean } -type CloudProps = JSX.IntrinsicElements['group'] & { +export type CloudProps = Omit & { /** A seeded random will show the same cloud consistently, default: Math.random() */ seed?: number /** How many segments or particles the cloud will have, default: 20 */ @@ -98,7 +96,7 @@ const cpos = /* @__PURE__ */ new Vector3() const cquat = /* @__PURE__ */ new Quaternion() const scale = /* @__PURE__ */ new Vector3() -const context = /* @__PURE__ */ React.createContext>(null!) +const context = /* @__PURE__ */ React.createContext>(null!) export const Clouds = /* @__PURE__ */ React.forwardRef( ( { children, material = MeshLambertMaterial, texture = CLOUD_URL, range, limit = 200, frustumCulled, ...props }, @@ -182,11 +180,11 @@ export const Clouds = /* @__PURE__ */ React.forwardRef( React.useLayoutEffect(() => { const count = Math.min(limit, range !== undefined ? range : limit, clouds.current.length) instance.current.count = count - setUpdateRange(instance.current.instanceMatrix, { offset: 0, count: count * 16 }) + setUpdateRange(instance.current.instanceMatrix, { start: 0, count: count * 16 }) if (instance.current.instanceColor) { - setUpdateRange(instance.current.instanceColor, { offset: 0, count: count * 3 }) + setUpdateRange(instance.current.instanceColor, { start: 0, count: count * 3 }) } - setUpdateRange(instance.current.geometry.attributes.cloudOpacity as BufferAttribute, { offset: 0, count: count }) + setUpdateRange(instance.current.geometry.attributes.cloudOpacity as BufferAttribute, { start: 0, count: count }) }) let imageBounds = [cloudTexture!.image.width ?? 1, cloudTexture!.image.height ?? 1] diff --git a/src/core/ComputedAttribute.tsx b/src/core/ComputedAttribute.tsx index a0cb80b25..e9e7366b9 100644 --- a/src/core/ComputedAttribute.tsx +++ b/src/core/ComputedAttribute.tsx @@ -1,8 +1,8 @@ -import { BufferAttributeProps } from '@react-three/fiber' +import { ThreeElements } from '@react-three/fiber' import * as React from 'react' import { BufferAttribute, BufferGeometry } from 'three' -type Props = { +export type ComputedAttributeProps = Omit & { compute: (geometry: BufferGeometry) => BufferAttribute name: string } @@ -12,18 +12,14 @@ type Props = { * Computes the BufferAttribute by calling the `compute` function * and attaches the attribute to the geometry. */ -export const ComputedAttribute = ({ - compute, - name, - ...props -}: React.PropsWithChildren) => { +export const ComputedAttribute = ({ compute, name, ...props }: ComputedAttributeProps) => { const [bufferAttribute] = React.useState(() => new BufferAttribute(new Float32Array(0), 1)) const primitive = React.useRef(null) React.useLayoutEffect(() => { if (primitive.current) { // @ts-expect-error brittle - const parent = (primitive.current.parent as BufferGeometry) ?? primitive.current.__r3f.parent + const parent = (primitive.current.parent as BufferGeometry) ?? primitive.current.__r3f.parent.object const attr = compute(parent) primitive.current.copy(attr) diff --git a/src/core/ContactShadows.tsx b/src/core/ContactShadows.tsx index c6d789879..bfc6d0b94 100644 --- a/src/core/ContactShadows.tsx +++ b/src/core/ContactShadows.tsx @@ -3,11 +3,11 @@ import * as React from 'react' import * as THREE from 'three' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' import { HorizontalBlurShader, VerticalBlurShader } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export type ContactShadowsProps = { +export type ContactShadowsProps = Omit & { opacity?: number width?: number height?: number @@ -22,10 +22,7 @@ export type ContactShadowsProps = { depthWrite?: boolean } -export const ContactShadows: ForwardRefComponent< - Omit & ContactShadowsProps, - THREE.Group -> = /* @__PURE__ */ React.forwardRef( +export const ContactShadows: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ( { scale = 10, @@ -42,7 +39,7 @@ export const ContactShadows: ForwardRefComponent< depthWrite = false, renderOrder, ...props - }: Omit & ContactShadowsProps, + }, fref ) => { const ref = React.useRef(null!) diff --git a/src/core/CubeCamera.tsx b/src/core/CubeCamera.tsx index 272ee09e0..bd8d7c42e 100644 --- a/src/core/CubeCamera.tsx +++ b/src/core/CubeCamera.tsx @@ -2,7 +2,7 @@ import * as THREE from 'three' import { HalfFloatType, Fog, FogExp2, WebGLCubeRenderTarget, Texture, Group } from 'three' import * as React from 'react' import { useEffect, useMemo } from 'react' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' export type CubeCameraOptions = { /** Resolution of the FBO, 256 */ @@ -54,14 +54,23 @@ export function useCubeCamera({ resolution = 256, near = 0.1, far = 1000, envMap } } -type Props = Omit & { +export type CubeCameraProps = Omit & { /** The contents of CubeCamera will be hidden when filming the cube */ children?: (tex: Texture) => React.ReactNode /** Number of frames to render, Infinity */ frames?: number } & CubeCameraOptions -export function CubeCamera({ children, frames = Infinity, resolution, near, far, envMap, fog, ...props }: Props) { +export function CubeCamera({ + children, + frames = Infinity, + resolution, + near, + far, + envMap, + fog, + ...props +}: CubeCameraProps) { const ref = React.useRef(null!) const { fbo, camera, update } = useCubeCamera({ resolution, diff --git a/src/core/CubeTexture.tsx b/src/core/CubeTexture.tsx index a5caf726b..f11a07688 100644 --- a/src/core/CubeTexture.tsx +++ b/src/core/CubeTexture.tsx @@ -2,33 +2,22 @@ import * as React from 'react' import { CubeTextureLoader, CubeTexture as _CubeTexture, Texture } from 'three' import { useLoader } from '@react-three/fiber' -type Options = { +export type CubeTextureOptions = { path: string } -export function useCubeTexture(files: string[], { path }: Options): _CubeTexture { - // @ts-ignore - const [cubeTexture] = useLoader( - // @ts-ignore - CubeTextureLoader, - [files], - (loader: CubeTextureLoader) => loader.setPath(path) - ) +export function useCubeTexture(files: string[], { path }: CubeTextureOptions): _CubeTexture { + const [cubeTexture] = useLoader(CubeTextureLoader, [files], (loader) => loader.setPath(path)) return cubeTexture } -useCubeTexture.preload = (files: string[], { path }: Options) => - useLoader.preload( - // @ts-ignore - CubeTextureLoader, - [files], - (loader: CubeTextureLoader) => loader.setPath(path) - ) +useCubeTexture.preload = (files: string[], { path }: CubeTextureOptions) => + useLoader.preload(CubeTextureLoader, [files], (loader) => loader.setPath(path)) -type CubeTextureProps = { +export type CubeTextureProps = CubeTextureOptions & { children?: (tex: Texture) => React.ReactNode files: Parameters[0] -} & Options +} export function CubeTexture({ children, files, ...options }: CubeTextureProps) { const texture = useCubeTexture(files, { ...options }) diff --git a/src/core/CubicBezierLine.tsx b/src/core/CubicBezierLine.tsx index db1da4c1a..46a9fc9c3 100644 --- a/src/core/CubicBezierLine.tsx +++ b/src/core/CubicBezierLine.tsx @@ -4,7 +4,7 @@ import { Line2 } from 'three-stdlib' import { Line, LineProps } from './Line' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = Omit & { +export type CubicBezierLineProps = Omit & { start: Vector3 | [number, number, number] end: Vector3 | [number, number, number] midA: Vector3 | [number, number, number] @@ -12,17 +12,18 @@ type Props = Omit & { segments?: number } -export const CubicBezierLine: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - function CubicBezierLine({ start, end, midA, midB, segments = 20, ...rest }, ref) { - const points = React.useMemo(() => { - const startV = start instanceof Vector3 ? start : new Vector3(...start) - const endV = end instanceof Vector3 ? end : new Vector3(...end) - const midAV = midA instanceof Vector3 ? midA : new Vector3(...midA) - const midBV = midB instanceof Vector3 ? midB : new Vector3(...midB) - const interpolatedV = new CubicBezierCurve3(startV, midAV, midBV, endV).getPoints(segments) - return interpolatedV - }, [start, end, midA, midB, segments]) +export const CubicBezierLine: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< + Line2, + CubicBezierLineProps +>(function CubicBezierLine({ start, end, midA, midB, segments = 20, ...rest }, ref) { + const points = React.useMemo(() => { + const startV = start instanceof Vector3 ? start : new Vector3(...start) + const endV = end instanceof Vector3 ? end : new Vector3(...end) + const midAV = midA instanceof Vector3 ? midA : new Vector3(...midA) + const midBV = midB instanceof Vector3 ? midB : new Vector3(...midB) + const interpolatedV = new CubicBezierCurve3(startV, midAV, midBV, endV).getPoints(segments) + return interpolatedV + }, [start, end, midA, midB, segments]) - return - } -) + return +}) diff --git a/src/core/CurveModifier.tsx b/src/core/CurveModifier.tsx index e23a6daa4..e5a9dd071 100644 --- a/src/core/CurveModifier.tsx +++ b/src/core/CurveModifier.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import * as THREE from 'three' -import { createPortal } from '@react-three/fiber' +import { createPortal, ThreeElements } from '@react-three/fiber' import { Flow } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' export interface CurveModifierProps { - children: React.ReactElement + children: React.ReactElement curve?: THREE.Curve } diff --git a/src/core/Decal.tsx b/src/core/Decal.tsx index a46ace885..e43f1db84 100644 --- a/src/core/Decal.tsx +++ b/src/core/Decal.tsx @@ -5,9 +5,9 @@ import { applyProps } from '@react-three/fiber' import { DecalGeometry } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export type DecalProps = Omit & { +export type DecalProps = Omit & { debug?: boolean - mesh?: React.MutableRefObject + mesh?: React.RefObject position?: FIBER.Vector3 /** FIBER.Euler for manual orientation or a single float for closest-vertex-normal orient */ rotation?: FIBER.Euler | number diff --git a/src/core/Detailed.tsx b/src/core/Detailed.tsx index 12a307b15..a55cc8f30 100644 --- a/src/core/Detailed.tsx +++ b/src/core/Detailed.tsx @@ -1,16 +1,16 @@ import * as React from 'react' import { LOD, Object3D } from 'three' -import { useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = JSX.IntrinsicElements['lOD'] & { +export type DetailedProps = Omit & { children: React.ReactElement[] hysteresis?: number distances: number[] } -export const Detailed: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ children, hysteresis = 0, distances, ...props }: Props, ref) => { +export const Detailed: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( + ({ children, hysteresis = 0, distances, ...props }, ref) => { const lodRef = React.useRef(null!) React.useImperativeHandle(ref, () => lodRef.current, []) React.useLayoutEffect(() => { diff --git a/src/core/DetectGPU.tsx b/src/core/DetectGPU.tsx index bee16143f..953e953f5 100644 --- a/src/core/DetectGPU.tsx +++ b/src/core/DetectGPU.tsx @@ -4,7 +4,7 @@ import { suspend } from 'suspend-react' export const useDetectGPU = (props?: GetGPUTier) => suspend(() => getGPUTier(props), ['useDetectGPU']) -type DetectGPUProps = { +export type DetectGPUProps = { children?: (result: ReturnType) => React.ReactNode } & Parameters[0] diff --git a/src/core/DeviceOrientationControls.tsx b/src/core/DeviceOrientationControls.tsx index 25a273e72..cca7bdeb8 100644 --- a/src/core/DeviceOrientationControls.tsx +++ b/src/core/DeviceOrientationControls.tsx @@ -1,13 +1,10 @@ -import { ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { ReactThreeFiber, ThreeElement, useFrame, useThree } from '@react-three/fiber' import * as React from 'react' import * as THREE from 'three' import { DeviceOrientationControls as DeviceOrientationControlsImp } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export type DeviceOrientationControlsProps = ReactThreeFiber.Object3DNode< - DeviceOrientationControlsImp, - typeof DeviceOrientationControlsImp -> & { +export type DeviceOrientationControlsProps = Omit, 'ref'> & { camera?: THREE.Camera onChange?: (e?: THREE.Event) => void makeDefault?: boolean diff --git a/src/core/Edges.tsx b/src/core/Edges.tsx index d25a058fb..39258bf3e 100644 --- a/src/core/Edges.tsx +++ b/src/core/Edges.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as THREE from 'three' -import { ReactThreeFiber, type ThreeElements } from '@react-three/fiber' +import { ThreeElement, type ThreeElements } from '@react-three/fiber' import { LineSegmentsGeometry, LineMaterial, LineMaterialParameters, Line2, LineSegments2 } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' import { Line } from './Line' @@ -10,8 +10,8 @@ export type EdgesProps = Partial & { threshold?: number lineWidth?: number } & Omit & - Omit, 'args' | 'geometry'> & - Omit, 'color' | 'vertexColors' | 'args'> & { + Omit, 'args' | 'geometry'> & + Omit, 'color' | 'vertexColors' | 'args'> & { geometry?: THREE.BufferGeometry color?: THREE.ColorRepresentation } @@ -22,8 +22,8 @@ export const Edges: ForwardRefComponent = /* @__PURE__ */ React.useImperativeHandle(fref, () => ref.current, []) const tmpPoints = React.useMemo(() => [0, 0, 0, 1, 0, 0], []) - const memoizedGeometry = React.useRef() - const memoizedThreshold = React.useRef() + const memoizedGeometry = React.useRef(null) + const memoizedThreshold = React.useRef(null) React.useLayoutEffect(() => { const parent = ref.current.parent as THREE.Mesh @@ -43,6 +43,6 @@ export const Edges: ForwardRefComponent = /* @__PURE__ */ ref.current.computeLineDistances() }) - return null} {...props} /> + return null} {...props} /> } ) diff --git a/src/core/Effects.tsx b/src/core/Effects.tsx index f86bf69e4..2e50b14ca 100644 --- a/src/core/Effects.tsx +++ b/src/core/Effects.tsx @@ -1,13 +1,12 @@ import * as React from 'react' import { RGBAFormat, HalfFloatType, WebGLRenderTarget, UnsignedByteType, TextureDataType } from 'three' -import { ReactThreeFiber, extend, useThree, useFrame } from '@react-three/fiber' +import { extend, useThree, useFrame, ThreeElement, ThreeElements } from '@react-three/fiber' import { EffectComposer, RenderPass, ShaderPass, GammaCorrectionShader } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -import { TextureEncoding } from '../helpers/deprecated' -type Props = ReactThreeFiber.Node & { +export type EffectsProps = Omit & { multisamping?: number - encoding?: TextureEncoding + colorSpace?: THREE.ColorSpace type?: TextureDataType renderIndex?: number disableGamma?: boolean @@ -18,13 +17,11 @@ type Props = ReactThreeFiber.Node & { anisotropy?: number } -declare global { - namespace JSX { - interface IntrinsicElements { - effectComposer: ReactThreeFiber.Node - renderPass: ReactThreeFiber.Node - shaderPass: ReactThreeFiber.Node - } +declare module '@react-three/fiber' { + interface ThreeElements { + effectComposer: ThreeElement + renderPass: ThreeElement + shaderPass: ThreeElement } } @@ -37,7 +34,7 @@ export const isWebGL2Available = () => { } } -export const Effects: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( +export const Effects: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ( { children, @@ -49,10 +46,10 @@ export const Effects: ForwardRefComponent = /* @__PURE__ depthBuffer = true, stencilBuffer = false, anisotropy = 1, - encoding, + colorSpace, type, ...props - }: Props, + }, ref ) => { React.useMemo(() => extend({ EffectComposer, RenderPass, ShaderPass }), []) @@ -69,9 +66,8 @@ export const Effects: ForwardRefComponent = /* @__PURE__ }) // sRGB textures must be RGBA8 since r137 https://github.com/mrdoob/three.js/pull/23129 - if (type === UnsignedByteType && encoding != null) { - if ('colorSpace' in t) (t.texture as any).colorSpace = encoding - else t.texture.encoding = encoding + if (type === UnsignedByteType && colorSpace != null) { + t.texture.colorSpace = colorSpace } t.samples = multisamping diff --git a/src/core/Environment.tsx b/src/core/Environment.tsx index df9fecd72..f100fb32c 100644 --- a/src/core/Environment.tsx +++ b/src/core/Environment.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { useThree, createPortal, useFrame, extend, Object3DNode, Euler, applyProps } from '@react-three/fiber' +import { useThree, createPortal, useFrame, extend, Euler, applyProps, ThreeElement } from '@react-three/fiber' import { WebGLCubeRenderTarget, Texture, Scene, CubeCamera, HalfFloatType, CubeTexture } from 'three' import { GroundProjectedEnv as GroundProjectedEnvImpl } from 'three-stdlib' import { PresetsType } from '../helpers/environment-assets' @@ -23,7 +23,7 @@ export type EnvironmentProps = { map?: Texture preset?: PresetsType - scene?: Scene | React.MutableRefObject + scene?: Scene | React.RefObject ground?: | boolean | { @@ -33,12 +33,12 @@ export type EnvironmentProps = { } } & EnvironmentLoaderProps -const isRef = (obj: any): obj is React.MutableRefObject => obj.current && obj.current.isScene -const resolveScene = (scene: Scene | React.MutableRefObject) => (isRef(scene) ? scene.current : scene) +const isRef = (obj: any): obj is React.RefObject => obj.current && obj.current.isScene +const resolveScene = (scene: Scene | React.RefObject) => (isRef(scene) ? scene.current : scene) function setEnvProps( background: boolean | 'only', - scene: Scene | React.MutableRefObject | undefined, + scene: Scene | React.RefObject | undefined, defaultScene: Scene, texture: Texture, sceneProps: Partial = {} @@ -202,11 +202,9 @@ export function EnvironmentPortal({ ) } -declare global { - namespace JSX { - interface IntrinsicElements { - groundProjectedEnvImpl: Object3DNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + groundProjectedEnvImpl: ThreeElement } } diff --git a/src/core/Fbo.tsx b/src/core/Fbo.tsx index f80704a0d..b1f702917 100644 --- a/src/core/Fbo.tsx +++ b/src/core/Fbo.tsx @@ -1,10 +1,6 @@ import * as React from 'react' import * as THREE from 'three' import { useThree } from '@react-three/fiber' -import { TextureEncoding } from '../helpers/deprecated' - -// TODO: consume this from three >r154 when SemVer allows -type ColorSpace = 'srgb' | 'srgb-linear' | '' | string type FBOSettings = { /** Defines the count of MSAA samples. Can only be used with WebGL 2. Default: 0 */ @@ -24,8 +20,7 @@ type FBOSettings = { stencilBuffer?: boolean | undefined // false; generateMipmaps?: boolean | undefined // true; depthTexture?: THREE.DepthTexture | undefined - encoding?: TextureEncoding | undefined - colorSpace?: ColorSpace | undefined + colorSpace?: THREE.ColorSpace | undefined } // 👇 uncomment when TS version supports function overloads diff --git a/src/core/FirstPersonControls.tsx b/src/core/FirstPersonControls.tsx index d69c9ff0c..f5d38afa6 100644 --- a/src/core/FirstPersonControls.tsx +++ b/src/core/FirstPersonControls.tsx @@ -1,9 +1,9 @@ import * as React from 'react' -import { EventManager, Object3DNode, useFrame, useThree } from '@react-three/fiber' +import { EventManager, ThreeElement, useFrame, useThree } from '@react-three/fiber' import { FirstPersonControls as FirstPersonControlImpl } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export type FirstPersonControlsProps = Object3DNode & { +export type FirstPersonControlsProps = Omit, 'ref'> & { domElement?: HTMLElement makeDefault?: boolean } diff --git a/src/core/Fisheye.tsx b/src/core/Fisheye.tsx index dfa3c2618..68a642078 100644 --- a/src/core/Fisheye.tsx +++ b/src/core/Fisheye.tsx @@ -5,10 +5,10 @@ import * as THREE from 'three' import * as React from 'react' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' import { RenderCubeTexture, RenderCubeTextureApi } from './RenderCubeTexture' -export type FisheyeProps = JSX.IntrinsicElements['mesh'] & { +export type FisheyeProps = ThreeElements['mesh'] & { /** Zoom factor, 0..1, 0 */ zoom?: number /** Number of segments, 64 */ @@ -94,7 +94,7 @@ export function Fisheye({ ) } -function UpdateCubeCamera({ api }: { api: React.MutableRefObject }) { +function UpdateCubeCamera({ api }: { api: React.RefObject }) { const t = new THREE.Vector3() const r = new THREE.Quaternion() const s = new THREE.Vector3() diff --git a/src/core/Float.tsx b/src/core/Float.tsx index bccad1583..8a4f1e8d7 100644 --- a/src/core/Float.tsx +++ b/src/core/Float.tsx @@ -1,9 +1,9 @@ import * as React from 'react' -import { useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' import * as THREE from 'three' import { ForwardRefComponent } from '../helpers/ts-utils' -export type FloatProps = JSX.IntrinsicElements['group'] & { +export type FloatProps = Omit & { enabled?: boolean speed?: number rotationIntensity?: number diff --git a/src/core/FlyControls.tsx b/src/core/FlyControls.tsx index f9227333f..191687b59 100644 --- a/src/core/FlyControls.tsx +++ b/src/core/FlyControls.tsx @@ -1,10 +1,10 @@ -import { EventManager, ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { EventManager, ThreeElement, useFrame, useThree } from '@react-three/fiber' import * as React from 'react' import * as THREE from 'three' import { FlyControls as FlyControlsImpl } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export type FlyControlsProps = ReactThreeFiber.Object3DNode & { +export type FlyControlsProps = Omit, 'ref' | 'args'> & { onChange?: (e?: THREE.Event) => void domElement?: HTMLElement makeDefault?: boolean diff --git a/src/core/GizmoHelper.tsx b/src/core/GizmoHelper.tsx index 1561b2b1d..9b03f0ca3 100644 --- a/src/core/GizmoHelper.tsx +++ b/src/core/GizmoHelper.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' import { Group, Matrix4, Object3D, OrthographicCamera as OrthographicCameraImpl, Quaternion, Vector3 } from 'three' import { OrthographicCamera } from './OrthographicCamera' import { OrbitControls as OrbitControlsType } from 'three-stdlib' @@ -25,7 +25,7 @@ const targetPosition = /* @__PURE__ */ new Vector3() type ControlsProto = { update(delta?: number): void; target: Vector3 } -export type GizmoHelperProps = JSX.IntrinsicElements['group'] & { +export type GizmoHelperProps = ThreeElements['group'] & { alignment?: | 'top-left' | 'top-right' diff --git a/src/core/GizmoViewcube.tsx b/src/core/GizmoViewcube.tsx index 44fe74f60..77550dc1a 100644 --- a/src/core/GizmoViewcube.tsx +++ b/src/core/GizmoViewcube.tsx @@ -117,7 +117,7 @@ const FaceCube = (props: GenericProps) => { ) } -const EdgeCube = ({ onClick, dimensions, position, hoverColor = colors.hover }: EdgeCubeProps): JSX.Element => { +const EdgeCube = ({ onClick, dimensions, position, hoverColor = colors.hover }: EdgeCubeProps): React.JSX.Element => { const { tweenCamera } = useGizmoContext() const [hover, setHover] = React.useState(false) const handlePointerOut = (e: ThreeEvent) => { diff --git a/src/core/GizmoViewport.tsx b/src/core/GizmoViewport.tsx index a8e12697e..60722cbd3 100644 --- a/src/core/GizmoViewport.tsx +++ b/src/core/GizmoViewport.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { useThree, ThreeEvent } from '@react-three/fiber' +import { useThree, ThreeEvent, ThreeElements } from '@react-three/fiber' import { CanvasTexture } from 'three' import { useGizmoContext } from './GizmoHelper' @@ -9,7 +9,7 @@ type AxisProps = { scale?: [number, number, number] } -type AxisHeadProps = JSX.IntrinsicElements['sprite'] & { +type AxisHeadProps = Omit & { arcStyle: string label?: string labelColor: string @@ -19,7 +19,7 @@ type AxisHeadProps = JSX.IntrinsicElements['sprite'] & { onClick?: (e: ThreeEvent) => null } -type GizmoViewportProps = JSX.IntrinsicElements['group'] & { +type GizmoViewportProps = ThreeElements['group'] & { axisColors?: [string, string, string] axisScale?: [number, number, number] labels?: [string, string, string] diff --git a/src/core/Gltf.tsx b/src/core/Gltf.tsx index 92e9420d3..8e01adf2e 100644 --- a/src/core/Gltf.tsx +++ b/src/core/Gltf.tsx @@ -1,7 +1,7 @@ import * as React from 'react' import { GLTFLoader, DRACOLoader, MeshoptDecoder, GLTF } from 'three-stdlib' import { ObjectMap, useLoader } from '@react-three/fiber' -import { Clone } from './Clone' +import { Clone, CloneProps } from './Clone' let dracoLoader: DRACOLoader | null = null let decoderPath: string = 'https://www.gstatic.com/draco/versioned/decoders/1.5.5/' @@ -47,10 +47,9 @@ useGLTF.setDecoderPath = (path: string) => { // -type CloneProps = React.ComponentProps -type GltfRef = React.ElementRef +type GltfRef = React.ComponentRef -type GltfProps = Omit & { +export type GltfProps = Omit & { src: string // simple string, not a string[] as useGLTF supports (otherwise we should render multiple s?) useDraco?: UseDraco useMeshOpt?: UseMeshopt diff --git a/src/core/GradientTexture.tsx b/src/core/GradientTexture.tsx index 3beb68f4e..e1871f276 100644 --- a/src/core/GradientTexture.tsx +++ b/src/core/GradientTexture.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { useThree } from '@react-three/fiber' +import { ThreeElements, useThree } from '@react-three/fiber' import * as THREE from 'three' export enum GradientType { @@ -7,7 +7,7 @@ export enum GradientType { Radial = 'radial', } -type Props = { +export type GradientTextureProps = { stops: Array colors: Array attach?: string @@ -16,7 +16,7 @@ type Props = { type?: GradientType innerCircleRadius?: number outerCircleRadius?: string | number -} & Omit +} & Omit export function GradientTexture({ stops, @@ -27,7 +27,7 @@ export function GradientTexture({ innerCircleRadius = 0, outerCircleRadius = 'auto', ...props -}: Props) { +}: GradientTextureProps) { const gl = useThree((state: any) => state.gl) const canvas: HTMLCanvasElement = React.useMemo(() => { const canvas = document.createElement('canvas') @@ -67,6 +67,5 @@ export function GradientTexture({ return canvas }, [stops]) - // @ts-ignore ???? return } diff --git a/src/core/Grid.tsx b/src/core/Grid.tsx index 909cf6de5..69c1e1c89 100644 --- a/src/core/Grid.tsx +++ b/src/core/Grid.tsx @@ -6,7 +6,7 @@ import * as React from 'react' import * as THREE from 'three' -import { extend, useFrame } from '@react-three/fiber' +import { extend, ThreeElements, useFrame } from '@react-three/fiber' import { shaderMaterial } from './shaderMaterial' import { ForwardRefComponent } from '../helpers/ts-utils' import { version } from '../helpers/constants' @@ -38,16 +38,15 @@ export type GridMaterialType = { side?: THREE.Side } -export type GridProps = GridMaterialType & { - /** Default plane-geometry arguments */ - args?: ConstructorParameters -} +export type GridProps = Omit & + GridMaterialType & { + /** Default plane-geometry arguments */ + args?: ConstructorParameters + } -declare global { - namespace JSX { - interface IntrinsicElements { - gridMaterial: JSX.IntrinsicElements['shaderMaterial'] & GridMaterialType - } +declare module '@react-three/fiber' { + interface ThreeElements { + gridMaterial: ThreeElements['shaderMaterial'] & GridMaterialType } } @@ -131,53 +130,52 @@ const GridMaterial = /* @__PURE__ */ shaderMaterial( ` ) -export const Grid: ForwardRefComponent & GridProps, THREE.Mesh> = - /* @__PURE__ */ React.forwardRef( - ( - { - args, - cellColor = '#000000', - sectionColor = '#2080ff', - cellSize = 0.5, - sectionSize = 1, - followCamera = false, - infiniteGrid = false, - fadeDistance = 100, - fadeStrength = 1, - fadeFrom = 1, - cellThickness = 0.5, - sectionThickness = 1, - side = THREE.BackSide, - ...props - }: Omit & GridProps, - fRef: React.ForwardedRef - ) => { - extend({ GridMaterial }) - - const ref = React.useRef(null!) - React.useImperativeHandle(fRef, () => ref.current, []) - const plane = new THREE.Plane() - const upVector = new THREE.Vector3(0, 1, 0) - const zeroVector = new THREE.Vector3(0, 0, 0) - useFrame((state) => { - plane.setFromNormalAndCoplanarPoint(upVector, zeroVector).applyMatrix4(ref.current.matrixWorld) - - const gridMaterial = ref.current.material as THREE.ShaderMaterial - const worldCamProjPosition = gridMaterial.uniforms.worldCamProjPosition as THREE.Uniform - const worldPlanePosition = gridMaterial.uniforms.worldPlanePosition as THREE.Uniform - - plane.projectPoint(state.camera.position, worldCamProjPosition.value) - worldPlanePosition.value.set(0, 0, 0).applyMatrix4(ref.current.matrixWorld) - }) - - const uniforms1 = { cellSize, sectionSize, cellColor, sectionColor, cellThickness, sectionThickness } - const uniforms2 = { fadeDistance, fadeStrength, fadeFrom, infiniteGrid, followCamera } - - return ( - - - - - ) - } - ) +export const Grid: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( + ( + { + args, + cellColor = '#000000', + sectionColor = '#2080ff', + cellSize = 0.5, + sectionSize = 1, + followCamera = false, + infiniteGrid = false, + fadeDistance = 100, + fadeStrength = 1, + fadeFrom = 1, + cellThickness = 0.5, + sectionThickness = 1, + side = THREE.BackSide, + ...props + }, + fRef + ) => { + extend({ GridMaterial }) + + const ref = React.useRef(null!) + React.useImperativeHandle(fRef, () => ref.current, []) + const plane = new THREE.Plane() + const upVector = new THREE.Vector3(0, 1, 0) + const zeroVector = new THREE.Vector3(0, 0, 0) + useFrame((state) => { + plane.setFromNormalAndCoplanarPoint(upVector, zeroVector).applyMatrix4(ref.current.matrixWorld) + + const gridMaterial = ref.current.material as THREE.ShaderMaterial + const worldCamProjPosition = gridMaterial.uniforms.worldCamProjPosition as THREE.Uniform + const worldPlanePosition = gridMaterial.uniforms.worldPlanePosition as THREE.Uniform + + plane.projectPoint(state.camera.position, worldCamProjPosition.value) + worldPlanePosition.value.set(0, 0, 0).applyMatrix4(ref.current.matrixWorld) + }) + + const uniforms1 = { cellSize, sectionSize, cellColor, sectionColor, cellThickness, sectionThickness } + const uniforms2 = { fadeDistance, fadeStrength, fadeFrom, infiniteGrid, followCamera } + + return ( + + + + + ) + } +) diff --git a/src/core/Helper.tsx b/src/core/Helper.tsx index 599cb42a3..12262dde8 100644 --- a/src/core/Helper.tsx +++ b/src/core/Helper.tsx @@ -8,11 +8,11 @@ type HelperConstructor = new (...args: any[]) => any type HelperArgs = T extends [infer _, ...infer R] ? R : never export function useHelper( - object3D: React.MutableRefObject | Falsey, + object3D: React.RefObject | Falsey, helperConstructor: T, ...args: HelperArgs> ) { - const helper = React.useRef() + const helper = React.useRef(null) const scene = useThree((state) => state.scene) React.useLayoutEffect(() => { let currentHelper: HelperType = undefined! @@ -26,7 +26,7 @@ export function useHelper( currentHelper.traverse((child) => (child.raycast = () => null)) scene.add(currentHelper) return () => { - helper.current = undefined + helper.current = undefined! scene.remove(currentHelper) currentHelper.dispose?.() } diff --git a/src/core/Hud.tsx b/src/core/Hud.tsx index 075ad661e..b6f9f6d3c 100644 --- a/src/core/Hud.tsx +++ b/src/core/Hud.tsx @@ -8,13 +8,6 @@ type RenderHudProps = { renderPriority?: number } -type HudProps = { - /** Any React node */ - children: React.ReactNode - /** Render priority, default: 1 */ - renderPriority?: number -} - function RenderHud({ defaultScene, defaultCamera, renderPriority = 1 }: RenderHudProps) { const { gl, scene, camera } = useThree() let oldCLear @@ -36,6 +29,13 @@ function RenderHud({ defaultScene, defaultCamera, renderPriority = 1 }: RenderHu return null} /> } +export type HudProps = { + /** Any React node */ + children: React.ReactNode + /** Render priority, default: 1 */ + renderPriority?: number +} + export function Hud({ children, renderPriority = 1 }: HudProps) { const { scene: defaultScene, camera: defaultCamera } = useThree() const [hudScene] = React.useState(() => new THREE.Scene()) diff --git a/src/core/Image.tsx b/src/core/Image.tsx index 658c009e1..7ee116c50 100644 --- a/src/core/Image.tsx +++ b/src/core/Image.tsx @@ -1,12 +1,12 @@ import * as React from 'react' import * as THREE from 'three' -import { Color, extend, useThree } from '@react-three/fiber' +import { Color, extend, ThreeElements, useThree } from '@react-three/fiber' import { shaderMaterial } from './shaderMaterial' import { useTexture } from './Texture' import { ForwardRefComponent } from '../helpers/ts-utils' import { version } from '../helpers/constants' -export type ImageProps = Omit & { +export type ImageProps = Omit & { segments?: number scale?: number | [number, number] color?: Color @@ -19,7 +19,7 @@ export type ImageProps = Omit & { side?: THREE.Side } & ({ texture: THREE.Texture; url?: never } | { texture?: never; url: string }) // {texture: THREE.Texture} XOR {url: string} -type ImageMaterialType = JSX.IntrinsicElements['shaderMaterial'] & { +type ImageMaterialType = ThreeElements['shaderMaterial'] & { scale?: number[] imageBounds?: number[] radius?: number @@ -30,11 +30,9 @@ type ImageMaterialType = JSX.IntrinsicElements['shaderMaterial'] & { grayscale?: number } -declare global { - namespace JSX { - interface IntrinsicElements { - imageMaterial: ImageMaterialType - } +declare module '@react-three/fiber' { + interface ThreeElements { + imageMaterial: ImageMaterialType } } diff --git a/src/core/Instances.tsx b/src/core/Instances.tsx index 61c54f9ce..5c0a19510 100644 --- a/src/core/Instances.tsx +++ b/src/core/Instances.tsx @@ -1,35 +1,32 @@ import * as THREE from 'three' import * as React from 'react' -import { ReactThreeFiber, extend, useFrame } from '@react-three/fiber' -import Composer from 'react-composer' +import { ThreeElement, ThreeElements, extend, useFrame } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' import { setUpdateRange } from '../helpers/deprecated' -declare global { - namespace JSX { - interface IntrinsicElements { - positionMesh: ReactThreeFiber.Object3DNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + positionMesh: ThreeElement } } type Api = { - getParent: () => React.MutableRefObject - subscribe: (ref: React.MutableRefObject) => void + getParent: () => React.RefObject + subscribe: (ref: React.RefObject) => void } -export type InstancesProps = JSX.IntrinsicElements['instancedMesh'] & { +export type InstancesProps = Omit & { context?: React.Context range?: number limit?: number frames?: number } -export type InstanceProps = JSX.IntrinsicElements['positionMesh'] & { +export type InstanceProps = Omit & { context?: React.Context } -export type InstancedAttributeProps = JSX.IntrinsicElements['instancedBufferAttribute'] & { +export type InstancedAttributeProps = Omit & { name: string defaultValue: any normalized?: boolean @@ -56,8 +53,8 @@ const _mesh = /* @__PURE__ */ new THREE.Mesh - instanceKey: React.MutableRefObject + instance: React.RefObject + instanceKey: React.RefObject constructor() { super() this.color = new THREE.Color('white') @@ -113,12 +110,12 @@ const isInstancedBufferAttribute = (attr: any): attr is THREE.InstancedBufferAtt export const Instance = /* @__PURE__ */ React.forwardRef(({ context, children, ...props }: InstanceProps, ref) => { React.useMemo(() => extend({ PositionMesh }), []) - const group = React.useRef() + const group = React.useRef(null!) React.useImperativeHandle(ref, () => group.current, []) - const { subscribe, getParent } = React.useContext(context || globalContext) + const { subscribe, getParent } = React.useContext(context || globalContext) React.useLayoutEffect(() => subscribe(group), []) return ( - + {children} ) @@ -140,7 +137,7 @@ export const Instances: ForwardRefComponent const parentRef = React.useRef(null!) React.useImperativeHandle(ref, () => parentRef.current, []) - const [instances, setInstances] = React.useState[]>([]) + const [instances, setInstances] = React.useState[]>([]) const [[matrices, colors]] = React.useState(() => { const mArray = new Float32Array(limit * 16) for (let i = 0; i < limit; i++) tempMatrix.identity().toArray(mArray, i * 16) @@ -170,8 +167,8 @@ export const Instances: ForwardRefComponent count = Math.min(limit, range !== undefined ? range : limit, instances.length) parentRef.current.count = count - setUpdateRange(parentRef.current.instanceMatrix, { offset: 0, count: count * 16 }) - setUpdateRange(parentRef.current.instanceColor, { offset: 0, count: count * 3 }) + setUpdateRange(parentRef.current.instanceMatrix, { start: 0, count: count * 16 }) + setUpdateRange(parentRef.current.instanceColor, { start: 0, count: count * 3 }) for (let i = 0; i < instances.length; i++) { const instance = instances[i].current @@ -208,20 +205,8 @@ export const Instances: ForwardRefComponent raycast={() => null} {...props} > - - + + {isFunctionChild(children) ? ( {children(instance)} ) : context ? ( @@ -238,32 +223,33 @@ export interface MergedProps extends InstancesProps { children: React.ReactNode } -export const Merged: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - function Merged({ meshes, children, ...props }, ref) { - const isArray = Array.isArray(meshes) - // Filter out meshes from collections, which may contain non-meshes - if (!isArray) for (const key of Object.keys(meshes)) if (!meshes[key].isMesh) delete meshes[key] - return ( - - ( - - ))} - > - {(args) => - isArray - ? children(...args) - : children( - Object.keys(meshes) - .filter((key) => meshes[key].isMesh) - .reduce((acc, key, i) => ({ ...acc, [key]: args[i] }), {}) - ) - } - - - ) +export const Merged: ForwardRefComponent = React.forwardRef(function Merged( + { meshes, children, ...rest }, + ref +) { + const instances: React.FC[] = [] + + if (Array.isArray(meshes)) { + for (const mesh of meshes) { + if (mesh?.isMesh) { + instances.push((props) => ( + + )) + } + } + } else if (meshes != null && typeof meshes === 'object') { + for (const key in meshes) { + const mesh = meshes[key] + if (mesh?.isMesh) { + instances.push((props) => ( + + )) + } + } } -) + + return {children(instances)} +}) /** Idea and implementation for global instances and instanced attributes by /* Matias Gonzalez Fernandez https://x.com/matiNotFound @@ -286,12 +272,13 @@ export const InstancedAttribute = React.forwardRef( const ref = React.useRef(null!) React.useImperativeHandle(fref, () => ref.current, []) React.useLayoutEffect(() => { - const parent = (ref.current as any).__r3f.parent + const parent = (ref.current as any).__r3f.parent.object parent.geometry.attributes[name] = ref.current const value = Array.isArray(defaultValue) ? defaultValue : [defaultValue] const array = Array.from({ length: parent.userData.limit }, () => value).flat() ref.current.array = new Float32Array(array) ref.current.itemSize = value.length + // @ts-expect-error ref.current.count = array.length / ref.current.itemSize return () => { delete parent.geometry.attributes[name] @@ -299,7 +286,7 @@ export const InstancedAttribute = React.forwardRef( }, [name]) let iterations = 0 useFrame(() => { - const parent = (ref.current as any).__r3f.parent + const parent = (ref.current as any).__r3f.parent.object if (parent.userData.frames === Infinity || iterations < parent.userData.frames) { for (let i = 0; i < parent.userData.instances.length; i++) { const instance = parent.userData.instances[i].current @@ -315,6 +302,7 @@ export const InstancedAttribute = React.forwardRef( iterations++ } }) + // @ts-expect-error we're abusing three API here by mutating immutable args return } ) diff --git a/src/core/Lightformer.tsx b/src/core/Lightformer.tsx index b709106fa..a66645b36 100644 --- a/src/core/Lightformer.tsx +++ b/src/core/Lightformer.tsx @@ -1,9 +1,9 @@ -import { applyProps, ReactThreeFiber } from '@react-three/fiber' +import { applyProps, ReactThreeFiber, ThreeElements } from '@react-three/fiber' import * as React from 'react' import * as THREE from 'three' import { ForwardRefComponent } from '../helpers/ts-utils' -export type LightProps = JSX.IntrinsicElements['mesh'] & { +export type LightProps = Omit & { args?: any[] map?: THREE.Texture toneMapped?: boolean @@ -12,7 +12,7 @@ export type LightProps = JSX.IntrinsicElements['mesh'] & { scale?: number | [number, number, number] | [number, number] intensity?: number target?: boolean | [number, number, number] | THREE.Vector3 - light?: Partial + light?: Partial } export const Lightformer: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( @@ -29,7 +29,7 @@ export const Lightformer: ForwardRefComponent = /* @__PU target = [0, 0, 0], children, ...props - }: LightProps, + }, forwardRef ) => { // Apply emissive power diff --git a/src/core/Line.tsx b/src/core/Line.tsx index df5998ab2..827405cde 100644 --- a/src/core/Line.tsx +++ b/src/core/Line.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import { Vector2, Vector3, Vector4, Color, ColorRepresentation } from 'three' -import { ReactThreeFiber, useThree, Vector2 as FiberVector2, Vector3 as FiberVector3 } from '@react-three/fiber' +import { useThree, Vector2 as FiberVector2, Vector3 as FiberVector3, ThreeElement } from '@react-three/fiber' import { LineGeometry, LineSegmentsGeometry, @@ -11,16 +11,19 @@ import { } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export type LineProps = { - points: ReadonlyArray - vertexColors?: ReadonlyArray - lineWidth?: number - segments?: boolean -} & Omit & - Omit, 'args'> & - Omit, 'color' | 'vertexColors' | 'args'> & { - color?: ColorRepresentation - } +export type LineProps = Omit< + { + points: ReadonlyArray + vertexColors?: ReadonlyArray + lineWidth?: number + segments?: boolean + } & Omit & + Omit, 'ref' | 'args'> & + Omit, 'ref' | 'color' | 'vertexColors' | 'args'> & { + color?: ColorRepresentation + }, + 'ref' +> export const Line: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< Line2 | LineSegments2, diff --git a/src/core/MapControls.tsx b/src/core/MapControls.tsx index 0c6d91387..f5eb3b069 100644 --- a/src/core/MapControls.tsx +++ b/src/core/MapControls.tsx @@ -1,20 +1,23 @@ -import { EventManager, ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { EventManager, ReactThreeFiber, ThreeElement, useFrame, useThree } from '@react-three/fiber' import * as React from 'react' import * as THREE from 'three' import { MapControls as MapControlsImpl } from 'three-stdlib' -import { ForwardRefComponent } from '../helpers/ts-utils' +import { ForwardRefComponent, Overwrite } from '../helpers/ts-utils' -export type MapControlsProps = ReactThreeFiber.Overwrite< - ReactThreeFiber.Object3DNode, - { - target?: ReactThreeFiber.Vector3 - camera?: THREE.Camera - makeDefault?: boolean - onChange?: (e?: THREE.Event) => void - onStart?: (e?: THREE.Event) => void - onEnd?: (e?: THREE.Event) => void - domElement?: HTMLElement - } +export type MapControlsProps = Omit< + Overwrite< + ThreeElement, + { + target?: ReactThreeFiber.Vector3 + camera?: THREE.Camera + makeDefault?: boolean + onChange?: (e?: THREE.Event) => void + onStart?: (e?: THREE.Event) => void + onEnd?: (e?: THREE.Event) => void + domElement?: HTMLElement + } + >, + 'ref' | 'args' > export const MapControls: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< diff --git a/src/core/MarchingCubes.tsx b/src/core/MarchingCubes.tsx index a3b3c7b48..2eb56ae12 100644 --- a/src/core/MarchingCubes.tsx +++ b/src/core/MarchingCubes.tsx @@ -2,11 +2,11 @@ import * as THREE from 'three' import * as React from 'react' import { Color, Group } from 'three' import { MarchingCubes as MarchingCubesImpl } from 'three-stdlib' -import { useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' type Api = { - getParent: () => React.MutableRefObject + getParent: () => React.RefObject } const globalContext = /* @__PURE__ */ React.createContext(null!) @@ -16,21 +16,11 @@ export type MarchingCubesProps = { maxPolyCount?: number enableUvs?: boolean enableColors?: boolean -} & JSX.IntrinsicElements['group'] +} & Omit export const MarchingCubes: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ( - { - resolution = 28, - maxPolyCount = 10000, - enableUvs = false, - enableColors = false, - children, - ...props - }: MarchingCubesProps, - ref - ) => { + ({ resolution = 28, maxPolyCount = 10000, enableUvs = false, enableColors = false, children, ...props }, ref) => { const marchingCubesRef = React.useRef(null!) React.useImperativeHandle(ref, () => marchingCubesRef.current, []) const marchingCubes = React.useMemo( @@ -55,11 +45,11 @@ export const MarchingCubes: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ({ strength = 0.5, subtract = 12, color, ...props }: MarchingCubeProps, ref) => { @@ -77,11 +67,11 @@ export const MarchingCube: ForwardRefComponent = } ) -type MarchingPlaneProps = { +export type MarchingPlaneProps = { planeType?: 'x' | 'y' | 'z' strength?: number subtract?: number -} & JSX.IntrinsicElements['group'] +} & ThreeElements['group'] export const MarchingPlane: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ({ planeType: _planeType = 'x', strength = 0.5, subtract = 12, ...props }: MarchingPlaneProps, ref) => { diff --git a/src/core/Mask.tsx b/src/core/Mask.tsx index 4e04769ba..b59e13fef 100644 --- a/src/core/Mask.tsx +++ b/src/core/Mask.tsx @@ -1,8 +1,9 @@ import * as THREE from 'three' import * as React from 'react' import { ForwardRefComponent } from '../helpers/ts-utils' +import { ThreeElements } from '@react-three/fiber' -type Props = Omit & { +export type MaskProps = Omit & { /** Each mask must have an id, you can have compound masks referring to the same id */ id: number /** If colors of the masks own material will leak through, default: false */ @@ -11,8 +12,8 @@ type Props = Omit & { depthWrite?: boolean } -export const Mask: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ id = 1, colorWrite = false, depthWrite = false, ...props }: Props, fref: React.ForwardedRef) => { +export const Mask: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( + ({ id = 1, colorWrite = false, depthWrite = false, ...props }: MaskProps, fref: React.ForwardedRef) => { const ref = React.useRef(null!) const spread = React.useMemo( () => ({ diff --git a/src/core/MeshDiscardMaterial.tsx b/src/core/MeshDiscardMaterial.tsx index f9e355641..db345ce48 100644 --- a/src/core/MeshDiscardMaterial.tsx +++ b/src/core/MeshDiscardMaterial.tsx @@ -1,21 +1,19 @@ import * as React from 'react' import { ShaderMaterial } from 'three' -import { extend, ReactThreeFiber } from '@react-three/fiber' +import { extend, ThreeElements } from '@react-three/fiber' import { DiscardMaterial as DiscardMaterialImpl } from '../materials/DiscardMaterial' import { ForwardRefComponent } from '../helpers/ts-utils' -declare global { - namespace JSX { - interface IntrinsicElements { - discardMaterialImpl: ReactThreeFiber.ShaderMaterialProps - } +declare module '@react-three/fiber' { + interface ThreeElements { + discardMaterialImpl: ThreeElements['shaderMaterial'] } } -export const MeshDiscardMaterial: ForwardRefComponent = - /* @__PURE__ */ React.forwardRef( - (props: JSX.IntrinsicElements['shaderMaterial'], fref: React.ForwardedRef) => { - extend({ DiscardMaterialImpl }) - return - } - ) +export type MeshDiscardMaterialProps = Omit + +export const MeshDiscardMaterial: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef((props, fref) => { + extend({ DiscardMaterialImpl }) + return + }) diff --git a/src/core/MeshDistortMaterial.tsx b/src/core/MeshDistortMaterial.tsx index 950b6508e..51c399a13 100644 --- a/src/core/MeshDistortMaterial.tsx +++ b/src/core/MeshDistortMaterial.tsx @@ -1,29 +1,10 @@ import * as React from 'react' import { IUniform, MeshPhysicalMaterial, MeshPhysicalMaterialParameters } from 'three' -import { useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' // @ts-ignore import distort from '../helpers/glsl/distort.vert.glsl' import { ForwardRefComponent } from '../helpers/ts-utils' -type DistortMaterialType = JSX.IntrinsicElements['meshPhysicalMaterial'] & { - time?: number - distort?: number - radius?: number -} - -type Props = DistortMaterialType & { - speed?: number - factor?: number -} - -declare global { - namespace JSX { - interface IntrinsicElements { - distortMaterialImpl: DistortMaterialType - } - } -} - interface Uniform { value: T } @@ -89,10 +70,24 @@ class DistortMaterialImpl extends MeshPhysicalMaterial { } } -export const MeshDistortMaterial: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ speed = 1, ...props }: Props, ref) => { +declare module '@react-three/fiber' { + interface ThreeElements { + distortMaterialImpl: ThreeElements['meshPhysicalMaterial'] & { + time?: number + distort?: number + radius?: number + } + } +} + +export type MeshDistortMaterialProps = Omit & { + speed?: number + factor?: number +} + +export const MeshDistortMaterial: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef(({ speed = 1, ...props }, ref) => { const [material] = React.useState(() => new DistortMaterialImpl()) useFrame((state) => material && (material.time = state.clock.getElapsedTime() * speed)) return - } -) + }) diff --git a/src/core/MeshPortalMaterial.tsx b/src/core/MeshPortalMaterial.tsx index 19b5c7aa7..7471b8d15 100644 --- a/src/core/MeshPortalMaterial.tsx +++ b/src/core/MeshPortalMaterial.tsx @@ -5,13 +5,14 @@ import * as THREE from 'three' import * as React from 'react' -import { ReactThreeFiber, extend, useFrame, useThree } from '@react-three/fiber' +import { ReactThreeFiber, ThreeElements, extend, useFrame, useThree } from '@react-three/fiber' import { useIntersect } from './useIntersect' import { useFBO } from './Fbo' import { RenderTexture } from './RenderTexture' import { shaderMaterial } from './shaderMaterial' import { FullScreenQuad } from 'three-stdlib' import { version } from '../helpers/constants' +import { ForwardRefComponent } from '../helpers/ts-utils' const PortalMaterialImpl = /* @__PURE__ */ shaderMaterial( { @@ -47,24 +48,20 @@ const PortalMaterialImpl = /* @__PURE__ */ shaderMaterial( }` ) -export type PortalMaterialType = { - resolution: ReactThreeFiber.Vector2 - blur: number - blend: number - size?: number - sdf?: THREE.Texture - map?: THREE.Texture -} & JSX.IntrinsicElements['shaderMaterial'] - -declare global { - namespace JSX { - interface IntrinsicElements { - portalMaterialImpl: PortalMaterialType +declare module '@react-three/fiber' { + interface ThreeElements { + portalMaterialImpl: ThreeElements['shaderMaterial'] & { + resolution: ReactThreeFiber.Vector2 + blur: number + blend: number + size?: number + sdf?: THREE.Texture + map?: THREE.Texture } } } -export type PortalProps = JSX.IntrinsicElements['shaderMaterial'] & { +export type PortalProps = Omit & { /** Mix the portals own scene with the world scene, 0 = world scene render, * 0.5 = both scenes render, 1 = portal scene renders, defaults to 0 */ blend?: number @@ -82,132 +79,133 @@ export type PortalProps = JSX.IntrinsicElements['shaderMaterial'] & { events?: boolean } -export const MeshPortalMaterial = /* @__PURE__ */ React.forwardRef( - ( - { - children, - events = undefined, - blur = 0, - eventPriority = 0, - renderPriority = 0, - worldUnits = false, - resolution = 512, - ...props - }: PortalProps, - fref: React.ForwardedRef - ) => { - extend({ PortalMaterialImpl }) +export const MeshPortalMaterial: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef( + ( + { + children, + events = undefined, + blur = 0, + eventPriority = 0, + renderPriority = 0, + worldUnits = false, + resolution = 512, + ...props + }, + fref + ) => { + extend({ PortalMaterialImpl }) - const ref = React.useRef(null!) - const { scene, gl, size, viewport, setEvents } = useThree() - const maskRenderTarget = useFBO(resolution, resolution) + const ref = React.useRef(null!) + const { scene, gl, size, viewport, setEvents } = useThree() + const maskRenderTarget = useFBO(resolution, resolution) - const [priority, setPriority] = React.useState(0) - useFrame(() => { - // If blend is > 0 then the portal is being entered, the render-priority must change - const p = ref.current.blend > 0 ? Math.max(1, renderPriority) : 0 - if (priority !== p) setPriority(p) - }) + const [priority, setPriority] = React.useState(0) + useFrame(() => { + // If blend is > 0 then the portal is being entered, the render-priority must change + const p = ref.current.blend > 0 ? Math.max(1, renderPriority) : 0 + if (priority !== p) setPriority(p) + }) - React.useEffect(() => { - if (events !== undefined) setEvents({ enabled: !events }) - }, [events]) + React.useEffect(() => { + if (events !== undefined) setEvents({ enabled: !events }) + }, [events]) - const [visible, setVisible] = React.useState(true) - // See if the parent mesh is in the camera frustum - const parent = useIntersect(setVisible) as React.MutableRefObject> - React.useLayoutEffect(() => { - // Since the ref above is not tied to a mesh directly (we're inside a material), - // it has to be tied to the parent mesh here - parent.current = (ref.current as any)?.__r3f.parent - }, []) + const [visible, setVisible] = React.useState(true) + // See if the parent mesh is in the camera frustum + const parent = useIntersect(setVisible) as React.RefObject> + React.useLayoutEffect(() => { + // Since the ref above is not tied to a mesh directly (we're inside a material), + // it has to be tied to the parent mesh here + parent.current = (ref.current as any)?.__r3f.parent?.object + }, []) - React.useLayoutEffect(() => { - if (!parent.current) return + React.useLayoutEffect(() => { + if (!parent.current) return - // Apply the SDF mask only once - if (blur && ref.current.sdf === null) { - const tempMesh = new THREE.Mesh(parent.current.geometry, new THREE.MeshBasicMaterial()) - const boundingBox = new THREE.Box3().setFromBufferAttribute( - tempMesh.geometry.attributes.position as THREE.BufferAttribute - ) - const orthoCam = new THREE.OrthographicCamera( - boundingBox.min.x * (1 + 2 / resolution), - boundingBox.max.x * (1 + 2 / resolution), - boundingBox.max.y * (1 + 2 / resolution), - boundingBox.min.y * (1 + 2 / resolution), - 0.1, - 1000 - ) - orthoCam.position.set(0, 0, 1) - orthoCam.lookAt(0, 0, 0) + // Apply the SDF mask only once + if (blur && ref.current.sdf === null) { + const tempMesh = new THREE.Mesh(parent.current.geometry, new THREE.MeshBasicMaterial()) + const boundingBox = new THREE.Box3().setFromBufferAttribute( + tempMesh.geometry.attributes.position as THREE.BufferAttribute + ) + const orthoCam = new THREE.OrthographicCamera( + boundingBox.min.x * (1 + 2 / resolution), + boundingBox.max.x * (1 + 2 / resolution), + boundingBox.max.y * (1 + 2 / resolution), + boundingBox.min.y * (1 + 2 / resolution), + 0.1, + 1000 + ) + orthoCam.position.set(0, 0, 1) + orthoCam.lookAt(0, 0, 0) - gl.setRenderTarget(maskRenderTarget) - gl.render(tempMesh, orthoCam) - const sg = makeSDFGenerator(resolution, resolution, gl) - const sdf = sg(maskRenderTarget.texture) - const readSdf = new Float32Array(resolution * resolution) - gl.readRenderTargetPixels(sdf, 0, 0, resolution, resolution, readSdf) - // Get smallest value in sdf - let min = Infinity - for (let i = 0; i < readSdf.length; i++) { - if (readSdf[i] < min) min = readSdf[i] - } - min = -min - ref.current.size = min - ref.current.sdf = sdf.texture + gl.setRenderTarget(maskRenderTarget) + gl.render(tempMesh, orthoCam) + const sg = makeSDFGenerator(resolution, resolution, gl) + const sdf = sg(maskRenderTarget.texture) + const readSdf = new Float32Array(resolution * resolution) + gl.readRenderTargetPixels(sdf, 0, 0, resolution, resolution, readSdf) + // Get smallest value in sdf + let min = Infinity + for (let i = 0; i < readSdf.length; i++) { + if (readSdf[i] < min) min = readSdf[i] + } + min = -min + ref.current.size = min + ref.current.sdf = sdf.texture - gl.setRenderTarget(null) - } - }, [resolution, blur]) + gl.setRenderTarget(null) + } + }, [resolution, blur]) - React.useImperativeHandle(fref, () => ref.current) + React.useImperativeHandle(fref, () => ref.current) - const compute = React.useCallback((event, state, previous) => { - if (!parent.current) return false - state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1) - state.raycaster.setFromCamera(state.pointer, state.camera) + const compute = React.useCallback((event, state, previous) => { + if (!parent.current) return false + state.pointer.set((event.offsetX / state.size.width) * 2 - 1, -(event.offsetY / state.size.height) * 2 + 1) + state.raycaster.setFromCamera(state.pointer, state.camera) - if (ref.current?.blend === 0) { - // We run a quick check against the parent, if it isn't hit there's no need to raycast at all - const [intersection] = state.raycaster.intersectObject(parent.current) - if (!intersection) { - // Cancel out the raycast camera if the parent mesh isn't hit - state.raycaster.camera = undefined - return false + if (ref.current?.blend === 0) { + // We run a quick check against the parent, if it isn't hit there's no need to raycast at all + const [intersection] = state.raycaster.intersectObject(parent.current) + if (!intersection) { + // Cancel out the raycast camera if the parent mesh isn't hit + state.raycaster.camera = undefined + return false + } } - } - }, []) + }, []) - return ( - - - {children} - - - - ) - } -) + + {children} + + + + ) + } + ) function ManagePortalScene({ events = undefined, @@ -218,7 +216,7 @@ function ManagePortalScene({ }: { events?: boolean rootScene: THREE.Scene - material: React.MutableRefObject + material: React.RefObject priority: number worldUnits: boolean }) { @@ -270,7 +268,7 @@ function ManagePortalScene({ }, []) useFrame((state) => { - let parent = (material?.current as any)?.__r3f.parent + let parent = (material?.current as any)?.__r3f.parent?.object if (parent) { // Move portal contents along with the parent if worldUnits is true if (!worldUnits) { diff --git a/src/core/MeshReflectorMaterial.tsx b/src/core/MeshReflectorMaterial.tsx index 1de3fd40b..01f9af715 100644 --- a/src/core/MeshReflectorMaterial.tsx +++ b/src/core/MeshReflectorMaterial.tsx @@ -10,44 +10,28 @@ import { DepthTexture, DepthFormat, UnsignedShortType, - Texture, HalfFloatType, } from 'three' -import { useFrame, useThree, extend } from '@react-three/fiber' +import { useFrame, useThree, extend, ThreeElements, ThreeElement } from '@react-three/fiber' import { BlurPass } from '../materials/BlurPass' -import { - MeshReflectorMaterialProps, - MeshReflectorMaterial as MeshReflectorMaterialImpl, -} from '../materials/MeshReflectorMaterial' +import { MeshReflectorMaterial as MeshReflectorMaterialImpl } from '../materials/MeshReflectorMaterial' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = JSX.IntrinsicElements['meshStandardMaterial'] & { +declare module '@react-three/fiber' { + interface ThreeElements { + meshReflectorMaterialImpl: ThreeElement + } +} + +export type MeshReflectorMaterialProps = ThreeElements['meshReflectorMaterialImpl'] & { resolution?: number - mixBlur?: number - mixStrength?: number blur?: [number, number] | number - mirror: number - minDepthThreshold?: number - maxDepthThreshold?: number - depthScale?: number - depthToBlurRatioBias?: number - distortionMap?: Texture - distortion?: number - mixContrast?: number reflectorOffset?: number } -declare global { - namespace JSX { - interface IntrinsicElements { - meshReflectorMaterialImpl: MeshReflectorMaterialProps - } - } -} - -export const MeshReflectorMaterial: ForwardRefComponent = - /* @__PURE__ */ React.forwardRef( +export const MeshReflectorMaterial: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef( ( { mixBlur = 0, @@ -89,8 +73,7 @@ export const MeshReflectorMaterial: ForwardRefComponent new PerspectiveCamera()) const beforeRender = React.useCallback(() => { - // TODO: As of R3f 7-8 this should be __r3f.parent - const parent = (materialRef.current as any).parent || (materialRef.current as any)?.__r3f.parent + const parent = (materialRef.current as any).parent || (materialRef.current as any)?.__r3f.parent?.object if (!parent) return reflectorWorldPosition.setFromMatrixPosition(parent.matrixWorld) @@ -210,8 +193,7 @@ export const MeshReflectorMaterial: ForwardRefComponent { - // TODO: As of R3f 7-8 this should be __r3f.parent - const parent = (materialRef.current as any).parent || (materialRef.current as any)?.__r3f.parent + const parent = (materialRef.current as any).parent || (materialRef.current as any)?.__r3f.parent?.object if (!parent) return parent.visible = false diff --git a/src/core/MeshRefractionMaterial.tsx b/src/core/MeshRefractionMaterial.tsx index 925ff9fb5..8671431ec 100644 --- a/src/core/MeshRefractionMaterial.tsx +++ b/src/core/MeshRefractionMaterial.tsx @@ -1,19 +1,17 @@ import * as THREE from 'three' import * as React from 'react' import { useLayoutEffect, useMemo, useRef } from 'react' -import { extend, ReactThreeFiber, useThree, useFrame } from '@react-three/fiber' +import { extend, ReactThreeFiber, useThree, useFrame, ThreeElements } from '@react-three/fiber' import { MeshBVHUniformStruct, MeshBVH, SAH } from 'three-mesh-bvh' import { MeshRefractionMaterial as MeshRefractionMaterial_ } from '../materials/MeshRefractionMaterial' -declare global { - namespace JSX { - interface IntrinsicElements { - meshRefractionMaterial: typeof MeshRefractionMaterial_ - } +declare module '@react-three/fiber' { + interface ThreeElements { + meshRefractionMaterial: typeof MeshRefractionMaterial_ } } -type MeshRefractionMaterialProps = JSX.IntrinsicElements['shaderMaterial'] & { +export type MeshRefractionMaterialProps = ThreeElements['shaderMaterial'] & { /** Environment map */ envMap: THREE.CubeTexture | THREE.Texture /** Number of ray-cast bounces, it can be expensive to have too many, 2 */ @@ -41,7 +39,7 @@ export function MeshRefractionMaterial({ }: MeshRefractionMaterialProps) { extend({ MeshRefractionMaterial: MeshRefractionMaterial_ }) - const material = useRef() + const material = useRef(null) const { size } = useThree() const defines = useMemo(() => { @@ -66,7 +64,7 @@ export function MeshRefractionMaterial({ useLayoutEffect(() => { // Get the geometry of this materials parent - const geometry = (material.current as any)?.__r3f?.parent?.geometry + const geometry = (material.current as any)?.__r3f?.parent?.object?.geometry // Update the BVH if (geometry) { ;(material.current as any).bvh = new MeshBVHUniformStruct() diff --git a/src/core/MeshTransmissionMaterial.tsx b/src/core/MeshTransmissionMaterial.tsx index 5eb8bcead..da1c735d7 100644 --- a/src/core/MeshTransmissionMaterial.tsx +++ b/src/core/MeshTransmissionMaterial.tsx @@ -6,13 +6,13 @@ import * as THREE from 'three' import * as React from 'react' -import { extend, useFrame } from '@react-three/fiber' +import { extend, ThreeElements, useFrame } from '@react-three/fiber' import { useFBO } from './Fbo' import { DiscardMaterial } from '../materials/DiscardMaterial' import { ForwardRefComponent } from '../helpers/ts-utils' type MeshTransmissionMaterialType = Omit< - JSX.IntrinsicElements['meshPhysicalMaterial'], + ThreeElements['meshPhysicalMaterial'], 'args' | 'roughness' | 'thickness' | 'transmission' > & { /* Transmission, default: 1 */ @@ -41,7 +41,7 @@ type MeshTransmissionMaterialType = Omit< args?: [samples: number, transmissionSampler: boolean] } -type MeshTransmissionMaterialProps = Omit & { +export type MeshTransmissionMaterialProps = Omit & { /** transmissionSampler, you can use the threejs transmission sampler texture that is * generated once for all transmissive materials. The upside is that it can be faster if you * use multiple MeshPhysical and Transmission materials, the downside is that transmissive materials @@ -72,11 +72,9 @@ interface Shader { fragmentShader: string } -declare global { - namespace JSX { - interface IntrinsicElements { - meshTransmissionMaterial: MeshTransmissionMaterialType - } +declare module '@react-three/fiber' { + interface ThreeElements { + meshTransmissionMaterial: MeshTransmissionMaterialType } } @@ -380,7 +378,7 @@ class MeshTransmissionMaterialImpl extends THREE.MeshPhysicalMaterial { export const MeshTransmissionMaterial: ForwardRefComponent< MeshTransmissionMaterialProps, - JSX.IntrinsicElements['meshTransmissionMaterial'] + ThreeElements['meshTransmissionMaterial'] > = /* @__PURE__ */ React.forwardRef( ( { @@ -404,7 +402,7 @@ export const MeshTransmissionMaterial: ForwardRefComponent< ) => { extend({ MeshTransmissionMaterial: MeshTransmissionMaterialImpl }) - const ref = React.useRef(null!) + const ref = React.useRef(null!) const [discardMaterial] = React.useState(() => new DiscardMaterial()) const fboBack = useFBO(backsideResolution || resolution) const fboMain = useFBO(resolution) @@ -417,7 +415,7 @@ export const MeshTransmissionMaterial: ForwardRefComponent< ref.current.time = state.clock.getElapsedTime() // Render only if the buffer matches the built-in and no transmission sampler is set if (ref.current.buffer === fboMain.texture && !transmissionSampler) { - parent = (ref.current as any).__r3f.parent as THREE.Object3D + parent = (ref.current as any).__r3f.parent?.object as THREE.Object3D | undefined if (parent) { // Save defaults oldTone = state.gl.toneMapping diff --git a/src/core/MeshWobbleMaterial.tsx b/src/core/MeshWobbleMaterial.tsx index 09e17fde3..03b8d71ee 100644 --- a/src/core/MeshWobbleMaterial.tsx +++ b/src/core/MeshWobbleMaterial.tsx @@ -1,27 +1,8 @@ import * as React from 'react' import { IUniform, MeshStandardMaterial, MeshStandardMaterialParameters } from 'three' -import { useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -type WobbleMaterialType = JSX.IntrinsicElements['meshStandardMaterial'] & { - time?: number - factor?: number - speed?: number -} - -type Props = WobbleMaterialType & { - speed?: number - factor?: number -} - -declare global { - namespace JSX { - interface IntrinsicElements { - wobbleMaterialImpl: WobbleMaterialType - } - } -} - interface Uniform { value: T } @@ -75,10 +56,24 @@ class WobbleMaterialImpl extends MeshStandardMaterial { } } -export const MeshWobbleMaterial: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ speed = 1, ...props }: Props, ref) => { +declare module '@react-three/fiber' { + interface ThreeElements { + wobbleMaterialImpl: ThreeElements['meshStandardMaterial'] & { + time?: number + factor?: number + speed?: number + } + } +} + +export type WobbleMaterialProps = Omit & { + speed?: number + factor?: number +} + +export const MeshWobbleMaterial: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef(({ speed = 1, ...props }, ref) => { const [material] = React.useState(() => new WobbleMaterialImpl()) useFrame((state) => material && (material.time = state.clock.getElapsedTime() * speed)) return - } -) + }) diff --git a/src/core/MotionPathControls.tsx b/src/core/MotionPathControls.tsx index a5584174e..160f9995c 100644 --- a/src/core/MotionPathControls.tsx +++ b/src/core/MotionPathControls.tsx @@ -1,17 +1,17 @@ import * as THREE from 'three' import * as React from 'react' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree, Instance } from '@react-three/fiber' import { easing, misc } from 'maath' -type MotionPathProps = JSX.IntrinsicElements['group'] & { +export type MotionPathProps = Omit & { /** An optional array of THREE curves */ curves?: THREE.Curve[] /** Show debug helpers */ debug?: boolean /** The target object that is moved, default: null (the default camera) */ - object?: React.MutableRefObject + object?: React.RefObject /** An object where the target looks towards, can also be a vector, default: null */ - focus?: [x: number, y: number, z: number] | React.MutableRefObject + focus?: [x: number, y: number, z: number] | React.RefObject /** Position between 0 (start) and end (1), if this is not set useMotion().current must be used, default: null */ offset?: number /** Optionally smooth the curve, default: false */ @@ -32,9 +32,9 @@ type MotionState = { /** The combined curve */ path: THREE.CurvePath /** The focus object */ - focus: React.MutableRefObject | [x: number, y: number, z: number] | undefined + focus: React.RefObject | [x: number, y: number, z: number] | undefined /** The target object that is moved along the curve */ - object: React.MutableRefObject + object: React.RefObject /** The 0-1 normalised and damped current goal position along curve */ offset: number /** The current point on the curve */ @@ -45,8 +45,7 @@ type MotionState = { next: THREE.Vector3 } -const isObject3DRef = (ref: any): ref is React.MutableRefObject => - ref?.current instanceof THREE.Object3D +const isObject3DRef = (ref: any): ref is React.RefObject => ref?.current instanceof THREE.Object3D const context = /* @__PURE__ */ React.createContext(null!) @@ -90,11 +89,11 @@ export const MotionPathControls = /* @__PURE__ */ React.forwardRef { const { camera } = useThree() - const ref = React.useRef() + const ref = React.useRef(null!) const [path] = React.useState(() => new THREE.CurvePath()) const pos = React.useRef(offset ?? 0) @@ -114,7 +113,12 @@ export const MotionPathControls = /* @__PURE__ */ React.forwardRef { path.curves = [] - const _curves = curves.length > 0 ? curves : ref.current?.__r3f.objects + const _curves = + curves.length > 0 + ? curves + : (ref.current as THREE.Group & { __r3f: Instance }).__r3f!.children.map( + (instance) => instance.object + ) for (var i = 0; i < _curves.length; i++) path.add(_curves[i]) //Smoothen curve diff --git a/src/core/MultiMaterial.tsx b/src/core/MultiMaterial.tsx index b78bf2dac..49b0cf596 100644 --- a/src/core/MultiMaterial.tsx +++ b/src/core/MultiMaterial.tsx @@ -2,16 +2,22 @@ * Original idea by https://x.com/verekia */ +import { ThreeElements, Instance } from '@react-three/fiber' import * as React from 'react' +import * as THREE from 'three' -export function MultiMaterial(props: JSX.IntrinsicElements['group']) { - const group = React.useRef(null!) +export type MultiMaterialProps = Omit + +export function MultiMaterial(props: MultiMaterialProps) { + const group = React.useRef(null!) React.useLayoutEffect(() => { - const parent = (group.current as any)?.parent + const parent = group.current.parent as THREE.Mesh | undefined const geometry = parent?.geometry if (geometry) { const oldMaterial = parent.material - parent.material = (group.current as any).__r3f.objects + parent.material = (group.current as THREE.Group & { __r3f: Instance }).__r3f.children.map( + (instance) => instance.object + ) as THREE.Material[] const oldGroups = [...geometry.groups] geometry.clearGroups() parent.material.forEach((material, index) => { diff --git a/src/core/OrbitControls.tsx b/src/core/OrbitControls.tsx index d08512944..eb3de5c46 100644 --- a/src/core/OrbitControls.tsx +++ b/src/core/OrbitControls.tsx @@ -1,8 +1,8 @@ -import { EventManager, ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { EventManager, ReactThreeFiber, ThreeElement, useFrame, useThree } from '@react-three/fiber' import * as React from 'react' import { Camera, Event, OrthographicCamera, PerspectiveCamera } from 'three' import { OrbitControls as OrbitControlsImpl } from 'three-stdlib' -import { ForwardRefComponent } from '../helpers/ts-utils' +import { ForwardRefComponent, Overwrite } from '../helpers/ts-utils' type ExtractCallback = T extends { addEventListener(event: E, callback: infer C): void } ? C @@ -11,8 +11,8 @@ type ExtractCallback = T extends { addEventListener(event: export type OrbitControlsChangeEvent = Parameters>[0] export type OrbitControlsProps = Omit< - ReactThreeFiber.Overwrite< - ReactThreeFiber.Object3DNode, + Overwrite< + ThreeElement, { camera?: Camera domElement?: HTMLElement @@ -26,7 +26,7 @@ export type OrbitControlsProps = Omit< keyEvents?: boolean | HTMLElement } >, - 'ref' + 'ref' | 'args' > export const OrbitControls: ForwardRefComponent = diff --git a/src/core/OrthographicCamera.tsx b/src/core/OrthographicCamera.tsx index 29a672ade..6f3bd989d 100644 --- a/src/core/OrthographicCamera.tsx +++ b/src/core/OrthographicCamera.tsx @@ -1,13 +1,13 @@ import * as THREE from 'three' import * as React from 'react' import { OrthographicCamera as OrthographicCameraImpl } from 'three' -import { useThree, useFrame } from '@react-three/fiber' +import { useThree, useFrame, ThreeElements } from '@react-three/fiber' import { useFBO } from './Fbo' import { ForwardRefComponent } from '../helpers/ts-utils' const isFunction = (node: any): node is Function => typeof node === 'function' -type Props = Omit & { +export type OrthographicCameraProps = Omit & { /** Registers the camera as the system default, fiber will start rendering with it */ makeDefault?: boolean /** Making it manual will stop responsiveness and you have to calculate aspect ratio yourself. */ @@ -22,67 +22,68 @@ type Props = Omit & { envMap?: THREE.Texture } -export const OrthographicCamera: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ envMap, resolution = 256, frames = Infinity, children, makeDefault, ...props }: Props, ref) => { - const set = useThree(({ set }) => set) - const camera = useThree(({ camera }) => camera) - const size = useThree(({ size }) => size) - const cameraRef = React.useRef(null!) - React.useImperativeHandle(ref, () => cameraRef.current, []) - const groupRef = React.useRef(null!) - const fbo = useFBO(resolution) +export const OrthographicCamera: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef( + ({ envMap, resolution = 256, frames = Infinity, children, makeDefault, ...props }, ref) => { + const set = useThree(({ set }) => set) + const camera = useThree(({ camera }) => camera) + const size = useThree(({ size }) => size) + const cameraRef = React.useRef(null!) + React.useImperativeHandle(ref, () => cameraRef.current, []) + const groupRef = React.useRef(null!) + const fbo = useFBO(resolution) - React.useLayoutEffect(() => { - if (!props.manual) { - cameraRef.current.updateProjectionMatrix() - } - }, [size, props]) + React.useLayoutEffect(() => { + if (!props.manual) { + cameraRef.current.updateProjectionMatrix() + } + }, [size, props]) - React.useLayoutEffect(() => { - cameraRef.current.updateProjectionMatrix() - }) + React.useLayoutEffect(() => { + cameraRef.current.updateProjectionMatrix() + }) - React.useLayoutEffect(() => { - if (makeDefault) { - const oldCam = camera - set(() => ({ camera: cameraRef.current! })) - return () => set(() => ({ camera: oldCam })) - } - // The camera should not be part of the dependency list because this components camera is a stable reference - // that must exchange the default, and clean up after itself on unmount. - }, [cameraRef, makeDefault, set]) + React.useLayoutEffect(() => { + if (makeDefault) { + const oldCam = camera + set(() => ({ camera: cameraRef.current! })) + return () => set(() => ({ camera: oldCam })) + } + // The camera should not be part of the dependency list because this components camera is a stable reference + // that must exchange the default, and clean up after itself on unmount. + }, [cameraRef, makeDefault, set]) - let count = 0 - let oldEnvMap: THREE.Color | THREE.Texture | null = null - const functional = isFunction(children) - useFrame((state) => { - if (functional && (frames === Infinity || count < frames)) { - groupRef.current.visible = false - state.gl.setRenderTarget(fbo) - oldEnvMap = state.scene.background - if (envMap) state.scene.background = envMap - state.gl.render(state.scene, cameraRef.current) - state.scene.background = oldEnvMap - state.gl.setRenderTarget(null) - groupRef.current.visible = true - count++ - } - }) + let count = 0 + let oldEnvMap: THREE.Color | THREE.Texture | null = null + const functional = isFunction(children) + useFrame((state) => { + if (functional && (frames === Infinity || count < frames)) { + groupRef.current.visible = false + state.gl.setRenderTarget(fbo) + oldEnvMap = state.scene.background + if (envMap) state.scene.background = envMap + state.gl.render(state.scene, cameraRef.current) + state.scene.background = oldEnvMap + state.gl.setRenderTarget(null) + groupRef.current.visible = true + count++ + } + }) - return ( - <> - - {!functional && children} - - {functional && children(fbo.texture)} - - ) - } -) + return ( + <> + + {!functional && children} + + {functional && children(fbo.texture)} + + ) + } + ) diff --git a/src/core/Outlines.tsx b/src/core/Outlines.tsx index f85185aba..a0bf79ec1 100644 --- a/src/core/Outlines.tsx +++ b/src/core/Outlines.tsx @@ -1,7 +1,7 @@ import * as THREE from 'three' import * as React from 'react' import { shaderMaterial } from './shaderMaterial' -import { extend, applyProps, ReactThreeFiber, useThree } from '@react-three/fiber' +import { extend, applyProps, ReactThreeFiber, useThree, ThreeElements } from '@react-three/fiber' import { toCreasedNormals } from 'three-stdlib' import { version } from '../helpers/constants' @@ -61,7 +61,7 @@ const OutlinesMaterial = /* @__PURE__ */ shaderMaterial( }` ) -type OutlinesProps = JSX.IntrinsicElements['group'] & { +export type OutlinesProps = Omit & { /** Outline color, default: black */ color?: ReactThreeFiber.Color /** Line thickness is independent of zoom, default: false */ @@ -95,14 +95,14 @@ export function Outlines({ clippingPlanes, ...props }: OutlinesProps) { - const ref = React.useRef() + const ref = React.useRef(null) const [material] = React.useState(() => new OutlinesMaterial({ side: THREE.BackSide })) const { gl } = useThree() const contextSize = gl.getDrawingBufferSize(new THREE.Vector2()) React.useMemo(() => extend({ OutlinesMaterial }), []) const oldAngle = React.useRef(0) - const oldGeometry = React.useRef() + const oldGeometry = React.useRef(null) React.useLayoutEffect(() => { const group = ref.current if (!group) return @@ -176,5 +176,5 @@ export function Outlines({ } }, []) - return } {...props} /> + return } diff --git a/src/core/PerformanceMonitor.tsx b/src/core/PerformanceMonitor.tsx index 1e49a29f7..0eb67fca5 100644 --- a/src/core/PerformanceMonitor.tsx +++ b/src/core/PerformanceMonitor.tsx @@ -24,10 +24,10 @@ export type PerformanceMonitorApi = { flipped: number fallback: boolean subscriptions: Map> - subscribe: (ref: React.MutableRefObject>) => () => void + subscribe: (ref: React.RefObject>) => () => void } -type PerformanceMonitorProps = { +export type PerformanceMonitorProps = { /** How much time in milliseconds to collect an average fps, 250 */ ms?: number /** How many interations of averages to collect, 10 */ diff --git a/src/core/PerspectiveCamera.tsx b/src/core/PerspectiveCamera.tsx index a78baeeb3..2f26c078a 100644 --- a/src/core/PerspectiveCamera.tsx +++ b/src/core/PerspectiveCamera.tsx @@ -1,13 +1,13 @@ import * as THREE from 'three' import * as React from 'react' import { PerspectiveCamera as PerspectiveCameraImpl } from 'three' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' import { useFBO } from './Fbo' import { ForwardRefComponent } from '../helpers/ts-utils' const isFunction = (node: any): node is Function => typeof node === 'function' -type Props = Omit & { +export type PerspectiveCameraProps = Omit & { /** Registers the camera as the system default, fiber will start rendering with it */ makeDefault?: boolean /** Making it manual will stop responsiveness and you have to calculate aspect ratio yourself. */ @@ -22,60 +22,61 @@ type Props = Omit & { envMap?: THREE.Texture } -export const PerspectiveCamera: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ envMap, resolution = 256, frames = Infinity, makeDefault, children, ...props }: Props, ref) => { - const set = useThree(({ set }) => set) - const camera = useThree(({ camera }) => camera) - const size = useThree(({ size }) => size) - const cameraRef = React.useRef(null!) - React.useImperativeHandle(ref, () => cameraRef.current, []) - const groupRef = React.useRef(null!) - const fbo = useFBO(resolution) +export const PerspectiveCamera: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef( + ({ envMap, resolution = 256, frames = Infinity, makeDefault, children, ...props }, ref) => { + const set = useThree(({ set }) => set) + const camera = useThree(({ camera }) => camera) + const size = useThree(({ size }) => size) + const cameraRef = React.useRef(null!) + React.useImperativeHandle(ref, () => cameraRef.current, []) + const groupRef = React.useRef(null!) + const fbo = useFBO(resolution) - React.useLayoutEffect(() => { - if (!props.manual) { - cameraRef.current.aspect = size.width / size.height - } - }, [size, props]) + React.useLayoutEffect(() => { + if (!props.manual) { + cameraRef.current.aspect = size.width / size.height + } + }, [size, props]) - React.useLayoutEffect(() => { - cameraRef.current.updateProjectionMatrix() - }) + React.useLayoutEffect(() => { + cameraRef.current.updateProjectionMatrix() + }) - let count = 0 - let oldEnvMap: THREE.Color | THREE.Texture | null = null - const functional = isFunction(children) - useFrame((state) => { - if (functional && (frames === Infinity || count < frames)) { - groupRef.current.visible = false - state.gl.setRenderTarget(fbo) - oldEnvMap = state.scene.background - if (envMap) state.scene.background = envMap - state.gl.render(state.scene, cameraRef.current) - state.scene.background = oldEnvMap - state.gl.setRenderTarget(null) - groupRef.current.visible = true - count++ - } - }) + let count = 0 + let oldEnvMap: THREE.Color | THREE.Texture | null = null + const functional = isFunction(children) + useFrame((state) => { + if (functional && (frames === Infinity || count < frames)) { + groupRef.current.visible = false + state.gl.setRenderTarget(fbo) + oldEnvMap = state.scene.background + if (envMap) state.scene.background = envMap + state.gl.render(state.scene, cameraRef.current) + state.scene.background = oldEnvMap + state.gl.setRenderTarget(null) + groupRef.current.visible = true + count++ + } + }) - React.useLayoutEffect(() => { - if (makeDefault) { - const oldCam = camera - set(() => ({ camera: cameraRef.current! })) - return () => set(() => ({ camera: oldCam })) - } - // The camera should not be part of the dependency list because this components camera is a stable reference - // that must exchange the default, and clean up after itself on unmount. - }, [cameraRef, makeDefault, set]) + React.useLayoutEffect(() => { + if (makeDefault) { + const oldCam = camera + set(() => ({ camera: cameraRef.current! })) + return () => set(() => ({ camera: oldCam })) + } + // The camera should not be part of the dependency list because this components camera is a stable reference + // that must exchange the default, and clean up after itself on unmount. + }, [cameraRef, makeDefault, set]) - return ( - <> - - {!functional && children} - - {functional && children(fbo.texture)} - - ) - } -) + return ( + <> + + {!functional && children} + + {functional && children(fbo.texture)} + + ) + } + ) diff --git a/src/core/PointMaterial.tsx b/src/core/PointMaterial.tsx index 083d5faf0..f74715862 100644 --- a/src/core/PointMaterial.tsx +++ b/src/core/PointMaterial.tsx @@ -1,18 +1,8 @@ import * as THREE from 'three' import * as React from 'react' -import { PrimitiveProps } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' import { version } from '../helpers/constants' - -type PointMaterialType = JSX.IntrinsicElements['pointsMaterial'] - -declare global { - namespace JSX { - interface IntrinsicElements { - pointMaterialImpl: PointMaterialType - } - } -} +import { ThreeElements } from '@react-three/fiber' const opaque_fragment = version >= 154 ? 'opaque_fragment' : 'output_fragment' @@ -42,10 +32,10 @@ export class PointMaterialImpl extends THREE.PointsMaterial { } } -export const PointMaterial: ForwardRefComponent< - Omit, - PointMaterialImpl -> = /* @__PURE__ */ React.forwardRef>((props, ref) => { - const [material] = React.useState(() => new PointMaterialImpl(null)) - return -}) +export type PointMaterialProps = Omit + +export const PointMaterial: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef((props, ref) => { + const [material] = React.useState(() => new PointMaterialImpl(null)) + return + }) diff --git a/src/core/PointerLockControls.tsx b/src/core/PointerLockControls.tsx index ecfb6e4d9..91af86b45 100644 --- a/src/core/PointerLockControls.tsx +++ b/src/core/PointerLockControls.tsx @@ -1,14 +1,11 @@ -import { EventManager, ReactThreeFiber, RootState, useThree } from '@react-three/fiber' +import { EventManager, RootState, ThreeElement, useThree } from '@react-three/fiber' import { DomEvent } from '@react-three/fiber/dist/declarations/src/core/events' import * as React from 'react' import * as THREE from 'three' import { PointerLockControls as PointerLockControlsImpl } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export type PointerLockControlsProps = ReactThreeFiber.Object3DNode< - PointerLockControlsImpl, - typeof PointerLockControlsImpl -> & { +export type PointerLockControlsProps = Omit, 'ref'> & { domElement?: HTMLElement selector?: string enabled?: boolean diff --git a/src/core/Points.tsx b/src/core/Points.tsx index 57719b35a..0cdc83c5f 100644 --- a/src/core/Points.tsx +++ b/src/core/Points.tsx @@ -1,22 +1,20 @@ import * as THREE from 'three' import * as React from 'react' -import { ReactThreeFiber, extend, useFrame } from '@react-three/fiber' +import { ThreeElement, ThreeElements, extend, useFrame } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -declare global { - namespace JSX { - interface IntrinsicElements { - positionPoint: ReactThreeFiber.Object3DNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + positionPoint: ThreeElement } } type Api = { - getParent: () => React.MutableRefObject + getParent: () => React.RefObject subscribe: (ref) => void } -type PointsInstancesProps = JSX.IntrinsicElements['points'] & { +export type PointsInstancesProps = Omit & { range?: number limit?: number } @@ -29,8 +27,8 @@ const _position = /* @__PURE__ */ new THREE.Vector3() export class PositionPoint extends THREE.Group { size: number color: THREE.Color - instance: React.MutableRefObject - instanceKey: React.MutableRefObject + instance: React.RefObject + instanceKey: React.RefObject constructor() { super() this.size = 0 @@ -94,7 +92,7 @@ const PointsInstances: ForwardRefComponent = >(({ children, range, limit = 1000, ...props }, ref) => { const parentRef = React.useRef(null!) React.useImperativeHandle(ref, () => parentRef.current, []) - const [refs, setRefs] = React.useState[]>([]) + const [refs, setRefs] = React.useState[]>([]) const [[positions, colors, sizes]] = React.useState(() => [ new Float32Array(limit * 3), Float32Array.from({ length: limit * 3 }, () => 1), @@ -140,35 +138,17 @@ const PointsInstances: ForwardRefComponent = return ( null} {...props}> - - - + + + {children} ) }) -export const Point: ForwardRefComponent = - /* @__PURE__ */ React.forwardRef(({ children, ...props }: JSX.IntrinsicElements['positionPoint'], ref) => { +export const Point: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef(({ children, ...props }: ThreeElements['positionPoint'], ref) => { React.useMemo(() => extend({ PositionPoint }), []) const group = React.useRef(null!) React.useImperativeHandle(ref, () => group.current, []) @@ -184,7 +164,7 @@ export const Point: ForwardRefComponent return ( - + {colors && ( )} {sizes && ( )} diff --git a/src/core/PositionalAudio.tsx b/src/core/PositionalAudio.tsx index 746e8875e..71b51c66d 100644 --- a/src/core/PositionalAudio.tsx +++ b/src/core/PositionalAudio.tsx @@ -1,16 +1,16 @@ import * as React from 'react' import { AudioLoader, AudioListener, PositionalAudio as PositionalAudioImpl } from 'three' -import { useThree, useLoader } from '@react-three/fiber' +import { useThree, useLoader, ThreeElements } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = JSX.IntrinsicElements['positionalAudio'] & { +export type PositionalAudioProps = Omit & { url: string distance?: number loop?: boolean } -export const PositionalAudio: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ url, distance = 1, loop = true, autoplay, ...props }: Props, ref) => { +export const PositionalAudio: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef(({ url, distance = 1, loop = true, autoplay, ...props }, ref) => { const sound = React.useRef(null!) React.useImperativeHandle(ref, () => sound.current, []) const camera = useThree(({ camera }) => camera) @@ -39,5 +39,4 @@ export const PositionalAudio: ForwardRefComponent = } }, []) return - } -) + }) diff --git a/src/core/Preload.tsx b/src/core/Preload.tsx index df6a3d42a..e450c425a 100644 --- a/src/core/Preload.tsx +++ b/src/core/Preload.tsx @@ -2,13 +2,13 @@ import { Object3D, Camera, WebGLCubeRenderTarget, CubeCamera, Scene } from 'thre import * as React from 'react' import { useThree } from '@react-three/fiber' -type Props = { +export type PreloadProps = { all?: boolean scene?: Object3D camera?: Camera } -export function Preload({ all, scene, camera }: Props) { +export function Preload({ all, scene, camera }: PreloadProps) { const gl = useThree(({ gl }) => gl) const dCamera = useThree(({ camera }) => camera) const dScene = useThree(({ scene }) => scene) diff --git a/src/core/QuadraticBezierLine.tsx b/src/core/QuadraticBezierLine.tsx index e9ff7a7d2..4d9b5da34 100644 --- a/src/core/QuadraticBezierLine.tsx +++ b/src/core/QuadraticBezierLine.tsx @@ -2,63 +2,65 @@ import * as React from 'react' import { QuadraticBezierCurve3, Vector3 } from 'three' import { Line2 } from 'three-stdlib' import { Line, LineProps } from './Line' -import { Object3DNode } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = Omit & { +export type QuadraticBezierLineRef = Line2 & { + setPoints( + start: Vector3 | [number, number, number], + end: Vector3 | [number, number, number], + mid: Vector3 | [number, number, number] + ): void +} + +export type QuadraticBezierLineProps = Omit & { start: Vector3 | [number, number, number] end: Vector3 | [number, number, number] mid?: Vector3 | [number, number, number] segments?: number } -export type Line2Props = Object3DNode & { - setPoints: ( - start: Vector3 | [number, number, number], - end: Vector3 | [number, number, number], - mid: Vector3 | [number, number, number] - ) => void -} - const v = /* @__PURE__ */ new Vector3() -export const QuadraticBezierLine: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< - Line2Props, - Props ->(function QuadraticBezierLine({ start = [0, 0, 0], end = [0, 0, 0], mid, segments = 20, ...rest }, forwardref) { - const ref = React.useRef(null!) - React.useImperativeHandle(forwardref, () => ref.current) - const [curve] = React.useState(() => new QuadraticBezierCurve3(undefined as any, undefined as any, undefined as any)) - const getPoints = React.useCallback((start, end, mid, segments = 20) => { - if (start instanceof Vector3) curve.v0.copy(start) - else curve.v0.set(...(start as [number, number, number])) - if (end instanceof Vector3) curve.v2.copy(end) - else curve.v2.set(...(end as [number, number, number])) - if (mid instanceof Vector3) { - curve.v1.copy(mid) - } else if (Array.isArray(mid)) { - curve.v1.set(...(mid as [number, number, number])) - } else { - curve.v1.copy( - curve.v0 - .clone() - .add(curve.v2.clone().sub(curve.v0)) - .add(v.set(0, curve.v0.y - curve.v2.y, 0)) - ) - } - return curve.getPoints(segments) - }, []) +export const QuadraticBezierLine: ForwardRefComponent = + /* @__PURE__ */ React.forwardRef(function QuadraticBezierLine( + { start = [0, 0, 0], end = [0, 0, 0], mid, segments = 20, ...rest }, + forwardref + ) { + const ref = React.useRef(null!) + React.useImperativeHandle(forwardref, () => ref.current) + const [curve] = React.useState( + () => new QuadraticBezierCurve3(undefined as any, undefined as any, undefined as any) + ) + const getPoints = React.useCallback((start, end, mid, segments = 20) => { + if (start instanceof Vector3) curve.v0.copy(start) + else curve.v0.set(...(start as [number, number, number])) + if (end instanceof Vector3) curve.v2.copy(end) + else curve.v2.set(...(end as [number, number, number])) + if (mid instanceof Vector3) { + curve.v1.copy(mid) + } else if (Array.isArray(mid)) { + curve.v1.set(...(mid as [number, number, number])) + } else { + curve.v1.copy( + curve.v0 + .clone() + .add(curve.v2.clone().sub(curve.v0)) + .add(v.set(0, curve.v0.y - curve.v2.y, 0)) + ) + } + return curve.getPoints(segments) + }, []) - React.useLayoutEffect(() => { - ref.current.setPoints = ( - start: Vector3 | [number, number, number], - end: Vector3 | [number, number, number], - mid: Vector3 | [number, number, number] - ) => { - const points = getPoints(start, end, mid) - if (ref.current.geometry) ref.current.geometry.setPositions(points.map((p) => p.toArray()).flat()) - } - }, []) + React.useLayoutEffect(() => { + ref.current.setPoints = ( + start: Vector3 | [number, number, number], + end: Vector3 | [number, number, number], + mid: Vector3 | [number, number, number] + ) => { + const points = getPoints(start, end, mid) + if (ref.current.geometry) ref.current.geometry.setPositions(points.map((p) => p.toArray()).flat()) + } + }, []) - const points = React.useMemo(() => getPoints(start, end, mid, segments), [start, end, mid, segments]) - return -}) + const points = React.useMemo(() => getPoints(start, end, mid, segments), [start, end, mid, segments]) + return + }) diff --git a/src/core/Reflector.tsx b/src/core/Reflector.tsx deleted file mode 100644 index 907ce1f21..000000000 --- a/src/core/Reflector.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import * as React from 'react' -import { - Plane, - Vector3, - Vector4, - Matrix4, - PerspectiveCamera, - Mesh, - LinearFilter, - WebGLRenderTarget, - DepthTexture, - DepthFormat, - UnsignedShortType, - Texture, - HalfFloatType, -} from 'three' -import { useFrame, useThree, extend } from '@react-three/fiber' - -import { BlurPass } from '../materials/BlurPass' -import { MeshReflectorMaterialProps, MeshReflectorMaterial } from '../materials/MeshReflectorMaterial' -import { ForwardRefComponent } from '../helpers/ts-utils' - -export type ReflectorProps = Omit & - Pick & { - resolution?: number - mixBlur?: number - mixStrength?: number - blur?: [number, number] | number - mirror: number - minDepthThreshold?: number - maxDepthThreshold?: number - depthScale?: number - depthToBlurRatioBias?: number - debug?: number - distortionMap?: Texture - distortion?: number - mixContrast?: number - children?: { - ( - Component: React.ElementType, - ComponentProps: MeshReflectorMaterialProps - ): JSX.Element | null - } - } - -declare global { - namespace JSX { - interface IntrinsicElements { - meshReflectorMaterial: MeshReflectorMaterialProps - } - } -} - -/** - * @deprecated Use MeshReflectorMaterial instead - */ -export const Reflector: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< - Mesh, - ReflectorProps ->( - ( - { - mixBlur = 0, - mixStrength = 0.5, - resolution = 256, - blur = [0, 0], - args = [1, 1], - minDepthThreshold = 0.9, - maxDepthThreshold = 1, - depthScale = 0, - depthToBlurRatioBias = 0.25, - mirror = 0, - children, - debug = 0, - distortion = 1, - mixContrast = 1, - distortionMap, - ...props - }, - ref - ) => { - extend({ MeshReflectorMaterial }) - - React.useEffect(() => { - console.warn( - 'Reflector has been deprecated and will be removed next major. Replace it with !' - ) - }, []) - - const gl = useThree(({ gl }) => gl) - const camera = useThree(({ camera }) => camera) - const scene = useThree(({ scene }) => scene) - blur = Array.isArray(blur) ? blur : [blur, blur] - const hasBlur = blur[0] + blur[1] > 0 - const meshRef = React.useRef(null!) - React.useImperativeHandle(ref, () => meshRef.current, []) - const [reflectorPlane] = React.useState(() => new Plane()) - const [normal] = React.useState(() => new Vector3()) - const [reflectorWorldPosition] = React.useState(() => new Vector3()) - const [cameraWorldPosition] = React.useState(() => new Vector3()) - const [rotationMatrix] = React.useState(() => new Matrix4()) - const [lookAtPosition] = React.useState(() => new Vector3(0, 0, -1)) - const [clipPlane] = React.useState(() => new Vector4()) - const [view] = React.useState(() => new Vector3()) - const [target] = React.useState(() => new Vector3()) - const [q] = React.useState(() => new Vector4()) - const [textureMatrix] = React.useState(() => new Matrix4()) - const [virtualCamera] = React.useState(() => new PerspectiveCamera()) - - const beforeRender = React.useCallback(() => { - reflectorWorldPosition.setFromMatrixPosition(meshRef.current.matrixWorld) - cameraWorldPosition.setFromMatrixPosition(camera.matrixWorld) - rotationMatrix.extractRotation(meshRef.current.matrixWorld) - normal.set(0, 0, 1) - normal.applyMatrix4(rotationMatrix) - view.subVectors(reflectorWorldPosition, cameraWorldPosition) - // Avoid rendering when reflector is facing away - if (view.dot(normal) > 0) return - view.reflect(normal).negate() - view.add(reflectorWorldPosition) - rotationMatrix.extractRotation(camera.matrixWorld) - lookAtPosition.set(0, 0, -1) - lookAtPosition.applyMatrix4(rotationMatrix) - lookAtPosition.add(cameraWorldPosition) - target.subVectors(reflectorWorldPosition, lookAtPosition) - target.reflect(normal).negate() - target.add(reflectorWorldPosition) - virtualCamera.position.copy(view) - virtualCamera.up.set(0, 1, 0) - virtualCamera.up.applyMatrix4(rotationMatrix) - virtualCamera.up.reflect(normal) - virtualCamera.lookAt(target) - virtualCamera.far = camera.far // Used in WebGLBackground - virtualCamera.updateMatrixWorld() - virtualCamera.projectionMatrix.copy(camera.projectionMatrix) - // Update the texture matrix - textureMatrix.set(0.5, 0.0, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.0, 1.0) - textureMatrix.multiply(virtualCamera.projectionMatrix) - textureMatrix.multiply(virtualCamera.matrixWorldInverse) - textureMatrix.multiply(meshRef.current.matrixWorld) - // Now update projection matrix with new clip plane, implementing code from: http://www.terathon.com/code/oblique.html - // Paper explaining this technique: http://www.terathon.com/lengyel/Lengyel-Oblique.pdf - reflectorPlane.setFromNormalAndCoplanarPoint(normal, reflectorWorldPosition) - reflectorPlane.applyMatrix4(virtualCamera.matrixWorldInverse) - clipPlane.set(reflectorPlane.normal.x, reflectorPlane.normal.y, reflectorPlane.normal.z, reflectorPlane.constant) - const projectionMatrix = virtualCamera.projectionMatrix - q.x = (Math.sign(clipPlane.x) + projectionMatrix.elements[8]) / projectionMatrix.elements[0] - q.y = (Math.sign(clipPlane.y) + projectionMatrix.elements[9]) / projectionMatrix.elements[5] - q.z = -1.0 - q.w = (1.0 + projectionMatrix.elements[10]) / projectionMatrix.elements[14] - // Calculate the scaled plane vector - clipPlane.multiplyScalar(2.0 / clipPlane.dot(q)) - // Replacing the third row of the projection matrix - projectionMatrix.elements[2] = clipPlane.x - projectionMatrix.elements[6] = clipPlane.y - projectionMatrix.elements[10] = clipPlane.z + 1.0 - projectionMatrix.elements[14] = clipPlane.w - }, []) - - const [fbo1, fbo2, blurpass, reflectorProps] = React.useMemo(() => { - const parameters = { - type: HalfFloatType, - minFilter: LinearFilter, - magFilter: LinearFilter, - } - const fbo1 = new WebGLRenderTarget(resolution, resolution, parameters) - fbo1.depthBuffer = true - fbo1.depthTexture = new DepthTexture(resolution, resolution) - fbo1.depthTexture.format = DepthFormat - fbo1.depthTexture.type = UnsignedShortType - const fbo2 = new WebGLRenderTarget(resolution, resolution, parameters) - const blurpass = new BlurPass({ - gl, - resolution, - width: blur[0], - height: blur[1], - minDepthThreshold, - maxDepthThreshold, - depthScale, - depthToBlurRatioBias, - }) - const reflectorProps = { - mirror, - textureMatrix, - mixBlur, - tDiffuse: fbo1.texture, - tDepth: fbo1.depthTexture, - tDiffuseBlur: fbo2.texture, - hasBlur, - mixStrength, - minDepthThreshold, - maxDepthThreshold, - depthScale, - depthToBlurRatioBias, - transparent: true, - debug, - distortion, - distortionMap, - mixContrast, - 'defines-USE_BLUR': hasBlur ? '' : undefined, - 'defines-USE_DEPTH': depthScale > 0 ? '' : undefined, - 'defines-USE_DISTORTION': distortionMap ? '' : undefined, - } - return [fbo1, fbo2, blurpass, reflectorProps] - }, [ - gl, - blur, - textureMatrix, - resolution, - mirror, - hasBlur, - mixBlur, - mixStrength, - minDepthThreshold, - maxDepthThreshold, - depthScale, - depthToBlurRatioBias, - debug, - distortion, - distortionMap, - mixContrast, - ]) - - useFrame(() => { - if (!meshRef?.current) return - meshRef.current.visible = false - const currentXrEnabled = gl.xr.enabled - const currentShadowAutoUpdate = gl.shadowMap.autoUpdate - beforeRender() - gl.xr.enabled = false - gl.shadowMap.autoUpdate = false - gl.setRenderTarget(fbo1) - gl.state.buffers.depth.setMask(true) - if (!gl.autoClear) gl.clear() - gl.render(scene, virtualCamera) - if (hasBlur) blurpass.render(gl, fbo1, fbo2) - gl.xr.enabled = currentXrEnabled - gl.shadowMap.autoUpdate = currentShadowAutoUpdate - meshRef.current.visible = true - gl.setRenderTarget(null) - }) - - return ( - - - {children ? children('meshReflectorMaterial', reflectorProps) : } - - ) - } -) diff --git a/src/core/RenderCubeTexture.tsx b/src/core/RenderCubeTexture.tsx index 2bebd43a3..833a7bd6f 100644 --- a/src/core/RenderCubeTexture.tsx +++ b/src/core/RenderCubeTexture.tsx @@ -1,9 +1,9 @@ import * as THREE from 'three' import * as React from 'react' -import { ComputeFunction, ReactThreeFiber, createPortal, useFrame, useThree } from '@react-three/fiber' +import { ComputeFunction, ReactThreeFiber, ThreeElements, createPortal, useFrame, useThree } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -export type RenderCubeTextureProps = Omit & { +export type RenderCubeTextureProps = Omit & { /** Optional stencil buffer, defaults to false */ stencilBuffer?: boolean /** Optional depth buffer, defaults to true */ @@ -131,7 +131,7 @@ function Container({ frames: number renderPriority: number children: React.ReactNode - camera: React.MutableRefObject + camera: React.RefObject }) { let count = 0 useFrame((state) => { diff --git a/src/core/RenderTexture.tsx b/src/core/RenderTexture.tsx index 077b1ee4c..58c7801da 100644 --- a/src/core/RenderTexture.tsx +++ b/src/core/RenderTexture.tsx @@ -1,10 +1,10 @@ import * as THREE from 'three' import * as React from 'react' -import { createPortal, useFrame, useThree } from '@react-three/fiber' +import { createPortal, ThreeElements, useFrame, useThree } from '@react-three/fiber' import { useFBO } from './Fbo' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = JSX.IntrinsicElements['texture'] & { +export type RenderTextureProps = Omit & { /** Optional width of the texture, defaults to viewport bounds */ width?: number /** Optional height of the texture, defaults to viewport bounds */ @@ -29,7 +29,7 @@ type Props = JSX.IntrinsicElements['texture'] & { children: React.ReactNode } -export const RenderTexture: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( +export const RenderTexture: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ( { children, @@ -44,7 +44,7 @@ export const RenderTexture: ForwardRefComponent = /* @__PU depthBuffer = true, generateMipmaps = false, ...props - }: Props, + }, forwardRef ) => { const { size, viewport } = useThree() @@ -60,9 +60,9 @@ export const RenderTexture: ForwardRefComponent = /* @__PU // Since this is only a texture it does not have an easy way to obtain the parent, which we // need to transform event coordinates to local coordinates. We use r3f internals to find the // next Object3D. - let parent = (fbo.texture as any)?.__r3f.parent + let parent = (fbo.texture as any)?.__r3f.parent?.object while (parent && !(parent instanceof THREE.Object3D)) { - parent = parent.__r3f.parent + parent = parent.__r3f.parent?.object } if (!parent) return false // First we call the previous state-onion-layers compute, this is what makes it possible to nest portals diff --git a/src/core/Resize.tsx b/src/core/Resize.tsx index 4f11343e7..58ed982b8 100644 --- a/src/core/Resize.tsx +++ b/src/core/Resize.tsx @@ -1,7 +1,8 @@ import * as THREE from 'three' import * as React from 'react' +import { ThreeElements } from '@react-three/fiber' -export type ResizeProps = JSX.IntrinsicElements['group'] & { +export type ResizeProps = Omit & { /** Whether to fit into width (x axis), undefined */ width?: boolean /** Whether to fit into height (y axis), undefined */ diff --git a/src/core/RoundedBox.tsx b/src/core/RoundedBox.tsx index 3c688f7e6..46742e53e 100644 --- a/src/core/RoundedBox.tsx +++ b/src/core/RoundedBox.tsx @@ -2,6 +2,7 @@ import * as React from 'react' import { Mesh, Shape, ExtrudeGeometry } from 'three' import { ForwardRefComponent, NamedArrayTuple } from '../helpers/ts-utils' import { toCreasedNormals } from 'three-stdlib' +import { ThreeElements } from '@react-three/fiber' const eps = 0.00001 @@ -15,56 +16,57 @@ function createShape(width: number, height: number, radius0: number) { return shape } -type Props = { +export type RoundedBoxProps = { args?: NamedArrayTuple<(width?: number, height?: number, depth?: number) => void> radius?: number smoothness?: number bevelSegments?: number steps?: number creaseAngle?: number -} & Omit +} & Omit -export const RoundedBox: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - function RoundedBox( - { - args: [width = 1, height = 1, depth = 1] = [], - radius = 0.05, - steps = 1, - smoothness = 4, - bevelSegments = 4, - creaseAngle = 0.4, - children, - ...rest - }, - ref - ) { - const shape = React.useMemo(() => createShape(width, height, radius), [width, height, radius]) - const params = React.useMemo( - () => ({ - depth: depth - radius * 2, - bevelEnabled: true, - bevelSegments: bevelSegments * 2, - steps, - bevelSize: radius - eps, - bevelThickness: radius, - curveSegments: smoothness, - }), - [depth, radius, smoothness] - ) - const geomRef = React.useRef(null!) +export const RoundedBox: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< + Mesh, + RoundedBoxProps +>(function RoundedBox( + { + args: [width = 1, height = 1, depth = 1] = [], + radius = 0.05, + steps = 1, + smoothness = 4, + bevelSegments = 4, + creaseAngle = 0.4, + children, + ...rest + }, + ref +) { + const shape = React.useMemo(() => createShape(width, height, radius), [width, height, radius]) + const params = React.useMemo( + () => ({ + depth: depth - radius * 2, + bevelEnabled: true, + bevelSegments: bevelSegments * 2, + steps, + bevelSize: radius - eps, + bevelThickness: radius, + curveSegments: smoothness, + }), + [depth, radius, smoothness] + ) + const geomRef = React.useRef(null!) - React.useLayoutEffect(() => { - if (geomRef.current) { - geomRef.current.center() - toCreasedNormals(geomRef.current, creaseAngle) - } - }, [shape, params]) + React.useLayoutEffect(() => { + if (geomRef.current) { + geomRef.current.center() + toCreasedNormals(geomRef.current, creaseAngle) + } + }, [shape, params]) - return ( - - - {children} - - ) - } -) + return ( + + + {children} + + ) +}) diff --git a/src/core/Sampler.tsx b/src/core/Sampler.tsx index 1f3ea3f9a..827c28b10 100644 --- a/src/core/Sampler.tsx +++ b/src/core/Sampler.tsx @@ -3,7 +3,7 @@ import * as React from 'react' import { MeshSurfaceSampler } from 'three-stdlib' import { Color, Group, InstancedBufferAttribute, InstancedMesh, Mesh, Object3D, Vector3 } from 'three' -import { GroupProps } from '@react-three/fiber' +import { ThreeElements } from '@react-three/fiber' type SamplePayload = { /** @@ -37,7 +37,7 @@ type TransformPayload = SamplePayload & { sampledMesh: Mesh } -type Props = { +export type SamplerProps = Omit & { /** * The mesh that will be used to sample. * Does not need to be in the scene graph. @@ -78,11 +78,11 @@ export interface useSurfaceSamplerProps { } export function useSurfaceSampler( - mesh: React.MutableRefObject, + mesh: React.RefObject, count: number = 16, transform?: TransformFn, weight?: string, - instanceMesh?: React.MutableRefObject | null + instanceMesh?: React.RefObject | null ) { const [buffer, setBuffer] = React.useState(() => { const arr = Array.from({ length: count }, () => [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]).flat() @@ -146,15 +146,7 @@ export function useSurfaceSampler( return buffer } -export function Sampler({ - children, - weight, - transform, - instances, - mesh, - count = 16, - ...props -}: React.PropsWithChildren) { +export function Sampler({ children, weight, transform, instances, mesh, count = 16, ...props }: SamplerProps) { const group = React.useRef(null!) const instancedRef = React.useRef(null!) const meshToSampleRef = React.useRef(null!) diff --git a/src/core/ScreenQuad.tsx b/src/core/ScreenQuad.tsx index 78f481531..4f8b83b85 100644 --- a/src/core/ScreenQuad.tsx +++ b/src/core/ScreenQuad.tsx @@ -3,6 +3,7 @@ import * as THREE from 'three' import * as React from 'react' import { ForwardRefComponent } from '../helpers/ts-utils' +import { ThreeElements } from '@react-three/fiber' function createScreenQuadGeometry() { const geometry = new THREE.BufferGeometry() @@ -13,16 +14,17 @@ function createScreenQuadGeometry() { return geometry } -type Props = Omit +export type ScreenQuadProps = Omit -export const ScreenQuad: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - function ScreenQuad({ children, ...restProps }, ref) { - const geometry = React.useMemo(createScreenQuadGeometry, []) +export const ScreenQuad: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< + THREE.Mesh, + ScreenQuadProps +>(function ScreenQuad({ children, ...restProps }, ref) { + const geometry = React.useMemo(createScreenQuadGeometry, []) - return ( - - {children} - - ) - } -) + return ( + + {children} + + ) +}) diff --git a/src/core/ScreenSizer.tsx b/src/core/ScreenSizer.tsx index f949ee370..ec86ef438 100644 --- a/src/core/ScreenSizer.tsx +++ b/src/core/ScreenSizer.tsx @@ -1,4 +1,4 @@ -import { Object3DProps, useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' import * as React from 'react' import { forwardRef, useRef } from 'react' import { Object3D, Vector3 } from 'three' @@ -7,7 +7,7 @@ import { ForwardRefComponent } from '../helpers/ts-utils' const worldPos = /* @__PURE__ */ new Vector3() -export interface ScreenSizerProps extends Object3DProps { +export type ScreenSizerProps = Omit & { /** Scale factor. Defaults to 1, which equals 1 pixel size. */ scale?: number } diff --git a/src/core/ScreenSpace.tsx b/src/core/ScreenSpace.tsx index 6170aeeb7..9fada8950 100644 --- a/src/core/ScreenSpace.tsx +++ b/src/core/ScreenSpace.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import { Group } from 'three' -import { useFrame } from '@react-three/fiber' +import { ThreeElements, useFrame } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' -export type ScreenSpaceProps = { +export type ScreenSpaceProps = Omit & { depth?: number -} & JSX.IntrinsicElements['group'] +} export const ScreenSpace: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< Group, diff --git a/src/core/Segments.tsx b/src/core/Segments.tsx index ef2c451e9..025c85ae6 100644 --- a/src/core/Segments.tsx +++ b/src/core/Segments.tsx @@ -1,10 +1,10 @@ import * as THREE from 'three' import * as React from 'react' -import { extend, useFrame, ReactThreeFiber } from '@react-three/fiber' +import { extend, useFrame, ReactThreeFiber, ThreeElements, ThreeElement } from '@react-three/fiber' import { Line2, LineSegmentsGeometry, LineMaterial, LineMaterialParameters } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -type SegmentsProps = LineMaterialParameters & { +export type SegmentsProps = LineMaterialParameters & { limit?: number lineWidth?: number children: React.ReactNode @@ -15,7 +15,7 @@ type Api = { } type SegmentRef = React.RefObject -type SegmentProps = Omit & { +export type SegmentProps = Omit & { start: ReactThreeFiber.Vector3 end: ReactThreeFiber.Vector3 color?: ReactThreeFiber.Color @@ -91,11 +91,9 @@ const Segments: ForwardRefComponent = /* @__PURE__ */ Reac } ) -declare global { - namespace JSX { - interface IntrinsicElements { - segmentObject: ReactThreeFiber.Object3DNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + segmentObject: ThreeElement } } diff --git a/src/core/Shadow.tsx b/src/core/Shadow.tsx index 81e71bf18..379821ed7 100644 --- a/src/core/Shadow.tsx +++ b/src/core/Shadow.tsx @@ -1,8 +1,9 @@ import * as React from 'react' import { Mesh, Color, DoubleSide, type PlaneGeometry, type MeshBasicMaterial } from 'three' import { ForwardRefComponent } from '../helpers/ts-utils' +import { ThreeElements } from '@react-three/fiber' -type Props = JSX.IntrinsicElements['mesh'] & { +export type ShadowProps = Omit & { colorStop?: number fog?: boolean color?: Color | number | string @@ -12,9 +13,9 @@ type Props = JSX.IntrinsicElements['mesh'] & { export type ShadowType = Mesh -export const Shadow: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( +export const Shadow: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ( - { fog = false, renderOrder, depthWrite = false, colorStop = 0.0, color = 'black', opacity = 0.5, ...props }: Props, + { fog = false, renderOrder, depthWrite = false, colorStop = 0.0, color = 'black', opacity = 0.5, ...props }, ref ) => { const canvas = React.useMemo(() => { @@ -37,7 +38,7 @@ export const Shadow: ForwardRefComponent = /* @__PURE__ */ Re return canvas }, [color, colorStop]) return ( - } rotation-x={-Math.PI / 2} {...props}> + } rotation-x={-Math.PI / 2} {...props}> diff --git a/src/core/ShadowAlpha.tsx b/src/core/ShadowAlpha.tsx index 1c8365d8d..d711ac7e2 100644 --- a/src/core/ShadowAlpha.tsx +++ b/src/core/ShadowAlpha.tsx @@ -12,7 +12,7 @@ import { useFrame } from '@react-three/fiber' import * as React from 'react' import * as THREE from 'three' -interface ShadowAlphaProps { +export interface ShadowAlphaProps { opacity?: number alphaMap?: THREE.Texture | boolean } @@ -96,7 +96,7 @@ export function ShadowAlpha({ opacity, alphaMap }: ShadowAlphaProps) { }, []) useFrame(() => { - const parent = (depthMaterialRef.current as any).__r3f?.parent + const parent = (depthMaterialRef.current as any).__r3f?.parent?.object if (parent) { const parentMainMaterial = parent.material if (parentMainMaterial) { diff --git a/src/core/Sky.tsx b/src/core/Sky.tsx index 677e0aed0..90a43f857 100644 --- a/src/core/Sky.tsx +++ b/src/core/Sky.tsx @@ -4,7 +4,7 @@ import { Sky as SkyImpl } from 'three-stdlib' import { Vector3 } from 'three' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = { +export type SkyProps = { distance?: number sunPosition?: ReactThreeFiber.Vector3 inclination?: number @@ -26,7 +26,7 @@ export function calcPosFromAngles(inclination: number, azimuth: number, vector: return vector } -export const Sky: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( +export const Sky: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ( { inclination = 0.6, @@ -38,7 +38,7 @@ export const Sky: ForwardRefComponent = /* @__PURE__ */ React.fo turbidity = 10, sunPosition = calcPosFromAngles(inclination, azimuth), ...props - }: Props, + }: SkyProps, ref ) => { const scale = React.useMemo(() => new Vector3().setScalar(distance), [distance]) diff --git a/src/core/Sparkles.tsx b/src/core/Sparkles.tsx index d840b69d4..69f12a10f 100644 --- a/src/core/Sparkles.tsx +++ b/src/core/Sparkles.tsx @@ -1,26 +1,9 @@ import * as React from 'react' import * as THREE from 'three' -import { PointsProps, useThree, useFrame, extend, MaterialNode } from '@react-three/fiber' +import { useThree, useFrame, extend, ThreeElement, ThreeElements } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' import { version } from '../helpers/constants' -interface Props { - /** Number of particles (default: 100) */ - count?: number - /** Speed of particles (default: 1) */ - speed?: number | Float32Array - /** Opacity of particles (default: 1) */ - opacity?: number | Float32Array - /** Color of particles (default: 100) */ - color?: THREE.ColorRepresentation | Float32Array - /** Size of particles (default: randomized between 0 and 1) */ - size?: number | Float32Array - /** The space the particles occupy (default: 1) */ - scale?: number | [number, number, number] | THREE.Vector3 - /** Movement factor (default: 1) */ - noise?: number | [number, number, number] | THREE.Vector3 | Float32Array -} - class SparklesImplMaterial extends THREE.ShaderMaterial { constructor() { super({ @@ -81,11 +64,9 @@ class SparklesImplMaterial extends THREE.ShaderMaterial { } } -declare global { - namespace JSX { - interface IntrinsicElements { - sparklesImplMaterial: MaterialNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + sparklesImplMaterial: ThreeElement } } @@ -125,9 +106,26 @@ function usePropAsIsOrAsAttribute( }, [prop]) } -export const Sparkles: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< +export type SparklesProps = Omit & { + /** Number of particles (default: 100) */ + count?: number + /** Speed of particles (default: 1) */ + speed?: number | Float32Array + /** Opacity of particles (default: 1) */ + opacity?: number | Float32Array + /** Color of particles (default: 100) */ + color?: THREE.ColorRepresentation | Float32Array + /** Size of particles (default: randomized between 0 and 1) */ + size?: number | Float32Array + /** The space the particles occupy (default: 1) */ + scale?: number | [number, number, number] | THREE.Vector3 + /** Movement factor (default: 1) */ + noise?: number | [number, number, number] | THREE.Vector3 | Float32Array +} + +export const Sparkles: ForwardRefComponent = /* @__PURE__ */ React.forwardRef< THREE.Points, - Props & PointsProps + SparklesProps >(({ noise = 1, count = 100, speed = 1, opacity = 1, scale = 1, size, color, children, ...props }, forwardRef) => { React.useMemo(() => extend({ SparklesImplMaterial }), []) const ref = React.useRef(null!) diff --git a/src/core/Splat.tsx b/src/core/Splat.tsx index 7a731e8c2..758af5990 100644 --- a/src/core/Splat.tsx +++ b/src/core/Splat.tsx @@ -6,7 +6,7 @@ import * as THREE from 'three' import * as React from 'react' -import { extend, useThree, useFrame, useLoader, LoaderProto } from '@react-three/fiber' +import { extend, useThree, useFrame, useLoader, ThreeElements } from '@react-three/fiber' import { shaderMaterial } from './shaderMaterial' import { version } from '../helpers/constants' @@ -53,15 +53,13 @@ export type SharedState = { onProgress?: (event: ProgressEvent) => void } -declare global { - namespace JSX { - interface IntrinsicElements { - splatMaterial: SplatMaterialType & JSX.IntrinsicElements['shaderMaterial'] - } +declare module '@react-three/fiber' { + interface ThreeElements { + splatMaterial: SplatMaterialType & ThreeElements['shaderMaterial'] } } -type SplatProps = { +export type SplatProps = { /** Url towards a *.splat file, no support for *.ply */ src: string /** Whether to use tone mapping, default: false */ @@ -72,7 +70,7 @@ type SplatProps = { alphaHash?: boolean /** Chunk size for lazy loading, prevents chokings the worker, default: 25000 (25kb) */ chunkSize?: number -} & JSX.IntrinsicElements['mesh'] +} & Omit const SplatMaterial = /* @__PURE__ */ shaderMaterial( { @@ -637,7 +635,7 @@ export function Splat({ const camera = useThree((state) => state.camera) // Shared state, globally memoized, the same url re-uses the same daza - const shared = useLoader(SplatLoader as unknown as LoaderProto, src, (loader) => { + const shared = useLoader(SplatLoader, src, (loader) => { loader.gl = gl loader.chunkSize = chunkSize }) as SharedState diff --git a/src/core/SpotLight.tsx b/src/core/SpotLight.tsx index f7c8844e6..63b380363 100644 --- a/src/core/SpotLight.tsx +++ b/src/core/SpotLight.tsx @@ -16,7 +16,7 @@ import { RepeatWrapping, Object3D, } from 'three' -import { useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' import { FullScreenQuad } from 'three-stdlib' import { SpotLightMaterial } from '../materials/SpotLightMaterial' @@ -24,7 +24,7 @@ import { SpotLightMaterial } from '../materials/SpotLightMaterial' import SpotlightShadowShader from '../helpers/glsl/DefaultSpotlightShadowShadows.glsl' import { ForwardRefComponent } from '../helpers/ts-utils' -type SpotLightProps = JSX.IntrinsicElements['spotLight'] & { +export type SpotLightProps = Omit & { depthBuffer?: DepthTexture attenuation?: number anglePower?: number @@ -94,8 +94,8 @@ function VolumetricMesh({ } function useCommon( - spotlight: React.MutableRefObject, - mesh: React.MutableRefObject, + spotlight: React.RefObject, + mesh: React.RefObject, width: number, height: number, distance: number diff --git a/src/core/SpriteAnimator.tsx b/src/core/SpriteAnimator.tsx index 6253417f9..1feb9bd76 100644 --- a/src/core/SpriteAnimator.tsx +++ b/src/core/SpriteAnimator.tsx @@ -1,6 +1,6 @@ /* eslint react-hooks/exhaustive-deps: 1 */ import * as React from 'react' -import { useFrame, Vector3 } from '@react-three/fiber' +import { ThreeElements, useFrame, Vector3 } from '@react-three/fiber' import * as THREE from 'three' import { Instances, Instance } from './Instances' import { Billboard } from './Billboard' @@ -94,7 +94,7 @@ export type SpriteAnimatorProps = { * @see https://threejs.org/docs/#api/en/core/Object3D */ meshProps?: CommonMeshProps -} & JSX.IntrinsicElements['group'] +} & Omit type SpriteAnimatorState = { current?: number @@ -503,7 +503,7 @@ export const SpriteAnimator = /* @__PURE__ */ React.forwardRef + {asSprite && ( diff --git a/src/core/Stage.tsx b/src/core/Stage.tsx index 93460b72b..59c3fad5d 100644 --- a/src/core/Stage.tsx +++ b/src/core/Stage.tsx @@ -10,6 +10,7 @@ import { } from './AccumulativeShadows' import { useBounds, Bounds } from './Bounds' import { PresetsType } from '../helpers/environment-assets' +import { ThreeElements } from '@react-three/fiber' const presets = { rembrandt: { @@ -44,7 +45,7 @@ type StageShadows = Partial & size?: number } -type StageProps = { +export type StageProps = Omit & { /** Lighting setup, default: "rembrandt" */ preset?: | 'rembrandt' @@ -81,7 +82,7 @@ export function Stage({ environment = 'city', preset = 'rembrandt', ...props -}: JSX.IntrinsicElements['group'] & StageProps) { +}: StageProps) { const config = typeof preset === 'string' ? presets[preset] : preset const [{ radius, height }, set] = React.useState({ radius: 0, width: 0, height: 0, depth: 0 }) const shadowBias = (shadows as StageShadows)?.bias ?? -0.0001 diff --git a/src/core/Stars.tsx b/src/core/Stars.tsx index ebcc2f7ac..de7611dd8 100644 --- a/src/core/Stars.tsx +++ b/src/core/Stars.tsx @@ -1,11 +1,10 @@ -import * as THREE from 'three' import * as React from 'react' -import { ReactThreeFiber, useFrame } from '@react-three/fiber' +import { ThreeElement, useFrame } from '@react-three/fiber' import { Points, Vector3, Spherical, Color, AdditiveBlending, ShaderMaterial } from 'three' import { ForwardRefComponent } from '../helpers/ts-utils' import { version } from '../helpers/constants' -type Props = { +export type StarsProps = { radius?: number depth?: number count?: number @@ -48,11 +47,9 @@ class StarfieldMaterial extends ShaderMaterial { } } -declare global { - namespace JSX { - interface IntrinsicElements { - starfieldMaterial: ReactThreeFiber.MaterialNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + starfieldMaterial: ThreeElement } } @@ -60,9 +57,9 @@ const genStar = (r: number) => { return new Vector3().setFromSpherical(new Spherical(r, Math.acos(1 - Math.random() * 2), Math.random() * 2 * Math.PI)) } -export const Stars: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( - ({ radius = 100, depth = 50, count = 5000, saturation = 0, factor = 4, fade = false, speed = 1 }: Props, ref) => { - const material = React.useRef() +export const Stars: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( + ({ radius = 100, depth = 50, count = 5000, saturation = 0, factor = 4, fade = false, speed = 1 }, ref) => { + const material = React.useRef(null) const [position, color, size] = React.useMemo(() => { const positions: any[] = [] const colors: any[] = [] @@ -85,7 +82,7 @@ export const Stars: ForwardRefComponent = /* @__PURE__ */ React.f const [starfieldMaterial] = React.useState(() => new StarfieldMaterial()) return ( - }> + }> diff --git a/src/core/Stats.tsx b/src/core/Stats.tsx index 302284ce2..bdb854229 100644 --- a/src/core/Stats.tsx +++ b/src/core/Stats.tsx @@ -4,13 +4,13 @@ import { addEffect, addAfterEffect } from '@react-three/fiber' import StatsImpl from 'stats.js' import { useEffectfulState } from '../helpers/useEffectfulState' -type Props = { +export type StatsProps = { showPanel?: number className?: string parent?: React.RefObject } -export function Stats({ showPanel = 0, className, parent }: Props): null { +export function Stats({ showPanel = 0, className, parent }: StatsProps): null { const stats = useEffectfulState(() => new StatsImpl(), []) React.useEffect(() => { if (stats) { diff --git a/src/core/StatsGl.tsx b/src/core/StatsGl.tsx index 334942c19..f660241bf 100644 --- a/src/core/StatsGl.tsx +++ b/src/core/StatsGl.tsx @@ -6,7 +6,7 @@ import Stats from 'stats-gl' type StatsOptions = ConstructorParameters[0] -type Props = Partial & { +export type StatsGlProps = Partial & { id?: string clearStatsGlStyle?: boolean showPanel?: number @@ -15,42 +15,41 @@ type Props = Partial & { ref?: React.RefObject } -export const StatsGl: ForwardRefComponent = /* @__PURE__ */ React.forwardRef(function StatsGl( - { className, parent, id, clearStatsGlStyle, ...props }, - fref -) { - const gl = useThree((state) => state.gl) - - const stats = React.useMemo(() => { - const stats = new Stats({ - ...props, - }) - stats.init(gl) - return stats - }, [gl]) - - React.useImperativeHandle(fref, () => stats.domElement, [stats]) - - React.useEffect(() => { - if (stats) { - const node = (parent && parent.current) || document.body - node?.appendChild(stats.domElement) - stats.domElement.querySelectorAll('canvas').forEach((canvas) => { - canvas.style.removeProperty('position') +export const StatsGl: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( + function StatsGl({ className, parent, id, clearStatsGlStyle, ...props }, fref) { + const gl = useThree((state) => state.gl) + + const stats = React.useMemo(() => { + const stats = new Stats({ + ...props, }) - if (id) stats.domElement.id = id - if (clearStatsGlStyle) stats.domElement.removeAttribute('style') - stats.domElement.removeAttribute('style') - const classNames = (className ?? '').split(' ').filter((cls) => cls) - if (classNames.length) stats.domElement.classList.add(...classNames) - const end = addAfterEffect(() => stats.update()) - return () => { - if (classNames.length) stats.domElement.classList.remove(...classNames) - node?.removeChild(stats.domElement) - end() + stats.init(gl) + return stats + }, [gl]) + + React.useImperativeHandle(fref, () => stats.domElement, [stats]) + + React.useEffect(() => { + if (stats) { + const node = (parent && parent.current) || document.body + node?.appendChild(stats.domElement) + stats.domElement.querySelectorAll('canvas').forEach((canvas) => { + canvas.style.removeProperty('position') + }) + if (id) stats.domElement.id = id + if (clearStatsGlStyle) stats.domElement.removeAttribute('style') + stats.domElement.removeAttribute('style') + const classNames = (className ?? '').split(' ').filter((cls) => cls) + if (classNames.length) stats.domElement.classList.add(...classNames) + const end = addAfterEffect(() => stats.update()) + return () => { + if (classNames.length) stats.domElement.classList.remove(...classNames) + node?.removeChild(stats.domElement) + end() + } } - } - }, [parent, stats, className, id, clearStatsGlStyle]) + }, [parent, stats, className, id, clearStatsGlStyle]) - return null -}) + return null + } +) diff --git a/src/core/Svg.tsx b/src/core/Svg.tsx index 37a31855a..732e2204d 100644 --- a/src/core/Svg.tsx +++ b/src/core/Svg.tsx @@ -1,19 +1,19 @@ -import { MeshBasicMaterialProps, MeshProps, Object3DProps, useLoader } from '@react-three/fiber' +import { useLoader, ThreeElements } from '@react-three/fiber' import * as React from 'react' import { forwardRef, Fragment, useEffect, useMemo } from 'react' import { DoubleSide, Object3D } from 'three' import { SVGLoader } from 'three-stdlib' import { ForwardRefComponent } from '../helpers/ts-utils' -export interface SvgProps extends Omit { +export interface SvgProps extends Omit { /** src can be a URL or SVG data */ src: string skipFill?: boolean skipStrokes?: boolean - fillMaterial?: MeshBasicMaterialProps - strokeMaterial?: MeshBasicMaterialProps - fillMeshProps?: MeshProps - strokeMeshProps?: MeshProps + fillMaterial?: ThreeElements['meshBasicMaterial'] + strokeMaterial?: ThreeElements['meshBasicMaterial'] + fillMeshProps?: ThreeElements['mesh'] + strokeMeshProps?: ThreeElements['mesh'] } export const Svg: ForwardRefComponent = /* @__PURE__ */ forwardRef( diff --git a/src/core/Text.tsx b/src/core/Text.tsx index dab91448d..2f6b2fcce 100644 --- a/src/core/Text.tsx +++ b/src/core/Text.tsx @@ -1,11 +1,11 @@ import * as React from 'react' // @ts-ignore import { Text as TextMeshImpl, preloadFont } from 'troika-three-text' -import { ReactThreeFiber, useThree } from '@react-three/fiber' +import { ReactThreeFiber, ThreeElements, useThree } from '@react-three/fiber' import { suspend } from 'suspend-react' import { ForwardRefComponent } from '../helpers/ts-utils' -type Props = JSX.IntrinsicElements['mesh'] & { +export type TextProps = Omit & { children: React.ReactNode characters?: string color?: ReactThreeFiber.Color @@ -41,7 +41,7 @@ type Props = JSX.IntrinsicElements['mesh'] & { onSync?: (troika: any) => void } -export const Text: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( +export const Text: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ( { sdfGlyphSize = 64, @@ -53,8 +53,8 @@ export const Text: ForwardRefComponent = /* @__PURE__ */ React.forwa characters, onSync, ...props - }: Props, - ref: React.ForwardedRef + }, + ref ) => { const invalidate = useThree(({ invalidate }) => invalidate) const [troikaMesh] = React.useState(() => new TextMeshImpl()) diff --git a/src/core/Text3D.tsx b/src/core/Text3D.tsx index 1aa809b9a..24d212982 100644 --- a/src/core/Text3D.tsx +++ b/src/core/Text3D.tsx @@ -1,26 +1,22 @@ import * as React from 'react' import * as THREE from 'three' -import { extend, MeshProps, Node } from '@react-three/fiber' +import { extend, ThreeElement, ThreeElements } from '@react-three/fiber' import { useMemo } from 'react' -import { suspend } from 'suspend-react' -import { mergeVertices, TextGeometry, TextGeometryParameters, FontLoader } from 'three-stdlib' +import { mergeVertices, TextGeometry, TextGeometryParameters } from 'three-stdlib' import { useFont, FontData } from './useFont' import { ForwardRefComponent } from '../helpers/ts-utils' -declare global { - namespace JSX { - interface IntrinsicElements { - renamedTextGeometry: Node - } +declare module '@react-three/fiber' { + interface ThreeElements { + renamedTextGeometry: ThreeElement } } -type Text3DProps = { +export type Text3DProps = Omit & { font: FontData | string bevelSegments?: number smooth?: number -} & Omit & - MeshProps +} & Omit const types = ['string', 'number'] const getTextFromChildren = (children) => { @@ -110,7 +106,7 @@ export const Text3D: ForwardRefComponent< return ( - + {rest} ) diff --git a/src/core/TrackballControls.tsx b/src/core/TrackballControls.tsx index e2cd01648..ab331222d 100644 --- a/src/core/TrackballControls.tsx +++ b/src/core/TrackballControls.tsx @@ -1,21 +1,24 @@ -import { ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { ReactThreeFiber, ThreeElement, useFrame, useThree } from '@react-three/fiber' import * as React from 'react' import * as THREE from 'three' import { TrackballControls as TrackballControlsImpl } from 'three-stdlib' -import { ForwardRefComponent } from '../helpers/ts-utils' +import { ForwardRefComponent, Overwrite } from '../helpers/ts-utils' -export type TrackballControlsProps = ReactThreeFiber.Overwrite< - ReactThreeFiber.Object3DNode, - { - target?: ReactThreeFiber.Vector3 - camera?: THREE.Camera - domElement?: HTMLElement - regress?: boolean - makeDefault?: boolean - onChange?: (e?: THREE.Event) => void - onStart?: (e?: THREE.Event) => void - onEnd?: (e?: THREE.Event) => void - } +export type TrackballControlsProps = Omit< + Overwrite< + ThreeElement, + { + target?: ReactThreeFiber.Vector3 + camera?: THREE.Camera + domElement?: HTMLElement + regress?: boolean + makeDefault?: boolean + onChange?: (e?: THREE.Event) => void + onStart?: (e?: THREE.Event) => void + onEnd?: (e?: THREE.Event) => void + } + >, + 'ref' > export const TrackballControls: ForwardRefComponent = diff --git a/src/core/Trail.tsx b/src/core/Trail.tsx index 0a17624a5..9ced0ddd0 100644 --- a/src/core/Trail.tsx +++ b/src/core/Trail.tsx @@ -18,10 +18,10 @@ type Settings = { interval: number } -type TrailProps = { +export type TrailProps = { color?: ColorRepresentation attenuation?: (width: number) => number - target?: React.MutableRefObject + target?: React.RefObject } & Partial const defaults: Partial = { @@ -45,7 +45,7 @@ export function useTrail(target: Object3D, settings: Partial) { ...settings, } as Settings - const points = React.useRef() + const points = React.useRef(null) const [worldPosition] = React.useState(() => new Vector3()) React.useLayoutEffect(() => { @@ -145,7 +145,7 @@ export const Trail: ForwardRefComponent< } } - if (typeof matOverride?.props === 'object') { + if (typeof matOverride?.props === 'object' && matOverride?.props !== null) { m.setValues(matOverride.props) } diff --git a/src/core/TrailTexture.tsx b/src/core/TrailTexture.tsx index d05373764..ebf30906c 100644 --- a/src/core/TrailTexture.tsx +++ b/src/core/TrailTexture.tsx @@ -208,12 +208,11 @@ export function useTrailTexture(config: Partial = {}): [Texture, (T // -export const TrailTexture = ({ - children, - ...config -}: { +export type TrailTextureProps = { children?: (texture: ReturnType) => React.ReactNode -} & TrailConfig) => { +} & TrailConfig + +export const TrailTexture = ({ children, ...config }: TrailTextureProps) => { const ret = useTrailTexture(config) return <>{children?.(ret)} diff --git a/src/core/TransformControls.tsx b/src/core/TransformControls.tsx index e9623215c..2fef5132f 100644 --- a/src/core/TransformControls.tsx +++ b/src/core/TransformControls.tsx @@ -1,4 +1,4 @@ -import { ReactThreeFiber, useThree } from '@react-three/fiber' +import { ReactThreeFiber, ThreeElement, ThreeElements, useThree } from '@react-three/fiber' import * as React from 'react' import * as THREE from 'three' import { TransformControls as TransformControlsImpl } from 'three-stdlib' @@ -8,9 +8,9 @@ type ControlsProto = { enabled: boolean } -export type TransformControlsProps = ReactThreeFiber.Object3DNode & - JSX.IntrinsicElements['group'] & { - object?: THREE.Object3D | React.MutableRefObject +export type TransformControlsProps = Omit, 'ref'> & + Omit & { + object?: THREE.Object3D | React.RefObject enabled?: boolean axis?: string | null domElement?: HTMLElement @@ -61,8 +61,7 @@ export const TransformControls: ForwardRefComponent { - // @ts-expect-error new in @react-three/fiber@7.0.5 - const defaultControls = useThree((state) => state.controls) as ControlsProto + const defaultControls = useThree((state) => state.controls) as unknown as ControlsProto | undefined const gl = useThree((state) => state.gl) const events = useThree((state) => state.events) const defaultCamera = useThree((state) => state.camera) @@ -95,10 +94,10 @@ export const TransformControls: ForwardRefComponent void>() - const onMouseDownRef = React.useRef<(e?: THREE.Event) => void>() - const onMouseUpRef = React.useRef<(e?: THREE.Event) => void>() - const onObjectChangeRef = React.useRef<(e?: THREE.Event) => void>() + const onChangeRef = React.useRef<((e?: THREE.Event) => void) | undefined>(undefined) + const onMouseDownRef = React.useRef<((e?: THREE.Event) => void) | undefined>(undefined) + const onMouseUpRef = React.useRef<((e?: THREE.Event) => void) | undefined>(undefined) + const onObjectChangeRef = React.useRef<((e?: THREE.Event) => void) | undefined>(undefined) React.useLayoutEffect(() => void (onChangeRef.current = onChange), [onChange]) React.useLayoutEffect(() => void (onMouseDownRef.current = onMouseDown), [onMouseDown]) diff --git a/src/core/VideoTexture.tsx b/src/core/VideoTexture.tsx index 489995672..88176c84c 100644 --- a/src/core/VideoTexture.tsx +++ b/src/core/VideoTexture.tsx @@ -85,8 +85,7 @@ export function useVideoTexture( const texture = new THREE.VideoTexture(video) - if ('colorSpace' in texture) (texture as any).colorSpace = (gl as any).outputColorSpace - else texture.encoding = gl.outputEncoding + texture.colorSpace = gl.outputColorSpace video.addEventListener(unsuspend, () => res(texture)) }), diff --git a/src/core/Wireframe.tsx b/src/core/Wireframe.tsx index 30e011064..b40bada27 100644 --- a/src/core/Wireframe.tsx +++ b/src/core/Wireframe.tsx @@ -1,6 +1,6 @@ import * as React from 'react' import * as THREE from 'three' -import { MaterialNode, extend } from '@react-three/fiber' +import { extend, ThreeElement } from '@react-three/fiber' import { WireframeMaterial, WireframeMaterialProps, @@ -9,11 +9,9 @@ import { useWireframeUniforms, } from '../materials/WireframeMaterial' -declare global { - namespace JSX { - interface IntrinsicElements { - meshWireframeMaterial: MaterialNode - } +declare module '@react-three/fiber' { + interface ThreeElements { + meshWireframeMaterial: ThreeElement } } diff --git a/src/core/index.ts b/src/core/index.ts index 1842b9299..014ee30af 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -110,7 +110,6 @@ export * from './Shadow' export * from './Caustics' export * from './ContactShadows' export * from './AccumulativeShadows' -export * from './Reflector' export * from './SpotLight' export * from './Environment' export * from './Lightformer' diff --git a/src/core/shapes.tsx b/src/core/shapes.tsx index 2a1bdb308..200380466 100644 --- a/src/core/shapes.tsx +++ b/src/core/shapes.tsx @@ -1,9 +1,10 @@ import * as React from 'react' import * as THREE from 'three' import { ForwardRefComponent } from '../helpers/ts-utils' +import { ThreeElements } from '@react-three/fiber' export type Args = T extends new (...args: any) => any ? ConstructorParameters : T -export type ShapeProps = Omit & { args?: Args } +export type ShapeProps = Omit & { args?: Args } function create(type: string, effect?: (mesh: THREE.Mesh) => void): ForwardRefComponent, THREE.Mesh> { const El: any = type + 'Geometry' diff --git a/src/core/softShadows.tsx b/src/core/softShadows.tsx index 6887da75a..ca016c1b0 100755 --- a/src/core/softShadows.tsx +++ b/src/core/softShadows.tsx @@ -17,7 +17,7 @@ import * as React from 'react' import * as THREE from 'three' import { useThree } from '@react-three/fiber' -type SoftShadowsProps = { +export type SoftShadowsProps = { /** Size of the light source (the larger the softer the light), default: 25 */ size?: number /** Number of samples (more samples less noise but more expensive), default: 10 */ diff --git a/src/core/useAnimations.tsx b/src/core/useAnimations.tsx index 403c26aa0..8f82616ff 100644 --- a/src/core/useAnimations.tsx +++ b/src/core/useAnimations.tsx @@ -3,7 +3,7 @@ import { Object3D, AnimationClip, AnimationAction, AnimationMixer } from 'three' import { useFrame } from '@react-three/fiber' type Api = { - ref: React.MutableRefObject + ref: React.RefObject clips: AnimationClip[] mixer: AnimationMixer names: T['name'][] @@ -12,9 +12,9 @@ type Api = { export function useAnimations( clips: T[], - root?: React.MutableRefObject | Object3D + root?: React.RefObject | Object3D ): Api { - const ref = React.useRef() + const ref = React.useRef(null) const [actualRef] = React.useState(() => (root ? (root instanceof Object3D ? { current: root } : root) : ref)) const [mixer] = React.useState(() => new AnimationMixer(undefined as unknown as Object3D)) React.useLayoutEffect(() => { diff --git a/src/core/useCamera.tsx b/src/core/useCamera.tsx index 1645c68ac..20bedecf1 100644 --- a/src/core/useCamera.tsx +++ b/src/core/useCamera.tsx @@ -2,18 +2,11 @@ import * as React from 'react' import { Raycaster, Camera, Intersection } from 'three' import { useThree, applyProps } from '@react-three/fiber' -export function useCamera(camera: Camera | React.MutableRefObject, props?: Partial) { +export function useCamera(camera: Camera | React.RefObject, props?: Partial) { const pointer = useThree((state) => state.pointer) const [raycast] = React.useState(() => { const raycaster = new Raycaster() - /** - * applyProps is an internal method of r3f and - * therefore requires its first arg to be an - * "Instance" a term used with the Reconciler - * so we have an expect error to mask this - */ - // @ts-expect-error - if (props) applyProps(raycaster, props, {}) + if (props) applyProps(raycaster, props) return function (_: Raycaster, intersects: Intersection[]): void { raycaster.setFromCamera(pointer, camera instanceof Camera ? camera : camera.current) const rc = this.constructor.prototype.raycast.bind(this) diff --git a/src/core/useContextBridge.tsx b/src/core/useContextBridge.tsx index 838664586..40a4cc144 100644 --- a/src/core/useContextBridge.tsx +++ b/src/core/useContextBridge.tsx @@ -6,7 +6,7 @@ export function useContextBridge(...contexts: Array>) { cRef.current = contexts.map((context) => React.useContext(context)) return React.useMemo( () => - ({ children }: { children: React.ReactNode }): JSX.Element => + ({ children }: { children: React.ReactNode }): React.JSX.Element => contexts.reduceRight( (acc, Context, i) => , children @@ -15,7 +15,7 @@ export function useContextBridge(...contexts: Array>) { * https://github.com/DefinitelyTyped/DefinitelyTyped/issues/44572#issuecomment-625878049 * https://github.com/microsoft/TypeScript/issues/14729 */ - ) as unknown as JSX.Element, + ) as unknown as React.JSX.Element, [] ) } diff --git a/src/core/useEnvironment.tsx b/src/core/useEnvironment.tsx index fc2c3f138..8205bf3e9 100644 --- a/src/core/useEnvironment.tsx +++ b/src/core/useEnvironment.tsx @@ -10,7 +10,6 @@ import { import { RGBELoader, EXRLoader } from 'three-stdlib' import { GainMapLoader, HDRJPGLoader } from '@monogrid/gainmap-js' import { presetsObj, PresetsType } from '../helpers/environment-assets' -import { LinearEncoding, sRGBEncoding, TextureEncoding } from '../helpers/deprecated' import { useLayoutEffect } from 'react' const CUBEMAP_ROOT = 'https://raw.githack.com/pmndrs/drei-assets/456060a26bbeb8fdf79326f224b6d99b8bcce736/hdri/' @@ -21,7 +20,7 @@ export type EnvironmentLoaderProps = { path?: string preset?: PresetsType extensions?: (loader: Loader) => void - encoding?: TextureEncoding + colorSpace?: THREE.ColorSpace } const defaultFiles = ['/px.png', '/nx.png', '/py.png', '/ny.png', '/pz.png', '/nz.png'] @@ -30,12 +29,9 @@ export function useEnvironment({ files = defaultFiles, path = '', preset = undefined, - encoding = undefined, + colorSpace = undefined, extensions, }: Partial = {}) { - let loader: typeof Loader | null = null - let multiFile: boolean = false - if (preset) { validatePreset(preset) files = presetsObj[preset] @@ -43,11 +39,11 @@ export function useEnvironment({ } // Everything else - multiFile = isArray(files) + const multiFile = isArray(files) const { extension, isCubemap } = getExtension(files) - loader = getLoader(extension) + const loader = getLoader(extension) if (!loader) throw new Error('useEnvironment: Unrecognized file extension: ' + files) const gl = useThree((state) => state.gl) @@ -57,26 +53,23 @@ export function useEnvironment({ if (extension !== 'webp' && extension !== 'jpg' && extension !== 'jpeg') return function clearGainmapTexture() { - useLoader.clear( - // @ts-expect-error - loader, - multiFile ? [files] : files - ) + useLoader.clear(loader!, (multiFile ? [files] : files) as string | string[] | string[][]) } gl.domElement.addEventListener('webglcontextlost', clearGainmapTexture, { once: true }) }, [files, gl.domElement]) const loaderResult: Texture | Texture[] = useLoader( - // @ts-expect-error loader, - multiFile ? [files] : files, + (multiFile ? [files] : files) as string | string[] | string[][], (loader) => { // Gainmap requires a renderer if (extension === 'webp' || extension === 'jpg' || extension === 'jpeg') { + // @ts-expect-error loader.setRenderer(gl) } loader.setPath?.(path) + // @ts-expect-error if (extensions) extensions(loader) } ) as Texture | Texture[] @@ -91,8 +84,7 @@ export function useEnvironment({ texture.mapping = isCubemap ? CubeReflectionMapping : EquirectangularReflectionMapping - if ('colorSpace' in texture) (texture as any).colorSpace = (encoding ?? isCubemap) ? 'srgb' : 'srgb-linear' - else (texture as any).encoding = (encoding ?? isCubemap) ? sRGBEncoding : LinearEncoding + texture.colorSpace = colorSpace ?? (isCubemap ? 'srgb' : 'srgb-linear') return texture } @@ -125,15 +117,11 @@ useEnvironment.preload = (preloadOptions?: EnvironmentLoaderPreloadOptions) => { const loader = getLoader(extension) if (!loader) throw new Error('useEnvironment: Unrecognized file extension: ' + files) - useLoader.preload( + useLoader.preload(loader, isArray(files) ? [files] : files, (loader) => { + loader.setPath?.(path) // @ts-expect-error - loader, - isArray(files) ? [files] : files, - (loader) => { - loader.setPath?.(path) - if (extensions) extensions(loader) - } - ) + if (extensions) extensions(loader) + }) } type EnvironmentLoaderClearOptions = Pick @@ -155,11 +143,7 @@ useEnvironment.clear = (clearOptions?: EnvironmentLoaderClearOptions) => { const { extension } = getExtension(files) const loader = getLoader(extension) if (!loader) throw new Error('useEnvironment: Unrecognized file extension: ' + files) - useLoader.clear( - // @ts-expect-error - loader, - isArray(files) ? [files] : files - ) + useLoader.clear(loader, isArray(files) ? [files] : files) } function validatePreset(preset: string) { @@ -188,7 +172,7 @@ function getExtension(files: string | string[]) { } function getLoader(extension: string | undefined) { - const loader: typeof Loader | null = + const loader = extension === 'cube' ? CubeTextureLoader : extension === 'hdr' @@ -196,9 +180,9 @@ function getLoader(extension: string | undefined) { : extension === 'exr' ? EXRLoader : extension === 'jpg' || extension === 'jpeg' - ? (HDRJPGLoader as unknown as typeof Loader) + ? HDRJPGLoader : extension === 'webp' - ? (GainMapLoader as unknown as typeof Loader) + ? GainMapLoader : null return loader diff --git a/src/helpers/deprecated.ts b/src/helpers/deprecated.ts index ba33ef62d..e30f8f915 100644 --- a/src/helpers/deprecated.ts +++ b/src/helpers/deprecated.ts @@ -5,21 +5,7 @@ import * as THREE from 'three' */ export const setUpdateRange = ( attribute: THREE.BufferAttribute, - updateRange: { offset: number; count: number } + updateRange: { start: number; count: number } ): void => { - if ('updateRanges' in attribute) { - // r159 - // @ts-ignore - attribute.updateRanges[0] = updateRange - } else { - attribute.updateRange = updateRange - } + attribute.updateRanges[0] = updateRange } - -export const LinearEncoding = 3000 -export const sRGBEncoding = 3001 - -/** - * TextureEncoding was deprecated in r152, and removed in r162. - */ -export type TextureEncoding = typeof LinearEncoding | typeof sRGBEncoding diff --git a/src/helpers/ts-utils.tsx b/src/helpers/ts-utils.tsx index 49806d329..ee706ee9a 100644 --- a/src/helpers/ts-utils.tsx +++ b/src/helpers/ts-utils.tsx @@ -10,3 +10,6 @@ export type NamedArrayTuple any> = Parameters * file. */ export type ForwardRefComponent = ForwardRefExoticComponent & RefAttributes> + +export type NonFunctionKeys = { [K in keyof T]-?: T[K] extends Function ? never : K }[keyof T] +export type Overwrite = Omit> & O diff --git a/src/helpers/useEffectfulState.tsx b/src/helpers/useEffectfulState.tsx index 67846e51a..27796fd6b 100644 --- a/src/helpers/useEffectfulState.tsx +++ b/src/helpers/useEffectfulState.tsx @@ -1,6 +1,6 @@ import * as React from 'react' -type RefType = React.MutableRefObject | ((state: T) => void) +type RefType = React.RefObject | ((state: T) => void) function call(ref: RefType | undefined, value: T | null) { if (typeof ref === 'function') ref(value as T) diff --git a/src/materials/BlurPass.tsx b/src/materials/BlurPass.tsx index cc9b00b7b..50943c4e0 100644 --- a/src/materials/BlurPass.tsx +++ b/src/materials/BlurPass.tsx @@ -55,7 +55,6 @@ export class BlurPass { this.convolutionMaterial.setTexelSize(1.0 / width, 1.0 / height) this.convolutionMaterial.setResolution(new Vector2(width, height)) this.scene = new Scene() - // @ts-expect-error fixed in r154 this.camera = new Camera() this.convolutionMaterial.uniforms.minDepthThreshold.value = minDepthThreshold this.convolutionMaterial.uniforms.maxDepthThreshold.value = maxDepthThreshold diff --git a/src/materials/MeshReflectorMaterial.tsx b/src/materials/MeshReflectorMaterial.tsx index cd01b86a3..be7f21c0b 100644 --- a/src/materials/MeshReflectorMaterial.tsx +++ b/src/materials/MeshReflectorMaterial.tsx @@ -230,20 +230,3 @@ export class MeshReflectorMaterial extends MeshStandardMaterial { this._mixContrast.value = v } } - -export type MeshReflectorMaterialProps = { - mixBlur: number - mixStrength: number - mirror: number - textureMatrix: Matrix4 - tDiffuse: Texture - distortionMap?: Texture - tDiffuseBlur: Texture - hasBlur: boolean - minDepthThreshold: number - maxDepthThreshold: number - depthScale: number - depthToBlurRatioBias: number - distortion: number - mixContrast: number -} & JSX.IntrinsicElements['meshStandardMaterial'] diff --git a/src/web/CycleRaycast.tsx b/src/web/CycleRaycast.tsx index 73774b072..e81f31664 100644 --- a/src/web/CycleRaycast.tsx +++ b/src/web/CycleRaycast.tsx @@ -7,7 +7,7 @@ export type CycleRaycastProps = { preventDefault?: boolean scroll?: boolean keyCode?: number - portal?: React.MutableRefObject + portal?: React.RefObject } export function CycleRaycast({ diff --git a/src/web/FaceControls.tsx b/src/web/FaceControls.tsx index 01a8c2c97..3e42b8bd9 100644 --- a/src/web/FaceControls.tsx +++ b/src/web/FaceControls.tsx @@ -13,7 +13,7 @@ import { RefObject, createContext, useContext, - ElementRef, + ComponentRef, } from 'react' import { useFrame, useThree } from '@react-three/fiber' import type { FaceLandmarker, FaceLandmarkerResult } from '@mediapipe/tasks-vision' @@ -217,7 +217,7 @@ export const FaceControls = /* @__PURE__ */ forwardRef>(null) + const videoTextureRef = useRef>(null) const [_faceLandmarkerResult, setFaceLandmarkerResult] = useState() const faceLandmarker = useFaceLandmarker() diff --git a/src/web/FaceLandmarker.tsx b/src/web/FaceLandmarker.tsx index 68c29f5bd..c680784ed 100644 --- a/src/web/FaceLandmarker.tsx +++ b/src/web/FaceLandmarker.tsx @@ -6,7 +6,7 @@ import { clear, suspend } from 'suspend-react' const FaceLandmarkerContext = /* @__PURE__ */ createContext({} as FaceLandmarkerImpl | undefined) -type FaceLandmarkerProps = { +export type FaceLandmarkerProps = { basePath?: string options?: FaceLandmarkerOptions children?: ReactNode diff --git a/src/web/Facemesh.tsx b/src/web/Facemesh.tsx index d4de6e82c..a0874f139 100644 --- a/src/web/Facemesh.tsx +++ b/src/web/Facemesh.tsx @@ -1,7 +1,7 @@ /* eslint react-hooks/exhaustive-deps: 1 */ import * as React from 'react' import * as THREE from 'three' -import { useThree } from '@react-three/fiber' +import { ThreeElements, useThree } from '@react-three/fiber' import { Line } from '../core/Line' @@ -40,7 +40,7 @@ export type FacemeshProps = { eyesAsOrigin?: boolean /** debug mode, default: false */ debug?: boolean -} & Omit +} & Omit export type FacemeshApi = { meshRef: React.RefObject @@ -266,12 +266,13 @@ export const Facemesh = /* @__PURE__ */ React.forwardRef( - () => ({ - outerRef, - meshRef, - eyeRightRef, - eyeLeftRef, - }), + () => + ({ + outerRef, + meshRef, + eyeRightRef, + eyeLeftRef, + }) as FacemeshApi, [] ) React.useImperativeHandle(fref, () => api, [api]) @@ -433,12 +434,13 @@ export const FacemeshEye = /* @__PURE__ */ React.forwardRef( - () => ({ - eyeMeshRef: eyeMeshRef, - irisDirRef: irisDirRef, - _computeSphere, - _update, - }), + () => + ({ + eyeMeshRef: eyeMeshRef, + irisDirRef: irisDirRef, + _computeSphere, + _update, + }) as FacemeshEyeApi, [_computeSphere, _update] ) React.useImperativeHandle(fref, () => api, [api]) diff --git a/src/web/Html.tsx b/src/web/Html.tsx index fde1f2598..8249fcb3a 100644 --- a/src/web/Html.tsx +++ b/src/web/Html.tsx @@ -14,7 +14,7 @@ import { Mesh, } from 'three' import { Assign } from 'utility-types' -import { ReactThreeFiber, useFrame, useThree } from '@react-three/fiber' +import { ThreeElements, useFrame, useThree } from '@react-three/fiber' import { ForwardRefComponent } from '../helpers/ts-utils' const v1 = /* @__PURE__ */ new Vector3() @@ -117,13 +117,12 @@ function isRefObject(ref: any): ref is React.RefObject { return ref && typeof ref === 'object' && 'current' in ref } -export interface HtmlProps - extends Omit, ReactThreeFiber.Object3DNode>, 'ref'> { +export interface HtmlProps extends Omit, ThreeElements['group']>, 'ref'> { prepend?: boolean center?: boolean fullscreen?: boolean eps?: number - portal?: React.MutableRefObject + portal?: React.RefObject distanceFactor?: number sprite?: boolean transform?: boolean @@ -176,7 +175,7 @@ export const Html: ForwardRefComponent = /* @__PURE__ const { gl, camera, scene, size, raycaster, events, viewport } = useThree() const [el] = React.useState(() => document.createElement(as)) - const root = React.useRef() + const root = React.useRef(null) const group = React.useRef(null!) const oldZoom = React.useRef(0) const oldPosition = React.useRef([0, 0]) diff --git a/src/web/KeyboardControls.tsx b/src/web/KeyboardControls.tsx index fc2f6f9f9..d02b73320 100644 --- a/src/web/KeyboardControls.tsx +++ b/src/web/KeyboardControls.tsx @@ -34,7 +34,7 @@ export type KeyboardControlsEntry = { up?: boolean } -type KeyboardControlsProps = { +export type KeyboardControlsProps = { /** A map of named keys */ map: KeyboardControlsEntry[] /** All children will be able to useKeyboardControls */ diff --git a/src/web/PresentationControls.tsx b/src/web/PresentationControls.tsx index d2f1ffb8d..e306a068c 100644 --- a/src/web/PresentationControls.tsx +++ b/src/web/PresentationControls.tsx @@ -1,11 +1,11 @@ import * as React from 'react' import { MathUtils } from 'three' -import { useThree } from '@react-three/fiber' -import { a, SpringConfig, useSpring } from '@react-spring/three' +import { useFrame, useThree } from '@react-three/fiber' import { useGesture } from '@use-gesture/react' +import { easing } from 'maath' export type PresentationControlProps = { - snap?: Boolean | SpringConfig + snap?: Boolean | number global?: boolean cursor?: boolean speed?: number @@ -13,7 +13,7 @@ export type PresentationControlProps = { rotation?: [number, number, number] polar?: [number, number] azimuth?: [number, number] - config?: any + damping?: number enabled?: boolean children?: React.ReactNode domElement?: HTMLElement @@ -31,7 +31,7 @@ export function PresentationControls({ zoom = 1, polar = [0, Math.PI / 2], azimuth = [-Infinity, Infinity], - config = { mass: 1, tension: 170, friction: 26 }, + damping = 0.25, }: PresentationControlProps) { const events = useThree((state) => state.events) const gl = useThree((state) => state.gl) @@ -50,8 +50,7 @@ export function PresentationControls({ () => [MathUtils.clamp(rotation[0], ...rPolar), MathUtils.clamp(rotation[1], ...rAzimuth), rotation[2]], [rotation[0], rotation[1], rotation[2], rPolar, rAzimuth] ) - const [spring, api] = useSpring(() => ({ scale: 1, rotation: rInitial, config })) - React.useEffect(() => void api.start({ scale: 1, rotation: rInitial, config }), [rInitial]) + React.useEffect(() => { if (global && cursor && enabled) { explDomElement.style.cursor = 'grab' @@ -62,30 +61,36 @@ export function PresentationControls({ } } }, [global, cursor, explDomElement, enabled]) + + const [animation] = React.useState({ scale: 1, rotation: rInitial, damping }) + const ref = React.useRef(null!) + useFrame((state, delta) => { + easing.damp3(ref.current.scale, animation.scale, animation.damping, delta) + easing.dampE(ref.current.rotation, animation.rotation as any, animation.damping, delta) + }) + const bind = useGesture( { onHover: ({ last }) => { if (cursor && !global && enabled) explDomElement.style.cursor = last ? 'auto' : 'grab' }, - onDrag: ({ down, delta: [x, y], memo: [oldY, oldX] = spring.rotation.animation.to || rInitial }) => { + onDrag: ({ down, delta: [x, y], memo: [oldY, oldX] = animation.rotation || rInitial }) => { if (!enabled) return [y, x] if (cursor) explDomElement.style.cursor = down ? 'grabbing' : 'grab' x = MathUtils.clamp(oldX + (x / size.width) * Math.PI * speed, ...rAzimuth) y = MathUtils.clamp(oldY + (y / size.height) * Math.PI * speed, ...rPolar) - const sConfig = snap && !down && typeof snap !== 'boolean' ? snap : config - api.start({ - scale: down && y > rPolar[1] / 2 ? zoom : 1, - rotation: snap && !down ? rInitial : [y, x, 0], - config: (n) => (n === 'scale' ? { ...sConfig, friction: sConfig.friction * 3 } : sConfig), - }) + + animation.scale = down && y > rPolar[1] / 2 ? zoom : 1 + animation.rotation = snap && !down ? rInitial : [y, x, 0] + animation.damping = snap && !down && typeof snap !== 'boolean' ? (snap as number) : damping return [y, x] }, }, { target: global ? explDomElement : undefined } ) return ( - + {children} - + ) } diff --git a/src/web/ScrollControls.tsx b/src/web/ScrollControls.tsx index 7b09c6e40..374142798 100644 --- a/src/web/ScrollControls.tsx +++ b/src/web/ScrollControls.tsx @@ -271,7 +271,7 @@ interface ScrollPropsWithTrueHtml { style?: React.CSSProperties } -type ScrollProps = ScrollPropsWithFalseHtml | ScrollPropsWithTrueHtml +export type ScrollProps = ScrollPropsWithFalseHtml | ScrollPropsWithTrueHtml export const Scroll: ForwardRefComponent = /* @__PURE__ */ React.forwardRef( ({ html, ...props }: ScrollProps, ref) => { diff --git a/src/web/Select.tsx b/src/web/Select.tsx index 1d33eb8ff..2924f323c 100644 --- a/src/web/Select.tsx +++ b/src/web/Select.tsx @@ -1,12 +1,12 @@ import * as React from 'react' import * as THREE from 'three' import { SelectionBox } from 'three-stdlib' -import { useThree } from '@react-three/fiber' +import { ThreeElements, useThree } from '@react-three/fiber' import { shallow } from 'zustand/shallow' const context = /* @__PURE__ */ React.createContext([]) -type Props = JSX.IntrinsicElements['group'] & { +export type SelectProps = Omit & { /** Allow multi select, default: false */ multiple?: boolean /** Allow box select, default: false */ @@ -33,15 +33,17 @@ export function Select({ backgroundColor = 'rgba(75, 160, 255, 0.1)', filter: customFilter = (item) => item, ...props -}: Props) { +}: SelectProps) { const [downed, down] = React.useState(false) const { setEvents, camera, raycaster, gl, controls, size, get } = useThree() const [hovered, hover] = React.useState(false) const [active, dispatch] = React.useReducer( + // @ts-expect-error (state, { object, shift }: { object?: THREE.Object3D | THREE.Object3D[]; shift?: boolean }): THREE.Object3D[] => { if (object === undefined) return [] else if (Array.isArray(object)) return object else if (!shift) return state[0] === object ? [] : [object] + // @ts-expect-error else if (state.includes(object)) return state.filter((o) => o !== object) else return [object, ...state] }, diff --git a/src/web/View.tsx b/src/web/View.tsx index 3f3e78096..fd1b0817a 100644 --- a/src/web/View.tsx +++ b/src/web/View.tsx @@ -8,24 +8,11 @@ const isOrthographicCamera = (def: any): def is THREE.OrthographicCamera => const col = /* @__PURE__ */ new THREE.Color() const tracked = /* @__PURE__ */ tunnel() -/** - * In `@react-three/fiber` after `v8.0.0` but prior to `v8.1.0`, `state.size` contained only dimension - * information. After `v8.1.0`, position information (`top`, `left`) was added - * - * @todo remove this when drei supports v9 and up - */ -type LegacyCanvasSize = { - height: number - width: number -} - -type CanvasSize = LegacyCanvasSize & { +type CanvasSize = { top: number left: number -} - -function isNonLegacyCanvasSize(size: Record): size is CanvasSize { - return 'top' in size + height: number + width: number } export type ContainerProps = { @@ -34,12 +21,12 @@ export type ContainerProps = { index: number children?: React.ReactNode frames: number - rect: React.MutableRefObject + rect: React.RefObject /** * @deprecated You can use inline Views now, see: https://github.com/pmndrs/drei/pull/1784 */ - track?: React.MutableRefObject - canvasSize: LegacyCanvasSize | CanvasSize + track?: React.RefObject + canvasSize: CanvasSize } export type ViewProps = { @@ -62,26 +49,27 @@ export type ViewProps = { /** The tracking element, the view will be cut according to its whereabouts * @deprecated You can use inline Views now, see: https://github.com/pmndrs/drei/pull/1784 */ - track?: React.MutableRefObject + track?: React.RefObject } -function computeContainerPosition(canvasSize: LegacyCanvasSize | CanvasSize, trackRect: DOMRect) { +function computeContainerPosition(canvasSize: CanvasSize, trackRect: DOMRect) { const { right, top, left: trackLeft, bottom: trackBottom, width, height } = trackRect const isOffscreen = trackRect.bottom < 0 || top > canvasSize.height || right < 0 || trackRect.left > canvasSize.width - if (isNonLegacyCanvasSize(canvasSize)) { - const canvasBottom = canvasSize.top + canvasSize.height - const bottom = canvasBottom - trackBottom - const left = trackLeft - canvasSize.left - return { position: { width, height, left, top, bottom, right }, isOffscreen } - } - // Fall back on old behavior if r3f < 8.1.0 - const bottom = canvasSize.height - trackBottom - return { position: { width, height, top, left: trackLeft, bottom, right }, isOffscreen } + + const canvasBottom = canvasSize.top + canvasSize.height + const bottom = canvasBottom - trackBottom + const left = trackLeft - canvasSize.left + return { position: { width, height, left, top, bottom, right }, isOffscreen } } function prepareSkissor( state: RootState, - { left, bottom, width, height }: LegacyCanvasSize & { top: number; left: number } & { bottom: number; right: number } + { + left, + bottom, + width, + height, + }: { width: number; height: number; top: number; left: number; bottom: number; right: number } ) { let autoClear const aspect = width / height @@ -174,14 +162,6 @@ function Container({ visible = true, canvasSize, scene, index, children, frames, } }, [track]) - React.useEffect(() => { - if (isNonLegacyCanvasSize(canvasSize)) return - console.warn( - 'Detected @react-three/fiber canvas size does not include position information. may not work as expected. ' + - 'Upgrade to @react-three/fiber ^8.1.0 for support.\n See https://github.com/pmndrs/drei/issues/944' - ) - }, []) - return ( <> {children} @@ -287,7 +267,7 @@ const HtmlView = /* @__PURE__ */ React.forwardRef( } ) -export type ViewportProps = { Port: () => JSX.Element } & React.ForwardRefExoticComponent< +export type ViewportProps = { Port: () => React.JSX.Element } & React.ForwardRefExoticComponent< ViewProps & React.RefAttributes > diff --git a/src/web/pivotControls/AxisArrow.tsx b/src/web/pivotControls/AxisArrow.tsx index ef9ae8c87..c0f295215 100644 --- a/src/web/pivotControls/AxisArrow.tsx +++ b/src/web/pivotControls/AxisArrow.tsx @@ -58,8 +58,7 @@ export const AxisArrow: React.FC<{ direction: THREE.Vector3; axis: 0 | 1 | 2 }> userData, } = React.useContext(context) - // @ts-expect-error new in @react-three/fiber@7.0.5 - const camControls = useThree((state) => state.controls) as { enabled: boolean } + const camControls = useThree((state) => state.controls) as unknown as { enabled: boolean } | undefined const divRef = React.useRef(null!) const objRef = React.useRef(null!) const clickInfo = React.useRef<{ clickPoint: THREE.Vector3; dir: THREE.Vector3 } | null>(null) diff --git a/src/web/pivotControls/AxisRotator.tsx b/src/web/pivotControls/AxisRotator.tsx index 35c19bd05..6e180b4c8 100644 --- a/src/web/pivotControls/AxisRotator.tsx +++ b/src/web/pivotControls/AxisRotator.tsx @@ -79,8 +79,7 @@ export const AxisRotator: React.FC<{ dir1: THREE.Vector3; dir2: THREE.Vector3; a userData, } = React.useContext(context) - // @ts-expect-error new in @react-three/fiber@7.0.5 - const camControls = useThree((state) => state.controls) as { enabled: boolean } + const camControls = useThree((state) => state.controls) as unknown as { enabled: boolean } | undefined const divRef = React.useRef(null!) const objRef = React.useRef(null!) const angle0 = React.useRef(0) diff --git a/src/web/pivotControls/PlaneSlider.tsx b/src/web/pivotControls/PlaneSlider.tsx index 7d35d6cac..e6be25312 100644 --- a/src/web/pivotControls/PlaneSlider.tsx +++ b/src/web/pivotControls/PlaneSlider.tsx @@ -54,8 +54,7 @@ export const PlaneSlider: React.FC<{ dir1: THREE.Vector3; dir2: THREE.Vector3; a userData, } = React.useContext(context) - // @ts-expect-error new in @react-three/fiber@7.0.5 - const camControls = useThree((state) => state.controls) as { enabled: boolean } + const camControls = useThree((state) => state.controls) as unknown as { enabled: boolean } | undefined const divRef = React.useRef(null!) const objRef = React.useRef(null!) const clickInfo = React.useRef<{ diff --git a/src/web/pivotControls/ScalingSphere.tsx b/src/web/pivotControls/ScalingSphere.tsx index 82f9636ed..5ef397470 100644 --- a/src/web/pivotControls/ScalingSphere.tsx +++ b/src/web/pivotControls/ScalingSphere.tsx @@ -60,8 +60,7 @@ export const ScalingSphere: React.FC<{ direction: THREE.Vector3; axis: 0 | 1 | 2 } = React.useContext(context) const size = useThree((state) => state.size) - // @ts-expect-error new in @react-three/fiber@7.0.5 - const camControls = useThree((state) => state.controls) as { enabled: boolean } + const camControls = useThree((state) => state.controls) as unknown as { enabled: boolean } | undefined const divRef = React.useRef(null!) const objRef = React.useRef(null!) const meshRef = React.useRef(null!) diff --git a/src/web/pivotControls/index.tsx b/src/web/pivotControls/index.tsx index 57b682cff..e6e78da56 100644 --- a/src/web/pivotControls/index.tsx +++ b/src/web/pivotControls/index.tsx @@ -32,7 +32,7 @@ const xDir = /* @__PURE__ */ new THREE.Vector3(1, 0, 0) const yDir = /* @__PURE__ */ new THREE.Vector3(0, 1, 0) const zDir = /* @__PURE__ */ new THREE.Vector3(0, 0, 1) -type PivotControlsProps = { +export type PivotControlsProps = { /** Enables/disables the control, true */ enabled?: boolean /** Scale of the gizmo, 1 */ diff --git a/test/e2e/e2e.sh b/test/e2e/e2e.sh index 87f212953..d5ba3b9ff 100755 --- a/test/e2e/e2e.sh +++ b/test/e2e/e2e.sh @@ -39,7 +39,7 @@ appdir="$tmp/$appname" (cd $tmp; npm create -y vite@latest $appname -- --template react-ts) # drei -(cd $appdir; npm i; npm i $TGZ) +(cd $appdir; npm i @types/react@latest @types/react-dom@latest react@latest react-dom@latest @react-three/fiber@rc $TGZ) # App.tsx cp App.tsx $appdir/src/App.tsx @@ -62,7 +62,7 @@ appname=nextapp appdir="$tmp/$appname" # create app -(cd $tmp; npx -y create-next-app@14 $appname --ts --no-eslint --no-tailwind --no-src-dir --app --import-alias "@/*") +(cd $tmp; npx -y create-next-app@latest $appname --ts --no-eslint --no-tailwind --no-src-dir --app --import-alias "@/*") # drei (cd $appdir; npm i $TGZ) @@ -89,7 +89,7 @@ appname=cjsapp appdir="$tmp/$appname" # create app -(cd $tmp; npx -y create-next-app@14 $appname --ts --no-eslint --no-tailwind --no-src-dir --app --import-alias "@/*") +(cd $tmp; npx -y create-next-app@latest $appname --ts --no-eslint --no-tailwind --no-src-dir --app --import-alias "@/*") # drei (cd $appdir; npm i $TGZ) diff --git a/yarn.lock b/yarn.lock index 25ac0abf5..06c14e313 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2753,73 +2753,6 @@ __metadata: languageName: node linkType: hard -"@react-spring/animated@npm:~9.7.5": - version: 9.7.5 - resolution: "@react-spring/animated@npm:9.7.5" - dependencies: - "@react-spring/shared": "npm:~9.7.5" - "@react-spring/types": "npm:~9.7.5" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/f8c2473c60f39a878c7dd0fdfcfcdbc720521e1506aa3f63c9de64780694a0a73d5ccc535a5ccec3520ddb70a71cf43b038b32c18e99531522da5388c510ecd7 - languageName: node - linkType: hard - -"@react-spring/core@npm:~9.7.5": - version: 9.7.5 - resolution: "@react-spring/core@npm:9.7.5" - dependencies: - "@react-spring/animated": "npm:~9.7.5" - "@react-spring/shared": "npm:~9.7.5" - "@react-spring/types": "npm:~9.7.5" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/5bfd83dfe248cd91889f215f015d908c7714ef445740fd5afa054b27ebc7d5a456abf6c309e2459d9b5b436e78d6fda16b62b9601f96352e9130552c02270830 - languageName: node - linkType: hard - -"@react-spring/rafz@npm:~9.7.5": - version: 9.7.5 - resolution: "@react-spring/rafz@npm:9.7.5" - checksum: 10c0/8bdad180feaa9a0e870a513043a5e98a4e9b7292a9f887575b7e6fadab2677825bc894b7ff16c38511b35bfe6cc1072df5851c5fee64448d67551559578ca759 - languageName: node - linkType: hard - -"@react-spring/shared@npm:~9.7.5": - version: 9.7.5 - resolution: "@react-spring/shared@npm:9.7.5" - dependencies: - "@react-spring/rafz": "npm:~9.7.5" - "@react-spring/types": "npm:~9.7.5" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/0207eacccdedd918a2fc55e78356ce937f445ce27ad9abd5d3accba8f9701a39349b55115641dc2b39bb9d3a155b058c185b411d292dc8cc5686bfa56f73b94f - languageName: node - linkType: hard - -"@react-spring/three@npm:~9.7.5": - version: 9.7.5 - resolution: "@react-spring/three@npm:9.7.5" - dependencies: - "@react-spring/animated": "npm:~9.7.5" - "@react-spring/core": "npm:~9.7.5" - "@react-spring/shared": "npm:~9.7.5" - "@react-spring/types": "npm:~9.7.5" - peerDependencies: - "@react-three/fiber": ">=6.0" - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - three: ">=0.126" - checksum: 10c0/793f2c27a55060b015880e661ede045f240e4be9b85f448616fb0f8f282bed21d08fb2389a70c73addffbdb868c38bc26d8c6f6f55dd2b0ef67ba6c5a11a55d9 - languageName: node - linkType: hard - -"@react-spring/types@npm:~9.7.5": - version: 9.7.5 - resolution: "@react-spring/types@npm:9.7.5" - checksum: 10c0/85c05121853cacb64f7cf63a4855e9044635e1231f70371cd7b8c78bc10be6f4dd7c68f592f92a2607e8bb68051540989b4677a2ccb525dba937f5cd95dc8bc1 - languageName: node - linkType: hard - "@react-three/drei@workspace:.": version: 0.0.0-use.local resolution: "@react-three/drei@workspace:." @@ -2840,8 +2773,7 @@ __metadata: "@mediapipe/tasks-vision": "npm:0.10.17" "@monogrid/gainmap-js": "npm:^3.0.6" "@playwright/test": "npm:^1.45.2" - "@react-spring/three": "npm:~9.7.5" - "@react-three/fiber": "npm:^8.0.8" + "@react-three/fiber": "npm:9.0.0-rc.4" "@rollup/plugin-babel": "npm:^5.3.0" "@rollup/plugin-commonjs": "npm:^19.0.0" "@rollup/plugin-json": "npm:^4.1.0" @@ -2854,9 +2786,9 @@ __metadata: "@storybook/react": "npm:^8.4.4" "@storybook/react-vite": "npm:^8.4.4" "@storybook/theming": "npm:^8.4.4" - "@types/react": "npm:^18.0.0" - "@types/react-dom": "npm:^18.0.0" - "@types/three": "npm:^0.151.0" + "@types/react": "npm:^19.0.2" + "@types/react-dom": "npm:^19.0.2" + "@types/three": "npm:^0.159.0" "@typescript-eslint/eslint-plugin": "npm:^8.15.0" "@typescript-eslint/parser": "npm:^8.15.0" "@use-gesture/react": "npm:^10.3.1" @@ -2880,9 +2812,8 @@ __metadata: meshline: "npm:^3.3.1" prettier: "npm:^3.3.3" pretty-quick: "npm:^4.0.0" - react: "npm:^18.0.0" - react-composer: "npm:^5.0.3" - react-dom: "npm:^18.0.0" + react: "npm:^19.0.0" + react-dom: "npm:^19.0.0" rimraf: "npm:^6.0.1" rollup: "npm:^2.79.2" rollup-plugin-glslify: "npm:^1.3.0" @@ -2894,50 +2825,54 @@ __metadata: stats.js: "npm:^0.17.0" storybook: "npm:^8.4.4" suspend-react: "npm:^0.1.3" - three: "npm:^0.151.0" - three-mesh-bvh: "npm:^0.7.8" + three: "npm:^0.159.0" + three-mesh-bvh: "npm:^0.8.3" three-stdlib: "npm:^2.35.6" troika-three-text: "npm:^0.52.0" ts-node: "npm:^10.9.2" tunnel-rat: "npm:^0.1.2" typescript: "npm:^5.6.3" + use-sync-external-store: "npm:^1.4.0" utility-types: "npm:^3.11.0" vite: "npm:^5.4.11" vite-plugin-glslify: "npm:^2.1.0" zustand: "npm:^5.0.1" peerDependencies: - "@react-three/fiber": ^8 - react: ^18 - react-dom: ^18 - three: ">=0.137" + "@react-three/fiber": 9.0.0-rc.4 + react: ^19 + react-dom: ^19 + three: ">=0.159" peerDependenciesMeta: react-dom: optional: true languageName: unknown linkType: soft -"@react-three/fiber@npm:^8.0.8": - version: 8.14.2 - resolution: "@react-three/fiber@npm:8.14.2" +"@react-three/fiber@npm:9.0.0-rc.4": + version: 9.0.0-rc.4 + resolution: "@react-three/fiber@npm:9.0.0-rc.4" dependencies: "@babel/runtime": "npm:^7.17.8" - "@types/react-reconciler": "npm:^0.26.7" + "@types/debounce": "npm:^1.2.1" + "@types/react-reconciler": "npm:^0.28.8" + "@types/webxr": "npm:*" base64-js: "npm:^1.5.1" - its-fine: "npm:^1.0.6" - react-reconciler: "npm:^0.27.0" - react-use-measure: "npm:^2.1.1" - scheduler: "npm:^0.21.0" + buffer: "npm:^6.0.3" + debounce: "npm:^1.2.1" + its-fine: "npm:^1.2.5" + react-reconciler: "npm:0.31.0" + scheduler: "npm:0.25.0" suspend-react: "npm:^0.1.3" - zustand: "npm:^3.7.1" + zustand: "npm:^4.1.2" peerDependencies: expo: ">=43.0" expo-asset: ">=8.4" expo-file-system: ">=11.0" expo-gl: ">=11.0" - react: ">=18.0" - react-dom: ">=18.0" - react-native: ">=0.64" - three: ">=0.133" + react: ^19.0.0 + react-dom: ^19.0.0 + react-native: ">=0.78" + three: ">=0.156" peerDependenciesMeta: expo: optional: true @@ -2951,7 +2886,7 @@ __metadata: optional: true react-native: optional: true - checksum: 10c0/ea4faa1bf036091cfba3bd516c730261563b03de814a175f7ca3fb0f66b6cd7f3385f0f91e56961069210fed6332984d2e6500a17887b6044a6b83f8c1f4ad83 + checksum: 10c0/1fe9d5e436b92360201d43c64bd206905a3e1f841c6310bd3253674d43fe6810e3872548dc7a5cc2d3ad3fb03972092cfa0b1d9872b51f4f1b2eac14cbd1c28a languageName: node linkType: hard @@ -3785,6 +3720,13 @@ __metadata: languageName: node linkType: hard +"@types/debounce@npm:^1.2.1": + version: 1.2.4 + resolution: "@types/debounce@npm:1.2.4" + checksum: 10c0/89db97397312b1273be74b326133af4744e5f63b726016bb6eb5c31b94f07a2f3bd3fb5bc3a3667bce506f01ede46c9abbfb88a4745ee9f34641fb636ab51210 + languageName: node + linkType: hard + "@types/doctrine@npm:^0.0.9": version: 0.0.9 resolution: "@types/doctrine@npm:0.0.9" @@ -3879,21 +3821,12 @@ __metadata: languageName: node linkType: hard -"@types/react-dom@npm:^18.0.0": - version: 18.2.18 - resolution: "@types/react-dom@npm:18.2.18" - dependencies: - "@types/react": "npm:*" - checksum: 10c0/74dba11a1b8156f3a763f3fca1fb4ec1dcd349153279b8bf79210024a69f994bf2cf0728198c047f8130c5318420ea56281b0a4ef84c8ae943cd9a0cac705220 - languageName: node - linkType: hard - -"@types/react-reconciler@npm:^0.26.7": - version: 0.26.7 - resolution: "@types/react-reconciler@npm:0.26.7" - dependencies: - "@types/react": "npm:*" - checksum: 10c0/ed01a9cdc81db41f244552a16a7b168acb32f2f11f7529b020a9a65fae77a12c5f51bbf62e147955c453a41582a5eceae5bedebbc022e9d99a2714d3cb68df21 +"@types/react-dom@npm:^19.0.2": + version: 19.0.2 + resolution: "@types/react-dom@npm:19.0.2" + peerDependencies: + "@types/react": ^19.0.0 + checksum: 10c0/3d0c7b78dbe8df64ea769f30af990a5950173a8321c745fe11094d765423f7964c3519dca6e7cd36b4be6521c8efc690bdd3b79b327b229dd1e9d5a8bad677dd languageName: node linkType: hard @@ -3906,6 +3839,15 @@ __metadata: languageName: node linkType: hard +"@types/react-reconciler@npm:^0.28.8": + version: 0.28.9 + resolution: "@types/react-reconciler@npm:0.28.9" + peerDependencies: + "@types/react": "*" + checksum: 10c0/9fe71fa856aab2cd4742fe0416bdd4f5c82ecc05ef6451ee7fcb65a5efdf5fa588f5820fbe2a665b15371b0da3bfc4097f28bb6d450b9a834af2d0fc00f403bd + languageName: node + linkType: hard + "@types/react@npm:*": version: 18.2.21 resolution: "@types/react@npm:18.2.21" @@ -3917,14 +3859,12 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:^18.0.0": - version: 18.2.48 - resolution: "@types/react@npm:18.2.48" +"@types/react@npm:^19.0.2": + version: 19.0.4 + resolution: "@types/react@npm:19.0.4" dependencies: - "@types/prop-types": "npm:*" - "@types/scheduler": "npm:*" csstype: "npm:^3.0.2" - checksum: 10c0/7e89f18ea2928b1638f564b156d692894dcb9352a7e0a807873c97e858abe1f23dbd165a25dd088a991344e973fdeef88ba5724bfb64504b74072cbc9c220c3a + checksum: 10c0/96ecd1a73af57fd7b7facf5b36ec069b131c7608a98a0f1098183023bfb21c60a26a0dc09004fbe0ac70c436ef887bbec5690882cfb77c6e0c679f7e45987722 languageName: node linkType: hard @@ -3965,15 +3905,15 @@ __metadata: languageName: node linkType: hard -"@types/three@npm:^0.151.0": - version: 0.151.0 - resolution: "@types/three@npm:0.151.0" +"@types/three@npm:^0.159.0": + version: 0.159.0 + resolution: "@types/three@npm:0.159.0" dependencies: "@types/stats.js": "npm:*" "@types/webxr": "npm:*" - fflate: "npm:~0.6.9" - lil-gui: "npm:~0.17.0" - checksum: 10c0/0a43947a7b0dbdeef6372091fe612c0f66678950aa735f8f806dec314f9ab5403ab412d47c4a4ab501caa9e8b9493d64c65f7fe8bfacffa91844238ce7fba8dc + fflate: "npm:~0.6.10" + meshoptimizer: "npm:~0.18.1" + checksum: 10c0/c5ec776ba213d148f0ebcce07b7a31514b8676d20e3d19458ba46d286c87c9d95224ee9225872d18c771110563c712f0237ec5d2264ad7ab7b47f2d4c1fd93a9 languageName: node linkType: hard @@ -4756,7 +4696,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.5.1": +"base64-js@npm:^1.3.1, base64-js@npm:^1.5.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf @@ -4932,6 +4872,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.2.1" + checksum: 10c0/2a905fbbcde73cc5d8bd18d1caa23715d5f83a5935867c2329f0ac06104204ba7947be098fe1317fbd8830e26090ff8e764f08cd14fefc977bb248c3487bcbd0 + languageName: node + linkType: hard + "builtin-modules@npm:^3.3.0": version: 3.3.0 resolution: "builtin-modules@npm:3.3.0" @@ -6703,7 +6653,7 @@ __metadata: languageName: node linkType: hard -"fflate@npm:^0.6.9, fflate@npm:~0.6.9": +"fflate@npm:^0.6.9, fflate@npm:~0.6.10": version: 0.6.10 resolution: "fflate@npm:0.6.10" checksum: 10c0/c452f720e4cb404b300fceb8ef0bdf345f18fbfd3f57f7d0974dce5f5e2ac0e8dd4b6ff4bb7061ae74fd919b9c707172f9dbd44d91149b1137199b8c705768f1 @@ -7633,6 +7583,13 @@ __metadata: languageName: node linkType: hard +"ieee754@npm:^1.2.1": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + "ignore-walk@npm:^7.0.0": version: 7.0.0 resolution: "ignore-walk@npm:7.0.0" @@ -8280,14 +8237,14 @@ __metadata: languageName: node linkType: hard -"its-fine@npm:^1.0.6": - version: 1.1.1 - resolution: "its-fine@npm:1.1.1" +"its-fine@npm:^1.2.5": + version: 1.2.5 + resolution: "its-fine@npm:1.2.5" dependencies: "@types/react-reconciler": "npm:^0.28.0" peerDependencies: react: ">=18.0" - checksum: 10c0/66059a859720de19563a8abdb10f06eb6117df1897d984a54b0a71ab9dc02da3dc366428b1f0c5e085740962abb7d6ccc0b8c3e00725b0a2120c846f352adffc + checksum: 10c0/5618955ac7ae0d7788580186f91ac9989301ead2c87250883258cd7ee32f9a4e08574728506d1deb90477090d7a688ba19571126aae9fb25661e90057dd772df languageName: node linkType: hard @@ -8694,13 +8651,6 @@ __metadata: languageName: node linkType: hard -"lil-gui@npm:~0.17.0": - version: 0.17.0 - resolution: "lil-gui@npm:0.17.0" - checksum: 10c0/889109145323be8496ac19b606b855447d33f937c6c2ad5c6730ca0e3333d1dcd2a9528c98d3e02fe54fdf946c582574a268a41b6fb599eccfcba740c4a84a44 - languageName: node - linkType: hard - "lines-and-columns@npm:^1.1.6": version: 1.2.4 resolution: "lines-and-columns@npm:1.2.4" @@ -10493,7 +10443,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.6.0, prop-types@npm:^15.8.1": +"prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -10564,17 +10514,6 @@ __metadata: languageName: node linkType: hard -"react-composer@npm:^5.0.3": - version: 5.0.3 - resolution: "react-composer@npm:5.0.3" - dependencies: - prop-types: "npm:^15.6.0" - peerDependencies: - react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - checksum: 10c0/6f0a62ce02f37d5b7b7959006b33eaa9c0949c404b25e78a3f19d8bdb7b31c8e30b836863601c3464dec847d45976e62274291ce60015892999094be1a0e7e03 - languageName: node - linkType: hard - "react-confetti@npm:^6.1.0": version: 6.1.0 resolution: "react-confetti@npm:6.1.0" @@ -10625,15 +10564,14 @@ __metadata: languageName: node linkType: hard -"react-dom@npm:^18.0.0": - version: 18.2.0 - resolution: "react-dom@npm:18.2.0" +"react-dom@npm:^19.0.0": + version: 19.0.0 + resolution: "react-dom@npm:19.0.0" dependencies: - loose-envify: "npm:^1.1.0" - scheduler: "npm:^0.23.0" + scheduler: "npm:^0.25.0" peerDependencies: - react: ^18.2.0 - checksum: 10c0/66dfc5f93e13d0674e78ef41f92ed21dfb80f9c4ac4ac25a4b51046d41d4d2186abc915b897f69d3d0ebbffe6184e7c5876f2af26bfa956f179225d921be713a + react: ^19.0.0 + checksum: 10c0/a36ce7ab507b237ae2759c984cdaad4af4096d8199fb65b3815c16825e5cfeb7293da790a3fc2184b52bfba7ba3ff31c058c01947aff6fd1a3701632aabaa6a9 languageName: node linkType: hard @@ -10644,15 +10582,14 @@ __metadata: languageName: node linkType: hard -"react-reconciler@npm:^0.27.0": - version: 0.27.0 - resolution: "react-reconciler@npm:0.27.0" +"react-reconciler@npm:0.31.0": + version: 0.31.0 + resolution: "react-reconciler@npm:0.31.0" dependencies: - loose-envify: "npm:^1.1.0" - scheduler: "npm:^0.21.0" + scheduler: "npm:^0.25.0" peerDependencies: - react: ^18.0.0 - checksum: 10c0/55daff215ff9544dc5b2ba6ecda20520df0f1b2ca145c00fd0905ced1b025f3779664715c36b511a8906aa24b8b433270b0032cd499c55c4e4c878cddc8fc986 + react: ^19.0.0 + checksum: 10c0/97920e1866c7206e200c3920c133c2e85f62a3c54fd9bc4b83c10c558d83d98eb378caab4fe37498e0cc1b1b2665d898627f2ae2537b29c8ab295ec8abc0c580 languageName: node linkType: hard @@ -10663,18 +10600,6 @@ __metadata: languageName: node linkType: hard -"react-use-measure@npm:^2.1.1": - version: 2.1.1 - resolution: "react-use-measure@npm:2.1.1" - dependencies: - debounce: "npm:^1.2.1" - peerDependencies: - react: ">=16.13" - react-dom: ">=16.13" - checksum: 10c0/77b035189dbd613f50014ae56cbfc1363a4eba5104f68f3bc09cbdd20719ae7fb42884e53328175c30b238215c5b8064c60098d70b3fa9b8d902db6ffb07c6a3 - languageName: node - linkType: hard - "react@npm:^16.8.0 || ^17.0.0 || ^18.0.0": version: 18.3.1 resolution: "react@npm:18.3.1" @@ -10684,12 +10609,10 @@ __metadata: languageName: node linkType: hard -"react@npm:^18.0.0": - version: 18.2.0 - resolution: "react@npm:18.2.0" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10c0/b562d9b569b0cb315e44b48099f7712283d93df36b19a39a67c254c6686479d3980b7f013dc931f4a5a3ae7645eae6386b4aa5eea933baa54ecd0f9acb0902b8 +"react@npm:^19.0.0": + version: 19.0.0 + resolution: "react@npm:19.0.0" + checksum: 10c0/9cad8f103e8e3a16d15cb18a0d8115d8bd9f9e1ce3420310aea381eb42aa0a4f812cf047bb5441349257a05fba8a291515691e3cb51267279b2d2c3253f38471 languageName: node linkType: hard @@ -11313,21 +11236,10 @@ __metadata: languageName: node linkType: hard -"scheduler@npm:^0.21.0": - version: 0.21.0 - resolution: "scheduler@npm:0.21.0" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10c0/083a9a0c83f4923f7f5bb28d8bcf13cff42c90f4303bc187166520fcfc576c97e946d426c707d5a9c0aa0a655819605dd0c741467c626824bbf191251c126f1b - languageName: node - linkType: hard - -"scheduler@npm:^0.23.0": - version: 0.23.0 - resolution: "scheduler@npm:0.23.0" - dependencies: - loose-envify: "npm:^1.1.0" - checksum: 10c0/b777f7ca0115e6d93e126ac490dbd82642d14983b3079f58f35519d992fa46260be7d6e6cede433a92db70306310c6f5f06e144f0e40c484199e09c1f7be53dd +"scheduler@npm:0.25.0, scheduler@npm:^0.25.0": + version: 0.25.0 + resolution: "scheduler@npm:0.25.0" + checksum: 10c0/a4bb1da406b613ce72c1299db43759526058fdcc413999c3c3e0db8956df7633acf395cb20eb2303b6a65d658d66b6585d344460abaee8080b4aa931f10eaafe languageName: node linkType: hard @@ -12202,12 +12114,12 @@ __metadata: languageName: node linkType: hard -"three-mesh-bvh@npm:^0.7.8": - version: 0.7.8 - resolution: "three-mesh-bvh@npm:0.7.8" +"three-mesh-bvh@npm:^0.8.3": + version: 0.8.3 + resolution: "three-mesh-bvh@npm:0.8.3" peerDependencies: - three: ">= 0.151.0" - checksum: 10c0/0d5f7fb4c9ae9a7477c394950a68bcd3eea88e3b0ea787dac4096cfdfcca2298268d1832575288d37ae0f19f3028f3ff79e7d70e3966bc2e8da51c95ba4209b1 + three: ">= 0.159.0" + checksum: 10c0/d1f052d4606498acf57e08e28d01c84157eb388d46be8e03bc839630dc4eb919d5bb5e2ed4e9113d39cbc3cda29f09212e3450e1bb21de6615ec330f2575fc28 languageName: node linkType: hard @@ -12227,10 +12139,10 @@ __metadata: languageName: node linkType: hard -"three@npm:^0.151.0": - version: 0.151.3 - resolution: "three@npm:0.151.3" - checksum: 10c0/892d2addd439e500b37669a86368c09a0a71dc404868b0347d48b5606ebd3264d2e9cce3cb85bc9ac83dc5cac8cc718ed0e9ef7cd22cad9f1233ea4c32108255 +"three@npm:^0.159.0": + version: 0.159.0 + resolution: "three@npm:0.159.0" + checksum: 10c0/e5250da9e5c5dab340c8ce68aae812d00e12a238e0f6f581c5dbec399aae30f35f7eec02953147a83365f8cdf58174e39b36c8dc1c26089b141ebdd5b86b9a9b languageName: node linkType: hard @@ -12843,6 +12755,15 @@ __metadata: languageName: node linkType: hard +"use-sync-external-store@npm:^1.2.2, use-sync-external-store@npm:^1.4.0": + version: 1.4.0 + resolution: "use-sync-external-store@npm:1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/ec011a5055962c0f6b509d6e78c0b143f8cd069890ae370528753053c55e3b360d3648e76cfaa854faa7a59eb08d6c5fb1015e60ffde9046d32f5b2a295acea5 + languageName: node + linkType: hard + "util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -13298,15 +13219,23 @@ __metadata: languageName: node linkType: hard -"zustand@npm:^3.7.1": - version: 3.7.2 - resolution: "zustand@npm:3.7.2" +"zustand@npm:^4.1.2": + version: 4.5.6 + resolution: "zustand@npm:4.5.6" + dependencies: + use-sync-external-store: "npm:^1.2.2" peerDependencies: + "@types/react": ">=16.8" + immer: ">=9.0.6" react: ">=16.8" peerDependenciesMeta: + "@types/react": + optional: true + immer: + optional: true react: optional: true - checksum: 10c0/6a56185ca67080c252dfe96039da02094cfd780bd7a45768708105f114dea39ae9abc80ffaa7f3f6104e6490db325bd443b857ab5eab8ebf9a697318cd163bb6 + checksum: 10c0/5b39aff2ef57e5a8ada647261ec1115697d397be311c51461d9ea81b5b63c6d2c498b960477ad2db72dc21db6aa229a92bdf644f6a8ecf7b1d71df5b4a5e95d3 languageName: node linkType: hard