# Animation & 3D Model System This document describes how to use the 3D model components and animation system in La-Fabrik. ## Table of Contents 1. [Model Types Overview](#model-types-overview) 2. [SimpleModel - Static Models](#simplemodel---static-models) 3. [AnimatedModel - Animated Models](#animatedmodel---animated-models) 4. [Animation Control](#animation-control) 5. [Other 3D Components](#other-3d-components) 6. [Technical Notes](#technical-notes) --- ## Model Types Overview The project provides three main types of model instantiation: | Type | Component | Use Case | | ----------- | -------------------------------------------------------- | -------------------------------------------- | | Static | `SimpleModel` | Props, decoration, objects without animation | | Animated | `AnimatedModel` | Characters, animated objects with skeleton | | Interactive | `GrabbableObject`, `TriggerObject`, `InteractableObject` | Objects player can interact with | --- ## SimpleModel - Static Models Use for GLTF models **without** skeleton/armature and no animations. ```tsx import { SimpleModel } from "@/components/3d"; ; ``` ### Props | Prop | Type | Default | Description | | --------------- | ------------------------ | ----------- | --------------------------------- | | `modelPath` | `string` | required | Path to GLTF file in `/public` | | `position` | `Vector3Tuple` | `[0, 0, 0]` | World position [x, y, z] | | `rotation` | `Vector3Tuple` | `[0, 0, 0]` | Rotation in degrees [x, y, z] | | `scale` | `number \| Vector3Tuple` | `1` | Scale factor or [x, y, z] | | `castShadow` | `boolean` | `true` | Enable shadow casting | | `receiveShadow` | `boolean` | `true` | Enable shadow receiving | | `children` | `ReactNode` | - | Child components to render inside | --- ## AnimatedModel - Animated Models Use for GLTF models **with** skeleton/armature and animations (like Mixamo characters). ```tsx import { AnimatedModel, useAnimatedModel } from "@/components/3d"; // Basic usage ; ``` ### Props | Prop | Type | Default | Description | | ------------------ | ------------------------ | ----------- | --------------------------------------------- | | `modelPath` | `string` | required | Path to GLTF file in `/public` | | `defaultAnimation` | `string` | `"Idle"` | Animation name to play by default | | `animations` | `string[]` | `[]` | List of animation names (optional) | | `position` | `Vector3Tuple` | `[0, 0, 0]` | World position [x, y, z] | | `rotation` | `Vector3Tuple` | `[0, 0, 0]` | Rotation in degrees [x, y, z] | | `scale` | `number \| Vector3Tuple` | `1` | Scale factor | | `autoPlay` | `boolean` | `true` | Auto-play default animation | | `speed` | `number` | `1` | Animation playback speed | | `fadeDuration` | `number` | `0.3` | Transition duration in seconds | | `onLoaded` | `() => void` | - | Callback when model loads | | `onAnimationEnd` | `(name: string) => void` | - | Callback when animation ends | | `children` | `ReactNode` | - | Child components (can use `useAnimatedModel`) | ### Important: Scale Animated models (like Mixamo exports) often need a small scale (e.g., `0.01`) because they are exported in meters while Three.js uses different units. Adjust until the model appears at the right size. --- ## Animation Control To control animations from inside or outside the `AnimatedModel`, use the `useAnimatedModel` hook. ### Basic Control ```tsx import { AnimatedModel, useAnimatedModel } from "@/components/3d"; // Create a controller component to use inside AnimatedModel function AnimationController() { const { play, stop, fadeTo, currentAnimation, names, setSpeed, isReady } = useAnimatedModel(); // names contains all available animation names // currentAnimation is the name of the currently playing animation // isReady is true when model and animations are loaded return ( play("Run", 0.5)}> ); } // Usage ; ``` ### Available Methods | Method | Signature | Description | | ------------------ | --------------------------------------- | ------------------------------------ | | `play` | `(name: string, fade?: number) => void` | Play animation with optional fade | | `fadeTo` | `(name: string, fade?: number) => void` | Fade to another animation | | `stop` | `(fade?: number) => void` | Stop and return to default animation | | `setSpeed` | `(speed: number) => void` | Set animation speed | | `currentAnimation` | `string` | Current animation name (getter) | | `names` | `string[]` | Available animation names | | `isReady` | `boolean` | Whether model is loaded | ### Transition Example ```tsx function Character() { const { play, fadeTo, currentAnimation } = useAnimatedModel(); const handleWalk = () => fadeTo("Walk", 0.5); // 0.5s fade const handleRun = () => play("Run", 0.3); // 0.3s fade const handleIdle = () => play("Idle", 0.5); // return to idle return ( ); } ``` ### Combined: GrabbableObject with Animation You can combine `AnimatedModel` inside `GrabbableObject` to create animated objects that can be picked up: ```tsx import { AnimatedModel, GrabbableObject } from "@/components/3d"; // Animated weapon/tool that player can pick up ; ``` Or create an animated character that can be grabbed: ```tsx import { AnimatedModel, GrabbableObject, useAnimatedModel, } from "@/components/3d"; // Controller that triggers animations when grabbed function AnimatedGrabber() { const { play, fadeTo } = useAnimatedModel(); return ( ); } // When grabbed, play "Grab" animation { // This would require a context or store to trigger console.log("Object grabbed!"); }} > ; ``` **Note:** For complex interactions (like playing specific animations when grabbing), you'll need to connect the grab events to animation controls via a state manager or context. --- ## Other 3D Components ### GrabbableObject Objects that can be picked up by the player. ```tsx import { GrabbableObject } from "@/components/3d"; ; ``` ### TriggerObject Objects that trigger events when interacted with. ```tsx import { TriggerObject } from "@/components/3d"; console.log("Triggered!")} > ; ``` ### InteractableObject Base object for interactions. ```tsx import { InteractableObject } from "@/components/3d"; console.log("Interacted!")} > ; ``` --- ## Technical Notes ### GLTF Models - Models should be placed in `/public/models/` - Supported formats: `.gltf`, `.glb` - Animated models must have an Armature/skeleton for animations to work ### Model Scale Issue If animated models don't appear, they may be too small or too large. Try: - Scale `0.01` for Mixamo-exported models - Scale `1` for models in correct units ### Cloning - `SimpleModel` uses `scene.clone()` for proper React lifecycle - `AnimatedModel` uses the original scene directly to preserve SkinnedMesh + Armature structure ### Animation System The animation system uses: - `@react-three/drei`: `useGLTF` for loading, `useAnimations` for animation control - Three.js: `AnimationMixer` for playback ### No State Machine This system intentionally avoids complex state machines (like Unity's Animator). For simple animation transitions, use the `play`, `fadeTo`, and `stop` methods directly. --- ## File Structure ``` src/ ├── components/3d/ │ ├── AnimatedModel.tsx # Animated model component + context │ ├── SimpleModel.tsx # Static model component │ ├── GrabbableObject.tsx # Pickable object │ ├── TriggerObject.tsx # Trigger event object │ ├── InteractableObject.tsx │ └── index.ts # Central exports └── hooks/ └── useCharacterAnimation.ts # Animation hook (legacy) ```