Compare commits

...

83 Commits

Author SHA1 Message Date
math-pixel 4e8a68b04a Merge pull request #10 from La-Fabrik-Durable/feat-animation
🔍 Lint / 🪄 Check lint (push) Waiting to run
🔍 Lint / 🎨 Check format (push) Waiting to run
🔍 Lint / 🔎 Typecheck (push) Waiting to run
🔍 Lint / 🏗 Build (push) Blocked by required conditions
📊 Quality / 🔒 Security Audit (push) Waiting to run
📊 Quality / 📋 Dependency Freshness (push) Waiting to run
📊 Quality / 📦 Bundle Size (push) Waiting to run
Feat/animation
2026-04-29 11:50:07 +02:00
math-pixel b997f576c5 fix: pr 2026-04-29 11:35:17 +02:00
math-pixel 20142b7e5f Merge branch 'feat-animation' of https://github.com/La-Fabrik-Durable/La-Fabrik into feat-animation 2026-04-29 11:23:57 +02:00
math-pixel 2b6b045f4a fix: pr issues 2026-04-29 11:23:40 +02:00
math-pixel 29cd03fc21 Update src/hooks/useCharacterAnimation.ts
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2026-04-29 11:07:40 +02:00
math-pixel 7c5d7f3834 update: animation doc 2026-04-29 11:00:32 +02:00
math-pixel 93744b15f7 fix : comflic 2026-04-29 10:51:40 +02:00
math-pixel 359417ecd4 feat: animator 2026-04-28 20:14:37 +02:00
math-pixel 9ada4298c3 wip 2026-04-28 16:54:00 +02:00
Tom Boullay 9b8bb1a182 Merge pull request #6 from La-Fabrik-Durable/feat/docs-routing
Feat/docs-routing
2026-04-28 15:04:29 +02:00
Astro a8c6fafbcd address docs routing review feedback 2026-04-28 15:02:50 +02:00
Astro 14a55e8dd1 clean docs router declarations 2026-04-28 14:53:28 +02:00
Astro 2dd5bfeda1 move debug components out of utils 2026-04-28 14:47:26 +02:00
Astro e20ead88e1 standardize source naming conventions 2026-04-28 14:46:27 +02:00
Astro 7e99d455b4 fix runtime map loading lifecycle 2026-04-28 14:42:49 +02:00
Astro 8c6af0ed6d rename pages 2026-04-28 14:25:29 +02:00
Astro 324aa9dc0f clean branch-scoped code quality issues 2026-04-28 14:23:37 +02:00
Astro 356bb5ef88 organize data configs by domain 2026-04-28 14:17:21 +02:00
Astro ece9b1268f refactor feature folders by code type 2026-04-28 14:14:15 +02:00
Astro d2735b72a0 refactor docs into feature folder 2026-04-28 13:54:41 +02:00
Astro fc5e4acba4 group docs navigation by audience 2026-04-28 13:48:03 +02:00
Astro a3db0b2f0d add editor documentation pages 2026-04-28 13:47:56 +02:00
Astro f9a0480121 fix react three peer dependencies 2026-04-28 13:47:49 +02:00
Astro aa7db176e6 update: app and main 2026-04-28 13:32:54 +02:00
Astro 0f83f57e23 Merge branch 'develop' into feat/docs-routing 2026-04-28 13:31:40 +02:00
Tom Boullay d4e7edaa89 Merge pull request #4 from La-Fabrik-Durable/feat-editor
Feat/editor
2026-04-28 13:22:44 +02:00
Astro ab21df18cb fix editor map reliability 2026-04-28 11:06:09 +02:00
Astro 7e067ecccd Update Map.tsx 2026-04-28 10:53:57 +02:00
Astro 3fac43d5f1 Create Map.tsx 2026-04-28 10:52:05 +02:00
Astro abfbb284f5 Update debugConfig.ts 2026-04-28 10:48:42 +02:00
math-pixel 7588f7f736 Merge pull request #8 from La-Fabrik-Durable/feat/deploy-test
Feat/deploy test
2026-04-28 10:44:12 +02:00
Astro 5ff5b89302 Merge branch 'feat-editor' of https://github.com/La-Fabrik-Durable/La-Fabrik into feat-editor 2026-04-28 10:43:08 +02:00
Astro af35150452 cleaaning 2026-04-28 10:42:57 +02:00
math-pixel 31a99902dd Delete test-editor.html 2026-04-28 10:39:58 +02:00
Astro a259c3d2e2 fix: style 2026-04-28 10:30:31 +02:00
Astro e19cc72ad5 tyle: refresh editor controls with monochrome UI 2026-04-28 10:08:17 +02:00
Astro e1d2bfdc75 refactor: move game map into world folder 2026-04-28 09:47:09 +02:00
Astro 8f40bb8133 docs: document editor architecture and user features 2026-04-28 09:43:51 +02:00
Astro 7b38f04a0d refactor: move editor page and types to conventional folders 2026-04-28 09:29:18 +02:00
math-pixel eade051241 update: deploy file 2026-04-27 20:46:52 +02:00
math-pixel e868e72402 update: deploy file 2026-04-27 20:40:05 +02:00
math-pixel 4783784fb3 feat: change version 2026-04-27 20:27:21 +02:00
math-pixel bfe8c49323 fix :editor 2026-04-27 17:25:56 +02:00
math-pixel 1a91fcaca0 fix: main model map 2026-04-27 16:38:05 +02:00
Astro 055e7b2e63 fix: address docs routing review 2026-04-27 16:32:23 +02:00
math-pixel 21d91f1de1 update models loading in /editor 2026-04-27 16:27:56 +02:00
Astro 68b0ceb593 feat: add localized docs pages 2026-04-27 16:27:08 +02:00
math-pixel 868f7a1cfd fix: load all models/ 2026-04-27 16:07:57 +02:00
Astro 7fd39f58d8 feat: add docs routing 2026-04-27 15:35:56 +02:00
math-pixel 2001955625 fix: address code review comments
- vite.config.ts: fix __dirname for ESM, add 1MB payload limit + JSON validation
- MapViewer.tsx: remove broken window.isTransforming checks, fix callback order
- EditorPage.tsx: derive undoCount from historyManager in handleTransformEnd
- package.json: support Node 20 or 22 in engines
2026-04-27 14:21:50 +02:00
math-pixel 3254291ba7 fix: lint 2026-04-27 14:19:26 +02:00
math-pixel 753a767662 fix: format 2026-04-27 13:57:17 +02:00
math-pixel bcf3a63fc5 Merge branch 'develop' into feat-editor 2026-04-27 13:55:13 +02:00
math-pixel ab8c84e006 Merge remote-tracking branch 'origin/develop' into feat-editor 2026-04-27 13:52:08 +02:00
Astro 8abc69ebc3 Merge branch 'develop' of https://github.com/La-Fabrik-Durable/La-Fabrik into develop 2026-04-27 13:50:50 +02:00
math-pixel b63412de13 update: map & update: package json for CI 2026-04-27 13:44:14 +02:00
Astro 9fdf065c1d Update model.gltf
🔍 Lint / 🪄 Check lint (push) Waiting to run
🔍 Lint / 🎨 Check format (push) Waiting to run
🔍 Lint / 🔎 Typecheck (push) Waiting to run
🔍 Lint / 🏗 Build (push) Blocked by required conditions
📊 Quality / 🔒 Security Audit (push) Waiting to run
📊 Quality / 📋 Dependency Freshness (push) Waiting to run
📊 Quality / 📦 Bundle Size (push) Waiting to run
2026-04-27 13:43:59 +02:00
Astro 4a697ab790 cleaning: repo + model 2026-04-27 13:36:35 +02:00
Tom Boullay 29144f8844 Merge pull request #3 from La-Fabrik-Durable/design
upload: models
2026-04-27 13:31:21 +02:00
Tom Boullay 74b9bf57c8 Merge pull request #2 from La-Fabrik-Durable/feat/implem-map-scene-physique
Feat/implem map scene physique
2026-04-27 11:58:38 +02:00
Astro 5402c343fa fix: docs update debug project tree 2026-04-27 11:57:21 +02:00
Astro 5569da07c1 Create .prettierignore 2026-04-27 11:23:29 +02:00
Astro 38abeb3b49 fix: format & lint 2026-04-27 11:20:59 +02:00
Astro eb0db21d29 clean 2026-04-27 11:14:43 +02:00
Astro 393b653cca fix: archi 2026-04-27 10:53:50 +02:00
Tom Boullay e87004652f update: upload-gltf update -> lafabrik
📦 Model
  ↔️ model.glb (compressed)

🎨 Textures (color)
  🔄 anneaux_base_color.png (compressed)

🪶 Textures (roughness)
  🔄 anneaux_roughness.png (compressed)
   verre_fenetre_roughness.png (compressed)

🧭 Textures (normal)
  🔄 anneaux_normal.png (compressed)
  🔄 anneaux_normal_opengl.png (compressed)

🔩 Textures (metalness)
  🔄 anneaux_metallic.png (compressed)

🧩 Assets
  🔄 anneaux_mixed_ao.png (compressed)
  🔄 anneaux_height.png (compressed)
2026-04-24 17:55:13 +02:00
Astro 8c84663472 add: a logger utils 2026-04-24 14:02:16 +02:00
math-pixel 6b8ba3d58d feat : save map.json on project 2026-04-23 15:40:10 +02:00
math-pixel d0cf876372 feat editor 2026-04-23 15:24:40 +02:00
Astro 38f9f087d1 Create package-lock.json 2026-04-19 16:51:10 +02:00
Astro dcbc1c73f5 refacto : cleaning the codebasebase again 2026-04-19 16:50:11 +02:00
Astro f9c4495610 refacto: cleanning the codebase 2026-04-17 16:03:29 +02:00
Astro 638022339e update : put every constante in the data folder 2026-04-17 15:42:10 +02:00
Astro 20fbaf05e1 update : add map model + octree algo 2026-04-17 11:36:03 +02:00
Astro ed7681a293 update: add a physic scenne 2026-04-17 10:48:18 +02:00
Astro b26da614f0 refacto: enleve la map 2026-04-16 16:11:20 +02:00
Astro 1eed905e8b fix: archi player 2026-04-16 11:00:08 +02:00
Astro 7769959135 refactor: tighten project structure and strengthen tooling 2026-04-16 10:45:05 +02:00
Astro fd7571fbe1 Update hero.png 2026-04-16 09:25:23 +02:00
Astro 3506858c96 fix: lint 2026-04-15 16:42:06 +02:00
Astro 61d7495ec9 feat: add player camera 2026-04-15 16:40:52 +02:00
Astro d486f6f381 feat: add the map 2026-04-15 16:09:02 +02:00
Astro f67799db30 feat: add map blocking and cleanup 2026-04-15 13:36:53 +02:00
308 changed files with 13277 additions and 1858 deletions
+55
View File
@@ -0,0 +1,55 @@
# Agent - La Fabrik
You are working on **La Fabrik**, an interactive 3D web experience built with React Three Fiber.
## Read This First
- `docs/technical/architecture.md` describes the code that exists today.
- `docs/technical/target-architecture.md` describes the intended target-state.
- Do not assume target-state systems already exist.
## Current Implementation
- Stack: React 19, Three.js, `@react-three/fiber`, `@react-three/drei`, `@react-three/rapier`, TypeScript, Vite
- No external global state library is used.
- Current singleton-style services are limited to:
- `InteractionManager`
- `AudioManager`
- `Debug`
- Current gameplay scope is still prototype-level:
- player movement
- trigger/grab interactions
- debug camera and scene switching
- simple audio playback
## Current Architecture Rules
- Scene objects live in `src/world/` and `src/components/3d/`.
- HTML overlays live in `src/components/ui/`.
- Shared static config lives in `src/data/`.
- Debug tooling lives in `src/utils/debug/` and `src/hooks/debug/`.
- Use the `@/` alias for imports from `src/`.
- Prefer small, direct changes over adding new abstraction layers.
- Shared types should live close to their domain and only move outward when they gain multiple real consumers.
## Target-State Guidance
The project may later grow toward a manager-driven gameplay architecture with clearer separation between:
- production world code
- gameplay orchestration
- UI overlays
- debug tooling
That target-state is aspirational until the matching code exists. If a target-state rule conflicts with the current implementation, treat the current code as the source of truth and improve it incrementally.
## Do Not Assume
- There is no `GameManager` in the current codebase.
- There are no implemented mission, zone, cinematic, or dialogue systems yet.
- Dependency versions are not pinned today; do not rewrite dependency strategy unless explicitly asked.
- The old `# route path ...` file header convention is not in use.
## Skills
Files in `.agent/skills/` are supplemental patterns and examples. Some describe target-state or generic practices rather than the exact current implementation, so verify against the code before applying them.
-89
View File
@@ -1,89 +0,0 @@
# Agent — La Fabrik
You are working on **La Fabrik**, an interactive 3D web experience built with React Three Fiber. The player steps into the role of a technician in Altera (2050) and completes missions: repairing an e-bike, fixing a power grid, upgrading a vertical farm.
## Project Identity
- **Stack:** React 19, Three.js, @react-three/fiber 9, @react-three/drei, @react-three/rapier, GSAP, TypeScript, Vite
- **No external state lib.** State is managed by a custom `GameManager` singleton with a subscribe/getState pattern.
- **No Zustand, no Redux, no Context for global state.**
- **Versions are pinned** (no `^` in dependencies). Do not upgrade packages without explicit request.
## Architecture Rules
### Two patterns coexist
1. **Singleton manager classes** — for orchestration, audio, cinematics, zone detection, debug
2. **Declarative React components** — for all 3D scene objects (map, zones, lights, player, postprocessing)
Scene objects are **never** singleton classes. Managers are **never** React components.
### State ownership
- `GameManager` is the single source of truth for durable gameplay state (phase, zone, mission, input lock, dialogue)
- Other managers (`CinematicManager`, `AudioManager`, `ZoneManager`) handle side effects only — they read from GameManager but do not duplicate its state
- React components subscribe to GameManager through `useGameState()` hook
- **High-frequency values** (movement, camera interpolation, physics) stay in `useRef` + `useFrame` — never in React state
### File conventions
- Every file starts with a comment: `# route path <relative_path>` (e.g. `# route path src/world/Map.tsx`)
- Scene components live in `src/world/` and `src/components/3d/`
- UI overlays live in `src/components/ui/`
- Managers live in `src/stateManager/`
- Hooks live in `src/hooks/`
- Static data lives in `src/data/`
- Shaders live in `src/shaders/`
- Utilities live in `src/utils/`
### Import paths
Use `@/` alias for imports from `src/`:
```ts
import { GameManager } from "@/stateManager/GameManager";
import { useGameState } from "@/hooks/useGameState";
```
### Memory management
- Dispose only what you own (custom materials, render targets, manual clones)
- Never blindly deep-dispose shared/cached assets (drei loaders cache models)
- Use `Dispose.material()`, `Dispose.mesh()`, `Dispose.renderTarget()` from `src/utils/Dispose.ts`
### Debug
- Debug panel activates with `?debug` in URL
- All debug logic goes through `Debug.getInstance()` from `src/utils/Debug.ts`
- Never scatter `if (isDev)` blocks across files
- `r3f-perf` is lazy-loaded only in debug mode via `src/components/3d/DebugPerf.tsx`
## Managers (4 max)
| Manager | Responsibility |
| ------------------ | ------------------------------------------------------------------- |
| `GameManager` | Phase, zone, mission, input lock, dialogue — single source of truth |
| `CinematicManager` | GSAP timelines, camera lock/unlock |
| `AudioManager` | Music, SFX, spatial audio |
| `ZoneManager` | Zone detection, LOD triggers |
## Do NOT
- Create new manager classes without explicit request
- Use Zustand, Redux, or React Context for global state
- Put high-frequency values in React state (`useState`)
- Import `CinematicManager`/`AudioManager`/`ZoneManager` directly from components — always go through `GameManager`
- Upgrade pinned dependency versions
- Create files outside the documented architecture without explicit request
## Skills
See `.agent/skills/` for detailed patterns per technology:
- `best-practices.md` — Code generation conventions (W3C, simple, scalable, modern)
- `r3f.md` — React Three Fiber component patterns
- `three.md` — Three.js conventions and AnimationMixer
- `gsap.md` — GSAP timeline and cinematic patterns
- `managers.md` — Singleton manager implementation
- `memory.md` — GPU memory and disposal rules
- `debug.md` — Debug utility and r3f-perf setup
+7 -4
View File
@@ -8,10 +8,12 @@ Append `?debug` to the URL:
http://localhost:5173?debug
```
The free debug camera is toggled from the debug panel, not mounted permanently.
## Debug singleton
```ts
// src/utils/Debug.ts
// src/utils/debug/Debug.ts
import GUI from "lil-gui";
export class Debug {
@@ -56,14 +58,15 @@ if (debug.active) {
r3f-perf is loaded only in debug mode to avoid dependency issues in production:
```tsx
// src/components/3d/DebugPerf.tsx
// src/utils/debug/DebugPerf.tsx
import { Suspense, lazy } from "react";
import { Debug } from "@/utils/debug/Debug";
const Perf = lazy(() => import("r3f-perf").then((m) => ({ default: m.Perf })));
export function DebugPerf() {
const debug = new URLSearchParams(window.location.search).has("debug");
if (!debug) return null;
const debug = Debug.getInstance();
if (!debug.active) return null;
return (
<Suspense fallback={null}>
+7 -1
View File
@@ -3,8 +3,12 @@ name: 🔍 Lint
on:
pull_request:
types: [opened, synchronize, reopened]
branches: [develop, main]
push:
branches: [main]
branches:
- main
- develop
workflow_dispatch:
jobs:
lint:
@@ -71,6 +75,8 @@ jobs:
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v6
with:
lfs: true
- name: 🧰 Setup Node
uses: actions/setup-node@v6
+11 -5
View File
@@ -3,8 +3,12 @@ name: 📊 Quality
on:
pull_request:
types: [opened, synchronize, reopened]
branches: [develop, main]
push:
branches: [main]
branches:
- main
- develop
workflow_dispatch:
jobs:
security:
@@ -53,6 +57,8 @@ jobs:
steps:
- name: ⬇️ Checkout
uses: actions/checkout@v6
with:
lfs: true
- name: 🧰 Setup Node
uses: actions/setup-node@v6
@@ -68,12 +74,12 @@ jobs:
- name: 📏 Check bundle size
run: |
# Get bundle size in KB
SIZE=$(du -k dist | cut -f1)
# Check generated app assets only; public/ model files are runtime assets copied to dist.
SIZE=$(du -k dist/assets | cut -f1)
echo "Bundle size: ${SIZE}KB"
# Threshold: 1000KB (configurable)
THRESHOLD=1000
# Threshold: 5000KB (configurable)
THRESHOLD=5000
if [ "$SIZE" -gt "$THRESHOLD" ]; then
echo "❌ Bundle size ${SIZE}KB exceeds threshold ${THRESHOLD}KB"
+6 -1
View File
@@ -37,4 +37,9 @@ Thumbs.db
# 3D Assets Cache (drei, GLTFJSX)
.drei/
.glitchdrei-cache/
.glitchdrei-cache/
# Temporaire
.backend/
backend/
temp/
+1
View File
@@ -0,0 +1 @@
22.12.0
+19
View File
@@ -0,0 +1,19 @@
dist
node_modules
public/models
public/**/*.glb
public/**/*.gltf
public/**/*.png
public/**/*.jpg
public/**/*.jpeg
public/**/*.webp
public/**/*.hdr
public/**/*.exr
public/**/*.ktx
public/**/*.ktx2
public/**/*.mp3
public/**/*.wav
public/**/*.ogg
public/**/*.mp4
public/**/*.webm
+40 -26
View File
@@ -8,31 +8,31 @@ Built with React, Three.js, and Vite. Runs in the browser, no installation requi
### Build & Language
| Package | Doc |
| -------------------------------------------------- | ------------------------------------ |
| [TypeScript](https://www.typescriptlang.org/docs/) | https://www.typescriptlang.org/docs/ |
| [React](https://react.dev/learn) | https://react.dev/learn |
| [Vite](https://vite.dev/guide/) | https://vite.dev/guide/ |
| [ESLint](https://eslint.org/docs/latest/) | https://eslint.org/docs/latest/ |
| [Prettier](https://prettier.io/docs/) | https://prettier.io/docs/ |
| Package |
| -------------------------------------------------- |
| [TypeScript](https://www.typescriptlang.org/docs/) |
| [React](https://react.dev/learn) |
| [Vite](https://vite.dev/guide/) |
| [ESLint](https://eslint.org/docs/latest/) |
| [Prettier](https://prettier.io/docs/) |
### 3D Engine
| Package | Doc |
| ----------------------------------------------------------------------------------------- | ---------------------------------------------- |
| [Three.js](https://threejs.org/docs/) | https://threejs.org/docs/ |
| [@react-three/fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction) | https://docs.pmnd.rs/react-three-fiber |
| [@react-three/drei](https://pmndrs.github.io/drei) | https://pmndrs.github.io/drei |
| [@react-three/rapier](https://rapier.rs/docs/) | https://rapier.rs/docs/user_guides/javascript/ |
| [@react-three/postprocessing](https://github.com/pmndrs/postprocessing) | https://github.com/pmndrs/postprocessing |
| [GSAP](https://gsap.com/docs/v3/Installation/) | https://gsap.com/docs/v3/ |
| Package |
| ----------------------------------------------------------------------------------------- |
| [Three.js](https://threejs.org/docs/) |
| [@react-three/fiber](https://docs.pmnd.rs/react-three-fiber/getting-started/introduction) |
| [@react-three/drei](https://pmndrs.github.io/drei) |
| [@react-three/rapier](https://rapier.rs/docs/) |
| [@react-three/postprocessing](https://github.com/pmndrs/postprocessing) |
| [GSAP](https://gsap.com/docs/v3/Installation/) |
### Performance & Effects
| Package | Doc |
| --------------------------------------------------------------------------- | --------------------------------------------------------- |
| [r3f-perf](https://github.com/utsuboco/r3f-perf) | https://github.com/utsuboco/r3f-perf |
| [AnimationMixer](https://threejs.org/docs/#api/en/animation/AnimationMixer) | https://threejs.org/docs/#api/en/animation/AnimationMixer |
| Package |
| --------------------------------------------------------------------------- |
| [r3f-perf](https://github.com/utsuboco/r3f-perf) |
| [AnimationMixer](https://threejs.org/docs/#api/en/animation/AnimationMixer) |
## 🗂 Project Structure
@@ -49,8 +49,9 @@ la-fabrik/
└── src/
├── world/ # Single persistent 3D world
│ ├── World.tsx # Main scene composition
│ ├── Map.tsx # Base map, always mounted
│ ├── Lighting.tsx # Ambient, directional, point lights
│ ├── Lighting.tsx # Ambient, directional, point lights
│ ├── Environment.tsx # HDRI, fog, sky
│ ├── PostFX.tsx # Bloom, SSAO, chromatic aberration
│ ├── zones/ # Spatial zones — LOD per zone
@@ -98,11 +99,24 @@ la-fabrik/
│ └── fragment.glsl
├── utils/
│ ├── Debug.ts # lil-gui panel
│ ├── EventEmitter.ts # Simple pub/sub for manager-to-manager events
── Dispose.ts # traverse() + dispose() helper
│ ├── EventEmitter.ts # Simple typed pub/sub utility
│ ├── Sizes.ts # Viewport size tracking
── Time.ts # Animation frame timing utility
│ └── debug/ # Dev-only tools and scene inspection
│ ├── Debug.ts # Global lil-gui manager
│ ├── DebugPerf.tsx # r3f-perf overlay mounted in Canvas
│ ├── isDebugEnabled.ts # Debug query-string helper
│ └── scene/
│ ├── DebugHelpers.tsx # Grid + axes helpers shown in debug mode
│ └── DebugCameraControls.tsx # Free debug camera for map inspection
├── hooks/
│ └── debug/
│ ├── useCameraMode.ts
│ ├── useDebugFolder.ts
│ ├── useDebugStore.ts
│ └── useSceneMode.ts
├── App.tsx # Canvas + UI superimposed
├── App.tsx # Canvas bootstrap
└── main.tsx
```
@@ -115,8 +129,8 @@ npm install
npm run dev
```
Open `http://localhost:5173` — standard experience.
Open `http://localhost:5173?debug` — debug panel + r3f-perf overlay.
- app: `http://localhost:5173`
- debug mode: `http://localhost:5173?debug`
## 📜 License
+338
View File
@@ -0,0 +1,338 @@
# 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)
```
+71 -381
View File
@@ -1,381 +1,71 @@
# Architecture Patterns
The project uses **two complementary patterns**:
- **Singleton service classes** for orchestration and side effects
- **Declarative React components** for all 3D scene objects
This distinction is intentional. Scene elements such as the map, lights, environment, zones, and player are implemented as **React Three Fiber components** and mounted through `<Canvas>`.
Global systems such as gameplay flow, cinematics, audio, and debug tooling are implemented as **manager classes**.
Consistency matters, but the codebase does **not** force the same lifecycle pattern on scene components and global services.
---
## 1. Singleton Pattern for Global Managers Only
Only cross-cutting services use the singleton pattern.
Examples:
- `GameManager`
- `CinematicManager`
- `AudioManager`
- `ZoneManager`
- `Debug`
- `EventEmitter`
These services must exist once, be accessible from anywhere, and coordinate the experience globally.
```ts
// stateManager/GameManager.ts
export class GameManager {
private static _instance: GameManager | null = null;
cinematic!: CinematicManager;
audio!: AudioManager;
zone!: ZoneManager;
static getInstance(): GameManager {
if (!GameManager._instance) {
GameManager._instance = new GameManager();
}
return GameManager._instance;
}
private constructor() {
this.cinematic = CinematicManager.getInstance();
this.audio = AudioManager.getInstance();
this.zone = ZoneManager.getInstance();
}
destroy(): void {
this.cinematic.destroy();
this.audio.destroy();
this.zone.destroy();
GameManager._instance = null;
}
}
```
Usage:
```ts
const game = GameManager.getInstance();
game.startMission("workshop");
```
**Important:** scene objects such as `Map`, `WorkshopZone`, `Lighting`, or `Environment` are **not** singletons and must remain standard React components.
---
## 2. Scene Objects Are React Components, Not Manager Classes
All 3D scene objects are implemented as **declarative React components**.
This includes:
- maps
- lights
- environments
- player controllers
- zones
- interactive props
- postprocessing layers
This keeps the code aligned with the R3F runtime instead of rebuilding a parallel imperative engine.
Example:
```tsx
// world/zones/WorkshopZone.tsx
import { useEffect, useRef } from "react";
import * as THREE from "three";
import { useFrame } from "@react-three/fiber";
import { useGLTF } from "@react-three/drei";
export function WorkshopZone() {
const root = useRef<THREE.Group>(null);
const gltf = useGLTF("/models/workshop/ebike.glb");
const mixer = useRef<THREE.AnimationMixer | null>(null);
useEffect(() => {
mixer.current = new THREE.AnimationMixer(gltf.scene);
return () => {
mixer.current?.stopAllAction();
mixer.current = null;
};
}, [gltf.scene]);
useFrame((_, delta) => {
mixer.current?.update(delta);
});
return <primitive ref={root} object={gltf.scene.clone()} />;
}
```
Per-frame values such as movement, interpolation, camera smoothing, and physics must stay in:
- `useRef`
- `useFrame`
- Rapier bodies
- other frame-based systems
They must **never** go through React state.
---
## 3. Single Source of Truth for Durable Gameplay State
The project uses a single authoritative `GameManager` for durable gameplay state.
React components subscribe to that state through thin hooks.
Other managers communicate through `GameManager`, which acts as the main gameplay orchestrator.
High-frequency values such as movement, camera interpolation, or physics never go through React state and stay in refs or frame-based systems.
```ts
// stateManager/GameManager.ts
type Phase = "loading" | "intro" | "exploring" | "cinematic" | "outro";
type ZoneId = "workshop" | "powerGrid" | "farm" | null;
type GameSnapshot = {
phase: Phase;
activeZone: ZoneId;
missionId: string | null;
missionStep: number;
inputLocked: boolean;
dialogueId: string | null;
};
export class GameManager {
private static _instance: GameManager | null = null;
private listeners = new Set<() => void>();
private state: GameSnapshot = {
phase: "loading",
activeZone: null,
missionId: null,
missionStep: 0,
inputLocked: false,
dialogueId: null,
};
static getInstance(): GameManager {
if (!GameManager._instance) {
GameManager._instance = new GameManager();
}
return GameManager._instance;
}
getState(): GameSnapshot {
return this.state;
}
subscribe(listener: () => void): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
private emit(): void {
this.listeners.forEach((cb) => cb());
}
setPhase(phase: Phase): void {
this.state.phase = phase;
this.emit();
}
setActiveZone(zone: ZoneId): void {
this.state.activeZone = zone;
this.emit();
}
startMission(id: string): void {
this.state.missionId = id;
this.state.missionStep = 0;
this.emit();
}
}
```
```ts
// hooks/useGameState.ts
import { useEffect, useState } from "react";
import { GameManager } from "@/stateManager/GameManager";
export function useGameState() {
const game = GameManager.getInstance();
const [state, setState] = useState(game.getState());
useEffect(() => {
return game.subscribe(() => {
setState({ ...game.getState() });
});
}, [game]);
return state;
}
```
This keeps the architecture simple:
- **GameManager** owns durable gameplay state
- **other managers** handle side effects
- **React components** render that state
- **R3F frame systems** handle fast-changing values
---
## 4. Side Effects Stay in Specialized Managers
Managers other than `GameManager` should not become secondary state stores.
Their role is to manage side effects and specialized runtime logic, such as:
- GSAP timelines
- audio playback
- zone entry detection
- interaction triggers
- camera lock/unlock
- temporary event coordination
They can read from `GameManager`, react to its state, or notify it of important transitions.
Example flow:
```
Component / Hook
GameManager.getInstance()
├── startMission('workshop')
├── cinematic.play('intro_workshop')
├── audio.playAmbience('workshop')
└── zone.setActive('workshop')
```
This keeps the dependency graph understandable while avoiding duplicated durable state.
---
## 5. Memory Management — Dispose Only What You Own
GPU memory must be cleaned carefully.
However, the project does **not** blindly deep-dispose every object on unmount.
Only resources explicitly created and owned by the current component or manager should be disposed.
This includes things like:
- custom materials
- render targets
- postprocessing passes
- manually created geometries
- manually created textures
- temporary clones with owned resources
Shared or cached assets must **not** be blindly disposed.
```ts
// utils/Dispose.ts
import * as THREE from "three";
export class Dispose {
static material(material: THREE.Material): void {
for (const value of Object.values(material)) {
if (value instanceof THREE.Texture) {
value.dispose();
}
}
material.dispose();
}
static mesh(mesh: THREE.Mesh): void {
mesh.geometry?.dispose();
const materials = Array.isArray(mesh.material)
? mesh.material
: [mesh.material];
for (const material of materials) {
if (material) this.material(material);
}
}
static renderTarget(rt: THREE.WebGLRenderTarget): void {
rt.texture.dispose();
rt.dispose();
}
}
```
Example usage:
```ts
useEffect(() => {
const material = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
});
meshRef.current.material = material;
return () => {
Dispose.material(material);
};
}, []);
```
**Rule:** disposal is ownership-based, not automatic and not blind.
---
## 6. Debug Utility
The debug panel can be activated by appending `?debug` to the URL:
`http://localhost:5173?debug`
All debug logic is centralized in `Debug.ts`.
Do not scatter debug checks across the codebase.
```ts
// utils/Debug.ts
import GUI from "lil-gui";
export class Debug {
private static _instance: Debug | null = null;
readonly active: boolean;
gui: GUI | null = null;
static getInstance(): Debug {
if (!Debug._instance) Debug._instance = new Debug();
return Debug._instance;
}
private constructor() {
this.active = new URLSearchParams(window.location.search).has("debug");
if (this.active) {
this.gui = new GUI({ title: "La-Fabrik Debug" });
}
}
destroy(): void {
this.gui?.destroy();
Debug._instance = null;
}
}
```
Usage:
```ts
const debug = Debug.getInstance();
if (debug.active) {
debug.gui!.add(params, "bloomIntensity", 0, 3).name("Bloom");
}
```
# Current Architecture
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:
- `/` 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:
- environment and lighting
- debug helpers and debug camera mode
- either the map scene or the debug physics test scene
- 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/TestScene.tsx` provides a debug-oriented interaction and physics scene.
- `src/world/player/PlayerComponent.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/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.
- Trigger interactions may play audio directly through `AudioManager`.
## Debug System
- 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.
## 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
- The repository is a prototype, not the full intended game runtime.
- `src/world/debug/TestScene.tsx` is part of the active scene composition.
- There is no central gameplay orchestrator such as `GameManager`.
- Missions, zones, cinematics, and dialogue systems are not implemented.
- 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.
-153
View File
@@ -1,153 +0,0 @@
# Best Practices
Generate code that is **simple**, **understandable**, **reviewable**, **scalable**, **optimized**, and **modern**. Follow W3C web standards and platform conventions.
## Naming Conventions
### Files
| Type | Convention | Example |
| ---------- | --------------------------- | -------------------- |
| Components | PascalCase | `WorkshopZone.tsx` |
| Hooks | camelCase with `use` prefix | `useGameState.ts` |
| Managers | PascalCase | `GameManager.ts` |
| Utils | PascalCase | `Dispose.ts` |
| Data | PascalCase | `missions.ts` |
| Shaders | kebab-case | `hologram.vert.glsl` |
### Variables & Functions
| Type | Convention | Example |
| ---------------- | -------------------- | ----------------------------------------- |
| Variables | camelCase | `activeZone`, `missionStep` |
| Functions | camelCase | `startMission()`, `setActiveZone()` |
| Constants | UPPER_SNAKE_CASE | `MAX_SPEED`, `DEFAULT_PHASE` |
| React components | PascalCase | `function WorkshopZone()` |
| React hooks | camelCase with `use` | `useGameState()`, `useFrame()` |
| Classes | PascalCase | `class GameManager` |
| Interfaces/Types | PascalCase | `type GameSnapshot`, `interface ZoneData` |
## Code Style
### Simplicity First
```ts
// Good — clear, direct
function getZoneRadius(zone: Zone): number {
return zone.radius;
}
// Bad — over-abstracted
function getZoneRadius(zone: Zone): number {
return zone[ZoneFields.RADIUS] ?? RADIUS_DEFAULTS[zone.type]?.default ?? 50;
}
```
### Early Return
```ts
// Good
if (!gltf) return null;
if (!visible) return null;
return <primitive object={gltf.scene} />;
```
### Avoid Nested Callbacks
```ts
// Good — flat structure
useEffect(() => {
const mixer = new THREE.AnimationMixer(model);
return () => mixer.stopAllAction();
}, [model]);
```
## TypeScript Rules
### Explicit Types for Exports
```ts
export function useGameState(): GameSnapshot { ... }
export function setPhase(phase: Phase): void { ... }
```
### Never use `any`
```ts
// Good
const ref = useRef<THREE.Group>(null);
// Bad
const ref = useRef<any>(null);
```
## Performance
### useRef for Mutable Values
```ts
// Good — no re-render
const position = useRef(new THREE.Vector3());
// Bad — triggers re-render every frame
const [position, setPosition] = useState(new THREE.Vector3());
```
### Memoize Expensive Computations
```ts
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
```
## Scalability
### Single Responsibility
```ts
// Good — focused component
export function WorkshopZone() {
// Only handles workshop zone logic
}
// Bad — does everything
export function WorkshopZone() {
// Handles zone logic + audio + cinematics + missions
}
```
### Constants for Magic Numbers
```ts
const DEFAULT_CAMERA_DISTANCE = 5;
const ZONE_DETECTION_RADIUS = 20;
```
## Accessibility (W3C)
### Semantic HTML
```tsx
// Good
<button onClick={handleInteract} aria-label="Interact with bike">
<Model />
</button>
// Bad
<div onClick={handleInteract}>
<Model />
</div>
```
## Rules
1. **Simplicity** — Every line of code must be justified.
2. **Readability** — Code is read 10x more than it's written.
3. **Reviewability** — PRs should be understandable in < 5 minutes.
4. **Scalability** — Architecture should support growth without refactoring.
5. **Performance** — Don't optimize prematurely, but don't introduce obvious bottlenecks.
6. **Modern** — Use ES2022+ features, TypeScript strict mode, React hooks.
7. **W3C** — Follow web standards: semantic HTML, ARIA, keyboard navigation.
8. **No Over-Engineering** — Avoid patterns that add complexity without benefit.
+144
View File
@@ -0,0 +1,144 @@
# 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.
+70
View File
@@ -0,0 +1,70 @@
# Target Architecture
This document describes the intended medium-term architecture for the project.
## Relationship To The Current Code
- `docs/technical/architecture.md` is the source of truth for what exists now.
- This document describes intended direction, not implemented behavior.
- If this document conflicts with the current implementation, the current implementation wins.
## Goals
- Keep `App.tsx` small and orchestration-oriented.
- Separate production world code from debug-only runtime paths.
- Keep one clear source of truth per concern.
- Grow gameplay systems incrementally instead of prebuilding empty architecture.
## Intended Layers
### App Layer
- `App.tsx` mounts the canvas scene and top-level HTML overlays.
- It should stay thin and avoid gameplay logic.
### World Layer
- `src/world/` should contain production scene composition and production scene objects.
- Expected responsibilities:
- world composition
- map, environment, lighting
- player controller
- production interaction anchors
- production post-processing, if needed
### Debug Layer
- Debug-only scenes and tooling should be isolated from the production world path.
- Expected responsibilities:
- `lil-gui`
- performance overlay
- scene helpers
- free camera and calibration controls
- debug test scenes used during development
### UI Layer
- `src/components/ui/` should contain player-facing HTML overlays.
- Candidate examples:
- crosshair
- loading flow
- mission HUD
- narrative overlays
### Gameplay Layer
- As the project grows, gameplay state can move toward a clearer orchestration layer.
- Likely concerns:
- missions
- zones
- cinematics
- dialogue
- audio
- interactions
## Rules
- Prefer direct, working code over speculative scaffolding.
- 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.
- Debug-only runtime paths should be clearly marked and easy to remove when obsolete.
+83
View File
@@ -0,0 +1,83 @@
# 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.
+63 -2
View File
@@ -1,3 +1,64 @@
# Features
# Implemented Features
TODO: Documenter les fonctionnalités du jeu.
This document lists features that are implemented in the current codebase.
## Scene
- Fullscreen React Three Fiber scene
- Main map scene loaded from `public/map.json` and matching `public/models/{name}/model.gltf` assets
- Debug physics test scene selectable from the debug panel
- Ambient and directional lighting
- Environment background setup
## Player
- Player camera mode
- Pointer lock mouse look
- Movement with `ZQSD`
- Jumping
- Octree-based collision against the loaded map
## Interactions
- Focus detection by distance and raycast
- Trigger interactions activated with `E`
- Grab interactions activated with the primary mouse button
- Interaction prompt shown for trigger interactions
## Audio
- One-shot sound playback for trigger interactions
- Simple per-sound pooling through `AudioManager`
## Debug Tooling
- `?debug` query param enables the debug panel
- `lil-gui` controls for camera mode, scene mode, and interaction spheres
- Debug scene helpers
- Free debug camera
- `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
- mission system
- zone system
- cinematic system
- dialogue system
- loading flow
- minimap and mission HUD
- full production separation between gameplay and debug scenes
- production backend persistence for editor saves
+2
View File
@@ -0,0 +1,2 @@
[phases.setup]
nixPkgs = ["nodejs"]
+1811 -444
View File
File diff suppressed because it is too large Load Diff
+13 -1
View File
@@ -3,6 +3,9 @@
"private": true,
"version": "0.0.1",
"type": "module",
"engines": {
"node": ">=20.19.0 || >=22.12.0"
},
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build",
@@ -18,10 +21,15 @@
"@react-three/fiber": "^9.6.0",
"@react-three/postprocessing": "^3.0.4",
"@react-three/rapier": "^2.2.0",
"@tanstack/react-router": "^1.168.25",
"gsap": "^3.15.0",
"lil-gui": "^0.21.0",
"lucide-react": "^1.11.0",
"r3f-perf": "^7.2.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"react-markdown": "^10.1.0",
"remark-gfm": "^4.0.1",
"three": "^0.183.2"
},
"devDependencies": {
@@ -36,10 +44,14 @@
"eslint-plugin-react-hooks": "^7.0.1",
"eslint-plugin-react-refresh": "^0.5.2",
"globals": "^17.4.0",
"lil-gui": "^0.21.0",
"prettier": "^3.8.2",
"typescript": "~6.0.2",
"typescript-eslint": "^8.58.0",
"vite": "^8.0.4"
},
"overrides": {
"r3f-perf": {
"@react-three/drei": "$@react-three/drei"
}
}
}
+4587
View File
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.
-1
View File
@@ -1 +0,0 @@
# public/models/environment/\*
-1
View File
@@ -1 +0,0 @@
# public/models/farm/\*
-1
View File
@@ -1 +0,0 @@
# public/models/general/\*
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cc33b99b9334f9db2ec496f20b73b503e4aa50d88bec2e8c98579856d69cd7a0
size 11654940
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:311989a67f7ffd19bf30bb5ea2faabd4d2d58c33b6cb0cf9af1bf6ce1ec24bcf
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:43b2e1ee941e3a640c8d010073e777d84d49080fc2da0a94673b5bf27f3bf932
size 15766
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b520c353a2bee68749ab5b3bcfc8790cab6c6d768b84f8140acd9b4137b46ddb
size 504386
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dd80b377721e70c5721f67e8241036179e27cd3d432d72f7c6022e97c586bd0
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cac93257ca84d489a73a4a132d7c128f7d8f17130c2c0f366e3e758323294527
size 327465
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9bc92d22231f432b9daef4887d29cffb76f5df323bd963618c64b215b711164f
size 15766
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:993c755409766ef7cb048ada6d22b0558c089542fb062fa74ac2e7e4238c3ec1
size 847081
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e1d69c50347120c0c95c001482653c59c0a3c47deae20c552543d1b92d37c5d
size 12332
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2f472061035616b0d31e3623fd89ea515deef730211701418d7dd230e8862d37
size 757042
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:32ca15471c5de27853efc168fe194efbae69b5e6ae9e8ea4afb24b43488ff6c9
size 298418
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1073fc36eebaf28b13640845ff6de475fda4ebf86e05cf7a99eec7ed6dde4aba
size 439434
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4ff25c8213d9810f20fc8fd19e89d4000621df2beefc2d42b4ef0097940b78b5
size 182316
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fd679eb6797a1238b8cb6a0a67d667a1dd20f9fb48632a7d8657af8a7dafe0d2
size 1267604
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e1d69c50347120c0c95c001482653c59c0a3c47deae20c552543d1b92d37c5d
size 12332
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:cdbd33ac4eb90e5b545812c2ac380a8584980bb2d0ee882ecbc2e758c93e6899
size 400516
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f9524a636fc86db283ac992df8e703cfb5a5e7eaded75cfabfd98b6560016818
size 254027
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f9524a636fc86db283ac992df8e703cfb5a5e7eaded75cfabfd98b6560016818
size 254027
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d6b64adf48e75d5eb0c2afdf75898ba78158a78f29b33ef1c7b9b53803fe7c4b
size 29747
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c6cf0a3ea97ec0a9645e11ddbd757d15d3bdfda540bade8f26bb1bed1773226e
size 15766
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e1d69c50347120c0c95c001482653c59c0a3c47deae20c552543d1b92d37c5d
size 12332
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:543df1f679770de0c3f9185083526c03a726b8278cc9282fbcef0063caee6ed5
size 712996
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dd80b377721e70c5721f67e8241036179e27cd3d432d72f7c6022e97c586bd0
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dd80b377721e70c5721f67e8241036179e27cd3d432d72f7c6022e97c586bd0
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9bc92d22231f432b9daef4887d29cffb76f5df323bd963618c64b215b711164f
size 15766
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:76723e5bc7d8fe5ab954f316613a27ff8d0796a19dfb2aef13d7530d74c3c6d9
size 233964
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e1d69c50347120c0c95c001482653c59c0a3c47deae20c552543d1b92d37c5d
size 12332
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:a7c35f3309eceb828a87207b23c36fefad59a6c89d18f38c5f2eb386d47be06e
size 319800
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6dd80b377721e70c5721f67e8241036179e27cd3d432d72f7c6022e97c586bd0
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4d7eb1095d25a9c7e9aa58373478ba9c783216e4e32eb056d1f53c42002edea0
size 16802
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9bc92d22231f432b9daef4887d29cffb76f5df323bd963618c64b215b711164f
size 15766
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9c2c9b5498fab82f7d2ea60d25ea55892d826f5de1549a391f74ccd99fce67cc
size 195872
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:9b97d8b8d019d246924f6731aa03ed8f36169f75e1d5b98d09c09e7f96b08eb9
size 752957
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e1d69c50347120c0c95c001482653c59c0a3c47deae20c552543d1b92d37c5d
size 12332
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d7ac13a3a383243a991c7bace0de4c3a1a57e2d1f76fc5ae78419bdf4192715b
size 948507
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:efd017718813e29db22cf18d3caad987aa631b7c6500f1dbb03731d01d590787
size 117180
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f1e604806defcd692e1b8b1dde2df3d74dab111cad818caee6587f6ddff825ce
size 487294
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:81d4cbb54f7cf13737e53f8a2dbef07bea84f5d91305e67f289f0a57193fd2be
size 25110
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:51bd212f1786589fe327011e317d06a87ac1753c97f783a58d5dd829e61fb150
size 761672
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e1d69c50347120c0c95c001482653c59c0a3c47deae20c552543d1b92d37c5d
size 12332
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:ec5e374fdf53803274f3db4f99a0ff73dd8ff98c9d39e00ada3df17e161de16d
size 517383
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6ee5ae4da883ed21807192e3ab78a327b3b32f7ac4fd7625f7c0027191a2db8a
size 167195
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:eb9948e4d989471eb977e172fff0fa8e26545f64915847d7b29c80da4d31e480
size 167040
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:f7f1b8d05ddf8991d200d55ee2a51e8ec194ed3c25d4e365d841197639521165
size 33325
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:84e69fbc325447188e4561b2709bc6b7f494b2eb942d3f292533c7d65eb1c301
size 350884
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0e1d69c50347120c0c95c001482653c59c0a3c47deae20c552543d1b92d37c5d
size 12332
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0d2f0cb413ff44ab6235c318ca9640afde4f67122efd75c01bd44a70135ded23
size 556212
-3
View File
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dcb2a2e534e728bc6937d5c984cd4c63dd226e432c52d0973b5ffc53fab331fc
size 126366
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2528c1e7b076b11bf6462215a6a797ee468c96648b6b213edc976f8e10483b26
size 167854
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:954e6b09f05c3848e26f5fc4a68f8419a387d38456698c2137682d1a854d950c
size 75784
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:14e25fcf300b7895a17fbe073fd4ef58e9ffda6cf6913ffa9b5c86f2d636f43e
size 59105
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3dbf4a607d88f01b1ccb6702c5600876ae9339ed6ef017b44f04da18045cb3ec
size 15765
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2c6fc8e0d3d23d39b9293e6cf95b641c2458ea9d1b140e51f3cfd0d8b6ba1788
size 48818
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:37faa3d88ee97556f2935c13feb309f06551042f94e02878cca65c1bf0edc893
size 13356
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5f1be2118699edd31eb3e4657a79c1c998d08f03f679e33545dd603ddee498d6
size 30651
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7419843e9e57550f2e7dbf8762f350451b455c3ab9a379d1b281184212df5182
size 30663
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d2648942081ffd0c83d4415fd34941963d8d5d05eb838113bd1a56306475a07b
size 24392
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4d4609e73fec0d94e7efc65521239647bf37c511489c7656e1925c1875744810
size 577758

Some files were not shown because too many files have changed in this diff Show More