import { useEffect, useMemo, useState } from "react"; import { useFrame } from "@react-three/fiber"; import * as THREE from "three"; const IKAnimationController = ({ ikSolver, process, selectedTrigger, targetBoneName, }: { ikSolver: any; process: { trigger: string; start: THREE.Vector3; end: THREE.Vector3; speed: number; }[]; selectedTrigger: string; targetBoneName: string; }) => { const [progress, setProgress] = useState(0); const restSpeed = 0.1; useEffect(() => { setProgress(0); }, [selectedTrigger]); const processedCurves = useMemo(() => { const restPosition = new THREE.Vector3(0.2, 2.3, 1.6); return process.map((p) => { const mid = new THREE.Vector3( (p.start.x + p.end.x) / 1, Math.max(p.start.y, p.end.y) + 0.8, (p.start.z + p.end.z) / 0.9 ); const points = [ restPosition.clone(), p.start.clone(), mid.clone(), p.end.clone(), restPosition.clone(), ]; const curve = new THREE.CatmullRomCurve3(points); const restToStartDist = points[0].distanceTo(points[1]); const startToEndDist = points[1].distanceTo(points[3]); const endToRestDist = points[3].distanceTo(points[4]); const totalDist = restToStartDist + startToEndDist + endToRestDist; const restToStartRange = [0, restToStartDist / totalDist]; const startToEndRange = [ restToStartRange[1], restToStartRange[1] + startToEndDist / totalDist, ]; const endToRestRange = [startToEndRange[1], 1]; return { trigger: p.trigger, curve, speed: p.speed, restToStartRange, startToEndRange, endToRestRange, }; }); }, [process]); const activeCurve = useMemo(() => { return processedCurves.find((c) => c.trigger === selectedTrigger); }, [processedCurves, selectedTrigger]); useFrame((_, delta) => { if (!ikSolver || !activeCurve) return; const { curve, speed, startToEndRange } = activeCurve; const targetBone = ikSolver.mesh.skeleton.bones.find( (b: any) => b.name === targetBoneName ); if (!targetBone) return; let currentSpeed = restSpeed; if (progress >= startToEndRange[0] && progress < startToEndRange[1]) { currentSpeed = speed; } setProgress((prev) => { const next = prev + delta * currentSpeed; if (next >= 1) { targetBone.position.copy(curve.getPoint(1)); return 1; } targetBone.position.copy(curve.getPoint(next)); return next; }); ikSolver.update(); }); return null; }; export default IKAnimationController;