import * as React from 'react' import type {PerspectiveCamera as PerspectiveCameraImpl, Object3D} from 'three' import {useFrame, useThree} from '@react-three/fiber' import mergeRefs from 'react-merge-refs' import {editable} from '../index' import {Vector3} from 'three' import {editorStore} from '../main/store' import type {MutableRefObject} from 'react' export type PerspectiveCameraProps = Omit< JSX.IntrinsicElements['perspectiveCamera'], 'lookAt' > & { lookAt?: | [number, number, number] | Vector3 | MutableRefObject makeDefault?: boolean manual?: boolean children?: React.ReactNode } export const PerspectiveCamera = editable( React.forwardRef( ({makeDefault, lookAt, ...props}: PerspectiveCameraProps, ref) => { const set = useThree(({set}) => set) const camera = useThree(({camera}) => camera) const size = useThree(({size}) => size) const cameraRef = React.useRef(null!) React.useLayoutEffect(() => { if (!props.manual) { cameraRef.current.aspect = size.width / size.height } }, [size, props]) 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]) useFrame(() => { if (lookAt && cameraRef.current) { cameraRef.current.lookAt( Array.isArray(lookAt) ? new Vector3(...lookAt) : (lookAt as MutableRefObject).current ? (lookAt as MutableRefObject).current.position : (lookAt as Vector3), ) // how could we make it possible for users to do something like this too? const snapshot = editorStore.getState().editablesSnapshot if (snapshot) { snapshot[ cameraRef.current.userData.__storeKey ].proxyObject?.rotation.copy(cameraRef.current.rotation) } } }) return }, ), 'perspectiveCamera', )