Constrained grid generation layer

This commit is contained in:
Rob Kelly 2025-10-03 17:11:59 -06:00
parent 3352e24771
commit 5272ebdc0a
2 changed files with 140 additions and 1 deletions

View File

@ -3,7 +3,29 @@ class_name GenerationFeature extends Node3D
##
## Layers contain features. Some features may contain layers.
@export var noise_scale := Vector3.ONE
@export var noise_offset := Vector3.ZERO
@export var sub_layers: Array[GenerationLayer]
var _generated := false
func probe() -> void:
# TODO may want to make low-detail & high-detail probes distinct
pass # Implemented in derived type.
if not _generated:
generate()
_generated = true
for layer in sub_layers:
layer.probe()
## Generate elements of this feature. Called by default on the first call to `probe`
func generate() -> void:
pass # Implemented in derived type
func sample_noise() -> float:
var sample_point := global_position * noise_scale + noise_offset
return WorldGenManager.noise.get_noise_3dv(sample_point)

View File

@ -0,0 +1,117 @@
@tool
class_name GridLayer extends GenerationLayer
## A layer that generates tiles in a locally-constrained grid.
## Bounding box for the grid on the local XZ plane.
##
## Note that only feature handles are checked to be within the bounding box.
## The Y component of this AABB is not used.
@export var bounding_box: AABB:
set(value):
bounding_box = value
if _debug_box:
_set_debug_box_shape(bounding_box)
## Size of a grid tile in the local XZ plane.
@export var grid_size: Vector2
@export_group("Debug Draw")
@export var draw_debug := true
@export var debug_color := Color("#a486006b"):
set(value):
debug_color = value
if _debug_box:
_set_debug_box_color(debug_color)
var _debug_meshinstance: MeshInstance3D
var _debug_box: BoxMesh
func _ready() -> void:
if Engine.is_editor_hint():
_init_debug_draw()
func probe() -> void:
var world_pos := WorldGenManager.get_generation_point()
probe_radius(world_pos, WorldGenManager.med_detail_radius)
# TODO high-detail & low-detail probes
func probe_radius(center: Vector3, radius: float) -> void:
var rad_diff := Vector3(radius, 0, radius)
# Translate probe box limits to grid space
var grid_low := world_to_local(center - rad_diff).floor()
var grid_high := world_to_local(center + rad_diff).floor()
var grid_max := _plane_size().floor()
# Constrain to bounding box
var x_min := maxf(grid_low.x, 0)
var x_max := minf(grid_high.x + 1, grid_max.x)
var y_min := maxf(grid_low.y, 0)
var y_max := minf(grid_high.y + 1, grid_max.y)
# Probe everything within radius
for i in range(x_min, x_max):
for j in range(y_min, y_max):
probe_grid(Vector2(i, j))
func probe_grid(_grid_pos: Vector2) -> void:
pass # Implement in derived type
func _plane_size() -> Vector2:
return Vector2(bounding_box.size.x / grid_size.x, bounding_box.size.z / grid_size.y)
func is_bounding_box_valid() -> bool:
return bounding_box.size.length_squared() > 0
func is_point_in_bounds(local_pos: Vector2) -> bool:
local_pos = local_pos.floor()
var grid_max := _plane_size()
return (
0 <= local_pos.x
and local_pos.x < grid_max.x
and 0 <= local_pos.y
and local_pos.y < grid_max.y
)
func local_to_world(local_pos: Vector2) -> Vector3:
var v3_pos := Vector3(local_pos.x * grid_size.x, 0, local_pos.y * grid_size.y)
return global_transform * (bounding_box.position + v3_pos)
func world_to_local(world_pos: Vector3) -> Vector2:
var rel_pos := world_pos * global_transform - bounding_box.position
return Vector2(rel_pos.x / grid_size.x, rel_pos.z / grid_size.y)
#region debug draw
func _init_debug_draw() -> void:
_debug_box = BoxMesh.new()
var mat := StandardMaterial3D.new()
mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA
mat.cull_mode = BaseMaterial3D.CULL_DISABLED
mat.shading_mode = BaseMaterial3D.SHADING_MODE_UNSHADED
_debug_box.material = mat
_debug_meshinstance = MeshInstance3D.new()
_debug_meshinstance.mesh = _debug_box
add_child(_debug_meshinstance)
_set_debug_box_color(debug_color)
_set_debug_box_shape(bounding_box)
func _set_debug_box_color(color: Color) -> void:
var mat := _debug_box.material as StandardMaterial3D
mat.albedo_color = color
func _set_debug_box_shape(aabb: AABB) -> void:
_debug_meshinstance.position = aabb.position + aabb.size / 2
_debug_box.size = aabb.size
#endregion