diff --git a/src/data/world/environmentConfig.ts b/src/data/world/environmentConfig.ts index bf287f7..5d7272e 100644 --- a/src/data/world/environmentConfig.ts +++ b/src/data/world/environmentConfig.ts @@ -1,5 +1,5 @@ export const GAME_SCENE_SKY_MODEL_PATH = "/models/skybox/skybox.gltf"; export const GAME_SCENE_FALLBACK_SKY_MODEL_PATH = "/models/sky/model.glb"; -export const GAME_SCENE_SKY_MODEL_SCALE = 300; +export const GAME_SCENE_SKY_MODEL_SCALE = 100; export const GAME_SCENE_FALLBACK_SKY_MODEL_SCALE = 1; export const PHYSICS_SCENE_BACKGROUND_COLOR = "#0b1018"; diff --git a/src/data/world/fogConfig.ts b/src/data/world/fogConfig.ts new file mode 100644 index 0000000..3365f80 --- /dev/null +++ b/src/data/world/fogConfig.ts @@ -0,0 +1,18 @@ +import { TERRAIN_COLORS } from "@/data/world/terrainConfig"; + +export const FOG_CONFIG = { + enabled: true, + color: "#c8dbbe", + near: 50, + far: 70, +}; + +export const CHUNK_CONFIG = { + enabled: true, + chunkSize: 40, + loadRadius: 70, + unloadRadius: 80, + updateInterval: 500, +}; + +export const GROUND_PLANE_COLOR = TERRAIN_COLORS.grass1.hex; diff --git a/src/data/world/graphicsConfig.ts b/src/data/world/graphicsConfig.ts new file mode 100644 index 0000000..5830594 --- /dev/null +++ b/src/data/world/graphicsConfig.ts @@ -0,0 +1,13 @@ +export const GRAPHICS_DEFAULTS = { + dynamicGrass: true, + dynamicTrees: true, + dynamicClouds: true, + shadowsEnabled: true, + grassDensity: 1.0, +}; + +export const GRAPHICS_BOUNDS = { + grassDensity: { min: 0.1, max: 2.0, step: 0.1 }, +}; + +export type GraphicsState = typeof GRAPHICS_DEFAULTS; diff --git a/src/data/world/terrainConfig.ts b/src/data/world/terrainConfig.ts new file mode 100644 index 0000000..fb6edb8 --- /dev/null +++ b/src/data/world/terrainConfig.ts @@ -0,0 +1,108 @@ +export const TERRAIN_COLORS = { + grass1: { + hex: "#84C66B", + rgb: [132, 198, 107] as const, + type: "grass" as const, + grassTipColor: "#84C66B", + }, + grass2: { + hex: "#67B058", + rgb: [103, 176, 88] as const, + type: "grass" as const, + grassTipColor: "#67B058", + }, + grass3: { + hex: "#A3CA5B", + rgb: [163, 202, 91] as const, + type: "grass" as const, + grassTipColor: "#A3CA5B", + }, + potager: { + hex: "#342420", + rgb: [52, 36, 32] as const, + type: "tile" as const, + tileModel: "/models/potager/potager.gltf", + tileSize: 1, + }, + terre: { + hex: "#513E2C", + rgb: [81, 62, 44] as const, + type: "none" as const, + }, + chemin: { + hex: "#F5D896", + rgb: [245, 216, 150] as const, + type: "tile" as const, + tileModel: "/models/chemins/model.gltf", + tileSize: 1, + }, + eau: { + hex: "#91DAF5", + rgb: [145, 218, 245] as const, + type: "water" as const, + }, + cailloux: { + hex: "#B6D3DE", + rgb: [182, 211, 222] as const, + type: "none" as const, + }, +} as const; + +export type TerrainColorKey = keyof typeof TERRAIN_COLORS; +export type TerrainType = "grass" | "tile" | "water" | "none"; + +export const GRASS_BASE_COLOR = "#1a3a1a"; + +export const COLOR_TOLERANCE = 15; + +export function colorMatchesTerrain( + r: number, + g: number, + b: number, + targetRgb: readonly [number, number, number], + tolerance: number = COLOR_TOLERANCE, +): boolean { + return ( + Math.abs(r - targetRgb[0]) <= tolerance && + Math.abs(g - targetRgb[1]) <= tolerance && + Math.abs(b - targetRgb[2]) <= tolerance + ); +} + +export function getTerrainTypeFromColor( + r: number, + g: number, + b: number, +): TerrainColorKey | null { + for (const [key, config] of Object.entries(TERRAIN_COLORS)) { + if (colorMatchesTerrain(r, g, b, config.rgb)) { + return key as TerrainColorKey; + } + } + return null; +} + +export function isGrassZone(r: number, g: number, b: number): boolean { + return ( + colorMatchesTerrain(r, g, b, TERRAIN_COLORS.grass1.rgb) || + colorMatchesTerrain(r, g, b, TERRAIN_COLORS.grass2.rgb) || + colorMatchesTerrain(r, g, b, TERRAIN_COLORS.grass3.rgb) + ); +} + +export function getGrassTipColor( + r: number, + g: number, + b: number, +): string | null { + if (colorMatchesTerrain(r, g, b, TERRAIN_COLORS.grass1.rgb)) { + return TERRAIN_COLORS.grass1.grassTipColor; + } + if (colorMatchesTerrain(r, g, b, TERRAIN_COLORS.grass2.rgb)) { + return TERRAIN_COLORS.grass2.grassTipColor; + } + if (colorMatchesTerrain(r, g, b, TERRAIN_COLORS.grass3.rgb)) { + return TERRAIN_COLORS.grass3.grassTipColor; + } + return null; +} diff --git a/src/data/world/windConfig.ts b/src/data/world/windConfig.ts new file mode 100644 index 0000000..8ad3c7a --- /dev/null +++ b/src/data/world/windConfig.ts @@ -0,0 +1,15 @@ +export const WIND_DEFAULTS = { + speed: 0.3, + direction: Math.PI * 0.25, + strength: 1.0, + noiseScale: 0.9, +}; + +export const WIND_BOUNDS = { + speed: { min: 0, max: 2, step: 0.1 }, + direction: { min: -Math.PI, max: Math.PI, step: 0.1 }, + strength: { min: 0, max: 3, step: 0.1 }, + noiseScale: { min: 0.1, max: 5, step: 0.1 }, +}; + +export type WindState = typeof WIND_DEFAULTS; diff --git a/src/hooks/world/useGraphicsSettings.ts b/src/hooks/world/useGraphicsSettings.ts new file mode 100644 index 0000000..1c03cbb --- /dev/null +++ b/src/hooks/world/useGraphicsSettings.ts @@ -0,0 +1,58 @@ +import { useWorldSettingsStore } from "@/managers/stores/useWorldSettingsStore"; +import type { GraphicsState } from "@/data/world/graphicsConfig"; + +export function useGraphicsSettings(): GraphicsState { + return useWorldSettingsStore((state) => state.graphics); +} + +export function useSetGraphicsSettings(): ( + graphics: Partial +) => void { + return useWorldSettingsStore((state) => state.setGraphics); +} + +export function useDynamicGrass(): boolean { + return useWorldSettingsStore((state) => state.graphics.dynamicGrass); +} + +export function useDynamicTrees(): boolean { + return useWorldSettingsStore((state) => state.graphics.dynamicTrees); +} + +export function useDynamicClouds(): boolean { + return useWorldSettingsStore((state) => state.graphics.dynamicClouds); +} + +export function useShadowsEnabled(): boolean { + return useWorldSettingsStore((state) => state.graphics.shadowsEnabled); +} + +export function useGrassDensity(): number { + return useWorldSettingsStore((state) => state.graphics.grassDensity); +} + +export function useGraphicsSetters() { + const setDynamicGrass = useWorldSettingsStore( + (state) => state.setDynamicGrass + ); + const setDynamicTrees = useWorldSettingsStore( + (state) => state.setDynamicTrees + ); + const setDynamicClouds = useWorldSettingsStore( + (state) => state.setDynamicClouds + ); + const setShadowsEnabled = useWorldSettingsStore( + (state) => state.setShadowsEnabled + ); + const setGrassDensity = useWorldSettingsStore( + (state) => state.setGrassDensity + ); + + return { + setDynamicGrass, + setDynamicTrees, + setDynamicClouds, + setShadowsEnabled, + setGrassDensity, + }; +} diff --git a/src/hooks/world/useWind.ts b/src/hooks/world/useWind.ts new file mode 100644 index 0000000..fc182d3 --- /dev/null +++ b/src/hooks/world/useWind.ts @@ -0,0 +1,22 @@ +import { useWorldSettingsStore } from "@/managers/stores/useWorldSettingsStore"; +import type { WindState } from "@/data/world/windConfig"; + +export function useWind(): WindState { + return useWorldSettingsStore((state) => state.wind); +} + +export function useSetWind(): (wind: Partial) => void { + return useWorldSettingsStore((state) => state.setWind); +} + +export function useWindSpeed(): number { + return useWorldSettingsStore((state) => state.wind.speed); +} + +export function useWindDirection(): number { + return useWorldSettingsStore((state) => state.wind.direction); +} + +export function useWindStrength(): number { + return useWorldSettingsStore((state) => state.wind.strength); +} diff --git a/src/managers/stores/useWorldSettingsStore.ts b/src/managers/stores/useWorldSettingsStore.ts new file mode 100644 index 0000000..dabf773 --- /dev/null +++ b/src/managers/stores/useWorldSettingsStore.ts @@ -0,0 +1,88 @@ +import { create } from "zustand"; +import { WIND_DEFAULTS, type WindState } from "@/data/world/windConfig"; +import { + GRAPHICS_DEFAULTS, + type GraphicsState, +} from "@/data/world/graphicsConfig"; + +interface WorldSettingsState { + wind: WindState; + graphics: GraphicsState; +} + +interface WorldSettingsActions { + setWind: (wind: Partial) => void; + setWindSpeed: (speed: number) => void; + setWindDirection: (direction: number) => void; + setWindStrength: (strength: number) => void; + setGraphics: (graphics: Partial) => void; + setDynamicGrass: (enabled: boolean) => void; + setDynamicTrees: (enabled: boolean) => void; + setDynamicClouds: (enabled: boolean) => void; + setShadowsEnabled: (enabled: boolean) => void; + setGrassDensity: (density: number) => void; + resetToDefaults: () => void; +} + +type WorldSettingsStore = WorldSettingsState & WorldSettingsActions; + +const DEFAULT_STATE: WorldSettingsState = { + wind: { ...WIND_DEFAULTS }, + graphics: { ...GRAPHICS_DEFAULTS }, +}; + +export const useWorldSettingsStore = create()((set) => ({ + ...DEFAULT_STATE, + + setWind: (windUpdate) => + set((state) => ({ + wind: { ...state.wind, ...windUpdate }, + })), + + setWindSpeed: (speed) => + set((state) => ({ + wind: { ...state.wind, speed }, + })), + + setWindDirection: (direction) => + set((state) => ({ + wind: { ...state.wind, direction }, + })), + + setWindStrength: (strength) => + set((state) => ({ + wind: { ...state.wind, strength }, + })), + + setGraphics: (graphicsUpdate) => + set((state) => ({ + graphics: { ...state.graphics, ...graphicsUpdate }, + })), + + setDynamicGrass: (dynamicGrass) => + set((state) => ({ + graphics: { ...state.graphics, dynamicGrass }, + })), + + setDynamicTrees: (dynamicTrees) => + set((state) => ({ + graphics: { ...state.graphics, dynamicTrees }, + })), + + setDynamicClouds: (dynamicClouds) => + set((state) => ({ + graphics: { ...state.graphics, dynamicClouds }, + })), + + setShadowsEnabled: (shadowsEnabled) => + set((state) => ({ + graphics: { ...state.graphics, shadowsEnabled }, + })), + + setGrassDensity: (grassDensity) => + set((state) => ({ + graphics: { ...state.graphics, grassDensity }, + })), + + resetToDefaults: () => set(DEFAULT_STATE), +}));