update
This commit is contained in:
@@ -74,12 +74,12 @@ jobs:
|
|||||||
|
|
||||||
- name: 📏 Check bundle size
|
- name: 📏 Check bundle size
|
||||||
run: |
|
run: |
|
||||||
# Check generated app assets only; public/ model files are runtime assets copied to dist.
|
# Get bundle size in KB
|
||||||
SIZE=$(du -k dist/assets | cut -f1)
|
SIZE=$(du -k dist | cut -f1)
|
||||||
echo "Bundle size: ${SIZE}KB"
|
echo "Bundle size: ${SIZE}KB"
|
||||||
|
|
||||||
# Threshold: 5000KB (configurable)
|
# Threshold: 1000KB (configurable)
|
||||||
THRESHOLD=5000
|
THRESHOLD=1000
|
||||||
|
|
||||||
if [ "$SIZE" -gt "$THRESHOLD" ]; then
|
if [ "$SIZE" -gt "$THRESHOLD" ]; then
|
||||||
echo "❌ Bundle size ${SIZE}KB exceeds threshold ${THRESHOLD}KB"
|
echo "❌ Bundle size ${SIZE}KB exceeds threshold ${THRESHOLD}KB"
|
||||||
|
|||||||
@@ -38,8 +38,3 @@ Thumbs.db
|
|||||||
# 3D Assets Cache (drei, GLTFJSX)
|
# 3D Assets Cache (drei, GLTFJSX)
|
||||||
.drei/
|
.drei/
|
||||||
.glitchdrei-cache/
|
.glitchdrei-cache/
|
||||||
|
|
||||||
# Temporaire
|
|
||||||
.backend/
|
|
||||||
backend/
|
|
||||||
temp/
|
|
||||||
|
|||||||
@@ -1,338 +0,0 @@
|
|||||||
# 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";
|
|
||||||
|
|
||||||
<SimpleModel
|
|
||||||
modelPath="/models/elecsimple/model.gltf"
|
|
||||||
position={[0, 0, -5]}
|
|
||||||
rotation={[0, 45, 0]}
|
|
||||||
scale={1}
|
|
||||||
castShadow={true}
|
|
||||||
receiveShadow={true}
|
|
||||||
/>;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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
|
|
||||||
<AnimatedModel
|
|
||||||
modelPath="/models/elec/model.gltf"
|
|
||||||
defaultAnimation="Idle"
|
|
||||||
position={[0, 0, -5]}
|
|
||||||
rotation={[0, 0, 0]}
|
|
||||||
scale={0.01}
|
|
||||||
autoPlay={true}
|
|
||||||
speed={1}
|
|
||||||
fadeDuration={0.3}
|
|
||||||
/>;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 (
|
|
||||||
<mesh onClick={() => play("Run", 0.5)}>
|
|
||||||
<boxGeometry />
|
|
||||||
</mesh>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Usage
|
|
||||||
<AnimatedModel
|
|
||||||
modelPath="/models/elec/model.gltf"
|
|
||||||
defaultAnimation="Idle"
|
|
||||||
position={[0, 0, -5]}
|
|
||||||
scale={0.01}
|
|
||||||
>
|
|
||||||
<AnimationController />
|
|
||||||
</AnimatedModel>;
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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 (
|
|
||||||
<group>
|
|
||||||
<mesh onClick={handleWalk} position={[-1, 0, 0]}>
|
|
||||||
<boxGeometry />
|
|
||||||
</mesh>
|
|
||||||
<mesh onClick={handleRun} position={[0, 0, 0]}>
|
|
||||||
<boxGeometry />
|
|
||||||
</mesh>
|
|
||||||
<mesh onClick={handleIdle} position={[1, 0, 0]}>
|
|
||||||
<boxGeometry />
|
|
||||||
</mesh>
|
|
||||||
</group>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 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
|
|
||||||
<GrabbableObject position={[0, 1, 0]} colliders="cuboid">
|
|
||||||
<AnimatedModel
|
|
||||||
modelPath="/models/sword/model.gltf"
|
|
||||||
defaultAnimation="Idle"
|
|
||||||
position={[0, 0, 0]}
|
|
||||||
scale={0.02}
|
|
||||||
autoPlay={true}
|
|
||||||
/>
|
|
||||||
</GrabbableObject>;
|
|
||||||
```
|
|
||||||
|
|
||||||
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 (
|
|
||||||
<AnimatedModel
|
|
||||||
modelPath="/models/elec/model.gltf"
|
|
||||||
defaultAnimation="Idle"
|
|
||||||
position={[0, 0, 0]}
|
|
||||||
scale={0.01}
|
|
||||||
autoPlay={true}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When grabbed, play "Grab" animation
|
|
||||||
<GrabbableObject
|
|
||||||
position={[0, 1, 0]}
|
|
||||||
colliders="cuboid"
|
|
||||||
onGrab={() => {
|
|
||||||
// This would require a context or store to trigger
|
|
||||||
console.log("Object grabbed!");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AnimatedGrabber />
|
|
||||||
</GrabbableObject>;
|
|
||||||
```
|
|
||||||
|
|
||||||
**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";
|
|
||||||
|
|
||||||
<GrabbableObject position={[0, 1, 0]} colliders="cuboid">
|
|
||||||
<mesh>
|
|
||||||
<boxGeometry args={[0.5, 0.5, 0.5]} />
|
|
||||||
<meshStandardMaterial color="red" />
|
|
||||||
</mesh>
|
|
||||||
</GrabbableObject>;
|
|
||||||
```
|
|
||||||
|
|
||||||
### TriggerObject
|
|
||||||
|
|
||||||
Objects that trigger events when interacted with.
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { TriggerObject } from "@/components/3d";
|
|
||||||
|
|
||||||
<TriggerObject
|
|
||||||
position={[0, 1, 0]}
|
|
||||||
soundPath="/sounds/click.mp3"
|
|
||||||
onTrigger={() => console.log("Triggered!")}
|
|
||||||
>
|
|
||||||
<mesh>
|
|
||||||
<sphereGeometry />
|
|
||||||
<meshStandardMaterial color="blue" />
|
|
||||||
</mesh>
|
|
||||||
</TriggerObject>;
|
|
||||||
```
|
|
||||||
|
|
||||||
### InteractableObject
|
|
||||||
|
|
||||||
Base object for interactions.
|
|
||||||
|
|
||||||
```tsx
|
|
||||||
import { InteractableObject } from "@/components/3d";
|
|
||||||
|
|
||||||
<InteractableObject
|
|
||||||
position={[0, 1, 0]}
|
|
||||||
onInteract={() => console.log("Interacted!")}
|
|
||||||
>
|
|
||||||
<mesh>
|
|
||||||
<cylinderGeometry />
|
|
||||||
<meshStandardMaterial color="green" />
|
|
||||||
</mesh>
|
|
||||||
</InteractableObject>;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 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)
|
|
||||||
```
|
|
||||||
@@ -4,16 +4,13 @@ This document describes the code that exists today in the repository.
|
|||||||
|
|
||||||
## Runtime Structure
|
## Runtime Structure
|
||||||
|
|
||||||
- `src/main.tsx` mounts React and wraps the app in `BrowserRouter`.
|
- `src/App.tsx` mounts the `Canvas`, the 3D `World`, the debug perf overlay, and the HTML overlays.
|
||||||
- `src/App.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:
|
- `src/world/World.tsx` composes the active scene, including:
|
||||||
- environment and lighting
|
- environment and lighting
|
||||||
- debug helpers and debug camera mode
|
- debug helpers and debug camera mode
|
||||||
- either the map scene or the debug physics test scene
|
- either the map scene or the debug physics test scene
|
||||||
- the player rig when the active camera mode is `player`
|
- 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/Map.tsx` loads the main map model and builds the collision octree.
|
||||||
- `src/world/debug/TestScene.tsx` provides a debug-oriented interaction and physics scene.
|
- `src/world/debug/TestScene.tsx` provides a debug-oriented interaction and physics scene.
|
||||||
- `src/world/player/PlayerComponent.tsx` mounts the camera and controller.
|
- `src/world/player/PlayerComponent.tsx` mounts the camera and controller.
|
||||||
- `src/world/player/PlayerController.tsx` owns pointer lock movement, jump handling, and interaction input.
|
- `src/world/player/PlayerController.tsx` owns pointer lock movement, jump handling, and interaction input.
|
||||||
@@ -41,31 +38,10 @@ This document describes the code that exists today in the repository.
|
|||||||
- `src/utils/debug/scene/DebugHelpers.tsx` mounts debug helpers.
|
- `src/utils/debug/scene/DebugHelpers.tsx` mounts debug helpers.
|
||||||
- `src/utils/debug/scene/DebugCameraControls.tsx` mounts the free debug camera.
|
- `src/utils/debug/scene/DebugCameraControls.tsx` mounts the free debug camera.
|
||||||
|
|
||||||
## Editor System
|
|
||||||
|
|
||||||
- `src/pages/editor/EditorPage.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.
|
|
||||||
- `src/controls/editor/FlyController.tsx` provides player-style editor navigation.
|
|
||||||
- `src/hooks/editor/useEditorSceneData.ts` loads scene data and handles folder upload fallback.
|
|
||||||
- `src/hooks/editor/useEditorHistory.ts` owns editor undo and redo state.
|
|
||||||
- `src/utils/editor/loadEditorScene.ts` handles editor-only folder upload parsing.
|
|
||||||
- `src/utils/loadMapSceneData.ts` is shared by the game scene and editor to load `public/map.json` and resolve model URLs.
|
|
||||||
- `src/types/editor.ts` contains the shared `MapNode`, `SceneData`, and `TransformMode` types.
|
|
||||||
|
|
||||||
## Map Data
|
|
||||||
|
|
||||||
- `public/map.json` is expected to be a `MapNode[]`.
|
|
||||||
- Each map node `name` maps to `public/models/{name}/model.gltf`.
|
|
||||||
- The editor renders a fallback cube for missing models.
|
|
||||||
- The game scene filters out nodes whose model cannot be resolved.
|
|
||||||
|
|
||||||
## Current Limitations
|
## Current Limitations
|
||||||
|
|
||||||
- The repository is a prototype, not the full intended game runtime.
|
- The repository is still a prototype, not the full intended game runtime.
|
||||||
- `src/world/debug/TestScene.tsx` is part of the active scene composition.
|
- `src/world/debug/TestScene.tsx` is still part of the active scene composition.
|
||||||
- There is no central gameplay orchestrator such as `GameManager`.
|
- There is no central gameplay orchestrator such as `GameManager` yet.
|
||||||
- Missions, zones, cinematics, and dialogue systems are not implemented.
|
- Missions, zones, cinematics, and dialogue systems are not implemented.
|
||||||
- The player uses octree collision and simple movement rules, not a complete gameplay physics stack.
|
- The player uses octree collision and simple movement rules, not a complete gameplay physics stack.
|
||||||
- Editor save-to-server is implemented as a Vite dev-server plugin, not a production backend API.
|
|
||||||
|
|||||||
@@ -1,144 +0,0 @@
|
|||||||
# Editor Technical Notes
|
|
||||||
|
|
||||||
This document describes the map editor that exists in the current codebase.
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
The editor is a React route used to inspect and adjust the `public/map.json` scene data from inside the La-Fabrik app. It shares the same `MapNode` data format as the game scene and uses React Three Fiber for rendering.
|
|
||||||
|
|
||||||
## Routing
|
|
||||||
|
|
||||||
- `/` renders the playable La-Fabrik scene.
|
|
||||||
- `/editor` renders the map editor.
|
|
||||||
- `src/App.tsx` mounts TanStack Router through `RouterProvider`.
|
|
||||||
- `src/router.tsx` defines the `/editor` route and imports `EditorPage` from `src/pages/editor/page.tsx`.
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```txt
|
|
||||||
src/
|
|
||||||
├── pages/
|
|
||||||
│ └── editor/
|
|
||||||
│ └── page.tsx
|
|
||||||
├── components/
|
|
||||||
│ └── editor/
|
|
||||||
│ ├── EditorControls.tsx
|
|
||||||
│ └── scene/
|
|
||||||
│ ├── EditorMap.tsx
|
|
||||||
│ └── EditorScene.tsx
|
|
||||||
├── controls/
|
|
||||||
│ └── editor/
|
|
||||||
│ └── FlyController.tsx
|
|
||||||
├── hooks/
|
|
||||||
│ └── editor/
|
|
||||||
│ ├── useEditorHistory.ts
|
|
||||||
│ └── useEditorSceneData.ts
|
|
||||||
├── types/
|
|
||||||
│ └── editor.ts
|
|
||||||
└── utils/
|
|
||||||
├── editor/
|
|
||||||
│ └── loadEditorScene.ts
|
|
||||||
└── loadMapSceneData.ts
|
|
||||||
```
|
|
||||||
|
|
||||||
## Responsibilities
|
|
||||||
|
|
||||||
`src/pages/editor/page.tsx` is the route-level composition component. It owns route-specific state such as selected object, hovered object, transform mode, and player-mode toggle.
|
|
||||||
|
|
||||||
`src/hooks/editor/useEditorSceneData.ts` loads the default map data and handles folder uploads.
|
|
||||||
|
|
||||||
`src/hooks/editor/useEditorHistory.ts` owns editor undo and redo history.
|
|
||||||
|
|
||||||
`src/components/editor/scene/EditorScene.tsx` composes the editor canvas scene, camera controls, lights, keyboard shortcuts, and `EditorMap`.
|
|
||||||
|
|
||||||
`src/components/editor/scene/EditorMap.tsx` renders map nodes, fallback cubes, selection highlighting, and transform controls.
|
|
||||||
|
|
||||||
`src/components/editor/EditorControls.tsx` renders the HTML control panel outside the canvas.
|
|
||||||
|
|
||||||
`src/controls/editor/FlyController.tsx` provides editor movement controls for player-style navigation.
|
|
||||||
|
|
||||||
`src/utils/loadMapSceneData.ts` is shared by the game map and editor. It loads `/map.json` and resolves available `public/models/{name}/model.gltf` files.
|
|
||||||
|
|
||||||
`src/utils/editor/loadEditorScene.ts` contains editor-only upload handling for user-selected folders.
|
|
||||||
|
|
||||||
## Data Format
|
|
||||||
|
|
||||||
The shared editor type lives in `src/types/editor.ts`.
|
|
||||||
|
|
||||||
```ts
|
|
||||||
interface MapNode {
|
|
||||||
name: string;
|
|
||||||
type: string;
|
|
||||||
position: [number, number, number];
|
|
||||||
rotation: [number, number, number];
|
|
||||||
scale: [number, number, number];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`public/map.json` is expected to be a `MapNode[]`.
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"name": "pylone",
|
|
||||||
"type": "Mesh",
|
|
||||||
"position": [0, 5, 0],
|
|
||||||
"rotation": [0, 1.57, 0],
|
|
||||||
"scale": [1, 1, 1]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
Each node `name` maps to a model folder:
|
|
||||||
|
|
||||||
```txt
|
|
||||||
public/
|
|
||||||
├── map.json
|
|
||||||
└── models/
|
|
||||||
└── pylone/
|
|
||||||
└── model.gltf
|
|
||||||
```
|
|
||||||
|
|
||||||
If a model is missing, the editor renders a fallback cube so the node can still be selected and transformed.
|
|
||||||
|
|
||||||
## Editor Flow
|
|
||||||
|
|
||||||
1. `EditorPage` mounts on `/editor`.
|
|
||||||
2. `useEditorSceneData` calls `loadMapSceneData()`.
|
|
||||||
3. `loadMapSceneData()` loads `/map.json` and available model URLs.
|
|
||||||
4. If `/map.json` is missing, the page displays a folder-upload flow.
|
|
||||||
5. `EditorScene` renders the grid, lights, camera controls, and map nodes.
|
|
||||||
6. `EditorControls` exposes transform mode, history actions, export, save, and selection info.
|
|
||||||
|
|
||||||
## Controls
|
|
||||||
|
|
||||||
- Click: select a node.
|
|
||||||
- `Esc`: clear selection.
|
|
||||||
- `T`: translate mode.
|
|
||||||
- `R`: rotate mode.
|
|
||||||
- `S`: scale mode.
|
|
||||||
- `Ctrl+Z` or `Cmd+Z`: undo.
|
|
||||||
- `Ctrl+Y` or `Cmd+Y`: redo.
|
|
||||||
- `WASD`, `ZQSD`, or arrow keys: move in player-controller mode.
|
|
||||||
- `Space`: move upward in player-controller mode.
|
|
||||||
- `Shift`: move downward in player-controller mode.
|
|
||||||
|
|
||||||
## Saving And Exporting
|
|
||||||
|
|
||||||
The editor supports two output paths:
|
|
||||||
|
|
||||||
- Export JSON downloads the current `MapNode[]` as `map.json`.
|
|
||||||
- Save to Server posts the current `MapNode[]` to `/api/save-map`.
|
|
||||||
|
|
||||||
The dev-only `/api/save-map` endpoint is implemented by the Vite plugin in `vite.config.ts`. It writes to `public/map.json` and enforces a maximum payload size.
|
|
||||||
|
|
||||||
## Styling
|
|
||||||
|
|
||||||
Editor styles are in `src/index.css` under the `/* Editor page */` section. Classes are prefixed with `editor-` to avoid collisions with the game UI.
|
|
||||||
|
|
||||||
## Known Limitations
|
|
||||||
|
|
||||||
- Uploaded model object URLs are not currently revoked after replacement or unmount.
|
|
||||||
- Large `map.json` files are not virtualized, culled, or LOD-managed.
|
|
||||||
- There is no snap-to-grid, duplication, material editing, or object creation workflow.
|
|
||||||
- Save to Server is a Vite dev-server helper, not a production backend API.
|
|
||||||
@@ -5,7 +5,7 @@ This document describes the intended medium-term architecture for the project.
|
|||||||
## Relationship To The Current Code
|
## Relationship To The Current Code
|
||||||
|
|
||||||
- `docs/technical/architecture.md` is the source of truth for what exists now.
|
- `docs/technical/architecture.md` is the source of truth for what exists now.
|
||||||
- This document describes intended direction, not implemented behavior.
|
- This document is intentionally aspirational.
|
||||||
- If this document conflicts with the current implementation, the current implementation wins.
|
- If this document conflicts with the current implementation, the current implementation wins.
|
||||||
|
|
||||||
## Goals
|
## Goals
|
||||||
@@ -40,12 +40,12 @@ This document describes the intended medium-term architecture for the project.
|
|||||||
- performance overlay
|
- performance overlay
|
||||||
- scene helpers
|
- scene helpers
|
||||||
- free camera and calibration controls
|
- free camera and calibration controls
|
||||||
- debug test scenes used during development
|
- temporary test scenes used during development
|
||||||
|
|
||||||
### UI Layer
|
### UI Layer
|
||||||
|
|
||||||
- `src/components/ui/` should contain player-facing HTML overlays.
|
- `src/components/ui/` should contain player-facing HTML overlays.
|
||||||
- Candidate examples:
|
- Expected future examples:
|
||||||
- crosshair
|
- crosshair
|
||||||
- loading flow
|
- loading flow
|
||||||
- mission HUD
|
- mission HUD
|
||||||
@@ -54,7 +54,7 @@ This document describes the intended medium-term architecture for the project.
|
|||||||
### Gameplay Layer
|
### Gameplay Layer
|
||||||
|
|
||||||
- As the project grows, gameplay state can move toward a clearer orchestration layer.
|
- As the project grows, gameplay state can move toward a clearer orchestration layer.
|
||||||
- Likely concerns:
|
- Likely future concerns:
|
||||||
- missions
|
- missions
|
||||||
- zones
|
- zones
|
||||||
- cinematics
|
- cinematics
|
||||||
@@ -67,4 +67,4 @@ This document describes the intended medium-term architecture for the project.
|
|||||||
- Prefer direct, working code over speculative scaffolding.
|
- Prefer direct, working code over speculative scaffolding.
|
||||||
- Shared types should stay close to their domain until they have multiple real consumers.
|
- Shared types should stay close to their domain until they have multiple real consumers.
|
||||||
- Avoid creating new managers or service layers without an active runtime need.
|
- Avoid creating new managers or service layers without an active runtime need.
|
||||||
- Debug-only runtime paths should be clearly marked and easy to remove when obsolete.
|
- Debug-only runtime paths should be clearly marked and easy to remove later.
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
# Editor User Guide
|
|
||||||
|
|
||||||
The map editor is available at `/editor`. It is a browser-based tool for inspecting and adjusting the objects listed in `public/map.json`.
|
|
||||||
|
|
||||||
## Purpose
|
|
||||||
|
|
||||||
Use the editor when you need to move, rotate, or scale existing map objects without editing JSON by hand.
|
|
||||||
|
|
||||||
The editor reads the same map data as the runtime scene:
|
|
||||||
|
|
||||||
- `public/map.json` contains the object list.
|
|
||||||
- `public/models/{name}/model.gltf` contains the matching 3D model for each object name.
|
|
||||||
- Missing models are displayed as gray fallback cubes, so incomplete maps remain editable.
|
|
||||||
|
|
||||||
## Map Node Format
|
|
||||||
|
|
||||||
Each entry in `public/map.json` represents one object:
|
|
||||||
|
|
||||||
| Field | Description |
|
|
||||||
| ---------- | ------------------------------------------------- |
|
|
||||||
| `name` | Model folder name in `public/models/{name}` |
|
|
||||||
| `type` | Object category |
|
|
||||||
| `position` | Object position as `[x, y, z]` |
|
|
||||||
| `rotation` | Object rotation as `[x, y, z]`, expressed radians |
|
|
||||||
| `scale` | Object scale as `[x, y, z]` |
|
|
||||||
|
|
||||||
## Editing Workflow
|
|
||||||
|
|
||||||
1. Open `/editor` in the local app.
|
|
||||||
2. Click an object in the scene to select it.
|
|
||||||
3. Choose a transform mode: translate, rotate, or scale.
|
|
||||||
4. Drag the transform gizmo in the 3D view.
|
|
||||||
5. Check the JSON inspector if you need exact values.
|
|
||||||
6. Use undo or redo if the transform is not correct.
|
|
||||||
7. Export the JSON or save it to the dev server.
|
|
||||||
|
|
||||||
## Controls
|
|
||||||
|
|
||||||
| Action | Input |
|
|
||||||
| -------------------- | -------------------------- |
|
|
||||||
| Select object | Click object |
|
|
||||||
| Deselect | `Esc` or click empty space |
|
|
||||||
| Translate mode | `T` |
|
|
||||||
| Rotate mode | `R` |
|
|
||||||
| Scale mode | `S` |
|
|
||||||
| Undo | `Ctrl+Z` |
|
|
||||||
| Redo | `Ctrl+Y` |
|
|
||||||
| Locked view movement | `WASD`, `ZQSD`, arrows |
|
|
||||||
| Move up | `Space` |
|
|
||||||
| Move down | `Shift` |
|
|
||||||
|
|
||||||
## View Mode
|
|
||||||
|
|
||||||
The `Lock view` action switches the editor into a movement mode closer to the runtime player camera. Use it to navigate larger scenes while keeping the transform tools available.
|
|
||||||
|
|
||||||
## JSON Inspector
|
|
||||||
|
|
||||||
The side panel includes a raw JSON inspector:
|
|
||||||
|
|
||||||
- When no object is selected, it shows the full map node list.
|
|
||||||
- When an object is selected, it highlights the JSON lines for that object.
|
|
||||||
|
|
||||||
This is useful for checking numeric transform values before saving or exporting.
|
|
||||||
|
|
||||||
## Saving Changes
|
|
||||||
|
|
||||||
### Export JSON
|
|
||||||
|
|
||||||
`Export JSON` downloads the current map node list as `map.json`. Use this when you want to manually replace `public/map.json`.
|
|
||||||
|
|
||||||
### Save To Server
|
|
||||||
|
|
||||||
`Save to server` is available only during local development. It writes the edited map back to `public/map.json` through the Vite dev-server endpoint.
|
|
||||||
|
|
||||||
The button is hidden in production builds because production persistence is not implemented.
|
|
||||||
|
|
||||||
## Current Limitations
|
|
||||||
|
|
||||||
- The editor only modifies existing nodes.
|
|
||||||
- It does not create or delete objects.
|
|
||||||
- It does not edit model files or textures.
|
|
||||||
- It does not provide production persistence.
|
|
||||||
- Fallback cubes indicate missing models; they are editor placeholders, not exported assets.
|
|
||||||
+1
-16
@@ -5,7 +5,7 @@ This document lists features that are implemented in the current codebase.
|
|||||||
## Scene
|
## Scene
|
||||||
|
|
||||||
- Fullscreen React Three Fiber scene
|
- Fullscreen React Three Fiber scene
|
||||||
- Main map scene loaded from `public/map.json` and matching `public/models/{name}/model.gltf` assets
|
- Main map scene loaded from `public/models/map/model.gltf`
|
||||||
- Debug physics test scene selectable from the debug panel
|
- Debug physics test scene selectable from the debug panel
|
||||||
- Ambient and directional lighting
|
- Ambient and directional lighting
|
||||||
- Environment background setup
|
- Environment background setup
|
||||||
@@ -38,20 +38,6 @@ This document lists features that are implemented in the current codebase.
|
|||||||
- Free debug camera
|
- Free debug camera
|
||||||
- `r3f-perf` overlay
|
- `r3f-perf` overlay
|
||||||
|
|
||||||
## Map Editor
|
|
||||||
|
|
||||||
- `/editor` route for inspecting and editing `public/map.json`
|
|
||||||
- Automatic loading of `public/map.json` when available
|
|
||||||
- Folder upload fallback when `map.json` is missing
|
|
||||||
- Rendering of available `public/models/{name}/model.gltf` assets
|
|
||||||
- Fallback cubes for nodes whose model is missing
|
|
||||||
- Object selection by click
|
|
||||||
- Transform modes for translate, rotate, and scale
|
|
||||||
- Keyboard shortcuts for `T`, `R`, `S`, `Esc`, undo, and redo
|
|
||||||
- Player-style navigation mode with `WASD`, `ZQSD`, arrow keys, `Space`, and `Shift`
|
|
||||||
- JSON export for downloading the edited map
|
|
||||||
- Dev-server save endpoint for writing changes back to `public/map.json`
|
|
||||||
|
|
||||||
## Not Implemented Yet
|
## Not Implemented Yet
|
||||||
|
|
||||||
- mission system
|
- mission system
|
||||||
@@ -61,4 +47,3 @@ This document lists features that are implemented in the current codebase.
|
|||||||
- loading flow
|
- loading flow
|
||||||
- minimap and mission HUD
|
- minimap and mission HUD
|
||||||
- full production separation between gameplay and debug scenes
|
- full production separation between gameplay and debug scenes
|
||||||
- production backend persistence for editor saves
|
|
||||||
|
|||||||
Generated
+299
-1660
File diff suppressed because it is too large
Load Diff
@@ -3,9 +3,6 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"engines": {
|
|
||||||
"node": ">=20.19.0 || >=22.12.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
@@ -21,15 +18,11 @@
|
|||||||
"@react-three/fiber": "^9.6.0",
|
"@react-three/fiber": "^9.6.0",
|
||||||
"@react-three/postprocessing": "^3.0.4",
|
"@react-three/postprocessing": "^3.0.4",
|
||||||
"@react-three/rapier": "^2.2.0",
|
"@react-three/rapier": "^2.2.0",
|
||||||
"@tanstack/react-router": "^1.168.25",
|
|
||||||
"gsap": "^3.15.0",
|
"gsap": "^3.15.0",
|
||||||
"lil-gui": "^0.21.0",
|
"lil-gui": "^0.21.0",
|
||||||
"lucide-react": "^1.11.0",
|
|
||||||
"r3f-perf": "^7.2.3",
|
"r3f-perf": "^7.2.3",
|
||||||
"react": "^19.2.4",
|
"react": "^19.2.4",
|
||||||
"react-dom": "^19.2.4",
|
"react-dom": "^19.2.4",
|
||||||
"react-markdown": "^10.1.0",
|
|
||||||
"remark-gfm": "^4.0.1",
|
|
||||||
"three": "^0.183.2"
|
"three": "^0.183.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -48,10 +41,5 @@
|
|||||||
"typescript": "~6.0.2",
|
"typescript": "~6.0.2",
|
||||||
"typescript-eslint": "^8.58.0",
|
"typescript-eslint": "^8.58.0",
|
||||||
"vite": "^8.0.4"
|
"vite": "^8.0.4"
|
||||||
},
|
|
||||||
"overrides": {
|
|
||||||
"r3f-perf": {
|
|
||||||
"@react-three/drei": "$@react-three/drei"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
-4587
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user