feat(debug): add map performance visibility controls

This commit is contained in:
tom-boullay
2026-05-21 15:38:23 +02:00
parent 5e594c51f7
commit e4ee2d768b
7 changed files with 258 additions and 9 deletions
+10 -1
View File
@@ -7,10 +7,17 @@ import {
PHYSICS_SCENE_BACKGROUND_COLOR,
} from "@/data/world/environmentConfig";
import { useSceneMode } from "@/hooks/debug/useSceneMode";
import {
isMapModelVisible,
useMapPerformanceStore,
} from "@/managers/stores/useMapPerformanceStore";
import { SkyModel } from "@/components/three/world/SkyModel";
export function Environment(): React.JSX.Element {
const sceneMode = useSceneMode();
const groups = useMapPerformanceStore((state) => state.groups);
const models = useMapPerformanceStore((state) => state.models);
const showSky = isMapModelVisible("sky", { groups, models });
if (sceneMode === "physics") {
return (
@@ -18,7 +25,7 @@ export function Environment(): React.JSX.Element {
);
}
return (
return showSky ? (
<SkyModel
fallbackColor={GAME_SCENE_FALLBACK_BACKGROUND_COLOR}
fallbackModelPath={GAME_SCENE_FALLBACK_SKY_MODEL_PATH}
@@ -26,5 +33,7 @@ export function Environment(): React.JSX.Element {
modelPath={GAME_SCENE_SKY_MODEL_PATH}
scale={GAME_SCENE_SKY_MODEL_SCALE}
/>
) : (
<color attach="background" args={[GAME_SCENE_FALLBACK_BACKGROUND_COLOR]} />
);
}
+26 -6
View File
@@ -11,6 +11,10 @@ import * as THREE from "three";
import { useClonedObject } from "@/hooks/three/useClonedObject";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import { TerrainModel } from "@/components/three/world/TerrainModel";
import {
isMapModelVisible,
useMapPerformanceStore,
} from "@/managers/stores/useMapPerformanceStore";
import { GameMapCollision } from "@/world/GameMapCollision";
import { GeneratedMapNodeInstance } from "@/world/map-generated/GeneratedMapNodeInstance";
import { isGeneratedMapModelName } from "@/world/map-generated/generatedMapModelConfig";
@@ -101,6 +105,8 @@ export function GameMap({
onOctreeReady,
}: GameMapProps): React.JSX.Element {
const settledMapNodesRef = useRef(new Set<number>());
const groups = useMapPerformanceStore((state) => state.groups);
const models = useMapPerformanceStore((state) => state.models);
const [mapNodes, setMapNodes] = useState<LoadedMapNode[]>([]);
const [mapLoaded, setMapLoaded] = useState(false);
const [settledMapNodeCount, setSettledMapNodeCount] = useState(0);
@@ -221,17 +227,23 @@ export function GameMap({
node={mapNode.node}
onSettled={() => handleMapNodeSettled(index)}
>
<MapNodeInstance
node={mapNode.node}
modelUrl={mapNode.modelUrl}
onSettled={() => handleMapNodeSettled(index)}
/>
{isMapModelVisible(mapNode.node.name, { groups, models }) ? (
<MapNodeInstance
node={mapNode.node}
modelUrl={mapNode.modelUrl}
onSettled={() => handleMapNodeSettled(index)}
/>
) : (
<HiddenMapNode onSettled={() => handleMapNodeSettled(index)} />
)}
</ModelErrorBoundary>
))}
</group>
<MapInstancingSystem />
<VegetationSystem />
<TerrainModel />
{isMapModelVisible("terrain", { groups, models }) ? (
<TerrainModel />
) : null}
<GameMapCollision
buildOctree={buildOctree}
mapReady={mapReady}
@@ -244,6 +256,14 @@ export function GameMap({
);
}
function HiddenMapNode({ onSettled }: { onSettled: () => void }): null {
useEffect(() => {
onSettled();
}, [onSettled]);
return null;
}
/**
* Temporary development-only map reducer.
*
+3
View File
@@ -5,6 +5,7 @@ import {
PLAYER_SPAWN_POSITION_PHYSICS,
} from "@/data/player/playerConfig";
import { useCameraMode } from "@/hooks/debug/useCameraMode";
import { useMapPerformanceDebug } from "@/hooks/debug/useMapPerformanceDebug";
import { useSceneMode } from "@/hooks/debug/useSceneMode";
import { useHandTrackingSnapshot } from "@/hooks/handTracking/useHandTrackingSnapshot";
import { useWorldSceneLoading } from "@/hooks/world/useWorldSceneLoading";
@@ -35,6 +36,8 @@ interface WorldProps {
}
export function World({ onLoadingStateChange }: WorldProps): React.JSX.Element {
useMapPerformanceDebug();
const cameraMode = useCameraMode();
const sceneMode = useSceneMode();
const mainState = useGameStore((state) => state.mainState);
@@ -1,4 +1,8 @@
import { Suspense } from "react";
import {
isMapModelVisible,
useMapPerformanceStore,
} from "@/managers/stores/useMapPerformanceStore";
import { InstancedMapAsset } from "@/world/map-instancing/InstancedMapAsset";
import {
MAP_INSTANCING_ASSETS,
@@ -7,6 +11,8 @@ import {
import { useMapInstancingData } from "@/world/map-instancing/useMapInstancingData";
export function MapInstancingSystem(): React.JSX.Element | null {
const groups = useMapPerformanceStore((state) => state.groups);
const models = useMapPerformanceStore((state) => state.models);
const { data, isLoading } = useMapInstancingData();
if (isLoading || !data) {
@@ -14,7 +20,8 @@ export function MapInstancingSystem(): React.JSX.Element | null {
}
const enabledAssets = Object.entries(MAP_INSTANCING_ASSETS).filter(
([, config]) => config.enabled,
([, config]) =>
config.enabled && isMapModelVisible(config.mapName, { groups, models }),
);
return (
+8 -1
View File
@@ -1,4 +1,8 @@
import { Suspense } from "react";
import {
isMapModelVisible,
useMapPerformanceStore,
} from "@/managers/stores/useMapPerformanceStore";
import { InstancedVegetation } from "@/world/vegetation/InstancedVegetation";
import { useVegetationData } from "@/world/vegetation/useVegetationData";
import {
@@ -7,6 +11,8 @@ import {
} from "@/world/vegetation/vegetationConfig";
export function VegetationSystem(): React.JSX.Element | null {
const groups = useMapPerformanceStore((state) => state.groups);
const models = useMapPerformanceStore((state) => state.models);
const { data, isLoading } = useVegetationData();
if (isLoading || !data) {
@@ -14,7 +20,8 @@ export function VegetationSystem(): React.JSX.Element | null {
}
const enabledTypes = Object.entries(VEGETATION_TYPES).filter(
([, config]) => config.enabled,
([, config]) =>
config.enabled && isMapModelVisible(config.mapName, { groups, models }),
);
return (