feat(world): add map lod graphics presets
This commit is contained in:
@@ -1,5 +1,20 @@
|
||||
import { GRAPHICS_PRESETS } from "@/data/world/graphicsConfig";
|
||||
import type {
|
||||
GraphicsPreset,
|
||||
GraphicsPresetConfig,
|
||||
} from "@/data/world/graphicsConfig";
|
||||
import { useWorldSettingsStore } from "@/managers/stores/useWorldSettingsStore";
|
||||
|
||||
export function useGraphicsPreset(): GraphicsPreset {
|
||||
return useWorldSettingsStore((state) => state.graphics.preset);
|
||||
}
|
||||
|
||||
export function useGraphicsPresetConfig(): GraphicsPresetConfig {
|
||||
return useWorldSettingsStore(
|
||||
(state) => GRAPHICS_PRESETS[state.graphics.preset],
|
||||
);
|
||||
}
|
||||
|
||||
export function useDynamicGrass(): boolean {
|
||||
return useWorldSettingsStore((state) => state.graphics.dynamicGrass);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { CHUNK_CONFIG } from "@/data/world/chunkStreamingConfig";
|
||||
import { selectMapModelPathByDistance } from "@/data/world/mapLodConfig";
|
||||
import { useGraphicsPreset } from "@/hooks/world/useGraphicsSettings";
|
||||
|
||||
interface UseMapLodModelPathArgs {
|
||||
modelName: string;
|
||||
modelPath: string;
|
||||
position: readonly [number, number, number];
|
||||
}
|
||||
|
||||
export function useMapLodModelPath({
|
||||
modelName,
|
||||
modelPath,
|
||||
position,
|
||||
}: UseMapLodModelPathArgs): string {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const graphicsPreset = useGraphicsPreset();
|
||||
const lastUpdateRef = useRef(-CHUNK_CONFIG.updateInterval);
|
||||
const initialModelPath = selectMapModelPathByDistance({
|
||||
distance: Math.hypot(
|
||||
position[0] - camera.position.x,
|
||||
position[2] - camera.position.z,
|
||||
),
|
||||
modelName,
|
||||
modelPath,
|
||||
preset: graphicsPreset,
|
||||
});
|
||||
const activeModelPathRef = useRef(initialModelPath);
|
||||
const [activeModelPath, setActiveModelPath] = useState(initialModelPath);
|
||||
|
||||
const updateModelPath = useCallback(() => {
|
||||
const distance = Math.hypot(
|
||||
position[0] - camera.position.x,
|
||||
position[2] - camera.position.z,
|
||||
);
|
||||
const nextModelPath = selectMapModelPathByDistance({
|
||||
distance,
|
||||
modelName,
|
||||
modelPath,
|
||||
preset: graphicsPreset,
|
||||
});
|
||||
|
||||
if (nextModelPath === activeModelPathRef.current) return;
|
||||
|
||||
activeModelPathRef.current = nextModelPath;
|
||||
setActiveModelPath(nextModelPath);
|
||||
}, [camera, graphicsPreset, modelName, modelPath, position]);
|
||||
|
||||
useEffect(() => {
|
||||
updateModelPath();
|
||||
}, [updateModelPath]);
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
const now = clock.elapsedTime * 1000;
|
||||
if (now - lastUpdateRef.current < CHUNK_CONFIG.updateInterval) return;
|
||||
lastUpdateRef.current = now;
|
||||
|
||||
updateModelPath();
|
||||
});
|
||||
|
||||
return activeModelPath;
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { CHUNK_CONFIG } from "@/data/world/chunkStreamingConfig";
|
||||
|
||||
@@ -8,6 +8,11 @@ export interface WorldChunkLike {
|
||||
key: string;
|
||||
}
|
||||
|
||||
interface WorldChunkVisibilityConfig {
|
||||
loadRadius: number;
|
||||
unloadRadius: number;
|
||||
}
|
||||
|
||||
function areSetsEqual(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {
|
||||
return a.size === b.size && [...a].every((key) => b.has(key));
|
||||
}
|
||||
@@ -15,6 +20,7 @@ function areSetsEqual(a: ReadonlySet<string>, b: ReadonlySet<string>): boolean {
|
||||
export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
chunks: readonly TChunk[],
|
||||
streamingEnabled: boolean,
|
||||
visibilityConfig: WorldChunkVisibilityConfig = CHUNK_CONFIG,
|
||||
): readonly TChunk[] {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const lastUpdateRef = useRef(-CHUNK_CONFIG.updateInterval);
|
||||
@@ -35,8 +41,8 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
);
|
||||
const wasActive = activeChunkKeysRef.current.has(chunk.key);
|
||||
const radius = wasActive
|
||||
? CHUNK_CONFIG.unloadRadius
|
||||
: CHUNK_CONFIG.loadRadius;
|
||||
? visibilityConfig.unloadRadius
|
||||
: visibilityConfig.loadRadius;
|
||||
|
||||
if (distance <= radius) {
|
||||
nextKeys.add(chunk.key);
|
||||
@@ -47,7 +53,18 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
|
||||
activeChunkKeysRef.current = nextKeys;
|
||||
setActiveChunkKeys(nextKeys);
|
||||
}, [camera, chunks]);
|
||||
}, [
|
||||
camera,
|
||||
chunks,
|
||||
visibilityConfig.loadRadius,
|
||||
visibilityConfig.unloadRadius,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!streamingEnabled) return;
|
||||
|
||||
updateActiveChunks();
|
||||
}, [streamingEnabled, updateActiveChunks]);
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!streamingEnabled) return;
|
||||
@@ -71,7 +88,7 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
Math.hypot(
|
||||
chunk.centerX - camera.position.x,
|
||||
chunk.centerZ - camera.position.z,
|
||||
) <= CHUNK_CONFIG.loadRadius
|
||||
) <= visibilityConfig.loadRadius
|
||||
);
|
||||
});
|
||||
}, [
|
||||
@@ -80,5 +97,6 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
camera.position.z,
|
||||
chunks,
|
||||
streamingEnabled,
|
||||
visibilityConfig.loadRadius,
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user