chore: add terrain surface config

This commit is contained in:
Tom Boullay
2026-05-25 15:51:02 +02:00
parent a52d57ae6c
commit 1f6d9659ed
6 changed files with 118 additions and 142 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ import tseslint from "typescript-eslint";
import { defineConfig, globalIgnores } from "eslint/config";
export default defineConfig([
globalIgnores(["dist", "POC-grass"]),
globalIgnores(["dist"]),
{
files: ["**/*.{ts,tsx}"],
extends: [
+21 -71
View File
@@ -40201,7 +40201,7 @@
"scale": [1, 1, 1],
"children": [
{
"name": "panneauxcentre",
"name": "panneauclassique",
"type": "Object3D",
"role": "group",
"position": [0, 0, 0],
@@ -40209,40 +40209,30 @@
"scale": [1, 1, 1],
"children": [
{
"name": "panneauxcentre",
"name": "panneauclassique",
"type": "Object3D",
"position": [-35.2895, 3.8515, 40.1864],
"rotation": [-3.0988, 0.9934, -3.1367],
"scale": [1, 1, 1],
"children": [
{
"name": "panneauxcentre",
"name": "panneauclassique",
"type": "Mesh",
"position": [-35.2895, 3.8515, 40.1864],
"rotation": [-3.0988, 0.9934, -3.1367],
"scale": [1, 1, 1]
}
]
}
]
},
{
"name": "panneauxdomaine",
"type": "Object3D",
"role": "group",
"position": [0, 0, 0],
"rotation": [0, 0, 0],
"scale": [1, 1, 1],
"children": [
},
{
"name": "panneauxdomaine",
"name": "panneauclassique",
"type": "Object3D",
"position": [-2.4347, 3.7803, -56.1439],
"rotation": [0.0474, 0.2148, -0.0027],
"scale": [1, 1, 1],
"children": [
{
"name": "panneauxdomaine",
"name": "panneauclassique",
"type": "Mesh",
"position": [-2.4347, 3.7803, -56.1439],
"rotation": [0.0474, 0.2148, -0.0027],
@@ -40253,7 +40243,7 @@
]
},
{
"name": "panneaudirresidences2",
"name": "panneaufleche",
"type": "Object3D",
"role": "group",
"position": [0, 0, 0],
@@ -40261,118 +40251,78 @@
"scale": [1, 1, 1],
"children": [
{
"name": "panneaudirresidences2",
"name": "panneaufleche",
"type": "Object3D",
"position": [30.1743, 7.1944, -13.5781],
"rotation": [0.0374, -1.297, -0.0099],
"scale": [1, 1, 1],
"children": [
{
"name": "panneaudirresidences2",
"name": "panneaufleche",
"type": "Mesh",
"position": [30.1743, 7.1944, -13.5781],
"rotation": [0.0374, -1.297, -0.0099],
"scale": [1, 1, 1]
}
]
}
]
},
{
"name": "panneaudirdomaine",
"type": "Object3D",
"role": "group",
"position": [0, 0, 0],
"rotation": [0, 0, 0],
"scale": [1, 1, 1],
"children": [
},
{
"name": "panneaudirdomaine",
"name": "panneaufleche",
"type": "Object3D",
"position": [8.1032, 7.1918, -23.0004],
"rotation": [0.0462, -0.2443, -0.0027],
"scale": [1, 1, 1],
"children": [
{
"name": "panneaudirdomaine",
"name": "panneaufleche",
"type": "Mesh",
"position": [8.1032, 7.1918, -23.0004],
"rotation": [0.0462, -0.2443, -0.0027],
"scale": [1, 1, 1]
}
]
}
]
},
{
"name": "panneaudirresidences1",
"type": "Object3D",
"role": "group",
"position": [0, 0, 0],
"rotation": [0, 0, 0],
"scale": [1, 1, 1],
"children": [
},
{
"name": "panneaudirresidences1",
"name": "panneaufleche",
"type": "Object3D",
"position": [-10.1125, 7.2582, -11.4511],
"rotation": [0.0478, 0.3436, -0.0028],
"scale": [1, 1, 1],
"children": [
{
"name": "panneaudirresidences1",
"name": "panneaufleche",
"type": "Mesh",
"position": [-10.1125, 7.2582, -11.4511],
"rotation": [0.0478, 0.3436, -0.0028],
"scale": [1, 1, 1]
}
]
}
]
},
{
"name": "panneaudircentre",
"type": "Object3D",
"role": "group",
"position": [0, 0, 0],
"rotation": [0, 0, 0],
"scale": [1, 1, 1],
"children": [
},
{
"name": "panneaudircentre",
"name": "panneaufleche",
"type": "Object3D",
"position": [-10.5207, 7.0874, 19.3819],
"rotation": [-3.1416, 1.5139, -3.0947],
"scale": [1, 1, 1],
"children": [
{
"name": "panneaudircentre",
"name": "panneaufleche",
"type": "Mesh",
"position": [-10.5207, 7.0874, 19.3819],
"rotation": [-3.1416, 1.5139, -3.0947],
"scale": [1, 1, 1]
}
]
}
]
},
{
"name": "panneaudirfabrik",
"type": "Object3D",
"role": "group",
"position": [0, 0, 0],
"rotation": [0, 0, 0],
"scale": [1, 1, 1],
"children": [
},
{
"name": "panneaudirfabrik",
"name": "panneaufleche",
"type": "Object3D",
"position": [29.0899, 6.9811, 23.4988],
"rotation": [3.1416, -0.8852, 3.1253],
"scale": [1, 1, 1],
"children": [
{
"name": "panneaudirfabrik",
"name": "panneaufleche",
"type": "Mesh",
"position": [29.0899, 6.9811, 23.4988],
"rotation": [3.1416, -0.8852, 3.1253],
+7
View File
@@ -10,6 +10,13 @@ const MESH_NAME_MAPPINGS = {
eoliennes: "eolienne",
immeuble_1: "immeuble1",
buissons: "buisson",
panneauxcentre: "panneauclassique",
panneauxdomaine: "panneauclassique",
panneaudircentre: "panneaufleche",
panneaudirdomaine: "panneaufleche",
panneaudirfabrik: "panneaufleche",
panneaudirresidences1: "panneaufleche",
panneaudirresidences2: "panneaufleche",
panneauxquartier: "panneauaffichage",
};
+20 -70
View File
@@ -1,108 +1,58 @@
import type { TerrainSurfaceColorConfig } from "@/types/world/terrainSurface";
export const TERRAIN_SURFACE_COLOR_TOLERANCE = 15;
export const TERRAIN_WATER_HEIGHT = 0;
export const TERRAIN_TILE_SIZE = 1;
export const GRASS_BASE_COLOR = "#1a3a1a";
export const TERRAIN_COLORS = {
grass1: {
hex: "#84C66B",
rgb: [132, 198, 107] as const,
type: "grass" as const,
kind: "grass",
grassTipColor: "#84C66B",
},
grass2: {
hex: "#67B058",
rgb: [103, 176, 88] as const,
type: "grass" as const,
kind: "grass",
grassTipColor: "#67B058",
},
grass3: {
hex: "#A3CA5B",
rgb: [163, 202, 91] as const,
type: "grass" as const,
kind: "grass",
grassTipColor: "#A3CA5B",
},
potager: {
hex: "#342420",
rgb: [52, 36, 32] as const,
type: "tile" as const,
tileModel: "/models/potager/potager.gltf",
tileSize: 1,
kind: "garden",
modelPath: "/models/potager/potager.gltf",
tileSize: TERRAIN_TILE_SIZE,
},
terre: {
hex: "#513E2C",
rgb: [81, 62, 44] as const,
type: "none" as const,
kind: "dirt",
},
chemin: {
hex: "#F5D896",
rgb: [245, 216, 150] as const,
type: "tile" as const,
tileModel: "/models/chemins/model.gltf",
tileSize: 1,
kind: "path",
modelPath: "/models/chemins/model.gltf",
tileSize: TERRAIN_TILE_SIZE,
},
eau: {
hex: "#91DAF5",
rgb: [145, 218, 245] as const,
type: "water" as const,
kind: "water",
},
cailloux: {
hex: "#B6D3DE",
rgb: [182, 211, 222] as const,
type: "none" as const,
kind: "rock",
},
} as const;
} satisfies Record<string, TerrainSurfaceColorConfig>;
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;
}
+18
View File
@@ -0,0 +1,18 @@
export type TerrainSurfaceKind =
| "grass"
| "path"
| "water"
| "garden"
| "dirt"
| "rock";
export type TerrainSurfaceRgb = readonly [number, number, number];
export interface TerrainSurfaceColorConfig {
hex: string;
rgb: TerrainSurfaceRgb;
kind: TerrainSurfaceKind;
grassTipColor?: string;
modelPath?: string;
tileSize?: number;
}
+51
View File
@@ -0,0 +1,51 @@
import {
TERRAIN_COLORS,
TERRAIN_SURFACE_COLOR_TOLERANCE,
type TerrainColorKey,
} from "@/data/world/terrainConfig";
import type { TerrainSurfaceRgb } from "@/types/world/terrainSurface";
export function colorMatchesTerrainSurface(
r: number,
g: number,
b: number,
targetRgb: TerrainSurfaceRgb,
tolerance: number = TERRAIN_SURFACE_COLOR_TOLERANCE,
): boolean {
return (
Math.abs(r - targetRgb[0]) <= tolerance &&
Math.abs(g - targetRgb[1]) <= tolerance &&
Math.abs(b - targetRgb[2]) <= tolerance
);
}
export function getTerrainColorKeyFromRgb(
r: number,
g: number,
b: number,
): TerrainColorKey | null {
for (const [key, config] of Object.entries(TERRAIN_COLORS)) {
if (colorMatchesTerrainSurface(r, g, b, config.rgb)) {
return key as TerrainColorKey;
}
}
return null;
}
export function isGrassTerrainColor(r: number, g: number, b: number): boolean {
const key = getTerrainColorKeyFromRgb(r, g, b);
return key !== null && TERRAIN_COLORS[key].kind === "grass";
}
export function getGrassTipColorFromRgb(
r: number,
g: number,
b: number,
): string | null {
const key = getTerrainColorKeyFromRgb(r, g, b);
if (key === null) return null;
const terrainColor = TERRAIN_COLORS[key];
return "grassTipColor" in terrainColor ? terrainColor.grassTipColor : null;
}