import * as React from 'react' import { Object3D } from 'three' import { useThree, useFrame } from '@react-three/fiber' import { Falsey } from 'utility-types' type HelperType = Object3D & { update: () => void; dispose: () => void } type HelperConstructor = new (...args: any[]) => any type HelperArgs = T extends [infer _, ...infer R] ? R : never export function useHelper( object3D: React.MutableRefObject | Falsey, helperConstructor: T, ...args: HelperArgs> ) { const helper = React.useRef() const scene = useThree((state) => state.scene) React.useLayoutEffect(() => { let currentHelper: HelperType = undefined! if (object3D && object3D?.current && helperConstructor) { helper.current = currentHelper = new (helperConstructor as any)(object3D.current, ...args) } if (currentHelper) { // Prevent the helpers from blocking rays currentHelper.traverse((child) => (child.raycast = () => null)) scene.add(currentHelper) return () => { helper.current = undefined scene.remove(currentHelper) currentHelper.dispose?.() } } }, [scene, helperConstructor, object3D, ...args]) useFrame(() => void helper.current?.update?.()) return helper } // export type HelperProps = { type: T args?: HelperArgs> } export const Helper = ({ type: helperConstructor, args = [] as never, }: HelperProps) => { const thisRef = React.useRef(null!) const parentRef = React.useRef(null!) React.useLayoutEffect(() => { parentRef.current = thisRef.current.parent! }) useHelper(parentRef, helperConstructor, ...args) return }