136 lines
4.3 KiB
GDScript
136 lines
4.3 KiB
GDScript
class_name BathymetricTerrainActor
|
|
extends StaticBody3D
|
|
|
|
const TerrainHeightField = preload("res://game/domain/sites/terrain_height_field.gd")
|
|
const TERRAIN_SHADER = preload("res://game/world/sites/seabed_terrain.gdshader")
|
|
|
|
const TILE_CELLS := 64
|
|
const VISIBILITY_RANGE_END_M := 260.0
|
|
const VISIBILITY_RANGE_MARGIN_M := 40.0
|
|
|
|
var _field: TerrainHeightField
|
|
var _material: ShaderMaterial
|
|
var _tiles_root: Node3D
|
|
var _collision: CollisionShape3D
|
|
|
|
|
|
func configure(field: TerrainHeightField) -> String:
|
|
if field == null:
|
|
return "terrain field is missing"
|
|
_clear()
|
|
_field = field
|
|
_material = ShaderMaterial.new()
|
|
_material.shader = TERRAIN_SHADER
|
|
_build_tiles()
|
|
_build_collision()
|
|
return ""
|
|
|
|
|
|
func height_field() -> TerrainHeightField:
|
|
return _field
|
|
|
|
|
|
func tile_count() -> int:
|
|
return 0 if _tiles_root == null else _tiles_root.get_child_count()
|
|
|
|
|
|
func sonar_material_at(world_position: Vector3) -> int:
|
|
return _field.material_at(world_position.x, world_position.z)
|
|
|
|
|
|
func sonar_reflectivity_at(world_position: Vector3) -> float:
|
|
return _field.reflectivity_for(sonar_material_at(world_position))
|
|
|
|
|
|
func _clear() -> void:
|
|
if _tiles_root != null:
|
|
remove_child(_tiles_root)
|
|
_tiles_root.free()
|
|
_tiles_root = null
|
|
if _collision != null:
|
|
remove_child(_collision)
|
|
_collision.free()
|
|
_collision = null
|
|
|
|
|
|
func _build_tiles() -> void:
|
|
_tiles_root = Node3D.new()
|
|
_tiles_root.name = "Tiles"
|
|
add_child(_tiles_root)
|
|
var last_column := _field.columns() - 1
|
|
var last_row := _field.rows() - 1
|
|
var start_row := 0
|
|
while start_row < last_row:
|
|
var end_row := mini(start_row + TILE_CELLS, last_row)
|
|
var start_column := 0
|
|
while start_column < last_column:
|
|
var end_column := mini(start_column + TILE_CELLS, last_column)
|
|
_tiles_root.add_child(_build_tile(start_column, end_column, start_row, end_row))
|
|
start_column = end_column
|
|
start_row = end_row
|
|
|
|
|
|
func _build_tile(start_column: int, end_column: int, start_row: int, end_row: int) -> MeshInstance3D:
|
|
var corner_min := _field.cell_world_position(start_column, start_row)
|
|
var corner_max := _field.cell_world_position(end_column, end_row)
|
|
var centre := Vector3(
|
|
(corner_min.x + corner_max.x) * 0.5,
|
|
0.0,
|
|
(corner_min.z + corner_max.z) * 0.5
|
|
)
|
|
var vertices := PackedVector3Array()
|
|
var normals := PackedVector3Array()
|
|
var indices := PackedInt32Array()
|
|
var columns_in_tile := end_column - start_column + 1
|
|
for row in range(start_row, end_row + 1):
|
|
for column in range(start_column, end_column + 1):
|
|
vertices.append(_field.cell_world_position(column, row) - centre)
|
|
normals.append(_field.normal_at_cell(column, row))
|
|
for row in range(end_row - start_row):
|
|
for column in range(end_column - start_column):
|
|
var top_left := row * columns_in_tile + column
|
|
var top_right := top_left + 1
|
|
var bottom_left := top_left + columns_in_tile
|
|
var bottom_right := bottom_left + 1
|
|
indices.append_array([top_left, top_right, bottom_left])
|
|
indices.append_array([top_right, bottom_right, bottom_left])
|
|
var arrays: Array = []
|
|
arrays.resize(Mesh.ARRAY_MAX)
|
|
arrays[Mesh.ARRAY_VERTEX] = vertices
|
|
arrays[Mesh.ARRAY_NORMAL] = normals
|
|
arrays[Mesh.ARRAY_INDEX] = indices
|
|
var mesh := ArrayMesh.new()
|
|
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
|
var instance := MeshInstance3D.new()
|
|
instance.name = "Tile_%d_%d" % [start_column / TILE_CELLS, start_row / TILE_CELLS]
|
|
instance.mesh = mesh
|
|
instance.material_override = _material
|
|
instance.position = centre
|
|
instance.visibility_range_end = VISIBILITY_RANGE_END_M
|
|
instance.visibility_range_end_margin = VISIBILITY_RANGE_MARGIN_M
|
|
instance.visibility_range_fade_mode = GeometryInstance3D.VISIBILITY_RANGE_FADE_SELF
|
|
return instance
|
|
|
|
|
|
func _build_collision() -> void:
|
|
var shape := HeightMapShape3D.new()
|
|
shape.map_width = _field.columns()
|
|
shape.map_depth = _field.rows()
|
|
var heights := PackedFloat32Array()
|
|
heights.resize(_field.columns() * _field.rows())
|
|
for row in _field.rows():
|
|
for column in _field.columns():
|
|
heights[row * _field.columns() + column] = _field.elevation_at_cell(column, row)
|
|
shape.map_data = heights
|
|
_collision = CollisionShape3D.new()
|
|
_collision.name = "Collision"
|
|
_collision.shape = shape
|
|
var bounds := _field.bounds_m()
|
|
_collision.position = Vector3(
|
|
bounds.position.x + bounds.size.x * 0.5,
|
|
0.0,
|
|
bounds.position.y + bounds.size.y * 0.5
|
|
)
|
|
_collision.scale = Vector3(_field.cell_size_m(), 1.0, _field.cell_size_m())
|
|
add_child(_collision)
|