quick peek at the code in case you don't want to waste time opening the link:
const brainRef = useRef<THREE.Mesh | null>(null);
const planeRef = useRef<THREE.Plane>(new THREE.Plane(new THREE.Vector3(0,0,1), window.innerWidth));
const raycasterRef = useRef(new THREE.Raycaster());
const pointOfIntersectionRef = useRef(new THREE.Vector3());
const { camera, gl } = useThree();
const { nodes, ...gltf } = useLoader(GLTFLoader, "/brain.gltf");
useFrame(({ mouse, clock }) => {
if (brainRef.current && planeRef.current) {
raycasterRef.current.setFromCamera(mouse, camera);
raycasterRef.current.ray.intersectPlane(
planeRef.current,
pointOfIntersectionRef.current
);
brainRef.current.lookAt(
pointOfIntersectionRef.current.x,
pointOfIntersectionRef.current.y,
- pointOfIntersectionRef.current.z
);
}
});
useLayoutEffect(() => {
const ctx = gsap.context((self) => {
if (!brainRef.current) return;
const animation = gsap.timeline();
const brainPosScrollTrigger: ScrollTrigger.Vars = {
trigger: "#hero",
start: "-5% top",
end: "bottom top",
immediateRender: false,
scrub: 1,
};
animation.to(brainRef.current.position, {
x: -152,
y: 13,
z: -350,
duration: 0.5,
ease: "linear",
scrollTrigger: brainPosScrollTrigger,
});
});
return () => ctx.revert();
}, []);
//...
<mesh
ref={brainRef}
geometry={(nodes["brain"] as THREE.Mesh).geometry}
position={[180, 20, -20]}
>
<meshStandardMaterial color="#b6b6b6" wireframe />
</mesh>```