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 */ samples?: number /** If set, the scene depth will be rendered into buffer.depthTexture. Default: false */ depth?: boolean // WebGLRenderTargetOptions => RenderTargetOptions wrapS?: THREE.Wrapping | undefined wrapT?: THREE.Wrapping | undefined magFilter?: THREE.MagnificationTextureFilter | undefined minFilter?: THREE.MinificationTextureFilter | undefined format?: number | undefined // RGBAFormat; type?: THREE.TextureDataType | undefined // UnsignedByteType; anisotropy?: number | undefined // 1; depthBuffer?: boolean | undefined // true; stencilBuffer?: boolean | undefined // false; generateMipmaps?: boolean | undefined // true; depthTexture?: THREE.DepthTexture | undefined encoding?: TextureEncoding | undefined colorSpace?: ColorSpace | undefined } // 👇 uncomment when TS version supports function overloads // export function useFBO(settings?: FBOSettings) export function useFBO( /** Width in pixels, or settings (will render fullscreen by default) */ width?: number | FBOSettings, /** Height in pixels */ height?: number, /**Settings */ settings?: FBOSettings ): THREE.WebGLRenderTarget { const size = useThree((state) => state.size) const viewport = useThree((state) => state.viewport) const _width = typeof width === 'number' ? width : size.width * viewport.dpr const _height = typeof height === 'number' ? height : size.height * viewport.dpr const _settings = (typeof width === 'number' ? settings : (width as FBOSettings)) || {} const { samples = 0, depth, ...targetSettings } = _settings const target = React.useMemo(() => { const target = new THREE.WebGLRenderTarget(_width, _height, { minFilter: THREE.LinearFilter, magFilter: THREE.LinearFilter, type: THREE.HalfFloatType, ...targetSettings, }) if (depth) { target.depthTexture = new THREE.DepthTexture(_width, _height, THREE.FloatType) } target.samples = samples return target }, []) React.useLayoutEffect(() => { target.setSize(_width, _height) if (samples) target.samples = samples }, [samples, target, _width, _height]) React.useEffect(() => { return () => target.dispose() }, []) return target } export const Fbo = ({ children, width, height, ...settings }: { children?: (target: ReturnType) => React.ReactNode width: Parameters[0] height: Parameters[1] } & FBOSettings) => { const target = useFBO(width, height, settings) return <>{children?.(target)} }