class_name SiteTerrainData extends Resource const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd") ## Regular single-surface heightfield. Column grows with world +X (east), ## row grows with world +Z (south). Elevations are world Y in metres. @export var columns: int = 0 @export var rows: int = 0 @export var cell_size_m: float = 1.0 @export var origin_x_m: float = 0.0 @export var origin_z_m: float = 0.0 @export var elevations_m := PackedFloat32Array() @export var materials := PackedByteArray() @export var rock_reflectivity: float = 0.72 @export var sand_reflectivity: float = 0.45 func index_of(column: int, row: int) -> int: return row * columns + column func validation_error() -> String: if columns < 2 or rows < 2: return "terrain needs at least 2 columns and 2 rows" if not is_finite(cell_size_m) or cell_size_m <= 0.0: return "terrain cell size must be positive" if not is_finite(origin_x_m) or not is_finite(origin_z_m): return "terrain origin must be finite" var cell_count := columns * rows if elevations_m.size() != cell_count: return "terrain elevation count does not match the grid" if materials.size() != cell_count: return "terrain material count does not match the grid" for elevation: float in elevations_m: if not is_finite(elevation): return "terrain elevations must be finite" for material: int in materials: if not SonarMaterial.is_terrain(material): return "terrain materials must be rock or sand" for reflectivity: float in [rock_reflectivity, sand_reflectivity]: if not is_finite(reflectivity) or reflectivity <= 0.0 or reflectivity > 1.0: return "terrain reflectivity must be within ]0, 1]" return ""