Rotating a gltf mesh based on mouse position drops the fps horribly

Hi,
Okay well, my scene is pretty optimized I’d say runs at 60fps when not animating.
but when I animate the mesh there is a sudden horrible drop in fps down to 7 fps and if I move mouse rigorously, the app crashes.

Here’s how I’m doing it. I’m managing the hover state and mouse position in a parent component and on mouse hover on canvas I’m doing all the required calculations and storing them in a state like so:

const handlePointerMove = (e: MouseEvent) => {
            // isMouse is set true when mouse hovers the canvas
		if (isMouse == true && canvas.current) {
			let w = canvas.current.clientWidth
			let h = canvas.current.clientHeight
			let x = e.pageX
			let y = e.pageY

			// setting the range [-0.5, 0.5] to map accross the screen horizontally and vertically
			x = (x - w / 2) / w
			y = (y - h / 2) / h

			// storing the coordinates in the state
			setMCoord({
				x: x,
				y: y,
			})
		}
	}

Then I pass this to my model as a prop.
Then I animate the mesh group using useFrame likeso:

useFrame(({ gl, scene, camera }) => {
		perfume.current.rotation.x = 0
		perfume.current.rotation.y = -Math.PI * mCoords.x
		perfume.current.rotation.z = 0
		gl.render(scene, camera)
	}, 1)

the group looks something like this

                    <group
						ref={perfume}
						position={[0, -0.6, 0]}
					>
						<mesh
							ref={model}
							castShadow
							receiveShadow
							position={[0, 0.3, -0.25]}
							geometry={nodes.Bottle.geometry}
						>
							<meshPhysicalMaterial
								attach="material"
								{...bottleConfig}
							/>
						</mesh>
						<mesh
							castShadow
							receiveShadow
							geometry={nodes.Head.geometry}
							material={materials.Head}
						/>
						
						<mesh
							castShadow
							receiveShadow
							geometry={nodes.SprayPipe.geometry}
							material={materials.FrontColor}
						/>
					</group>

What I think the problem is: the state change is triggering a rerender when the mouse moves.
What I tried: I tried to memoize the state but idk that seems stupid bcoz this will also change every time the state changes. but might increase a tiny bit of performance when rerender is triggered by other states.

Can someone please guide how I can optimize this? it has to be at least 50+ fps

		setMCoord({
			x: x,
			y: y,
		})

read this

and why do you need to render inside useFrame?

i also don’t understand the pointermove, this is all inbuilt into fiber

useFrame((state) => {
  state.pointer.x
  state.pointer.y 

these are normalised coordinates [-1/0/-1]. you also get state.viewport.width/height so you could for instance do: pointer.x * viewport.width / 2 and now you have the exact x screen coordinate in three units.

basically delete all that code, it’s just this:

useFrame((state) => {
  perfume.current.rotation.set(0, -Math.PI * state.pointer.x, 0)
})

btw, add damp, because the above will look :poop:. unity damping looks beautiful:

import { easing } from 'maath'

useFrame((state, delta) => {
  easing.dampE(perfume.current.rotation, [0, -Math.PI * state.pointer.x, 0], 0.1, delta)
})
1 Like

ps you don’t need attach=“material”/attach=“geometry”

drcmda? are you kidding me :grinning: You are one of the top contributors in r3f and drei. You are an inspiration to me. I’m still learning and one day I wanna be like you. You guys are really making such a difference. you, poimandres, mrdoob and a lot of names. It seems like magic how easy webGL has become. All thanks to you guys. Thank you Sir!

I didn’t know about the state access in useFrame maybe I missed it in the docs. I was thinking wow I’m so intelligent until now. :melting_face: