# Skill — Three.js ## AnimationMixer The standard way to play GLTF animations in this project: ```tsx import { useEffect, useRef } from "react"; import * as THREE from "three"; import { useFrame } from "@react-three/fiber"; import { useGLTF, useAnimations } from "@react-three/drei"; export function AnimatedObject() { const group = useRef(null); const { scene, animations } = useGLTF("/models/object.glb"); const { actions } = useAnimations(animations, group); useEffect(() => { actions["Idle"]?.play(); return () => { actions["Idle"]?.stop(); }; }, [actions]); return ; } ``` ### Manual mixer (when drei's useAnimations is not enough) ```tsx const mixer = useRef(null); useEffect(() => { mixer.current = new THREE.AnimationMixer(gltf.scene); const clip = gltf.animations.find((a) => a.name === "Walk"); if (clip) mixer.current.clipAction(clip).play(); return () => { mixer.current?.stopAllAction(); mixer.current = null; }; }, [gltf]); useFrame((_, delta) => { mixer.current?.update(delta); }); ``` ## Materials - Prefer `meshStandardMaterial` for PBR - Use `meshBasicMaterial` for unlit UI elements or hologram base - Custom shaders go in `src/shaders/` as `.glsl` files ```tsx ``` ## Raycasting In R3F, raycasting is built into the event system: ```tsx { e.stopPropagation() // handle click on this mesh }} onPointerOver={() => setHovered(true)} onPointerOut={() => setHovered(false)} > ``` For custom raycasting, use `useThree` to access the raycaster: ```tsx const { raycaster, camera, scene } = useThree(); ``` ## Rules - Never instantiate `THREE.WebGLRenderer` or `THREE.Scene` — R3F owns these - Use `useThree()` to access renderer, camera, scene, gl, size - Texture format: prefer `.jpg` for diffuse, `.png` for alpha, `.hdr`/`.exr` for HDRI - Model format: always `.glb` (binary GLTF) — smaller and faster than `.gltf` - Keep triangle count reasonable per zone: aim for < 100k tris visible at once