refactor: organize three components by domain

This commit is contained in:
Tom Boullay
2026-04-30 11:35:53 +02:00
parent 37eded8d7e
commit 9ac5844182
30 changed files with 465 additions and 327 deletions
+29 -24
View File
@@ -30,7 +30,7 @@ The project provides three main types of model instantiation:
Use for GLTF models **without** skeleton/armature and no animations.
```tsx
import { SimpleModel } from "@/components/3d";
import { SimpleModel } from "@/components/three/models/SimpleModel";
<SimpleModel
modelPath="/models/elecsimple/model.gltf"
@@ -48,7 +48,7 @@ import { SimpleModel } from "@/components/3d";
| --------------- | ------------------------ | ----------- | --------------------------------- |
| `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] |
| `rotation` | `Vector3Tuple` | `[0, 0, 0]` | Rotation in radians [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 |
@@ -61,7 +61,8 @@ import { SimpleModel } from "@/components/3d";
Use for GLTF models **with** skeleton/armature and animations (like Mixamo characters).
```tsx
import { AnimatedModel, useAnimatedModel } from "@/components/3d";
import { AnimatedModel } from "@/components/three/models/AnimatedModel";
import { useAnimatedModel } from "@/components/three/models/useAnimatedModel";
// Basic usage
<AnimatedModel
@@ -84,7 +85,7 @@ import { AnimatedModel, useAnimatedModel } from "@/components/3d";
| `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] |
| `rotation` | `Vector3Tuple` | `[0, 0, 0]` | Rotation in radians [x, y, z] |
| `scale` | `number \| Vector3Tuple` | `1` | Scale factor |
| `autoPlay` | `boolean` | `true` | Auto-play default animation |
| `speed` | `number` | `1` | Animation playback speed |
@@ -106,7 +107,8 @@ To control animations from inside or outside the `AnimatedModel`, use the `useAn
### Basic Control
```tsx
import { AnimatedModel, useAnimatedModel } from "@/components/3d";
import { AnimatedModel } from "@/components/three/models/AnimatedModel";
import { useAnimatedModel } from "@/components/three/models/useAnimatedModel";
// Create a controller component to use inside AnimatedModel
function AnimationController() {
@@ -178,7 +180,8 @@ function Character() {
You can combine `AnimatedModel` inside `GrabbableObject` to create animated objects that can be picked up:
```tsx
import { AnimatedModel, GrabbableObject } from "@/components/3d";
import { GrabbableObject } from "@/components/three/interaction/GrabbableObject";
import { AnimatedModel } from "@/components/three/models/AnimatedModel";
// Animated weapon/tool that player can pick up
<GrabbableObject position={[0, 1, 0]} colliders="cuboid">
@@ -195,11 +198,9 @@ import { AnimatedModel, GrabbableObject } from "@/components/3d";
Or create an animated character that can be grabbed:
```tsx
import {
AnimatedModel,
GrabbableObject,
useAnimatedModel,
} from "@/components/3d";
import { GrabbableObject } from "@/components/three/interaction/GrabbableObject";
import { AnimatedModel } from "@/components/three/models/AnimatedModel";
import { useAnimatedModel } from "@/components/three/models/useAnimatedModel";
// Controller that triggers animations when grabbed
function AnimatedGrabber() {
@@ -240,7 +241,7 @@ function AnimatedGrabber() {
Objects that can be picked up by the player.
```tsx
import { GrabbableObject } from "@/components/3d";
import { GrabbableObject } from "@/components/three/interaction/GrabbableObject";
<GrabbableObject position={[0, 1, 0]} colliders="cuboid">
<mesh>
@@ -255,7 +256,7 @@ import { GrabbableObject } from "@/components/3d";
Objects that trigger events when interacted with.
```tsx
import { TriggerObject } from "@/components/3d";
import { TriggerObject } from "@/components/three/interaction/TriggerObject";
<TriggerObject
position={[0, 1, 0]}
@@ -274,11 +275,13 @@ import { TriggerObject } from "@/components/3d";
Base object for interactions.
```tsx
import { InteractableObject } from "@/components/3d";
import { InteractableObject } from "@/components/three/interaction/InteractableObject";
<InteractableObject
kind="trigger"
label="Interact"
position={[0, 1, 0]}
onInteract={() => console.log("Interacted!")}
onPress={() => console.log("Interacted!")}
>
<mesh>
<cylinderGeometry />
@@ -306,8 +309,8 @@ If animated models don't appear, they may be too small or too large. Try:
### Cloning
- `SimpleModel` uses `scene.clone()` for proper React lifecycle
- `AnimatedModel` uses the original scene directly to preserve SkinnedMesh + Armature structure
- `SimpleModel` memoizes a cloned scene for proper React lifecycle
- `AnimatedModel` memoizes a cloned scene and binds animations through a group ref
### Animation System
@@ -326,13 +329,15 @@ This system intentionally avoids complex state machines (like Unity's Animator).
```
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
├── components/three/
│ ├── models/
│ ├── AnimatedModel.tsx # Animated model component + context
│ ├── SimpleModel.tsx # Static model component
│ └── useAnimatedModel.ts # Animated model context hook
── interaction/
├── GrabbableObject.tsx # Pickable object
│ ├── TriggerObject.tsx # Trigger event object
│ └── InteractableObject.tsx
└── hooks/
└── useCharacterAnimation.ts # Animation hook (legacy)
```
+20 -12
View File
@@ -4,8 +4,9 @@ This document describes the code that exists today in the repository.
## Runtime Structure
- `src/main.tsx` mounts React and wraps the app in `BrowserRouter`.
- `src/App.tsx` declares the top-level routes:
- `src/main.tsx` mounts React.
- `src/App.tsx` mounts the TanStack `RouterProvider`.
- `src/router.tsx` declares the top-level routes:
- `/` mounts the playable 3D scene, debug perf overlay, and HTML overlays.
- `/editor` mounts the map editor page.
- `src/world/World.tsx` composes the active scene, including:
@@ -15,21 +16,21 @@ This document describes the code that exists today in the repository.
- the player rig when the active camera mode is `player`
- `src/world/GameMap.tsx` loads map nodes from `public/map.json`, resolves available models, and builds the collision octree.
- `src/world/debug/TestMap.tsx` provides a debug-oriented interaction and physics map.
- `src/world/player/PlayerComponent.tsx` mounts the camera and controller.
- `src/world/player/Player.tsx` mounts the camera and controller.
- `src/world/player/PlayerController.tsx` owns pointer lock movement, jump handling, and interaction input.
## Interaction Model
- `src/stateManager/InteractionManager.ts` is the current interaction state source.
- `src/components/3d/InteractableObject.tsx` handles focus detection through distance and raycasting.
- `src/components/3d/TriggerObject.tsx` implements trigger-style interactions.
- `src/components/3d/GrabbableObject.tsx` implements hold-and-release interactions.
- `src/managers/InteractionManager.ts` is the current interaction state source.
- `src/components/three/interaction/InteractableObject.tsx` handles focus detection through distance and raycasting.
- `src/components/three/interaction/TriggerObject.tsx` implements trigger-style interactions.
- `src/components/three/interaction/GrabbableObject.tsx` implements hold-and-release interactions.
- `src/hooks/useInteraction.ts` exposes the interaction snapshot to React UI.
- `src/components/ui/InteractPrompt.tsx` shows the `E` prompt for trigger interactions.
## Audio
- `src/stateManager/AudioManager.ts` currently provides pooled one-shot sound playback.
- `src/managers/AudioManager.ts` currently provides pooled one-shot sound playback and looped music playback.
- Trigger interactions may play audio directly through `AudioManager`.
## Debug System
@@ -37,13 +38,20 @@ This document describes the code that exists today in the repository.
- Debug mode is enabled with `?debug`.
- `src/utils/debug/Debug.ts` owns the `lil-gui` instance and debug controls.
- `src/hooks/debug/useCameraMode.ts` and `src/hooks/debug/useSceneMode.ts` subscribe to debug state.
- `src/utils/debug/DebugPerf.tsx` lazily mounts `r3f-perf` in debug mode.
- `src/utils/debug/scene/DebugHelpers.tsx` mounts debug helpers.
- `src/utils/debug/scene/DebugCameraControls.tsx` mounts the free debug camera.
- `src/components/debug/DebugPerf.tsx` lazily mounts `r3f-perf` in debug mode.
- `src/components/debug/scene/DebugHelpers.tsx` mounts debug helpers.
- `src/components/debug/scene/DebugCameraControls.tsx` mounts the free debug camera.
## 3D Component Domains
- `src/components/three/models/` contains reusable model loaders such as `SimpleModel`, `AnimatedModel`, and `ExplodableModel`.
- `src/components/three/interaction/` contains reusable interaction wrappers such as `InteractableObject`, `TriggerObject`, and `GrabbableObject`.
- `src/components/three/gameplay/repairGame/` contains the current core repair gameplay prototype: the repair case, repair game zone, and module slots.
- `src/components/three/world/` contains reusable world/environment objects such as `SkyModel`.
## Editor System
- `src/pages/editor/EditorPage.tsx` is the route-level editor page for `/editor`.
- `src/pages/editor/page.tsx` is the route-level editor page for `/editor`.
- `src/components/editor/EditorControls.tsx` renders the HTML editor control panel.
- `src/components/editor/scene/EditorScene.tsx` composes the editor canvas scene, camera controls, lights, shortcuts, and map rendering.
- `src/components/editor/scene/EditorMap.tsx` renders map nodes, fallback cubes, selection highlighting, and transform controls.
+1 -1
View File
@@ -64,7 +64,7 @@ interface HandTrackingHand {
## Grab Targeting
The hand grab logic lives in `src/components/three/GrabbableObject.tsx`.
The hand grab logic lives in `src/components/three/interaction/GrabbableObject.tsx`.
The object is moved toward the visual center of the hand. That center is computed from the bounding box of all landmarks: