Terrain des sites de plongée : champ de hauteur authoré, Banc du Vigilant, formes variées et rendu sous-marin #4
No files matched your search
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 104 KiB |
@@ -82,7 +82,7 @@ ou `smoothstep`) et **mono-segment**. Il manque :
|
|||||||
|
|
||||||
| Primitive | Paramètres | Forme |
|
| Primitive | Paramètres | Forme |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `Canyon` | `path: PackedVector2Array`, `half_width_m`, `depth_m`, `wall_softness_m`, `edge_seed`, `edge_amplitude_m` | tranchée à fond plat le long d'une polyligne ; parois `smoothstep` sur `wall_softness_m` ; distance au tracé perturbée par un bruit de contour ; se referme sur une demi-largeur à chaque bout |
|
| `Canyon` | `path: PackedVector2Array`, `half_width_m`, `depth_m`, `wall_softness_m`, `edge_seed`, `edge_amplitude_m`, `floor_material` | tranchée à fond plat le long d'une polyligne ; parois `smoothstep` sur `wall_softness_m` ; distance au tracé perturbée par un bruit de contour ; se referme sur une demi-largeur à chaque bout ; impose `floor_material` (sable en général) sur le fond plat, même à travers une butte rocheuse |
|
||||||
| `Outcrop` | `centre`, `radius_x_m`, `radius_z_m`, `height_m`, `wall_width_m`, `edge_seed`, `edge_amplitude_m`, `rock_top` | butte elliptique à sommet plat, flanc de largeur `wall_width_m`, contour perturbé ; matériau roche partout où elle dépasse 30 % de sa hauteur si `rock_top` |
|
| `Outcrop` | `centre`, `radius_x_m`, `radius_z_m`, `height_m`, `wall_width_m`, `edge_seed`, `edge_amplitude_m`, `rock_top` | butte elliptique à sommet plat, flanc de largeur `wall_width_m`, contour perturbé ; matériau roche partout où elle dépasse 30 % de sa hauteur si `rock_top` |
|
||||||
| `BoulderField` | `centre`, `radius_m`, `seed`, `count`, `boulder_radius_min_m`, `boulder_radius_max_m`, `height_ratio` | `count` demi-ellipsoïdes tirés par `RandomNumberGenerator` seedé dans le disque, combinés par **maximum** ; roche sur toute leur emprise |
|
| `BoulderField` | `centre`, `radius_m`, `seed`, `count`, `boulder_radius_min_m`, `boulder_radius_max_m`, `height_ratio` | `count` demi-ellipsoïdes tirés par `RandomNumberGenerator` seedé dans le disque, combinés par **maximum** ; roche sur toute leur emprise |
|
||||||
| `Escarpment` | `path: PackedVector2Array`, `drop_m`, `softness_m`, `slope_beyond`, `edge_seed`, `edge_amplitude_m` | `Step` le long d'une polyligne : le terrain descend à **droite** du sens de parcours ; ligne de crête perturbée par un bruit de contour |
|
| `Escarpment` | `path: PackedVector2Array`, `drop_m`, `softness_m`, `slope_beyond`, `edge_seed`, `edge_amplitude_m` | `Step` le long d'une polyligne : le terrain descend à **droite** du sens de parcours ; ligne de crête perturbée par un bruit de contour |
|
||||||
@@ -129,7 +129,7 @@ Plan de masse (nord en haut) :
|
|||||||
| 55°, gradins 4 m, fond ridé entre buttes |
|
| 55°, gradins 4 m, fond ridé entre buttes |
|
||||||
| [départ] |
|
| [départ] |
|
||||||
| SW : chaos de blocs SE : éperons |
|
| SW : chaos de blocs SE : éperons |
|
||||||
| 80 blocs r 2–6 m sur deux buttes |
|
| 90 blocs r 2–6 m sur deux buttes |
|
||||||
| sable ridé alvéolées, |
|
| sable ridé alvéolées, |
|
||||||
| chenal 20 m |
|
| chenal 20 m |
|
||||||
| ====== falaise sinueuse (Escarpment) ===== |
|
| ====== falaise sinueuse (Escarpment) ===== |
|
||||||
@@ -148,13 +148,15 @@ Plan de masse (nord en haut) :
|
|||||||
gradins : au moins deux niveaux plats ; un fond plat ne bouge pas) ;
|
gradins : au moins deux niveaux plats ; un fond plat ne bouge pas) ;
|
||||||
cuisson du Banc du Vigilant identique à l'artefact versionné.
|
cuisson du Banc du Vigilant identique à l'artefact versionné.
|
||||||
- **Contrat (`run_site_content_tests.gd`, section showcase)** : canyon
|
- **Contrat (`run_site_content_tests.gd`, section showcase)** : canyon
|
||||||
(profondeur ≥ 20 m sous la mesa, fond plat à ± 1 m sur 10 m, paroi ≥ 60°
|
(profondeur ≥ 18 m sous le sommet bruité de la mesa, fond de sable plat à
|
||||||
quelque part sur une transversale, ≥ 2 replats de gradin) ; crevasse
|
± 1 m sur 10 m, paroi ≥ 60° quelque part sur une transversale, ≥ 2 replats
|
||||||
(fond ≥ 16 m sous les bords, largeur au fond ≤ 8 m) ; chaos (roche sur un
|
distincts) ; crevasse (fond de sable ≥ 16 m sous le plateau ouvert, 3 à 8 m
|
||||||
|
de large à 10 m sous le plateau) ; chaos (roche sur un
|
||||||
bloc, sable entre deux blocs, ≥ 40 cellules de roche distinctes… mesuré par
|
bloc, sable entre deux blocs, ≥ 40 cellules de roche distinctes… mesuré par
|
||||||
fraction de roche dans le disque) ; éperons (chenal plus bas que les deux
|
fraction de roche dans le disque) ; éperons (chenal plus bas que les deux
|
||||||
buttes, roche sur chaque butte) ; falaise (drop ≥ 20 m entre deux points de
|
buttes, roche sur chaque butte) ; falaise (marche ≥ 18 m mesurée dans la
|
||||||
part et d'autre) ; bornes et profondeurs globales.
|
seule bande de crête, pour que la pente en contrebas ne masque pas une
|
||||||
|
marche absente) ; bornes et profondeurs globales.
|
||||||
- **Intégration (`run_site_terrain_integration.gd`)** : chargement du showcase
|
- **Intégration (`run_site_terrain_integration.gd`)** : chargement du showcase
|
||||||
sous 3 s, rayon dans le canyon plus bas que sur la mesa, scan de référence
|
sous 3 s, rayon dans le canyon plus bas que sur la mesa, scan de référence
|
||||||
lisant roche et sable.
|
lisant roche et sable.
|
||||||
|
|||||||
@@ -15,20 +15,23 @@ const DiveRulesResolver = preload("res://game/domain/rules/dive_rules_resolver.g
|
|||||||
const SITE_PATH := "res://content/sites/zone1_vigilant.tres"
|
const SITE_PATH := "res://content/sites/zone1_vigilant.tres"
|
||||||
const SHOWCASE_PATH := "res://content/sites/terrain_showcase.tres"
|
const SHOWCASE_PATH := "res://content/sites/terrain_showcase.tres"
|
||||||
# Showcase probe positions (docs/briefs/terrain-varied-landforms.md §5). The canyon
|
# Showcase probe positions (docs/briefs/terrain-varied-landforms.md §5). The canyon
|
||||||
# transect crosses its first leg at right angles; the crevasse transect is the
|
# transect crosses its second leg at right angles; the crevasse transect is the
|
||||||
# z = -120 row between the two buttes.
|
# z = -120 row between the two buttes, compared with the open plateau on the same row;
|
||||||
|
# the escarpment is probed across its step band at x = 0, where the crest is at z = 217.
|
||||||
const SHOWCASE_MESA_TOP := Vector2(-80.0, -90.0)
|
const SHOWCASE_MESA_TOP := Vector2(-80.0, -90.0)
|
||||||
const SHOWCASE_CANYON_MID := Vector2(-130.0, -135.0)
|
const SHOWCASE_CANYON_MID := Vector2(-130.0, -135.0)
|
||||||
const SHOWCASE_CANYON_AXIS := Vector2(40.0, -30.0)
|
const SHOWCASE_CANYON_AXIS := Vector2(40.0, -30.0)
|
||||||
const SHOWCASE_CREVASSE_ROW_Z := -120.0
|
const SHOWCASE_CREVASSE_ROW_Z := -120.0
|
||||||
const SHOWCASE_CREVASSE_SHELF_X := 118.0
|
const SHOWCASE_CREVASSE_PLAIN_X := 0.0
|
||||||
const SHOWCASE_CREVASSE_FLOOR_X := 124.0
|
const SHOWCASE_CREVASSE_FLOOR_X := 124.0
|
||||||
const SHOWCASE_BOULDER_FIELD_CENTRE := Vector2(-120.0, 110.0)
|
const SHOWCASE_BOULDER_FIELD_CENTRE := Vector2(-120.0, 110.0)
|
||||||
const SHOWCASE_BOULDER_FIELD_RADIUS_M := 75.0
|
const SHOWCASE_BOULDER_FIELD_RADIUS_M := 75.0
|
||||||
const SHOWCASE_WEST_SPUR := Vector2(80.0, 120.0)
|
const SHOWCASE_WEST_SPUR := Vector2(80.0, 120.0)
|
||||||
const SHOWCASE_CHANNEL := Vector2(125.0, 120.0)
|
const SHOWCASE_CHANNEL := Vector2(125.0, 120.0)
|
||||||
const SHOWCASE_EAST_SPUR := Vector2(170.0, 125.0)
|
const SHOWCASE_EAST_SPUR := Vector2(170.0, 125.0)
|
||||||
const STEEP_WALL_RISE_PER_M := 1.7 # tan(60 degrees)
|
const SHOWCASE_ESCARPMENT_CREST_Z := 217.0
|
||||||
|
const SHOWCASE_ESCARPMENT_BAND_M := 14.0 # softness 6 m plus 8 m of outline noise
|
||||||
|
const STEEP_WALL_RISE_PER_M := 1.73 # tan(60 degrees)
|
||||||
const LEDGE_RISE_PER_M := 0.35
|
const LEDGE_RISE_PER_M := 0.35
|
||||||
const ROUTE_CLEARANCE_M := 3.0
|
const ROUTE_CLEARANCE_M := 3.0
|
||||||
const MINIMUM_LANDMARK_SPACING_M := 60.0
|
const MINIMUM_LANDMARK_SPACING_M := 60.0
|
||||||
@@ -36,7 +39,7 @@ const MAXIMUM_ANCHOR_TO_WRECK_M := 320.0
|
|||||||
const MAXIMUM_WRECK_SPACING_M := 260.0
|
const MAXIMUM_WRECK_SPACING_M := 260.0
|
||||||
const HULL_HALF_LENGTH_M := 14.0
|
const HULL_HALF_LENGTH_M := 14.0
|
||||||
const HULL_SEABED_TOLERANCE_M := 2.0
|
const HULL_SEABED_TOLERANCE_M := 2.0
|
||||||
const TEST_COUNT := 52
|
const TEST_COUNT := 54
|
||||||
|
|
||||||
var _failures: Array[String] = []
|
var _failures: Array[String] = []
|
||||||
|
|
||||||
@@ -161,7 +164,7 @@ func _test_showcase_content() -> void:
|
|||||||
return
|
return
|
||||||
_expect(field.cell_size_m() == 1.0 and field.columns() == 513 and field.rows() == 513, "showcase grid is 513 x 513 cells of 1 m")
|
_expect(field.cell_size_m() == 1.0 and field.columns() == 513 and field.rows() == 513, "showcase grid is 513 x 513 cells of 1 m")
|
||||||
_expect(definition.playable_bounds_m.is_equal_approx(Rect2(-220.0, -220.0, 440.0, 440.0)), "showcase playable zone is 440 x 440 m")
|
_expect(definition.playable_bounds_m.is_equal_approx(Rect2(-220.0, -220.0, 440.0, 440.0)), "showcase playable zone is 440 x 440 m")
|
||||||
_expect(field.minimum_elevation_m() >= -80.5 and field.maximum_elevation_m() <= -18.5, "showcase terrain stays between 19 m and 80 m deep")
|
_expect(field.minimum_elevation_m() >= -80.5 and field.maximum_elevation_m() <= -19.0, "showcase terrain stays between 19 m and 80 m deep")
|
||||||
var anchors := definition.placements_of_kind(SitePlacement.Kind.VESSEL_ANCHOR)
|
var anchors := definition.placements_of_kind(SitePlacement.Kind.VESSEL_ANCHOR)
|
||||||
_expect(anchors.size() == 1 and definition.placements.size() == 1, "showcase has a single placement, the mooring beacon")
|
_expect(anchors.size() == 1 and definition.placements.size() == 1, "showcase has a single placement, the mooring beacon")
|
||||||
var spawn := definition.spawn_position_m
|
var spawn := definition.spawn_position_m
|
||||||
@@ -171,6 +174,7 @@ func _test_showcase_content() -> void:
|
|||||||
var mesa_top := field.elevation_at(SHOWCASE_MESA_TOP.x, SHOWCASE_MESA_TOP.y)
|
var mesa_top := field.elevation_at(SHOWCASE_MESA_TOP.x, SHOWCASE_MESA_TOP.y)
|
||||||
var canyon_floor := field.elevation_at(SHOWCASE_CANYON_MID.x, SHOWCASE_CANYON_MID.y)
|
var canyon_floor := field.elevation_at(SHOWCASE_CANYON_MID.x, SHOWCASE_CANYON_MID.y)
|
||||||
_expect(mesa_top - canyon_floor >= 18.0, "canyon floor is at least 18 m below the mesa top (%.1f m)" % (mesa_top - canyon_floor))
|
_expect(mesa_top - canyon_floor >= 18.0, "canyon floor is at least 18 m below the mesa top (%.1f m)" % (mesa_top - canyon_floor))
|
||||||
|
_expect(field.material_at(SHOWCASE_CANYON_MID.x, SHOWCASE_CANYON_MID.y) == SonarMaterial.Value.SAND, "canyon floor is sand through the rock mesa")
|
||||||
var axis := SHOWCASE_CANYON_AXIS.normalized()
|
var axis := SHOWCASE_CANYON_AXIS.normalized()
|
||||||
var lowest := INF
|
var lowest := INF
|
||||||
var highest := -INF
|
var highest := -INF
|
||||||
@@ -186,23 +190,30 @@ func _test_showcase_content() -> void:
|
|||||||
var point := SHOWCASE_CANYON_MID + normal * float(offset)
|
var point := SHOWCASE_CANYON_MID + normal * float(offset)
|
||||||
transect.append(field.elevation_at(point.x, point.y))
|
transect.append(field.elevation_at(point.x, point.y))
|
||||||
var steepest := 0.0
|
var steepest := 0.0
|
||||||
var ledge_pairs := 0
|
var ledges := 0
|
||||||
|
var on_ledge := false
|
||||||
for index in range(transect.size() - 1):
|
for index in range(transect.size() - 1):
|
||||||
var rise := absf(transect[index + 1] - transect[index])
|
var rise := absf(transect[index + 1] - transect[index])
|
||||||
steepest = maxf(steepest, rise)
|
steepest = maxf(steepest, rise)
|
||||||
var in_wall := transect[index] > canyon_floor + 1.0 and transect[index] < mesa_top - 1.0
|
var in_wall := transect[index] > canyon_floor + 1.0 and transect[index] < mesa_top - 1.0
|
||||||
|
# A ledge is a run of flat metres inside the wall, separated from the next by a riser.
|
||||||
if in_wall and rise < LEDGE_RISE_PER_M:
|
if in_wall and rise < LEDGE_RISE_PER_M:
|
||||||
ledge_pairs += 1
|
if not on_ledge:
|
||||||
_expect(steepest >= STEEP_WALL_RISE_PER_M, "canyon wall is steeper than 60 degrees somewhere (%.1f m per m)" % steepest)
|
ledges += 1
|
||||||
_expect(ledge_pairs >= 2, "canyon wall carries at least two ledges (%d flat metres)" % ledge_pairs)
|
on_ledge = true
|
||||||
|
else:
|
||||||
|
on_ledge = false
|
||||||
|
_expect(steepest >= STEEP_WALL_RISE_PER_M, "canyon wall is steeper than 60 degrees somewhere (%.2f m per m)" % steepest)
|
||||||
|
_expect(ledges >= 2, "canyon walls carry at least two distinct ledges (%d)" % ledges)
|
||||||
|
|
||||||
# NE: crevasse between the buttes.
|
# NE: crevasse between the buttes.
|
||||||
var shelf := field.elevation_at(SHOWCASE_CREVASSE_SHELF_X, SHOWCASE_CREVASSE_ROW_Z)
|
var plain := field.elevation_at(SHOWCASE_CREVASSE_PLAIN_X, SHOWCASE_CREVASSE_ROW_Z)
|
||||||
var crevasse_floor := field.elevation_at(SHOWCASE_CREVASSE_FLOOR_X, SHOWCASE_CREVASSE_ROW_Z)
|
var crevasse_floor := field.elevation_at(SHOWCASE_CREVASSE_FLOOR_X, SHOWCASE_CREVASSE_ROW_Z)
|
||||||
_expect(shelf - crevasse_floor >= 16.0, "crevasse floor is at least 16 m below the rim (%.1f m)" % (shelf - crevasse_floor))
|
_expect(plain - crevasse_floor >= 16.0, "crevasse floor is at least 16 m below the open plateau (%.1f m)" % (plain - crevasse_floor))
|
||||||
|
_expect(field.material_at(SHOWCASE_CREVASSE_FLOOR_X, SHOWCASE_CREVASSE_ROW_Z) == SonarMaterial.Value.SAND, "crevasse floor is sand")
|
||||||
var deep_cells := 0
|
var deep_cells := 0
|
||||||
for x in range(100, 146):
|
for x in range(100, 146):
|
||||||
if field.elevation_at(float(x), SHOWCASE_CREVASSE_ROW_Z) < shelf - 10.0:
|
if field.elevation_at(float(x), SHOWCASE_CREVASSE_ROW_Z) < plain - 10.0:
|
||||||
deep_cells += 1
|
deep_cells += 1
|
||||||
_expect(deep_cells >= 3 and deep_cells <= 8, "crevasse is 3 to 8 m wide at 10 m down (%d m)" % deep_cells)
|
_expect(deep_cells >= 3 and deep_cells <= 8, "crevasse is 3 to 8 m wide at 10 m down (%d m)" % deep_cells)
|
||||||
|
|
||||||
@@ -234,10 +245,11 @@ func _test_showcase_content() -> void:
|
|||||||
)
|
)
|
||||||
_expect(field.material_at(SHOWCASE_CHANNEL.x, SHOWCASE_CHANNEL.y + 30.0) == SonarMaterial.Value.SAND, "the channel floor is sand")
|
_expect(field.material_at(SHOWCASE_CHANNEL.x, SHOWCASE_CHANNEL.y + 30.0) == SonarMaterial.Value.SAND, "the channel floor is sand")
|
||||||
|
|
||||||
# S: escarpment.
|
# S: escarpment, measured across its step band only so the slope below cannot
|
||||||
var above := field.elevation_at(0.0, 190.0)
|
# stand in for a missing step (the slope alone would give about 7 m here).
|
||||||
var below := field.elevation_at(0.0, 250.0)
|
var above := field.elevation_at(0.0, SHOWCASE_ESCARPMENT_CREST_Z - SHOWCASE_ESCARPMENT_BAND_M)
|
||||||
_expect(above - below >= 20.0, "the escarpment drops at least 20 m (%.1f m)" % (above - below))
|
var below := field.elevation_at(0.0, SHOWCASE_ESCARPMENT_CREST_Z + SHOWCASE_ESCARPMENT_BAND_M)
|
||||||
|
_expect(above - below >= 18.0, "the escarpment steps down at least 18 m across its crest band (%.1f m)" % (above - below))
|
||||||
|
|
||||||
|
|
||||||
func _rests_on_terrain(field: TerrainHeightField, placement: SitePlacement) -> bool:
|
func _rests_on_terrain(field: TerrainHeightField, placement: SitePlacement) -> bool:
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ func _test_showcase_site() -> void:
|
|||||||
var mesa_top := _terrain_height(space, terrain, -80.0, -90.0, [])
|
var mesa_top := _terrain_height(space, terrain, -80.0, -90.0, [])
|
||||||
_expect(mesa_top - canyon_floor >= 18.0, "collision rays find the canyon under the mesa (%.1f m vs %.1f m)" % [canyon_floor, mesa_top])
|
_expect(mesa_top - canyon_floor >= 18.0, "collision rays find the canyon under the mesa (%.1f m vs %.1f m)" % [canyon_floor, mesa_top])
|
||||||
|
|
||||||
# Sensor above the canyon looking along its first leg (the acquisition probes along
|
# Sensor above the canyon looking along its second leg (the acquisition probes along
|
||||||
# local +Z): rock walls and sand floor.
|
# local +Z): rock walls and sand floor.
|
||||||
var sensor := Transform3D(Basis.IDENTITY, Vector3(-130.0, -15.0, -135.0))
|
var sensor := Transform3D(Basis.IDENTITY, Vector3(-130.0, -15.0, -135.0))
|
||||||
sensor.basis = Basis.looking_at(-Vector3(40.0, 0.0, -30.0).normalized(), Vector3.UP)
|
sensor.basis = Basis.looking_at(-Vector3(40.0, 0.0, -30.0).normalized(), Vector3.UP)
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const SiteTerrainData = preload("res://game/domain/sites/site_terrain_data.gd")
|
|||||||
|
|
||||||
const ZONE1_TERRAIN_PATH := "res://content/sites/zone1_vigilant_terrain.res"
|
const ZONE1_TERRAIN_PATH := "res://content/sites/zone1_vigilant_terrain.res"
|
||||||
|
|
||||||
const TEST_COUNT := 40
|
const TEST_COUNT := 44
|
||||||
|
|
||||||
var _failures: Array[String] = []
|
var _failures: Array[String] = []
|
||||||
|
|
||||||
@@ -121,16 +121,18 @@ func _test_modifiers_and_primitive_materials() -> void:
|
|||||||
func _test_canyon() -> void:
|
func _test_canyon() -> void:
|
||||||
var canyon := SiteTerrainBaker.Canyon.new(
|
var canyon := SiteTerrainBaker.Canyon.new(
|
||||||
PackedVector2Array([Vector2(-20.0, 0.0), Vector2(0.0, 0.0), Vector2(0.0, 20.0)]),
|
PackedVector2Array([Vector2(-20.0, 0.0), Vector2(0.0, 0.0), Vector2(0.0, 20.0)]),
|
||||||
4.0, 10.0, 1.0, 0, 0.0
|
4.0, 10.0, 1.0, 0, 0.0, SonarMaterial.Value.SAND
|
||||||
)
|
)
|
||||||
_expect(is_equal_approx(canyon.elevation_delta(-10.0, 0.0), -10.0), "canyon floor sits at depth on the first leg")
|
_expect(is_equal_approx(canyon.elevation_delta(-10.0, 0.0), -10.0), "canyon floor sits at depth on the first leg")
|
||||||
|
_expect(canyon.material_at(-10.0, 0.0) == SonarMaterial.Value.SAND, "canyon imposes its floor material on the flat floor")
|
||||||
|
_expect(canyon.material_at(-10.0, 4.0) == SiteTerrainBaker.NO_MATERIAL, "canyon leaves its walls to the slope rule")
|
||||||
_expect(is_equal_approx(canyon.elevation_delta(1.5, 10.0), -10.0), "canyon floor is flat across its width on the second leg")
|
_expect(is_equal_approx(canyon.elevation_delta(1.5, 10.0), -10.0), "canyon floor is flat across its width on the second leg")
|
||||||
_expect(is_equal_approx(canyon.elevation_delta(-10.0, 6.0), 0.0), "canyon stops past half width plus softness")
|
_expect(is_equal_approx(canyon.elevation_delta(-10.0, 6.0), 0.0), "canyon stops past half width plus softness")
|
||||||
_expect(is_equal_approx(canyon.elevation_delta(15.0, -15.0), 0.0), "canyon is zero far from the path")
|
_expect(is_equal_approx(canyon.elevation_delta(15.0, -15.0), 0.0), "canyon is zero far from the path")
|
||||||
_expect(is_equal_approx(canyon.elevation_delta(0.0, 20.0), 0.0), "canyon closes at its last vertex")
|
_expect(is_equal_approx(canyon.elevation_delta(0.0, 20.0), 0.0), "canyon closes at its last vertex")
|
||||||
_expect(canyon.elevation_delta(-10.0, 4.0) < -4.0 and canyon.elevation_delta(-10.0, 4.0) > -6.0, "canyon wall is half way down at half width")
|
_expect(canyon.elevation_delta(-10.0, 4.0) < -4.0 and canyon.elevation_delta(-10.0, 4.0) > -6.0, "canyon wall is half way down at half width")
|
||||||
var rough := SiteTerrainBaker.Canyon.new(
|
var rough := SiteTerrainBaker.Canyon.new(
|
||||||
PackedVector2Array([Vector2(-200.0, 0.0), Vector2(200.0, 0.0)]), 8.0, 10.0, 2.0, 7, 3.0
|
PackedVector2Array([Vector2(-200.0, 0.0), Vector2(200.0, 0.0)]), 8.0, 10.0, 2.0, 7, 3.0, SiteTerrainBaker.NO_MATERIAL
|
||||||
)
|
)
|
||||||
var wall_values: Dictionary = {}
|
var wall_values: Dictionary = {}
|
||||||
for x in range(-100, 101, 10):
|
for x in range(-100, 101, 10):
|
||||||
|
|||||||
@@ -170,13 +170,15 @@ class Groove extends Primitive:
|
|||||||
## Flat-floored trench along a polyline. The floor sits depth_m below the surroundings
|
## Flat-floored trench along a polyline. The floor sits depth_m below the surroundings
|
||||||
## wherever the (noise-perturbed) distance to the path is under half_width_m minus
|
## wherever the (noise-perturbed) distance to the path is under half_width_m minus
|
||||||
## wall_softness_m; the walls climb over 2 * wall_softness_m; the trench closes over one
|
## wall_softness_m; the walls climb over 2 * wall_softness_m; the trench closes over one
|
||||||
## half-width at each end.
|
## half-width at each end. floor_material (a SonarMaterial value, or NO_MATERIAL) is
|
||||||
|
## imposed on the flat floor, so a canyon cut through a rock outcrop keeps a sand bed.
|
||||||
class Canyon extends Primitive:
|
class Canyon extends Primitive:
|
||||||
var path: PackedVector2Array
|
var path: PackedVector2Array
|
||||||
var half_width_m: float
|
var half_width_m: float
|
||||||
var depth_m: float
|
var depth_m: float
|
||||||
var wall_softness_m: float
|
var wall_softness_m: float
|
||||||
var edge_amplitude_m: float
|
var edge_amplitude_m: float
|
||||||
|
var floor_material: int
|
||||||
var _noise: FastNoiseLite
|
var _noise: FastNoiseLite
|
||||||
var _length_m: float
|
var _length_m: float
|
||||||
|
|
||||||
@@ -186,17 +188,31 @@ class Canyon extends Primitive:
|
|||||||
p_depth_m: float,
|
p_depth_m: float,
|
||||||
p_wall_softness_m: float,
|
p_wall_softness_m: float,
|
||||||
p_edge_seed: int,
|
p_edge_seed: int,
|
||||||
p_edge_amplitude_m: float
|
p_edge_amplitude_m: float,
|
||||||
|
p_floor_material: int
|
||||||
) -> void:
|
) -> void:
|
||||||
|
assert(p_path.size() >= 2, "a canyon path needs at least two vertices")
|
||||||
|
assert(p_half_width_m > 0.0, "a canyon needs a positive half width")
|
||||||
path = p_path
|
path = p_path
|
||||||
half_width_m = p_half_width_m
|
half_width_m = p_half_width_m
|
||||||
depth_m = p_depth_m
|
depth_m = p_depth_m
|
||||||
wall_softness_m = p_wall_softness_m
|
wall_softness_m = p_wall_softness_m
|
||||||
edge_amplitude_m = p_edge_amplitude_m
|
edge_amplitude_m = p_edge_amplitude_m
|
||||||
|
floor_material = p_floor_material
|
||||||
_noise = edge_noise(p_edge_seed, p_edge_amplitude_m)
|
_noise = edge_noise(p_edge_seed, p_edge_amplitude_m)
|
||||||
_length_m = path_length(p_path)
|
_length_m = path_length(p_path)
|
||||||
|
|
||||||
func elevation_delta(x_m: float, z_m: float) -> float:
|
func elevation_delta(x_m: float, z_m: float) -> float:
|
||||||
|
return -depth_m * _presence(x_m, z_m)
|
||||||
|
|
||||||
|
func material_at(x_m: float, z_m: float) -> int:
|
||||||
|
# Full presence means the flat floor: inside the walls and clear of the end tapers.
|
||||||
|
if floor_material != NO_MATERIAL and _presence(x_m, z_m) >= 0.999:
|
||||||
|
return floor_material
|
||||||
|
return NO_MATERIAL
|
||||||
|
|
||||||
|
## 1 on the flat floor, 0 outside the trench, in between on the walls and end tapers.
|
||||||
|
func _presence(x_m: float, z_m: float) -> float:
|
||||||
var sample := path_sample(Vector2(x_m, z_m), path)
|
var sample := path_sample(Vector2(x_m, z_m), path)
|
||||||
var distance := sample.x + edge_offset(_noise, edge_amplitude_m, x_m, z_m)
|
var distance := sample.x + edge_offset(_noise, edge_amplitude_m, x_m, z_m)
|
||||||
var inside := 1.0 - smoothstep(
|
var inside := 1.0 - smoothstep(
|
||||||
@@ -205,13 +221,14 @@ class Canyon extends Primitive:
|
|||||||
distance
|
distance
|
||||||
)
|
)
|
||||||
var taper := smoothstep(0.0, half_width_m, sample.y) * smoothstep(0.0, half_width_m, _length_m - sample.y)
|
var taper := smoothstep(0.0, half_width_m, sample.y) * smoothstep(0.0, half_width_m, _length_m - sample.y)
|
||||||
return -depth_m * inside * taper
|
return inside * taper
|
||||||
|
|
||||||
|
|
||||||
## Step along a polyline: the terrain drops by drop_m on the right of the travel direction
|
## Step along a polyline: the terrain drops by drop_m on the right of the travel direction
|
||||||
## (south of an eastbound path), over 2 * softness_m, then keeps falling at slope_beyond.
|
## (south of an eastbound path), over 2 * softness_m, then keeps falling at slope_beyond.
|
||||||
## The crest line is roughened by outline noise. Extend the path past the field edges:
|
## The crest line is roughened by outline noise. The path must extend past the field
|
||||||
## beyond its ends the contour wraps around the end vertex.
|
## edges: beyond an end vertex the drop is decided by the half-plane of the last segment,
|
||||||
|
## which leaves a seam along the path's extension.
|
||||||
class Escarpment extends Primitive:
|
class Escarpment extends Primitive:
|
||||||
var path: PackedVector2Array
|
var path: PackedVector2Array
|
||||||
var drop_m: float
|
var drop_m: float
|
||||||
@@ -228,6 +245,7 @@ class Escarpment extends Primitive:
|
|||||||
p_edge_seed: int,
|
p_edge_seed: int,
|
||||||
p_edge_amplitude_m: float
|
p_edge_amplitude_m: float
|
||||||
) -> void:
|
) -> void:
|
||||||
|
assert(p_path.size() >= 2, "an escarpment path needs at least two vertices")
|
||||||
path = p_path
|
path = p_path
|
||||||
drop_m = p_drop_m
|
drop_m = p_drop_m
|
||||||
softness_m = p_softness_m
|
softness_m = p_softness_m
|
||||||
@@ -267,6 +285,7 @@ class Outcrop extends Primitive:
|
|||||||
p_edge_amplitude_m: float,
|
p_edge_amplitude_m: float,
|
||||||
p_rock_top: bool
|
p_rock_top: bool
|
||||||
) -> void:
|
) -> void:
|
||||||
|
assert(p_radius_x_m > 0.0 and p_radius_z_m > 0.0, "an outcrop needs positive radii")
|
||||||
centre = p_centre
|
centre = p_centre
|
||||||
radius_x_m = p_radius_x_m
|
radius_x_m = p_radius_x_m
|
||||||
radius_z_m = p_radius_z_m
|
radius_z_m = p_radius_z_m
|
||||||
@@ -435,6 +454,7 @@ class Strata extends Modifier:
|
|||||||
p_centre: Vector2,
|
p_centre: Vector2,
|
||||||
p_radius_m: float
|
p_radius_m: float
|
||||||
) -> void:
|
) -> void:
|
||||||
|
assert(p_layer_height_m > 0.0, "strata need a positive layer height")
|
||||||
layer_height_m = p_layer_height_m
|
layer_height_m = p_layer_height_m
|
||||||
sharpness = clampf(p_sharpness, 0.0, 0.999)
|
sharpness = clampf(p_sharpness, 0.0, 0.999)
|
||||||
centre = p_centre
|
centre = p_centre
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ extends RefCounted
|
|||||||
## mooring beacon at the centre. Field 512 x 512 m at 1 m, +X east, +Z south.
|
## mooring beacon at the centre. Field 512 x 512 m at 1 m, +X east, +Z south.
|
||||||
|
|
||||||
const SiteTerrainBaker = preload("res://tools/site_terrain_baker.gd")
|
const SiteTerrainBaker = preload("res://tools/site_terrain_baker.gd")
|
||||||
|
const SonarMaterial = preload("res://game/domain/sonar/sonar_material.gd")
|
||||||
const SiteTerrainData = preload("res://game/domain/sites/site_terrain_data.gd")
|
const SiteTerrainData = preload("res://game/domain/sites/site_terrain_data.gd")
|
||||||
const DiveSiteDefinition = preload("res://game/domain/sites/dive_site_definition.gd")
|
const DiveSiteDefinition = preload("res://game/domain/sites/dive_site_definition.gd")
|
||||||
const SitePlacement = preload("res://game/domain/sites/site_placement.gd")
|
const SitePlacement = preload("res://game/domain/sites/site_placement.gd")
|
||||||
@@ -59,15 +60,15 @@ static func recipe() -> SiteTerrainBaker.Recipe:
|
|||||||
# modifier (one ledge every 2.8 m across, resolvable at 1 m per cell), rippled
|
# modifier (one ledge every 2.8 m across, resolvable at 1 m per cell), rippled
|
||||||
# sand on the floor.
|
# sand on the floor.
|
||||||
SiteTerrainBaker.Outcrop.new(MESA_CENTRE, 95.0, 85.0, 6.0, 10.0, 101, 6.0, true),
|
SiteTerrainBaker.Outcrop.new(MESA_CENTRE, 95.0, 85.0, 6.0, 10.0, 101, 6.0, true),
|
||||||
SiteTerrainBaker.Canyon.new(PackedVector2Array(CANYON_PATH), CANYON_HALF_WIDTH_M, CANYON_DEPTH_M, 7.0, 102, 2.0),
|
SiteTerrainBaker.Canyon.new(PackedVector2Array(CANYON_PATH), CANYON_HALF_WIDTH_M, CANYON_DEPTH_M, 7.0, 102, 2.0, SonarMaterial.Value.SAND),
|
||||||
SiteTerrainBaker.Ripples.new(Vector2(0.8, -0.6), 4.0, 0.25, Vector2(-130.0, -140.0), 70.0),
|
SiteTerrainBaker.Ripples.new(Vector2(0.8, -0.6), 4.0, 0.25, Vector2(-130.0, -140.0), 70.0),
|
||||||
|
|
||||||
# NE — la Fente: two buttes 8-9 m high with a 6 m wide, 20 m deep slot between them.
|
# NE — la Fente: two buttes 8-9 m high with a 6 m wide, 20 m deep slot between them.
|
||||||
SiteTerrainBaker.Outcrop.new(WEST_BUTTE_CENTRE, 38.0, 70.0, 8.0, 8.0, 201, 5.0, true),
|
SiteTerrainBaker.Outcrop.new(WEST_BUTTE_CENTRE, 38.0, 70.0, 8.0, 8.0, 201, 5.0, true),
|
||||||
SiteTerrainBaker.Outcrop.new(EAST_BUTTE_CENTRE, 38.0, 75.0, 9.0, 8.0, 202, 5.0, true),
|
SiteTerrainBaker.Outcrop.new(EAST_BUTTE_CENTRE, 38.0, 75.0, 9.0, 8.0, 202, 5.0, true),
|
||||||
SiteTerrainBaker.Canyon.new(PackedVector2Array(CREVASSE_PATH), CREVASSE_HALF_WIDTH_M, CREVASSE_DEPTH_M, 1.0, 203, 1.0),
|
SiteTerrainBaker.Canyon.new(PackedVector2Array(CREVASSE_PATH), CREVASSE_HALF_WIDTH_M, CREVASSE_DEPTH_M, 1.0, 203, 1.0, SonarMaterial.Value.SAND),
|
||||||
|
|
||||||
# SW — le Chaos: ninety boulders 2-6 m across plus six coral heads, on rippled sand.
|
# SW — le Chaos: ninety boulders of 2-6 m radius plus six coral heads, on rippled sand.
|
||||||
SiteTerrainBaker.BoulderField.new(BOULDER_FIELD_CENTRE, BOULDER_FIELD_RADIUS_M, 301, 90, 2.0, 6.0, 0.7),
|
SiteTerrainBaker.BoulderField.new(BOULDER_FIELD_CENTRE, BOULDER_FIELD_RADIUS_M, 301, 90, 2.0, 6.0, 0.7),
|
||||||
SiteTerrainBaker.BoulderField.new(Vector2(-100.0, 130.0), 40.0, 302, 6, 6.0, 9.0, 0.6),
|
SiteTerrainBaker.BoulderField.new(Vector2(-100.0, 130.0), 40.0, 302, 6, 6.0, 9.0, 0.6),
|
||||||
SiteTerrainBaker.Ripples.new(Vector2(1.0, 0.3), 3.5, 0.3, BOULDER_FIELD_CENTRE, 110.0),
|
SiteTerrainBaker.Ripples.new(Vector2(1.0, 0.3), 3.5, 0.3, BOULDER_FIELD_CENTRE, 110.0),
|
||||||
|
|||||||
Reference in new issue
Block a user