From 4c42e112682c5fad166c3807a2884c929a8584f1 Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Mon, 25 May 2026 01:10:10 +0200 Subject: [PATCH] fix(debug): stabilize map debug controls --- src/hooks/debug/useMapPerformanceDebug.ts | 5 +- src/utils/debug/Debug.ts | 68 ++++++++++++++++------- src/world/vegetation/VegetationSystem.tsx | 33 ++++++++--- 3 files changed, 76 insertions(+), 30 deletions(-) diff --git a/src/hooks/debug/useMapPerformanceDebug.ts b/src/hooks/debug/useMapPerformanceDebug.ts index 74cfc78..f52b7f1 100644 --- a/src/hooks/debug/useMapPerformanceDebug.ts +++ b/src/hooks/debug/useMapPerformanceDebug.ts @@ -1,4 +1,5 @@ import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; +import { Debug } from "@/utils/debug/Debug"; import { MAP_PERFORMANCE_GROUP_NAMES, MAP_PERFORMANCE_MODEL_NAMES, @@ -14,7 +15,9 @@ function toLabel(value: string): string { } export function useMapPerformanceDebug(): void { - useDebugFolder("Performance / Map", (folder) => { + useDebugFolder("Map", (folder) => { + Debug.getInstance().addFogControl(folder); + const { groups, models, diff --git a/src/utils/debug/Debug.ts b/src/utils/debug/Debug.ts index fab6c24..3c29920 100644 --- a/src/utils/debug/Debug.ts +++ b/src/utils/debug/Debug.ts @@ -17,12 +17,11 @@ interface DebugEvents { } const DEBUG_FOLDER_ORDER = [ - "La Fabrik", "Lighting", "Game", "Interaction", "Hand Tracking", - "Performance / Map", + "Map", ] as const; function isRecord(value: unknown): value is Record { @@ -100,14 +99,13 @@ export class Debug { sceneMode: storedControls.sceneMode ?? "game", }; - this.gui = this.active ? new GUI({ title: "Debug" }) : null; + this.gui = this.active ? new GUI({ title: "La Fabrik" }) : null; if (this.gui) { - const folder = this.createFolder("La Fabrik", { open: true }); + this.gui.open(); + this.createOrderedFolders(); - if (!folder) return; - - folder + this.gui .add(this.controls, "cameraMode", { Player: "player", Debug: "debug" }) .name("Camera Mode") .onChange((value: CameraMode) => { @@ -115,7 +113,7 @@ export class Debug { this.saveAndEmit(); }); - folder + this.gui .add(this.controls, "sceneMode", { Game: "game", Physics: "physics" }) .name("Scene") .onChange((value: SceneMode) => { @@ -123,7 +121,7 @@ export class Debug { this.saveAndEmit(); }); - folder + this.gui .add(this.controls, "showPerf") .name("R3F Perf") .onChange((value: boolean) => { @@ -131,7 +129,7 @@ export class Debug { this.emit(); }); - folder + this.gui .add(this.controls, "showDebugOverlay") .name("Debug Overlay") .onChange((value: boolean) => { @@ -139,14 +137,6 @@ export class Debug { this.emit(); }); - folder - .add(this.controls, "fogEnabled") - .name("Fog") - .onChange((value: boolean) => { - this.controls.fogEnabled = value; - this.emit(); - }); - const handTrackingFolder = this.createFolder("Hand Tracking"); handTrackingFolder @@ -180,8 +170,15 @@ export class Debug { const existing = this.folders.get(name); if (existing) { - this.folderRefCounts.set(name, (this.folderRefCounts.get(name) ?? 0) + 1); - return null; + const refCount = this.folderRefCounts.get(name) ?? 0; + + if (refCount > 0) { + this.folderRefCounts.set(name, refCount + 1); + return null; + } + + this.folderRefCounts.set(name, 1); + return existing; } const folder = this.gui.addFolder(name); @@ -198,6 +195,16 @@ export class Debug { return folder; } + addFogControl(folder: GUI): void { + folder + .add(this.controls, "fogEnabled") + .name("Fog") + .onChange((value: boolean) => { + this.controls.fogEnabled = value; + this.emit(); + }); + } + destroyFolder(name: string): void { const folder = this.folders.get(name); const refCount = this.folderRefCounts.get(name); @@ -279,6 +286,27 @@ export class Debug { this.emit(); } + private createOrderedFolders(): void { + for (const folderName of DEBUG_FOLDER_ORDER) { + this.ensureFolder(folderName); + } + } + + private ensureFolder(name: string): GUI | null { + if (!this.gui) return null; + + const existing = this.folders.get(name); + if (existing) return existing; + + const folder = this.gui.addFolder(name); + folder.close(); + this.folders.set(name, folder); + this.folderRefCounts.set(name, 0); + this.sortFolders(); + + return folder; + } + private sortFolders(): void { if (!this.gui) return; diff --git a/src/world/vegetation/VegetationSystem.tsx b/src/world/vegetation/VegetationSystem.tsx index cb84d67..dfc9539 100644 --- a/src/world/vegetation/VegetationSystem.tsx +++ b/src/world/vegetation/VegetationSystem.tsx @@ -1,4 +1,4 @@ -import { Suspense, useMemo, useRef, useState } from "react"; +import { Suspense, useCallback, useMemo, useRef, useState } from "react"; import { useFrame, useThree } from "@react-three/fiber"; import { CHUNK_CONFIG } from "@/data/world/fogConfig"; import { useCameraMode } from "@/hooks/debug/useCameraMode"; @@ -106,16 +106,21 @@ export function VegetationSystem(): React.JSX.Element | null { }, [data, groups, models]); const visibleChunks = streamingEnabled - ? chunks.filter((chunk) => activeChunkKeys.has(chunk.key)) + ? chunks.filter((chunk) => { + if (activeChunkKeys.size > 0) { + return activeChunkKeys.has(chunk.key); + } + + return ( + Math.hypot( + chunk.centerX - camera.position.x, + chunk.centerZ - camera.position.z, + ) <= CHUNK_CONFIG.loadRadius + ); + }) : chunks; - useFrame(({ clock }) => { - if (!streamingEnabled) return; - - const now = clock.elapsedTime * 1000; - if (now - lastUpdateRef.current < CHUNK_CONFIG.updateInterval) return; - lastUpdateRef.current = now; - + const updateActiveChunks = useCallback(() => { const nextKeys = new Set(); const cameraX = camera.position.x; const cameraZ = camera.position.z; @@ -143,6 +148,16 @@ export function VegetationSystem(): React.JSX.Element | null { } setActiveChunkKeys(nextKeys); + }, [activeChunkKeys, camera, chunks]); + + useFrame(({ clock }) => { + if (!streamingEnabled) return; + + const now = clock.elapsedTime * 1000; + if (now - lastUpdateRef.current < CHUNK_CONFIG.updateInterval) return; + lastUpdateRef.current = now; + + updateActiveChunks(); }); if (isLoading || !data) {