refactor: nettoie l'architecture monde et les docs
This commit is contained in:
@@ -1,66 +0,0 @@
|
||||
import * as THREE from "three";
|
||||
|
||||
type TextureMaterialKey = Extract<
|
||||
| keyof THREE.MeshBasicMaterial
|
||||
| keyof THREE.MeshStandardMaterial
|
||||
| keyof THREE.MeshPhysicalMaterial
|
||||
| keyof THREE.MeshToonMaterial,
|
||||
string
|
||||
>;
|
||||
|
||||
type MaterialWithTextureSlots = THREE.Material &
|
||||
Partial<Record<TextureMaterialKey, THREE.Texture | null>>;
|
||||
|
||||
const MATERIAL_TEXTURE_KEYS = [
|
||||
"alphaMap",
|
||||
"aoMap",
|
||||
"bumpMap",
|
||||
"clearcoatMap",
|
||||
"clearcoatNormalMap",
|
||||
"clearcoatRoughnessMap",
|
||||
"displacementMap",
|
||||
"emissiveMap",
|
||||
"envMap",
|
||||
"gradientMap",
|
||||
"lightMap",
|
||||
"map",
|
||||
"metalnessMap",
|
||||
"normalMap",
|
||||
"roughnessMap",
|
||||
"sheenColorMap",
|
||||
"sheenRoughnessMap",
|
||||
"specularColorMap",
|
||||
"specularIntensityMap",
|
||||
"specularMap",
|
||||
"thicknessMap",
|
||||
"transmissionMap",
|
||||
] as const satisfies readonly TextureMaterialKey[];
|
||||
|
||||
export function disposeObject3D(object: THREE.Object3D): void {
|
||||
object.traverse((child) => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
child.geometry?.dispose();
|
||||
|
||||
if (Array.isArray(child.material)) {
|
||||
for (const material of child.material) {
|
||||
disposeMaterial(material);
|
||||
}
|
||||
} else if (child.material) {
|
||||
disposeMaterial(child.material);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function disposeMaterial(material: THREE.Material): void {
|
||||
material.dispose();
|
||||
const materialWithTextures = material as MaterialWithTextureSlots;
|
||||
|
||||
for (const key of MATERIAL_TEXTURE_KEYS) {
|
||||
const value = materialWithTextures[key];
|
||||
|
||||
if (value instanceof THREE.Texture) {
|
||||
value.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,125 +0,0 @@
|
||||
import * as THREE from "three";
|
||||
import { TERRAIN_COLORS } from "@/data/world/terrainConfig";
|
||||
import type {
|
||||
TerrainSurfaceBounds,
|
||||
TerrainSurfaceProjectionConfig,
|
||||
TerrainSurfaceRgb,
|
||||
TerrainSurfaceSample,
|
||||
TerrainSurfaceUv,
|
||||
} from "@/types/world/terrainSurface";
|
||||
import { getTerrainColorKeyFromRgb } from "@/utils/world/terrainSurfaceColor";
|
||||
|
||||
type TerrainSurfaceImageSource =
|
||||
| HTMLImageElement
|
||||
| HTMLCanvasElement
|
||||
| ImageBitmap;
|
||||
|
||||
const imageDataCache = new WeakMap<TerrainSurfaceImageSource, ImageData>();
|
||||
function clamp01(value: number): number {
|
||||
return Math.min(Math.max(value, 0), 1);
|
||||
}
|
||||
|
||||
function wrap01(value: number): number {
|
||||
return ((value % 1) + 1) % 1;
|
||||
}
|
||||
|
||||
function isTerrainSurfaceImageSource(
|
||||
value: unknown,
|
||||
): value is TerrainSurfaceImageSource {
|
||||
return (
|
||||
value instanceof HTMLImageElement ||
|
||||
value instanceof HTMLCanvasElement ||
|
||||
(typeof ImageBitmap !== "undefined" && value instanceof ImageBitmap)
|
||||
);
|
||||
}
|
||||
|
||||
export function createTerrainSurfaceImageData(
|
||||
texture: THREE.Texture,
|
||||
): ImageData | null {
|
||||
if (typeof document === "undefined") return null;
|
||||
|
||||
const image = texture.image as unknown;
|
||||
if (!isTerrainSurfaceImageSource(image)) return null;
|
||||
|
||||
const cachedImageData = imageDataCache.get(image);
|
||||
if (cachedImageData) return cachedImageData;
|
||||
|
||||
const width = image.width;
|
||||
const height = image.height;
|
||||
if (width <= 0 || height <= 0) return null;
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const context = canvas.getContext("2d");
|
||||
if (!context) return null;
|
||||
|
||||
canvas.width = width;
|
||||
canvas.height = height;
|
||||
context.drawImage(image, 0, 0, width, height);
|
||||
|
||||
const imageData = context.getImageData(0, 0, width, height);
|
||||
imageDataCache.set(image, imageData);
|
||||
return imageData;
|
||||
}
|
||||
|
||||
export function sampleTerrainSurfaceAtUv(
|
||||
imageData: ImageData,
|
||||
uv: TerrainSurfaceUv,
|
||||
): TerrainSurfaceSample {
|
||||
const x = Math.round(clamp01(uv.u) * (imageData.width - 1));
|
||||
const y = Math.round((1 - clamp01(uv.v)) * (imageData.height - 1));
|
||||
const index = (y * imageData.width + x) * 4;
|
||||
|
||||
const rgb: TerrainSurfaceRgb = [
|
||||
imageData.data[index] ?? 0,
|
||||
imageData.data[index + 1] ?? 0,
|
||||
imageData.data[index + 2] ?? 0,
|
||||
];
|
||||
const key = getTerrainColorKeyFromRgb(rgb[0], rgb[1], rgb[2]);
|
||||
|
||||
return {
|
||||
rgb,
|
||||
key,
|
||||
config: key === null ? null : TERRAIN_COLORS[key],
|
||||
};
|
||||
}
|
||||
|
||||
export function terrainSurfaceUvFromXZ(
|
||||
x: number,
|
||||
z: number,
|
||||
bounds: TerrainSurfaceBounds,
|
||||
projection?: TerrainSurfaceProjectionConfig,
|
||||
): TerrainSurfaceUv {
|
||||
const width = bounds.maxX - bounds.minX;
|
||||
const depth = bounds.maxZ - bounds.minZ;
|
||||
let u = width === 0 ? 0 : x / width + 0.5;
|
||||
let v = depth === 0 ? 0 : z / depth + 0.5;
|
||||
|
||||
if (projection?.flipX) {
|
||||
u = 1 - u;
|
||||
}
|
||||
|
||||
if (projection?.flipZ) {
|
||||
v = 1 - v;
|
||||
}
|
||||
|
||||
u = wrap01(u + (projection?.offsetX ?? 0));
|
||||
v = wrap01(v + (projection?.offsetZ ?? 0));
|
||||
|
||||
return {
|
||||
u,
|
||||
v,
|
||||
};
|
||||
}
|
||||
|
||||
export function sampleTerrainSurfaceAtXZ(
|
||||
imageData: ImageData,
|
||||
x: number,
|
||||
z: number,
|
||||
bounds: TerrainSurfaceBounds,
|
||||
projection?: TerrainSurfaceProjectionConfig,
|
||||
): TerrainSurfaceSample {
|
||||
return sampleTerrainSurfaceAtUv(
|
||||
imageData,
|
||||
terrainSurfaceUvFromXZ(x, z, bounds, projection),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user