Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
69 lines
1.8 KiB
TypeScript
69 lines
1.8 KiB
TypeScript
import { useRef, useEffect } from 'react';
|
|
import { useGLTF, useAnimations } from '@react-three/drei';
|
|
|
|
type DuckModelProps = {
|
|
onUpdate: (info: { position: number[]; rotation: number[]; scale: number }) => void;
|
|
onAssetsLoaded: () => void;
|
|
currentSection: number;
|
|
};
|
|
|
|
export function DuckModel({ onUpdate, onAssetsLoaded, currentSection }: DuckModelProps) {
|
|
const character = useGLTF('/duck3D/Character_output.glb');
|
|
const animations = useGLTF('/duck3D/Meshy_Merged_Animations.glb');
|
|
const meshRef = useRef<any>(null);
|
|
const { actions } = useAnimations(animations.animations, meshRef);
|
|
|
|
useEffect(() => {
|
|
onAssetsLoaded();
|
|
}, [character, animations, onAssetsLoaded]);
|
|
|
|
useEffect(() => {
|
|
if (!actions) return;
|
|
|
|
const isContactSection = currentSection === 6;
|
|
|
|
if (isContactSection) {
|
|
Object.values(actions).forEach((a) => a?.fadeOut(0.4));
|
|
return;
|
|
}
|
|
|
|
const action = actions['Walking'];
|
|
if (!action) return;
|
|
|
|
Object.values(actions).forEach((a) => a?.fadeOut(0.4));
|
|
|
|
action.reset();
|
|
action.setLoop(2201, Infinity);
|
|
action.clampWhenFinished = false;
|
|
action.fadeIn(0.4);
|
|
action.play();
|
|
}, [actions, currentSection]);
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
if (meshRef.current) {
|
|
onUpdate({
|
|
position: meshRef.current.position.toArray(),
|
|
rotation: meshRef.current.rotation.toArray().slice(0, 3),
|
|
scale: meshRef.current.scale.x,
|
|
});
|
|
}
|
|
}, 100);
|
|
|
|
return () => clearInterval(interval);
|
|
}, [onUpdate]);
|
|
|
|
const isContactSection = currentSection === 6;
|
|
const rotationY = isContactSection ? 0 : (10 * Math.PI) / 180;
|
|
|
|
return (
|
|
<primitive
|
|
ref={meshRef}
|
|
object={character.scene}
|
|
position={[0, -0.2, 0]}
|
|
scale={4.5}
|
|
rotation={[-0.1, rotationY, 0]}
|
|
/>
|
|
);
|
|
}
|