102 lines
3.0 KiB
TypeScript
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;
|