Files
Trinittu/proc-terrain/terrain_control_layers.gd

111 lines
2.9 KiB
GDScript

@tool
class_name TerrainControlLayers
extends Resource
## Resource containing artist-controlled terrain layers for procedural generation.
## Includes biome maps, height profiles, masks, and slope limits.
signal layers_changed
@export var resolution: Vector2i = Vector2i(512, 512):
set(value):
resolution = value
emit_changed()
layers_changed.emit()
@export_group("Biome Control")
@export var biome_map: Texture2D:
set(value):
biome_map = value
emit_changed()
layers_changed.emit()
@export var height_profile: Curve:
set(value):
if height_profile and height_profile.changed.is_connected(_on_curve_changed):
height_profile.changed.disconnect(_on_curve_changed)
height_profile = value
if height_profile:
height_profile.changed.connect(_on_curve_changed)
emit_changed()
layers_changed.emit()
@export_group("Slope Control")
@export var slope_limit_map: Texture2D:
set(value):
slope_limit_map = value
emit_changed()
layers_changed.emit()
@export_range(0.0, 90.0) var default_slope_limit: float = 45.0:
set(value):
default_slope_limit = value
emit_changed()
layers_changed.emit()
@export_group("Mask Layers")
@export var mask_layers: Array[Texture2D] = []:
set(value):
mask_layers = value
emit_changed()
layers_changed.emit()
@export var mask_names: Array[String] = []:
set(value):
mask_names = value
emit_changed()
layers_changed.emit()
func _on_curve_changed() -> void:
emit_changed()
layers_changed.emit()
func get_biome_value(uv: Vector2) -> Color:
if not biome_map:
return Color.WHITE
var img := biome_map.get_image()
if not img:
return Color.WHITE
var px := clampi(int(uv.x * img.get_width()), 0, img.get_width() - 1)
var py := clampi(int(uv.y * img.get_height()), 0, img.get_height() - 1)
return img.get_pixel(px, py)
func get_slope_limit(uv: Vector2) -> float:
if not slope_limit_map:
return default_slope_limit
var img := slope_limit_map.get_image()
if not img:
return default_slope_limit
var px := clampi(int(uv.x * img.get_width()), 0, img.get_width() - 1)
var py := clampi(int(uv.y * img.get_height()), 0, img.get_height() - 1)
return img.get_pixel(px, py).r * 90.0
func get_height_profile_value(normalized_height: float) -> float:
if not height_profile:
return normalized_height
return height_profile.sample(clampf(normalized_height, 0.0, 1.0))
func get_mask_value(mask_index: int, uv: Vector2) -> float:
if mask_index < 0 or mask_index >= mask_layers.size():
return 0.0
var tex := mask_layers[mask_index]
if not tex:
return 0.0
var img := tex.get_image()
if not img:
return 0.0
var px := clampi(int(uv.x * img.get_width()), 0, img.get_width() - 1)
var py := clampi(int(uv.y * img.get_height()), 0, img.get_height() - 1)
return img.get_pixel(px, py).r
func get_mask_name(mask_index: int) -> String:
if mask_index < 0 or mask_index >= mask_names.size():
return "mask_%d" % mask_index
return mask_names[mask_index]