1- import * as THREE from 'three'
2- import React , {
3- useRef ,
1+ import { createPortal , useFrame , useThree , type Vector3 as R3FVector3 } from '@react-three/fiber'
2+ import { easing } from 'maath'
3+ import { CopyPass , DepthOfFieldEffect , DepthPickingPass } from 'postprocessing'
4+ import {
5+ Ref ,
6+ useCallback ,
47 useContext ,
5- useState ,
68 useEffect ,
7- useCallback ,
8- forwardRef ,
99 useImperativeHandle ,
10- RefObject ,
1110 useMemo ,
11+ useRef ,
12+ useState ,
13+ type ComponentProps ,
14+ type RefObject ,
1215} from 'react'
13- import { useThree , useFrame , createPortal , type Vector3 } from '@react-three/fiber'
14- import { CopyPass , DepthPickingPass , DepthOfFieldEffect } from 'postprocessing'
15- import { easing } from 'maath'
16+ import { Mesh , Vector3 } from 'three'
1617
17- import { DepthOfField } from './DepthOfField'
1818import { EffectComposerContext } from '../EffectComposer'
19+ import { DepthOfField } from './DepthOfField'
1920
20- export type AutofocusProps = React . ComponentProps < typeof DepthOfField > & {
21- target ?: Vector3
21+ export type AutofocusProps = ComponentProps < typeof DepthOfField > & {
22+ target ?: R3FVector3
2223 /** should the target follow the pointer */
2324 mouse ?: boolean
2425 /** size of the debug green point */
@@ -27,127 +28,131 @@ export type AutofocusProps = React.ComponentProps<typeof DepthOfField> & {
2728 manual ?: boolean
2829 /** approximate time to reach the target */
2930 smoothTime ?: number
31+ ref ?: Ref < AutofocusApi >
3032}
3133
3234export type AutofocusApi = {
3335 dofRef : RefObject < DepthOfFieldEffect | null >
34- hitpoint : THREE . Vector3
36+ hitpoint : Vector3
3537 update : ( delta : number , updateTarget : boolean ) => void
3638}
3739
38- export const Autofocus = /* @__PURE__ */ forwardRef < AutofocusApi , AutofocusProps > (
39- (
40- { target = undefined , mouse : followMouse = false , debug = undefined , manual = false , smoothTime = 0.25 , ...props } ,
41- fref
42- ) => {
43- const dofRef = useRef < DepthOfFieldEffect > ( null )
44- const hitpointRef = useRef < THREE . Mesh > ( null )
45- const targetRef = useRef < THREE . Mesh > ( null )
40+ export function Autofocus ( {
41+ target = undefined ,
42+ mouse : followMouse = false ,
43+ debug = undefined ,
44+ manual = false ,
45+ smoothTime = 0.25 ,
46+ ref,
47+ ...props
48+ } : AutofocusProps ) {
49+ const dofRef = useRef < DepthOfFieldEffect > ( null )
50+ const hitpointRef = useRef < Mesh > ( null )
51+ const targetRef = useRef < Mesh > ( null )
4652
47- const scene = useThree ( ( { scene } ) => scene )
48- const pointer = useThree ( ( { pointer } ) => pointer )
49- const { composer, camera } = useContext ( EffectComposerContext )
53+ const scene = useThree ( ( { scene } ) => scene )
54+ const pointer = useThree ( ( { pointer } ) => pointer )
55+ const { composer, camera } = useContext ( EffectComposerContext )
5056
51- // see: https://codesandbox.io/s/depthpickingpass-x130hg
52- const [ depthPickingPass ] = useState ( ( ) => new DepthPickingPass ( ) )
53- const [ copyPass ] = useState ( ( ) => new CopyPass ( ) )
54- useEffect ( ( ) => {
55- composer . addPass ( depthPickingPass )
56- composer . addPass ( copyPass )
57- return ( ) => {
58- composer . removePass ( depthPickingPass )
59- composer . removePass ( copyPass )
60- }
61- } , [ composer , depthPickingPass , copyPass ] )
57+ // see: https://codesandbox.io/s/depthpickingpass-x130hg
58+ const [ depthPickingPass ] = useState ( ( ) => new DepthPickingPass ( ) )
59+ const [ copyPass ] = useState ( ( ) => new CopyPass ( ) )
60+ useEffect ( ( ) => {
61+ composer . addPass ( depthPickingPass )
62+ composer . addPass ( copyPass )
63+ return ( ) => {
64+ composer . removePass ( depthPickingPass )
65+ composer . removePass ( copyPass )
66+ }
67+ } , [ composer , depthPickingPass , copyPass ] )
6268
63- useEffect ( ( ) => {
64- return ( ) => {
65- depthPickingPass . dispose ( )
66- copyPass . dispose ( )
67- }
68- } , [ depthPickingPass , copyPass ] )
69+ useEffect ( ( ) => {
70+ return ( ) => {
71+ depthPickingPass . dispose ( )
72+ copyPass . dispose ( )
73+ }
74+ } , [ depthPickingPass , copyPass ] )
6975
70- const [ hitpoint ] = useState ( ( ) => new THREE . Vector3 ( 0 , 0 , 0 ) )
76+ const [ hitpoint ] = useState ( ( ) => new Vector3 ( 0 , 0 , 0 ) )
7177
72- const [ ndc ] = useState ( ( ) => new THREE . Vector3 ( 0 , 0 , 0 ) )
73- const getHit = useCallback (
74- async ( x : number , y : number ) => {
75- ndc . x = x
76- ndc . y = y
77- ndc . z = await depthPickingPass . readDepth ( ndc )
78- ndc . z = ndc . z * 2.0 - 1.0
79- const hit = 1 - ndc . z > 0.0000001 // it is missed if ndc.z is close to 1
80- return hit ? ndc . unproject ( camera ) : false
81- } ,
82- [ ndc , depthPickingPass , camera ]
83- )
78+ const [ ndc ] = useState ( ( ) => new Vector3 ( 0 , 0 , 0 ) )
79+ const getHit = useCallback (
80+ async ( x : number , y : number ) => {
81+ ndc . x = x
82+ ndc . y = y
83+ ndc . z = await depthPickingPass . readDepth ( ndc )
84+ ndc . z = ndc . z * 2.0 - 1.0
85+ const hit = 1 - ndc . z > 0.0000001 // it is missed if ndc.z is close to 1
86+ return hit ? ndc . unproject ( camera ) : false
87+ } ,
88+ [ ndc , depthPickingPass , camera ]
89+ )
8490
85- const update = useCallback (
86- async ( delta : number , updateTarget = true ) => {
87- // Update hitpoint
88- if ( target ) {
89- hitpoint . set ( ...( target as [ number , number , number ] ) )
90- } else {
91- const { x, y } = followMouse ? pointer : { x : 0 , y : 0 }
92- const hit = await getHit ( x , y )
93- if ( hit ) hitpoint . copy ( hit )
94- }
91+ const update = useCallback (
92+ async ( delta : number , updateTarget = true ) => {
93+ // Update hitpoint
94+ if ( target ) {
95+ hitpoint . set ( ...( target as unknown as [ number , number , number ] ) )
96+ } else {
97+ const { x, y } = followMouse ? pointer : { x : 0 , y : 0 }
98+ const hit = await getHit ( x , y )
99+ if ( hit ) hitpoint . copy ( hit )
100+ }
95101
96- // Update target
97- if ( updateTarget && dofRef . current ?. target ) {
98- if ( smoothTime > 0 && delta > 0 ) {
99- easing . damp3 ( dofRef . current . target , hitpoint , smoothTime , delta )
100- } else {
101- dofRef . current . target . copy ( hitpoint )
102- }
102+ // Update target
103+ if ( updateTarget && dofRef . current ?. target ) {
104+ if ( smoothTime > 0 && delta > 0 ) {
105+ easing . damp3 ( dofRef . current . target , hitpoint , smoothTime , delta )
106+ } else {
107+ dofRef . current . target . copy ( hitpoint )
103108 }
104- } ,
105- [ target , hitpoint , followMouse , getHit , smoothTime , pointer ]
106- )
107-
108- useFrame ( async ( _ , delta ) => {
109- if ( ! manual ) {
110- update ( delta )
111- }
112- if ( hitpointRef . current ) {
113- hitpointRef . current . position . copy ( hitpoint )
114- }
115- if ( targetRef . current && dofRef . current ?. target ) {
116- targetRef . current . position . copy ( dofRef . current . target )
117109 }
118- } )
110+ } ,
111+ [ target , hitpoint , followMouse , getHit , smoothTime , pointer ]
112+ )
119113
120- // Ref API
121- const api = useMemo < AutofocusApi > (
122- ( ) => ( {
123- dofRef,
124- hitpoint,
125- update,
126- } ) ,
127- [ hitpoint , update ]
128- )
129- useImperativeHandle ( fref , ( ) => api , [ api ] )
114+ useFrame ( async ( _ , delta ) => {
115+ if ( ! manual ) {
116+ update ( delta )
117+ }
118+ if ( hitpointRef . current ) {
119+ hitpointRef . current . position . copy ( hitpoint )
120+ }
121+ if ( targetRef . current && dofRef . current ?. target ) {
122+ targetRef . current . position . copy ( dofRef . current . target )
123+ }
124+ } )
130125
131- return (
132- < >
133- { debug
134- ? createPortal (
135- < >
136- < mesh ref = { hitpointRef } >
137- < sphereGeometry args = { [ debug , 16 , 16 ] } />
138- < meshBasicMaterial color = "#00ff00" opacity = { 1 } transparent depthWrite = { false } />
139- </ mesh >
140- < mesh ref = { targetRef } >
141- < sphereGeometry args = { [ debug / 2 , 16 , 16 ] } />
142- < meshBasicMaterial color = "#00ff00" opacity = { 0.5 } transparent depthWrite = { false } />
143- </ mesh >
144- </ > ,
145- scene
146- )
147- : null }
126+ // Ref API
127+ const api = useMemo < AutofocusApi > (
128+ ( ) => ( {
129+ dofRef,
130+ hitpoint,
131+ update,
132+ } ) ,
133+ [ hitpoint , update ]
134+ )
135+ useImperativeHandle ( ref , ( ) => api , [ api ] )
148136
149- < DepthOfField ref = { dofRef } { ...props } target = { hitpoint } />
150- </ >
151- )
152- }
153- )
137+ return (
138+ < >
139+ { debug
140+ ? createPortal (
141+ < >
142+ < mesh ref = { hitpointRef } >
143+ < sphereGeometry args = { [ debug , 16 , 16 ] } />
144+ < meshBasicMaterial color = "#00ff00" opacity = { 1 } transparent depthWrite = { false } />
145+ </ mesh >
146+ < mesh ref = { targetRef } >
147+ < sphereGeometry args = { [ debug / 2 , 16 , 16 ] } />
148+ < meshBasicMaterial color = "#00ff00" opacity = { 0.5 } transparent depthWrite = { false } />
149+ </ mesh >
150+ </ > ,
151+ scene
152+ )
153+ : null }
154+
155+ < DepthOfField ref = { dofRef } { ...props } target = { hitpoint } />
156+ </ >
157+ )
158+ }
0 commit comments