Dwinzo_dev/app/src/modules/simulation/armbot/IKAnimationController.tsx

102 lines
3.0 KiB
TypeScript

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;