Feat/map-environment #6

Merged
math-pixel merged 116 commits from feat/map-environment into develop 2026-05-29 00:00:51 +00:00
10 changed files with 148 additions and 28 deletions
Showing only changes of commit 25e0f7e062 - Show all commits
+27 -5
View File
@@ -1,17 +1,39 @@
import { TERRAIN_COLORS } from "@/data/world/terrainConfig"; import { TERRAIN_COLORS } from "@/data/world/terrainConfig";
export type FogMode = "linear" | "exp2";
export const FOG_CONFIG = { export const FOG_CONFIG = {
enabled: true, enabled: true,
color: "#dce8df", mode: "exp2" as FogMode,
near: 38, color: "#dfe7d8",
far: 45, near: 32,
far: 48,
density: 0.032,
}; };
export const FOG_LIGHTING_COLOR_MIX = {
ambient: 0.3,
sun: 0.7,
};
export const FOG_BOUNDS = {
near: { min: 0, max: 100, step: 1 },
far: { min: 1, max: 160, step: 1 },
density: { min: 0.001, max: 0.05, step: 0.001 },
};
export interface FogState {
density: number;
far: number;
mode: FogMode;
near: number;
}
export const CHUNK_CONFIG = { export const CHUNK_CONFIG = {
enabled: true, enabled: true,
chunkSize: 35, chunkSize: 35,
loadRadius: 45, loadRadius: 65,
unloadRadius: 45, unloadRadius: 75,
updateInterval: 350, updateInterval: 350,
}; };
+29 -1
View File
@@ -1,7 +1,9 @@
import { CLOUD_BOUNDS } from "@/data/world/cloudConfig"; import { CLOUD_BOUNDS } from "@/data/world/cloudConfig";
import { FOG_BOUNDS, type FogMode } from "@/data/world/fogConfig";
import { WIND_BOUNDS } from "@/data/world/windConfig"; import { WIND_BOUNDS } from "@/data/world/windConfig";
import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
import { useWorldSettingsStore } from "@/managers/stores/useWorldSettingsStore"; import { useWorldSettingsStore } from "@/managers/stores/useWorldSettingsStore";
import { Debug } from "@/utils/debug/Debug";
export function useEnvironmentDebug(): void { export function useEnvironmentDebug(): void {
useDebugFolder("Dynamic Wind", (folder) => { useDebugFolder("Dynamic Wind", (folder) => {
@@ -49,13 +51,39 @@ export function useEnvironmentDebug(): void {
}); });
useDebugFolder("Environment", (folder) => { useDebugFolder("Environment", (folder) => {
const { clouds, graphics, setClouds, setDynamicClouds } = Debug.getInstance().addFogControl(folder);
const { clouds, fog, graphics, setClouds, setDynamicClouds, setFog } =
useWorldSettingsStore.getState(); useWorldSettingsStore.getState();
const controls = { const controls = {
...clouds, ...clouds,
...fog,
dynamicClouds: graphics.dynamicClouds, dynamicClouds: graphics.dynamicClouds,
}; };
folder
.add(controls, "mode", { Linear: "linear", Exp2: "exp2" })
.name("Fog mode")
.onChange((mode: FogMode) => setFog({ mode }));
folder
.add(controls, "near", FOG_BOUNDS.near.min, FOG_BOUNDS.near.max)
.step(FOG_BOUNDS.near.step)
.name("Fog near")
.onChange((near: number) => setFog({ near }));
folder
.add(controls, "far", FOG_BOUNDS.far.min, FOG_BOUNDS.far.max)
.step(FOG_BOUNDS.far.step)
.name("Fog far")
.onChange((far: number) => setFog({ far }));
folder
.add(controls, "density", FOG_BOUNDS.density.min, FOG_BOUNDS.density.max)
.step(FOG_BOUNDS.density.step)
.name("Fog density")
.onChange((density: number) => setFog({ density }));
folder folder
.add(controls, "dynamicClouds") .add(controls, "dynamicClouds")
.name("Clouds") .name("Clouds")
@@ -1,5 +1,4 @@
import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
import { Debug } from "@/utils/debug/Debug";
import { import {
MAP_PERFORMANCE_GROUP_NAMES, MAP_PERFORMANCE_GROUP_NAMES,
MAP_PERFORMANCE_MODEL_NAMES, MAP_PERFORMANCE_MODEL_NAMES,
@@ -16,8 +15,6 @@ function toLabel(value: string): string {
export function useMapPerformanceDebug(): void { export function useMapPerformanceDebug(): void {
useDebugFolder("Map", (folder) => { useDebugFolder("Map", (folder) => {
Debug.getInstance().addFogControl(folder);
const { const {
groups, groups,
models, models,
+10
View File
@@ -0,0 +1,10 @@
import { useWorldSettingsStore } from "@/managers/stores/useWorldSettingsStore";
import type { FogState } from "@/data/world/fogConfig";
export function useFogSettings(): FogState {
return useWorldSettingsStore((state) => state.fog);
}
export function useSetFogSettings(): (fog: Partial<FogState>) => void {
return useWorldSettingsStore((state) => state.setFog);
}
@@ -1,5 +1,6 @@
import { create } from "zustand"; import { create } from "zustand";
import { CLOUD_DEFAULTS, type CloudState } from "@/data/world/cloudConfig"; import { CLOUD_DEFAULTS, type CloudState } from "@/data/world/cloudConfig";
import { FOG_CONFIG, type FogState } from "@/data/world/fogConfig";
import { WIND_DEFAULTS, type WindState } from "@/data/world/windConfig"; import { WIND_DEFAULTS, type WindState } from "@/data/world/windConfig";
import { import {
GRAPHICS_DEFAULTS, GRAPHICS_DEFAULTS,
@@ -8,12 +9,14 @@ import {
interface WorldSettingsState { interface WorldSettingsState {
clouds: CloudState; clouds: CloudState;
fog: FogState;
wind: WindState; wind: WindState;
graphics: GraphicsState; graphics: GraphicsState;
} }
interface WorldSettingsActions { interface WorldSettingsActions {
setClouds: (clouds: Partial<CloudState>) => void; setClouds: (clouds: Partial<CloudState>) => void;
setFog: (fog: Partial<FogState>) => void;
setWind: (wind: Partial<WindState>) => void; setWind: (wind: Partial<WindState>) => void;
setWindSpeed: (speed: number) => void; setWindSpeed: (speed: number) => void;
setWindDirection: (direction: number) => void; setWindDirection: (direction: number) => void;
@@ -31,6 +34,12 @@ type WorldSettingsStore = WorldSettingsState & WorldSettingsActions;
const DEFAULT_STATE: WorldSettingsState = { const DEFAULT_STATE: WorldSettingsState = {
clouds: { ...CLOUD_DEFAULTS }, clouds: { ...CLOUD_DEFAULTS },
fog: {
density: FOG_CONFIG.density,
far: FOG_CONFIG.far,
mode: FOG_CONFIG.mode,
near: FOG_CONFIG.near,
},
wind: { ...WIND_DEFAULTS }, wind: { ...WIND_DEFAULTS },
graphics: { ...GRAPHICS_DEFAULTS }, graphics: { ...GRAPHICS_DEFAULTS },
}; };
@@ -43,6 +52,11 @@ export const useWorldSettingsStore = create<WorldSettingsStore>()((set) => ({
clouds: { ...state.clouds, ...cloudsUpdate }, clouds: { ...state.clouds, ...cloudsUpdate },
})), })),
setFog: (fogUpdate) =>
set((state) => ({
fog: { ...state.fog, ...fogUpdate },
})),
setWind: (windUpdate) => setWind: (windUpdate) =>
set((state) => ({ set((state) => ({
wind: { ...state.wind, ...windUpdate }, wind: { ...state.wind, ...windUpdate },
+36 -6
View File
@@ -1,3 +1,6 @@
import { useMemo } from "react";
import { useFrame, useThree } from "@react-three/fiber";
import * as THREE from "three";
import { import {
GAME_SCENE_FALLBACK_BACKGROUND_COLOR, GAME_SCENE_FALLBACK_BACKGROUND_COLOR,
GAME_SCENE_FALLBACK_SKY_MODEL_PATH, GAME_SCENE_FALLBACK_SKY_MODEL_PATH,
@@ -6,24 +9,46 @@ import {
GAME_SCENE_SKY_MODEL_SCALE, GAME_SCENE_SKY_MODEL_SCALE,
PHYSICS_SCENE_BACKGROUND_COLOR, PHYSICS_SCENE_BACKGROUND_COLOR,
} from "@/data/world/environmentConfig"; } from "@/data/world/environmentConfig";
import { FOG_CONFIG } from "@/data/world/fogConfig"; import { FOG_CONFIG, FOG_LIGHTING_COLOR_MIX } from "@/data/world/fogConfig";
import { useCameraMode } from "@/hooks/debug/useCameraMode"; import { useCameraMode } from "@/hooks/debug/useCameraMode";
import { useSceneMode } from "@/hooks/debug/useSceneMode"; import { useSceneMode } from "@/hooks/debug/useSceneMode";
import { useFogSettings } from "@/hooks/world/useFogSettings";
import { import {
isMapModelVisible, isMapModelVisible,
useMapPerformanceStore, useMapPerformanceStore,
} from "@/managers/stores/useMapPerformanceStore"; } from "@/managers/stores/useMapPerformanceStore";
import { SkyModel } from "@/components/three/world/SkyModel"; import { SkyModel } from "@/components/three/world/SkyModel";
import { useDebugStore } from "@/hooks/debug/useDebugStore"; import { useDebugStore } from "@/hooks/debug/useDebugStore";
import { LIGHTING_STATE } from "@/world/lightingState";
const tempSunFogColor = new THREE.Color();
function getLightingFogColor(target: THREE.Color): THREE.Color {
target.set(LIGHTING_STATE.ambientColor);
target.multiplyScalar(FOG_LIGHTING_COLOR_MIX.ambient);
tempSunFogColor.set(LIGHTING_STATE.sunColor);
target.add(tempSunFogColor.multiplyScalar(FOG_LIGHTING_COLOR_MIX.sun));
return target;
}
export function Environment(): React.JSX.Element { export function Environment(): React.JSX.Element {
const cameraMode = useCameraMode(); const cameraMode = useCameraMode();
const sceneMode = useSceneMode(); const sceneMode = useSceneMode();
const fog = useFogSettings();
const fogEnabled = useDebugStore((debug) => debug.getFogEnabled()); const fogEnabled = useDebugStore((debug) => debug.getFogEnabled());
const groups = useMapPerformanceStore((state) => state.groups); const groups = useMapPerformanceStore((state) => state.groups);
const models = useMapPerformanceStore((state) => state.models); const models = useMapPerformanceStore((state) => state.models);
const scene = useThree((state) => state.scene);
const fogColor = useMemo(() => getLightingFogColor(new THREE.Color()), []);
const showSky = isMapModelVisible("sky", { groups, models }); const showSky = isMapModelVisible("sky", { groups, models });
useFrame(() => {
if (!scene.fog) return;
getLightingFogColor(scene.fog.color);
});
if (sceneMode === "physics") { if (sceneMode === "physics") {
return ( return (
<color attach="background" args={[PHYSICS_SCENE_BACKGROUND_COLOR]} /> <color attach="background" args={[PHYSICS_SCENE_BACKGROUND_COLOR]} />
@@ -35,11 +60,16 @@ export function Environment(): React.JSX.Element {
{FOG_CONFIG.enabled && {FOG_CONFIG.enabled &&
fogEnabled && fogEnabled &&
sceneMode === "game" && sceneMode === "game" &&
cameraMode === "player" ? ( cameraMode === "player" &&
<fog fog.mode === "linear" ? (
attach="fog" <fog attach="fog" args={[fogColor, fog.near, fog.far]} />
args={[FOG_CONFIG.color, FOG_CONFIG.near, FOG_CONFIG.far]} ) : null}
/> {FOG_CONFIG.enabled &&
fogEnabled &&
sceneMode === "game" &&
cameraMode === "player" &&
fog.mode === "exp2" ? (
<fogExp2 attach="fog" args={[fogColor, fog.density]} />
) : null} ) : null}
{showSky ? ( {showSky ? (
<SkyModel <SkyModel
+1 -13
View File
@@ -5,7 +5,6 @@ import {
AMBIENT_INTENSITY_MAX, AMBIENT_INTENSITY_MAX,
AMBIENT_INTENSITY_MIN, AMBIENT_INTENSITY_MIN,
AMBIENT_INTENSITY_STEP, AMBIENT_INTENSITY_STEP,
LIGHTING_DEFAULTS,
SUN_INTENSITY_MAX, SUN_INTENSITY_MAX,
SUN_INTENSITY_MIN, SUN_INTENSITY_MIN,
SUN_INTENSITY_STEP, SUN_INTENSITY_STEP,
@@ -20,24 +19,13 @@ import {
SUN_Z_STEP, SUN_Z_STEP,
} from "@/data/world/lightingConfig"; } from "@/data/world/lightingConfig";
import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; import { useDebugFolder } from "@/hooks/debug/useDebugFolder";
import { LIGHTING_STATE } from "@/world/lightingState";
const SHADOW_MAP_SIZE = 2048; const SHADOW_MAP_SIZE = 2048;
const SHADOW_CAMERA_SIZE = 170; const SHADOW_CAMERA_SIZE = 170;
const SHADOW_CAMERA_NEAR = 0.5; const SHADOW_CAMERA_NEAR = 0.5;
const SHADOW_CAMERA_FAR = 300; const SHADOW_CAMERA_FAR = 300;
type LightingState = {
ambientColor: string;
ambientIntensity: number;
sunColor: string;
sunIntensity: number;
sunX: number;
sunY: number;
sunZ: number;
};
const LIGHTING_STATE: LightingState = { ...LIGHTING_DEFAULTS };
export function Lighting(): React.JSX.Element { export function Lighting(): React.JSX.Element {
const ambient = useRef<AmbientLight>(null); const ambient = useRef<AmbientLight>(null);
const sun = useRef<DirectionalLight>(null); const sun = useRef<DirectionalLight>(null);
+13
View File
@@ -0,0 +1,13 @@
import { LIGHTING_DEFAULTS } from "@/data/world/lightingConfig";
export interface LightingState {
ambientColor: string;
ambientIntensity: number;
sunColor: string;
sunIntensity: number;
sunX: number;
sunY: number;
sunZ: number;
}
export const LIGHTING_STATE: LightingState = { ...LIGHTING_DEFAULTS };
+10
View File
@@ -44,8 +44,10 @@ export function WaterSurface({
uOpacity: { value: WATER_SHADER_CONFIG.opacity }, uOpacity: { value: WATER_SHADER_CONFIG.opacity },
uDeepOpacity: { value: WATER_SHADER_CONFIG.deepOpacity }, uDeepOpacity: { value: WATER_SHADER_CONFIG.deepOpacity },
uFogEnabled: { value: 0 }, uFogEnabled: { value: 0 },
uFogMode: { value: 0 },
uFogNear: { value: FOG_CONFIG.near }, uFogNear: { value: FOG_CONFIG.near },
uFogFar: { value: FOG_CONFIG.far }, uFogFar: { value: FOG_CONFIG.far },
uFogDensity: { value: FOG_CONFIG.density },
uFogColor: { value: new THREE.Color(FOG_CONFIG.color) }, uFogColor: { value: new THREE.Color(FOG_CONFIG.color) },
}), }),
[], [],
@@ -61,8 +63,10 @@ export function WaterSurface({
uFlowX, uFlowX,
uFlowZ, uFlowZ,
uFogColor, uFogColor,
uFogDensity,
uFogEnabled, uFogEnabled,
uFogFar, uFogFar,
uFogMode,
uFogNear, uFogNear,
uNoiseScale, uNoiseScale,
uTime, uTime,
@@ -77,9 +81,15 @@ export function WaterSurface({
if (scene.fog instanceof THREE.Fog) { if (scene.fog instanceof THREE.Fog) {
if (uFogEnabled) uFogEnabled.value = 1; if (uFogEnabled) uFogEnabled.value = 1;
if (uFogMode) uFogMode.value = 0;
if (uFogNear) uFogNear.value = scene.fog.near; if (uFogNear) uFogNear.value = scene.fog.near;
if (uFogFar) uFogFar.value = scene.fog.far; if (uFogFar) uFogFar.value = scene.fog.far;
if (uFogColor) uFogColor.value.copy(scene.fog.color); if (uFogColor) uFogColor.value.copy(scene.fog.color);
} else if (scene.fog instanceof THREE.FogExp2) {
if (uFogEnabled) uFogEnabled.value = 1;
if (uFogMode) uFogMode.value = 1;
if (uFogDensity) uFogDensity.value = scene.fog.density;
if (uFogColor) uFogColor.value.copy(scene.fog.color);
} else if (uFogEnabled) { } else if (uFogEnabled) {
uFogEnabled.value = 0; uFogEnabled.value = 0;
} }
+8
View File
@@ -33,8 +33,10 @@ export const WATER_FRAGMENT_SHADER = /* glsl */ `
uniform float uOpacity; uniform float uOpacity;
uniform float uDeepOpacity; uniform float uDeepOpacity;
uniform float uFogEnabled; uniform float uFogEnabled;
uniform float uFogMode;
uniform float uFogNear; uniform float uFogNear;
uniform float uFogFar; uniform float uFogFar;
uniform float uFogDensity;
uniform vec3 uFogColor; uniform vec3 uFogColor;
varying vec2 vUv; varying vec2 vUv;
@@ -152,6 +154,12 @@ export const WATER_FRAGMENT_SHADER = /* glsl */ `
if (uFogEnabled > 0.5) { if (uFogEnabled > 0.5) {
float fogDistance = distance(cameraPosition, vWorldPosition); float fogDistance = distance(cameraPosition, vWorldPosition);
float fogFactor = smoothstep(uFogNear, uFogFar, fogDistance); float fogFactor = smoothstep(uFogNear, uFogFar, fogDistance);
if (uFogMode > 0.5) {
fogFactor = 1.0 - exp(-uFogDensity * uFogDensity * fogDistance * fogDistance);
}
fogFactor = clamp(fogFactor, 0.0, 1.0);
color = mix(color, uFogColor, fogFactor); color = mix(color, uFogColor, fogFactor);
alpha *= 1.0 - fogFactor; alpha *= 1.0 - fogFactor;
} }