154 lines
4.4 KiB
GDScript
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
|