diff --git a/src/components/three/world/TerrainModel.tsx b/src/components/three/world/TerrainModel.tsx index c1f2a29..57e3d35 100644 --- a/src/components/three/world/TerrainModel.tsx +++ b/src/components/three/world/TerrainModel.tsx @@ -2,10 +2,10 @@ import { useEffect, useMemo, useRef } from "react"; import * as THREE from "three"; import { useGLTF } from "@react-three/drei"; import { useThree } from "@react-three/fiber"; +import { TERRAIN_MODEL_PATH } from "@/data/world/terrainConfig"; import type { Vector3Tuple } from "@/types/three/three"; import { optimizeGLTFSceneTextures } from "@/utils/three/optimizeGLTFScene"; -const TERRAIN_MODEL_PATH = "/models/terrain/model.gltf"; const TERRAIN_DEFAULT_POSITION: Vector3Tuple = [0, 0, 0]; interface TerrainModelProps { diff --git a/src/data/world/terrainConfig.ts b/src/data/world/terrainConfig.ts index b2f358c..4cd6eb6 100644 --- a/src/data/world/terrainConfig.ts +++ b/src/data/world/terrainConfig.ts @@ -1,5 +1,6 @@ import type { TerrainSurfaceColorConfig } from "@/types/world/terrainSurface"; +export const TERRAIN_MODEL_PATH = "/models/terrain/model.gltf"; export const TERRAIN_SURFACE_COLOR_TOLERANCE = 15; export const TERRAIN_WATER_HEIGHT = 0; export const TERRAIN_TILE_SIZE = 1; diff --git a/src/hooks/world/useTerrainSurfaceData.ts b/src/hooks/world/useTerrainSurfaceData.ts new file mode 100644 index 0000000..4a2725d --- /dev/null +++ b/src/hooks/world/useTerrainSurfaceData.ts @@ -0,0 +1,63 @@ +import { useMemo } from "react"; +import * as THREE from "three"; +import { useGLTF } from "@react-three/drei"; +import { TERRAIN_MODEL_PATH } from "@/data/world/terrainConfig"; +import type { + TerrainSurfaceBounds, + TerrainSurfaceData, +} from "@/types/world/terrainSurface"; +import { createTerrainSurfaceImageData } from "@/utils/world/terrainSurfaceSampler"; + +function findTerrainBaseColorTexture( + scene: THREE.Object3D, +): THREE.Texture | null { + let texture: THREE.Texture | null = null; + + scene.traverse((child) => { + if (texture || !(child instanceof THREE.Mesh)) return; + + const materials = Array.isArray(child.material) + ? child.material + : [child.material]; + + for (const material of materials) { + if (material instanceof THREE.MeshStandardMaterial && material.map) { + texture = material.map; + return; + } + } + }); + + return texture; +} + +function createTerrainSurfaceBounds( + scene: THREE.Object3D, +): TerrainSurfaceBounds { + scene.updateWorldMatrix(true, true); + + const box = new THREE.Box3().setFromObject(scene); + return { + minX: box.min.x, + maxX: box.max.x, + minZ: box.min.z, + maxZ: box.max.z, + }; +} + +export function useTerrainSurfaceData(): TerrainSurfaceData | null { + const { scene } = useGLTF(TERRAIN_MODEL_PATH); + + return useMemo(() => { + const texture = findTerrainBaseColorTexture(scene); + if (!texture) return null; + + const imageData = createTerrainSurfaceImageData(texture); + if (!imageData) return null; + + return { + bounds: createTerrainSurfaceBounds(scene), + imageData, + }; + }, [scene]); +} diff --git a/src/types/world/terrainSurface.ts b/src/types/world/terrainSurface.ts index 49f18cc..ca64927 100644 --- a/src/types/world/terrainSurface.ts +++ b/src/types/world/terrainSurface.ts @@ -34,3 +34,8 @@ export interface TerrainSurfaceSample { key: string | null; config: TerrainSurfaceColorConfig | null; } + +export interface TerrainSurfaceData { + bounds: TerrainSurfaceBounds; + imageData: ImageData; +}