Files
Trinittu/proc-terrain/terrain_props_data.gd

154 lines
4.4 KiB
GDScript

@tool
class_name TerrainPropsData
extends RefCounted
## Data structure containing terrain information for props placement.
## Generated by TerrainGenerator after terrain generation.
var resolution: Vector2i = Vector2i(128, 128)
var terrain_size: Vector2 = Vector2(200, 200)
var heights: Array[float] = []
var biome_ids: Array[int] = []
var slope_values: Array[float] = []
var rail_distances: Array[float] = []
var station_distances: Array[float] = []
var mask_values: Array[Array] = []
func get_index(x: int, z: int) -> int:
return z * resolution.x + x
func get_uv(x: int, z: int) -> Vector2:
return Vector2(float(x) / float(resolution.x - 1), float(z) / float(resolution.y - 1))
func get_world_position(x: int, z: int) -> Vector3:
var half_x := terrain_size.x / 2.0
var half_z := terrain_size.y / 2.0
var step_x := terrain_size.x / float(resolution.x - 1)
var step_z := terrain_size.y / float(resolution.y - 1)
var idx := get_index(x, z)
return Vector3(
x * step_x - half_x,
heights[idx] if idx < heights.size() else 0.0,
z * step_z - half_z
)
func get_height(x: int, z: int) -> float:
var idx := get_index(x, z)
if idx < 0 or idx >= heights.size():
return 0.0
return heights[idx]
func get_biome_id(x: int, z: int) -> int:
var idx := get_index(x, z)
if idx < 0 or idx >= biome_ids.size():
return 0
return biome_ids[idx]
func get_slope_limit(x: int, z: int) -> float:
var idx := get_index(x, z)
if idx < 0 or idx >= slope_values.size():
return 45.0
return slope_values[idx]
func get_rail_distance(x: int, z: int) -> float:
var idx := get_index(x, z)
if idx < 0 or idx >= rail_distances.size():
return INF
return rail_distances[idx]
func get_station_distance(x: int, z: int) -> float:
var idx := get_index(x, z)
if idx < 0 or idx >= station_distances.size():
return INF
return station_distances[idx]
func get_mask_value(mask_index: int, x: int, z: int) -> float:
if mask_index < 0 or mask_index >= mask_values.size():
return 0.0
var mask_arr: Array = mask_values[mask_index]
var idx := get_index(x, z)
if idx < 0 or idx >= mask_arr.size():
return 0.0
return mask_arr[idx]
func get_calculated_slope(x: int, z: int) -> float:
if x <= 0 or x >= resolution.x - 1 or z <= 0 or z >= resolution.y - 1:
return 0.0
var step_x := terrain_size.x / float(resolution.x - 1)
var step_z := terrain_size.y / float(resolution.y - 1)
var h := get_height(x, z)
var h_xm := get_height(x - 1, z)
var h_xp := get_height(x + 1, z)
var h_zm := get_height(x, z - 1)
var h_zp := get_height(x, z + 1)
var dx := (h_xp - h_xm) / (2.0 * step_x)
var dz := (h_zp - h_zm) / (2.0 * step_z)
return rad_to_deg(atan(sqrt(dx * dx + dz * dz)))
func can_place_prop(x: int, z: int, rules: Dictionary) -> bool:
var slope := get_calculated_slope(x, z)
var rail_dist := get_rail_distance(x, z)
var station_dist := get_station_distance(x, z)
var biome := get_biome_id(x, z)
if rules.has("max_slope") and slope > rules["max_slope"]:
return false
if rules.has("min_rail_distance") and rail_dist < rules["min_rail_distance"]:
return false
if rules.has("min_station_distance") and station_dist < rules["min_station_distance"]:
return false
if rules.has("allowed_biomes") and not biome in rules["allowed_biomes"]:
return false
if rules.has("excluded_biomes") and biome in rules["excluded_biomes"]:
return false
if rules.has("required_mask"):
var mask_idx: int = rules["required_mask"]
var threshold: float = rules.get("mask_threshold", 0.5)
if get_mask_value(mask_idx, x, z) < threshold:
return false
if rules.has("excluded_mask"):
var mask_idx: int = rules["excluded_mask"]
var threshold: float = rules.get("exclude_threshold", 0.5)
if get_mask_value(mask_idx, x, z) >= threshold:
return false
return true
func get_density_multiplier(x: int, z: int, rules: Dictionary) -> float:
var multiplier := 1.0
if rules.has("slope_density_curve"):
var slope := get_calculated_slope(x, z)
var curve: Curve = rules["slope_density_curve"]
multiplier *= curve.sample(clampf(slope / 90.0, 0.0, 1.0))
if rules.has("rail_distance_curve"):
var dist := get_rail_distance(x, z)
var curve: Curve = rules["rail_distance_curve"]
var max_dist: float = rules.get("rail_distance_max", 100.0)
multiplier *= curve.sample(clampf(dist / max_dist, 0.0, 1.0))
if rules.has("density_mask"):
var mask_idx: int = rules["density_mask"]
multiplier *= get_mask_value(mask_idx, x, z)
return multiplier