From 8f40bb8133b5b2907885e795f094ce958a7d90b5 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Tue, 28 Apr 2026 09:43:51 +0200 Subject: [PATCH] docs: document editor architecture and user features --- .gitignore | 1 + EDITOR-README.md | 164 ------- docs/technical/architecture.md | 28 +- docs/technical/editor.md | 143 ++++++ docs/user/features.md | 17 +- src/App.tsx | 2 +- src/components/editor/EditorCamera.tsx | 21 - src/components/editor/EditorFPSController.tsx | 144 ------ src/components/game/GameMap.tsx | 40 +- .../editor/components}/EditorControls.tsx | 42 +- .../editor/controls}/FlyController.tsx | 4 +- src/features/editor/hooks/useEditorHistory.ts | 161 +++++++ .../editor/hooks/useEditorSceneData.ts | 65 +++ .../editor/scene/EditorMap.tsx} | 17 +- .../editor/scene/EditorScene.tsx} | 15 +- src/features/editor/utils/loadEditorScene.ts | 43 ++ src/index.css | 80 ++-- src/pages/EditorPage.tsx | 442 ------------------ src/pages/editor/EditorPage.tsx | 194 ++++++++ src/types/editor.ts | 11 - src/utils/loadMapSceneData.ts | 53 +++ 21 files changed, 776 insertions(+), 911 deletions(-) delete mode 100644 EDITOR-README.md create mode 100644 docs/technical/editor.md delete mode 100644 src/components/editor/EditorCamera.tsx delete mode 100644 src/components/editor/EditorFPSController.tsx rename src/{components/editor => features/editor/components}/EditorControls.tsx (72%) rename src/{components/editor => features/editor/controls}/FlyController.tsx (96%) create mode 100644 src/features/editor/hooks/useEditorHistory.ts create mode 100644 src/features/editor/hooks/useEditorSceneData.ts rename src/{components/editor/MapViewer.tsx => features/editor/scene/EditorMap.tsx} (96%) rename src/{components/editor/EditorViewer.tsx => features/editor/scene/EditorScene.tsx} (91%) create mode 100644 src/features/editor/utils/loadEditorScene.ts delete mode 100644 src/pages/EditorPage.tsx create mode 100644 src/pages/editor/EditorPage.tsx create mode 100644 src/utils/loadMapSceneData.ts diff --git a/.gitignore b/.gitignore index f2b6c27..9b04b43 100644 --- a/.gitignore +++ b/.gitignore @@ -39,5 +39,6 @@ Thumbs.db .drei/ .glitchdrei-cache/ +# Temporaire .backend/ backend/ diff --git a/EDITOR-README.md b/EDITOR-README.md deleted file mode 100644 index ed0b8ea..0000000 --- a/EDITOR-README.md +++ /dev/null @@ -1,164 +0,0 @@ -# Editor Integration - La-Fabrik - -## ✅ Intégration terminée - -L'éditeur du POC-Editor a été entièrement intégré dans La-Fabrik sous la route `/editor`. - -## 🎯 Fonctionnalités implémentées - -### 1. **Routing React** (React Router DOM) - -- Route `/` → Jeu original La-Fabrik -- Route `/editor` → Éditeur de map 3D -- Navigation fluide entre les deux - -### 2. **Chargement automatique de map.json** - -- Cherche `/map.json` dans `public/` au démarrage -- Scan automatique des modèles dans `public/models/` -- Fallback: interface d'upload de dossier - -### 3. **Système de caméra hybride** - -- Réutilise `useCameraMode()` existant de La-Fabrik -- **Mode debug** : OrbitControls (édition libre) -- **Mode player** : FPSController custom (WASD/ZQSD + souris + jump) -- Pas de dépendance à `octree` dans l'éditeur - -### 4. **Visualisation 3D avancée** - -- Support complet des **MapNode** (position, rotation, scale) -- Chargement de modèles GLB/GLTF depuis `models/{nom}/model.glb` -- Fallback: cubes colorés pour modèles manquants -- Highlight orange pour sélection - -### 5. **Panneau de contrôle latéral** - -- Transformations: Translate (T), Rotate (R), Scale (S) -- Undo/Redo avec compteurs (Ctrl+Z / Ctrl+Y) -- Export JSON des modifications -- Info sur la sélection et contrôles - -### 6. **Interaction et sélection** - -- Click pour sélectionner objets -- ESC pour désélectionner -- Intégration avec `InteractionManager` (système existant) -- Visual feedback avec highlighting - -## 📁 Structure des fichiers - -``` -src/components/editor/ -├── EditorPage.tsx # Composant route /editor + CSS + upload -├── EditorViewer.tsx # Composant principal (logique état + effets) -├── EditorCamera.tsx # Caméra (switch OrbitControls/FPS) -├── EditorFPSController.tsx # Controller FPS sans collisions -├── MapViewer.tsx # Visualisation map.json + modèles -├── EditorControls.tsx # Panneau latéral UI -├── types.ts # Types: MapNode, SceneData, TransformMode -└── EditorPage.css # Styles scoped (~150 lignes) -``` - -## 🔧 Setup initial - -1. **Dépendances installées** : - -```bash -npm install react-router-dom --legacy-peer-deps -``` - -2. **Modifications apportées** : - -- `main.tsx` → Enveloppé par `` -- `App.tsx` → Routes avec `` -- `vite.config.ts` → Pas de modif nécessaire - -## 🚀 Utilisation - -### Accès à l'éditeur - -1. Lancer le serveur : `npm run dev` -2. Ouvrir `http://localhost:5176/editor` -3. Si `map.json` manque → uploader un dossier - -### Structure du dossier - -``` -public/ -├── map.json # Fichier JSON avec MapNode[] -└── models/ - ├── arbre/ - │ └── model.glb # Modèle 3D - ├── building/ - │ └── model.glb - └── ... -``` - -### Format map.json - -```json -[ - { - "name": "arbre", - "type": "Mesh", - "position": [0, 5, 0], - "rotation": [0, 1.57, 0], - "scale": [1, 1, 1] - } -] -``` - -### Contrôles claviers - -- **T** : Translate (déplacement) -- **R** : Rotate (rotation) -- **S** : Scale (échelle) -- **Ctrl+Z** : Undo -- **Ctrl+Y** : Redo -- **ESC** : Désélection -- **WASD/ZQSD** : Déplacement (mode player) -- **Espace** : Saut (mode player) -- **Clic souris** : Sélection objet - -## 🧪 Tests recommandés - -1. **Navigation** : `/` ↔ `/editor` -2. **Upload dossier** : Télécharger un dossier test -3. **Caméra** : Tester debug vs player mode -4. **Transformations** : T/R/S sur objets sélectionnés -5. **Undo/Redo** : Modifier puis annuler/rétablir -6. **Export JSON** : Exporter map.json modifié -7. **Performances** : Avec maps complexes (test map.json 12K lignes) - -## 📝 Points d'attention - -### Performance - -- Map.json de 12K+ lignes peut impacter perfs -- Optimization: implémenter LOD si nécessaire -- Cleanup des blob URLs après upload - -### Intégration systèmes existants - -- Réutilise `useCameraMode()` de La-Fabrik -- Compatible avec `InteractionManager` -- Styles scoped (`editor-` prefix) pour éviter conflits - -### Évolution future - -- Ajouter snap/toggle grid -- Implémenter duplication objets -- Ajouter outils texturing/matériaux -- Synchronisation avec backend - -## 🔗 URLs de test - -- Jeu : `http://localhost:5176/` -- Éditeur : `http://localhost:5176/editor` -- Page de test : `file:///C:/Users/mathc/Documents/Programation/LA-FABRIK/La-Fabrik/test-editor.html` - ---- - -**Statut** : ✅ Intégration complète avec toutes les fonctionnalités demandées. -**Prochaines étapes** : Tests utilisateur + optimisation performance. diff --git a/docs/technical/architecture.md b/docs/technical/architecture.md index 769575d..bce96e2 100644 --- a/docs/technical/architecture.md +++ b/docs/technical/architecture.md @@ -4,13 +4,16 @@ This document describes the code that exists today in the repository. ## Runtime Structure -- `src/App.tsx` mounts the `Canvas`, the 3D `World`, the debug perf overlay, and the HTML overlays. +- `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/Map.tsx` loads the main map model and builds the collision octree. +- `src/components/game/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. @@ -38,6 +41,26 @@ This document describes the code that exists today in the repository. - `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/features/editor/components/EditorControls.tsx` renders the HTML editor control panel. +- `src/features/editor/scene/EditorScene.tsx` composes the editor canvas scene, camera controls, lights, shortcuts, and map rendering. +- `src/features/editor/scene/EditorMap.tsx` renders map nodes, fallback cubes, selection highlighting, and transform controls. +- `src/features/editor/controls/FlyController.tsx` provides player-style editor navigation. +- `src/features/editor/hooks/useEditorSceneData.ts` loads scene data and handles folder upload fallback. +- `src/features/editor/hooks/useEditorHistory.ts` owns editor undo and redo state. +- `src/features/editor/utils/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 still a prototype, not the full intended game runtime. @@ -45,3 +68,4 @@ This document describes the code that exists today in the repository. - There is no central gameplay orchestrator such as `GameManager` yet. - 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. diff --git a/docs/technical/editor.md b/docs/technical/editor.md new file mode 100644 index 0000000..46b6d68 --- /dev/null +++ b/docs/technical/editor.md @@ -0,0 +1,143 @@ +# 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/main.tsx` wraps the app with `BrowserRouter`. +- `src/App.tsx` defines the route and imports `EditorPage` from `src/pages/editor/EditorPage.tsx`. + +## File Structure + +```txt +src/ +├── pages/ +│ └── editor/ +│ └── EditorPage.tsx +├── features/ +│ └── editor/ +│ ├── components/ +│ │ └── EditorControls.tsx +│ ├── controls/ +│ │ └── FlyController.tsx +│ ├── hooks/ +│ │ ├── useEditorHistory.ts +│ │ └── useEditorSceneData.ts +│ ├── scene/ +│ │ ├── EditorMap.tsx +│ │ └── EditorScene.tsx +│ └── utils/ +│ └── loadEditorScene.ts +├── types/ +│ └── editor.ts +└── utils/ + └── loadMapSceneData.ts +``` + +## Responsibilities + +`src/pages/editor/EditorPage.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/features/editor/hooks/useEditorSceneData.ts` loads the default map data and handles folder uploads. + +`src/features/editor/hooks/useEditorHistory.ts` owns editor undo and redo history. + +`src/features/editor/scene/EditorScene.tsx` composes the editor canvas scene, camera controls, lights, keyboard shortcuts, and `EditorMap`. + +`src/features/editor/scene/EditorMap.tsx` renders map nodes, fallback cubes, selection highlighting, and transform controls. + +`src/features/editor/components/EditorControls.tsx` renders the HTML control panel outside the canvas. + +`src/features/editor/controls/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/features/editor/utils/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 may need virtualization, culling, or LOD support later. +- There is no snap-to-grid, duplication, material editing, or object creation workflow yet. +- Save to Server is a Vite dev-server helper, not a production backend API. diff --git a/docs/user/features.md b/docs/user/features.md index 133c126..a04fb80 100644 --- a/docs/user/features.md +++ b/docs/user/features.md @@ -5,7 +5,7 @@ This document lists features that are implemented in the current codebase. ## Scene - Fullscreen React Three Fiber scene -- Main map scene loaded from `public/models/map/model.gltf` +- 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 @@ -38,6 +38,20 @@ This document lists features that are implemented in the current codebase. - 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 @@ -47,3 +61,4 @@ This document lists features that are implemented in the current codebase. - loading flow - minimap and mission HUD - full production separation between gameplay and debug scenes +- production backend persistence for editor saves diff --git a/src/App.tsx b/src/App.tsx index 627c226..9cc5acc 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,7 +5,7 @@ import { Crosshair } from "@/components/ui/Crosshair"; import { InteractPrompt } from "@/components/ui/InteractPrompt"; import { DebugPerf } from "@/utils/debug/DebugPerf"; import { World } from "@/world/World"; -import { EditorPage } from "@/pages/EditorPage"; +import { EditorPage } from "@/pages/editor/EditorPage"; function App(): React.JSX.Element { return ( diff --git a/src/components/editor/EditorCamera.tsx b/src/components/editor/EditorCamera.tsx deleted file mode 100644 index 10debb5..0000000 --- a/src/components/editor/EditorCamera.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { useCameraMode } from "@/hooks/debug/useCameraMode"; -import { OrbitControls } from "@react-three/drei"; - -export default function EditorCamera() { - const cameraMode = useCameraMode(); - - if (cameraMode === "debug") { - return ( - - ); - } - - return null; -} diff --git a/src/components/editor/EditorFPSController.tsx b/src/components/editor/EditorFPSController.tsx deleted file mode 100644 index 1e68ed5..0000000 --- a/src/components/editor/EditorFPSController.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { useEffect, useRef, useCallback } from "react"; -import { useFrame, useThree } from "@react-three/fiber"; -import * as THREE from "three"; - -const WALK_SPEED = 8; -const JUMP_SPEED = 7; -const MOUSE_SENSITIVITY = 0.002; - -export default function EditorFPSController() { - const { camera: rawCamera } = useThree(); - const cameraRef = useRef(rawCamera); - const keys = useRef>(new Set()); - const velocity = useRef(new THREE.Vector3()); - const wantsJump = useRef(false); - const mouseLocked = useRef(false); - - const handleKeyDown = useCallback((e: KeyboardEvent) => { - keys.current.add(e.code); - - switch (e.key.toLowerCase()) { - case " ": - wantsJump.current = true; - e.preventDefault(); - break; - case "e": - e.preventDefault(); - break; - } - }, []); - - const handleKeyUp = useCallback((e: KeyboardEvent) => { - keys.current.delete(e.code); - }, []); - - const handleMouseMove = useCallback( - (e: MouseEvent) => { - if (!mouseLocked.current) return; - - const movementX = e.movementX || 0; - const movementY = e.movementY || 0; - - cameraRef.current.rotation.y -= movementX * MOUSE_SENSITIVITY; - cameraRef.current.rotation.x -= movementY * MOUSE_SENSITIVITY; - cameraRef.current.rotation.x = Math.max( - -Math.PI / 2, - Math.min(Math.PI / 2, cameraRef.current.rotation.x), - ); - }, - [cameraRef], - ); - - const handleMouseDown = useCallback((e: MouseEvent) => { - if (e.button === 0) { - if (!mouseLocked.current) { - mouseLocked.current = true; - document.body.requestPointerLock(); - } - } - }, []); - - useEffect(() => { - window.addEventListener("keydown", handleKeyDown); - window.addEventListener("keyup", handleKeyUp); - window.addEventListener("mousemove", handleMouseMove); - window.addEventListener("mousedown", handleMouseDown); - - return () => { - window.removeEventListener("keydown", handleKeyDown); - window.removeEventListener("keyup", handleKeyUp); - window.removeEventListener("mousemove", handleMouseMove); - window.removeEventListener("mousedown", handleMouseDown); - }; - }, [handleKeyDown, handleKeyUp, handleMouseMove, handleMouseDown]); - - useFrame((_, delta) => { - const dt = Math.min(delta, 0.05); - - if (!mouseLocked.current) return; - - const forward = new THREE.Vector3(0, 0, -1); - const right = new THREE.Vector3(1, 0, 0); - const up = new THREE.Vector3(0, 1, 0); - - forward.applyQuaternion(cameraRef.current.quaternion); - right.applyQuaternion(cameraRef.current.quaternion); - - forward.setY(0); - right.setY(0); - - if (forward.lengthSq() > 0) forward.normalize(); - if (right.lengthSq() > 0) right.normalize(); - if (up.lengthSq() > 0) up.normalize(); - - const isForward = - keys.current.has("KeyW") || - keys.current.has("ArrowUp") || - keys.current.has("KeyZ"); - const isBackward = - keys.current.has("KeyS") || keys.current.has("ArrowDown"); - const isLeft = - keys.current.has("KeyA") || - keys.current.has("ArrowLeft") || - keys.current.has("KeyQ"); - const isRight = keys.current.has("KeyD") || keys.current.has("ArrowRight"); - - const wishDir = new THREE.Vector3(); - if (isForward) wishDir.add(forward); - if (isBackward) wishDir.sub(forward); - if (isLeft) wishDir.sub(right); - if (isRight) wishDir.add(right); - - if (wishDir.lengthSq() > 0) { - wishDir.normalize().multiplyScalar(WALK_SPEED * dt * 10); - velocity.current.x += wishDir.x; - velocity.current.z += wishDir.z; - } - - const damping = Math.exp(-8 * dt); - velocity.current.x *= damping; - velocity.current.z *= damping; - - if (wantsJump.current) { - velocity.current.y = JUMP_SPEED; - wantsJump.current = false; - } else { - velocity.current.y -= 20 * dt; - } - - cameraRef.current.position.copy( - cameraRef.current.position - .clone() - .add(velocity.current.clone().multiplyScalar(dt)), - ); - - if (cameraRef.current.position.y < 2) { - cameraRef.current.position.y = 2; - velocity.current.y = 0; - velocity.current.x *= 0.9; - velocity.current.z *= 0.9; - } - }); - - return null; -} diff --git a/src/components/game/GameMap.tsx b/src/components/game/GameMap.tsx index e9ca10b..f5b7695 100644 --- a/src/components/game/GameMap.tsx +++ b/src/components/game/GameMap.tsx @@ -1,21 +1,17 @@ -import { useEffect, useState, useMemo, useRef } from "react"; +import { useEffect, useState, useRef } from "react"; import { useGLTF } from "@react-three/drei"; import * as THREE from "three"; import { useOctreeGraphNode } from "@/hooks/useOctreeGraphNode"; +import { loadMapSceneData } from "@/utils/loadMapSceneData"; import type { OctreeReadyHandler } from "@/types/3d"; import type { MapNode } from "@/types/editor"; -const MAP_JSON_PATH = "/map.json"; - interface GameMapProps { onOctreeReady: OctreeReadyHandler; } export function GameMap({ onOctreeReady }: GameMapProps): React.JSX.Element { const [mapNodes, setMapNodes] = useState([]); - const [availableModels, setAvailableModels] = useState>( - new Set(), - ); const [isLoading, setIsLoading] = useState(true); const groupRef = useRef(null); @@ -24,32 +20,16 @@ export function GameMap({ onOctreeReady }: GameMapProps): React.JSX.Element { useEffect(() => { const loadMap = async () => { try { - const nodesResponse = await fetch(MAP_JSON_PATH); - if (!nodesResponse.ok) { + const sceneData = await loadMapSceneData(); + if (!sceneData) { console.warn("map.json not found"); setIsLoading(false); return; } - const nodes: MapNode[] = await nodesResponse.json(); - setMapNodes(nodes); - - const uniqueModelNames = [...new Set(nodes.map((n) => n.name))]; - const available = new Set(); - - for (const modelName of uniqueModelNames) { - try { - const modelUrl = `/models/${modelName}/model.gltf`; - const modelResponse = await fetch(modelUrl); - const contentType = modelResponse.headers.get("content-type") || ""; - if (contentType.includes("gltf") || contentType.includes("model")) { - available.add(modelName); - } - } catch { - /* empty */ - } - } - setAvailableModels(available); + setMapNodes( + sceneData.mapNodes.filter((node) => sceneData.models.has(node.name)), + ); } catch (error) { console.error("Error loading map:", error); } finally { @@ -60,17 +40,13 @@ export function GameMap({ onOctreeReady }: GameMapProps): React.JSX.Element { loadMap(); }, []); - const nodesToRender = useMemo(() => { - return mapNodes.filter((node) => availableModels.has(node.name)); - }, [mapNodes, availableModels]); - if (isLoading) { return <>; } return ( - {nodesToRender.map((node, index) => ( + {mapNodes.map((node, index) => ( ))} diff --git a/src/components/editor/EditorControls.tsx b/src/features/editor/components/EditorControls.tsx similarity index 72% rename from src/components/editor/EditorControls.tsx rename to src/features/editor/components/EditorControls.tsx index 31b6498..01e6a73 100644 --- a/src/components/editor/EditorControls.tsx +++ b/src/features/editor/components/EditorControls.tsx @@ -12,12 +12,11 @@ interface EditorControlsProps { onRedo: () => void; onExportJson: () => void; onSaveToServer?: () => void; - onResetCamera?: () => void; onPlayerMode?: () => void; isPlayerMode?: boolean; } -export default function EditorControls({ +export function EditorControls({ transformMode, onTransformModeChange, selectedNodeIndex, @@ -29,10 +28,9 @@ export default function EditorControls({ onRedo, onExportJson, onSaveToServer, - onResetCamera, onPlayerMode, isPlayerMode, -}: EditorControlsProps) { +}: EditorControlsProps): React.JSX.Element { const cameraPosition = [0, 50, 100]; return ( @@ -47,30 +45,30 @@ export default function EditorControls({

Transform

-
+
-
+
- {onSaveToServer && ( - )}

View

- {onResetCamera && ( - - )} - {onPlayerMode && (