152 lines
4.3 KiB
GDScript
152 lines
4.3 KiB
GDScript
class_name SonarReliefMeshBuilder
|
|
extends RefCounted
|
|
|
|
const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd")
|
|
const SonarSample = preload("res://game/domain/sonar/sonar_sample.gd")
|
|
const SonarScan = preload("res://game/domain/sonar/sonar_scan.gd")
|
|
|
|
const COLOR_DEEP := Color("173f91")
|
|
const COLOR_LOW := Color("168fc1")
|
|
const COLOR_MIDDLE := Color("36b979")
|
|
const COLOR_HIGH := Color("d7d93d")
|
|
const COLOR_PEAK := Color("ee5734")
|
|
const COLOR_METAL := Color("ff6b22")
|
|
|
|
|
|
static func build_full_mesh(
|
|
scan: SonarScan,
|
|
material: StandardMaterial3D
|
|
) -> ArrayMesh:
|
|
var mesh := ArrayMesh.new()
|
|
for row in range(1, scan.samples.rows):
|
|
add_row_band(mesh, scan, row, material)
|
|
return mesh
|
|
|
|
|
|
static func add_row_band(
|
|
mesh: ArrayMesh,
|
|
scan: SonarScan,
|
|
row: int,
|
|
material: StandardMaterial3D
|
|
) -> int:
|
|
var vertices := PackedVector3Array()
|
|
var normals := PackedVector3Array()
|
|
var colors := PackedColorArray()
|
|
for column in range(scan.samples.columns - 1):
|
|
var top_left: SonarSample = scan.samples.sample_at(row - 1, column)
|
|
var top_right: SonarSample = scan.samples.sample_at(row - 1, column + 1)
|
|
var bottom_left: SonarSample = scan.samples.sample_at(row, column)
|
|
var bottom_right: SonarSample = scan.samples.sample_at(row, column + 1)
|
|
if not (
|
|
top_left.observed
|
|
and top_right.observed
|
|
and bottom_left.observed
|
|
and bottom_right.observed
|
|
):
|
|
continue
|
|
_append_triangle(
|
|
vertices, normals, colors, scan,
|
|
top_left, bottom_left, top_right
|
|
)
|
|
_append_triangle(
|
|
vertices, normals, colors, scan,
|
|
top_right, bottom_left, bottom_right
|
|
)
|
|
if vertices.is_empty():
|
|
return 0
|
|
var arrays: Array = []
|
|
arrays.resize(Mesh.ARRAY_MAX)
|
|
arrays[Mesh.ARRAY_VERTEX] = vertices
|
|
arrays[Mesh.ARRAY_NORMAL] = normals
|
|
arrays[Mesh.ARRAY_COLOR] = colors
|
|
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
|
|
mesh.surface_set_material(mesh.get_surface_count() - 1, material)
|
|
return vertices.size() / 3
|
|
|
|
|
|
static func add_grid_band(
|
|
mesh: ArrayMesh,
|
|
scan: SonarScan,
|
|
row: int,
|
|
material: StandardMaterial3D
|
|
) -> int:
|
|
var vertices := PackedVector3Array()
|
|
for column in scan.samples.columns:
|
|
if column % 6 != 0 and column != scan.samples.columns - 1:
|
|
continue
|
|
_append_grid_segment(
|
|
vertices,
|
|
scan.samples.sample_at(row - 1, column),
|
|
scan.samples.sample_at(row, column)
|
|
)
|
|
if row == 1 or row % 4 == 0 or row == scan.samples.rows - 1:
|
|
for column in range(scan.samples.columns - 1):
|
|
_append_grid_segment(
|
|
vertices,
|
|
scan.samples.sample_at(row, column),
|
|
scan.samples.sample_at(row, column + 1)
|
|
)
|
|
if vertices.is_empty():
|
|
return 0
|
|
var arrays: Array = []
|
|
arrays.resize(Mesh.ARRAY_MAX)
|
|
arrays[Mesh.ARRAY_VERTEX] = vertices
|
|
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_LINES, arrays)
|
|
mesh.surface_set_material(mesh.get_surface_count() - 1, material)
|
|
return vertices.size() / 2
|
|
|
|
|
|
static func _append_grid_segment(
|
|
vertices: PackedVector3Array,
|
|
a: SonarSample,
|
|
b: SonarSample
|
|
) -> void:
|
|
if not a.observed or not b.observed:
|
|
return
|
|
vertices.append(a.position_m + a.normal * 0.08)
|
|
vertices.append(b.position_m + b.normal * 0.08)
|
|
|
|
|
|
static func _append_triangle(
|
|
vertices: PackedVector3Array,
|
|
normals: PackedVector3Array,
|
|
colors: PackedColorArray,
|
|
scan: SonarScan,
|
|
a: SonarSample,
|
|
b: SonarSample,
|
|
c: SonarSample
|
|
) -> void:
|
|
for sample: SonarSample in [a, b, c]:
|
|
vertices.append(sample.position_m)
|
|
normals.append(sample.normal)
|
|
colors.append(_sample_color(scan, sample))
|
|
|
|
|
|
static func _sample_color(scan: SonarScan, sample: SonarSample) -> Color:
|
|
if sample.material == SonarMaterial.Value.METAL:
|
|
return COLOR_METAL
|
|
var height := inverse_lerp(
|
|
scan.minimum_elevation_m,
|
|
scan.maximum_elevation_m,
|
|
sample.elevation_m
|
|
)
|
|
var color: Color
|
|
if height <= 0.28:
|
|
color = COLOR_DEEP.lerp(COLOR_LOW, height / 0.28)
|
|
elif height <= 0.52:
|
|
color = COLOR_LOW.lerp(COLOR_MIDDLE, (height - 0.28) / 0.24)
|
|
elif height <= 0.76:
|
|
color = COLOR_MIDDLE.lerp(COLOR_HIGH, (height - 0.52) / 0.24)
|
|
else:
|
|
color = COLOR_HIGH.lerp(COLOR_PEAK, (height - 0.76) / 0.24)
|
|
var light_direction := Vector3(-0.35, 0.82, -0.45).normalized()
|
|
var slope_light := clampf(sample.normal.dot(light_direction), 0.0, 1.0)
|
|
var shade := lerpf(0.68, 1.0, slope_light)
|
|
var lit_color := color.lerp(Color.WHITE, sample.intensity * 0.08)
|
|
return Color(
|
|
lit_color.r * shade,
|
|
lit_color.g * shade,
|
|
lit_color.b * shade,
|
|
lit_color.a
|
|
)
|