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
@@ -0,0 +1,179 @@
|
||||
# Proposition — formes de relief variées pour les sites de plongée
|
||||
|
||||
**Statut : exécuté en autonomie le 2026-10-06** sur demande d'Olivier
|
||||
(« implémente ce genre de terrains variés, terrain statique, ne te concentre que
|
||||
sur le terrain, pas les effets de rendu ou la végétation »). Les décisions
|
||||
ouvertes sont listées en §8.
|
||||
|
||||
Rédigée à partir de cinq photos de référence fournies par Olivier, de l'ADR
|
||||
0012, de `tools/site_terrain_baker.gd` et du brief
|
||||
`docs/briefs/terrain-site-zone1-gameplay.md`.
|
||||
|
||||
---
|
||||
|
||||
## 1. Objectif
|
||||
|
||||
Étendre le vocabulaire de formes du script de cuisson pour que les sites
|
||||
suivants (U-Boot « sous falaise », navire océanographique « dans un canyon »,
|
||||
GDP §15) puissent être authorés avec le même pipeline que le Banc du Vigilant :
|
||||
canyons à parois raides et strates, crevasses étroites, éperons et chaos de
|
||||
blocs, buttes rocheuses, falaises sinueuses. Terrain statique, surface unique
|
||||
(ADR 0010), aucun changement de rendu ni de végétation.
|
||||
|
||||
Terminé quand :
|
||||
|
||||
- chaque forme des cinq références a une primitive nommée, testée unitairement,
|
||||
dans `tools/site_terrain_baker.gd` ;
|
||||
- un site de démonstration cuit (`content/sites/terrain_showcase.tres`) montre
|
||||
les cinq formes côte à côte, se charge en moins de 3 s et passe des tests de
|
||||
contrat géométriques (profondeur, pente, largeur, matériau) ;
|
||||
- le Banc du Vigilant est cuit à l'identique (aucun octet de
|
||||
`zone1_vigilant_terrain.res` ne change) ;
|
||||
- `bash scripts/quality_gate.sh` passe.
|
||||
|
||||
## 2. Lecture des références
|
||||
|
||||
| Photo | Ce qu'on voit | Forme de relief | Ce qu'un champ de hauteur peut en garder |
|
||||
|---|---|---|---|
|
||||
| 1 | Deux masses rocheuses alvéolées encadrant un chenal de sable grossier | éperons rocheux + chenal | deux buttes aux bords irréguliers, chenal plat entre elles, rugosité forte sur la roche |
|
||||
| 2 | Fente très étroite et profonde entre deux parois quasi verticales | crevasse | sillon de 5–6 m de large, 20 m de profondeur, parois à 80°+ ; pas de surplomb |
|
||||
| 3 | Pente de mamelons rocheux arrondis | mamelons | buttes à flancs doux sur une pente, rugosité moyenne |
|
||||
| 4 | Canyon aux parois verticales **stratifiées**, fond de sable plat **ridé**, mesas en haut | canyon à strates | tranchée à fond plat, parois raides quantifiées en gradins horizontaux, rides sur le fond, plateaux sommitaux |
|
||||
| 5 | Platier : têtes de corail et blocs épars sur sable ridé | chaos de blocs | champ de demi-ellipsoïdes de 2–6 m de rayon, matériau roche, sable ridé entre |
|
||||
|
||||
Hors périmètre, volontairement : lumière, caustiques, couleur de l'eau,
|
||||
herbier, corail (rendu et végétation).
|
||||
|
||||
## 3. Ce qui existe et ce qui manque
|
||||
|
||||
Le cuiseur additionne des primitives `elevation_delta(x, z)` puis déduit le
|
||||
matériau de la pente (roche au-dessus de 20°) et applique des `MaterialPatch`
|
||||
circulaires. Les primitives actuelles (`SlopedPlane`, `Blob`, `Ridge`,
|
||||
`Groove`, `Step`, `Ripples`, `Roughness`) sont toutes **lisses** (gaussiennes
|
||||
ou `smoothstep`) et **mono-segment**. Il manque :
|
||||
|
||||
1. des **tracés en polyligne** (canyon, crevasse, falaise sinueuse) ;
|
||||
2. des **bords irréguliers** (bruit de contour) pour casser l'aspect « gaussien » ;
|
||||
3. des **formes à sommet plat et flancs raides** (butte, mesa) ;
|
||||
4. des **champs d'objets discrets** combinés par maximum, pas par somme
|
||||
(blocs qui ne fusionnent pas en bosse) ;
|
||||
5. une phase de **modification** après la somme, qui connaît la pente locale,
|
||||
pour quantifier les parois en **strates** ;
|
||||
6. un **matériau imposé par une primitive** (un bloc est roche même sur son
|
||||
sommet plat).
|
||||
|
||||
## 4. Décision : extension du cuiseur
|
||||
|
||||
### 4.1 Contrat
|
||||
|
||||
- `Primitive.elevation_delta(x_m, z_m) -> float` : inchangé, additif.
|
||||
- `Primitive.material_at(x_m, z_m) -> int` : nouveau, renvoie
|
||||
`NO_MATERIAL` (-1) par défaut ; une valeur `SonarMaterial.Value` l'emporte sur
|
||||
la règle de pente, dans l'ordre des primitives, avant les `MaterialPatch`.
|
||||
- `Modifier.modify(x_m, z_m, elevation_m, gradient) -> float` : nouvelle phase.
|
||||
`gradient` est la pente locale `Vector2(dE/dx, dE/dz)` calculée par
|
||||
différences centrées sur le champ courant ; les modificateurs s'enchaînent
|
||||
dans l'ordre de `Recipe.modifiers`, après la somme et avant le plancher.
|
||||
- Ordre de cuisson : somme des primitives → modificateurs → plancher
|
||||
`floor_elevation_m` → matériau par pente → matériau par primitive →
|
||||
`MaterialPatch`.
|
||||
|
||||
### 4.2 Nouvelles primitives
|
||||
|
||||
| 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 |
|
||||
| `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 |
|
||||
| `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 |
|
||||
|
||||
### 4.3 Nouveau modificateur
|
||||
|
||||
| Modificateur | Paramètres | Effet |
|
||||
|---|---|---|
|
||||
| `Strata` | `layer_height_m`, `sharpness` (0–1), `minimum_slope_degrees`, `full_slope_degrees`, `centre`, `radius_m` (0 = partout) | quantifie l'élévation en gradins de `layer_height_m` là où la pente dépasse `minimum_slope_degrees` (plein effet à `full_slope_degrees`) ; `sharpness` 0 = aucun effet, 1 = marches franches ; le fond plat et le plateau ne bougent pas |
|
||||
|
||||
Formule : `f = frac(e / h)`, `f' = clamp((f - 0.5) / (1 - s) + 0.5, 0, 1)`,
|
||||
`e' = lerp(e, floor(e / h) * h + h * f', w)` avec
|
||||
`w = smoothstep(tan(min), tan(full), |gradient|) * masque`.
|
||||
|
||||
### 4.4 Ce qui ne change pas
|
||||
|
||||
Le contrat de données (`SiteTerrainData`, ADR 0012 §2), l'acteur de monde, la
|
||||
collision, le sonar, le shader. Les `.res` restent les artefacts dérivés ; la
|
||||
recette reste le document d'auteur. Le bruit de contour est un
|
||||
`FastNoiseLite` seedé, donc déterministe.
|
||||
|
||||
## 5. Site de démonstration : `terrain_showcase`
|
||||
|
||||
But : prouver chaque forme en jeu et au sonar, servir de catalogue pour
|
||||
l'auteur des sites suivants. Pas un site de mission : un seul placement
|
||||
(balise de mouillage), aucune épave, aucune route.
|
||||
|
||||
| Donnée | Valeur |
|
||||
|---|---|
|
||||
| Champ | 513 × 513 cellules, 1,0 m, origine (-256, -256) |
|
||||
| Zone jouable | 440 × 440 m centrée |
|
||||
| Plateau de base | 30 m, incliné de 4 m vers le sud |
|
||||
| Profondeur min / max | 18 m / 80 m |
|
||||
| Départ | (0, -12, 0), cap nord |
|
||||
|
||||
Plan de masse (nord en haut) :
|
||||
|
||||
```text
|
||||
+---------------------------------------------+
|
||||
| NW : canyon à strates NE : crevasse |
|
||||
| mesas à -24 m, tranchée fente 6 m de |
|
||||
| 28 m de large, 24 m de large, 20 m |
|
||||
| profond, gradins 2,5 m, de profond, |
|
||||
| fond ridé entre buttes |
|
||||
| [départ] |
|
||||
| SW : chaos de blocs SE : éperons |
|
||||
| 80 blocs r 2–6 m sur deux buttes |
|
||||
| sable ridé alvéolées, |
|
||||
| chenal 20 m |
|
||||
| ====== falaise sinueuse (Escarpment) ===== |
|
||||
| tombant vers -80 m, mamelons sur la pente |
|
||||
+---------------------------------------------+
|
||||
```
|
||||
|
||||
## 6. Tests
|
||||
|
||||
- **Unitaires (`run_site_baker_tests.gd`)** : `Canyon` (fond plat à la
|
||||
profondeur demandée, nul hors tracé, suit le coude d'une polyligne,
|
||||
fermeture aux bouts) ; `Outcrop` (sommet à `height_m`, roche sur le sommet
|
||||
si `rock_top`, nul à deux rayons) ; `BoulderField` (déterministe, nul hors
|
||||
disque, roche sur un bloc, maximum et non somme) ; `Escarpment` (côté droit
|
||||
plus bas, drop égal à `drop_m`) ; `Strata` (une pente raide devient des
|
||||
gradins : au moins deux niveaux plats ; un fond plat ne bouge pas) ;
|
||||
cuisson du Banc du Vigilant identique à l'artefact versionné.
|
||||
- **Contrat (`run_site_content_tests.gd`, section showcase)** : canyon
|
||||
(profondeur ≥ 20 m sous la mesa, fond plat à ± 1 m sur 10 m, paroi ≥ 60°
|
||||
quelque part sur une transversale, ≥ 2 replats de gradin) ; crevasse
|
||||
(fond ≥ 16 m sous les bords, largeur au fond ≤ 8 m) ; chaos (roche sur un
|
||||
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
|
||||
buttes, roche sur chaque butte) ; falaise (drop ≥ 20 m entre deux points de
|
||||
part et d'autre) ; bornes et profondeurs globales.
|
||||
- **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
|
||||
lisant roche et sable.
|
||||
|
||||
## 7. Documentation
|
||||
|
||||
- ADR 0012 : amendement §3 (liste des primitives, phase de modification,
|
||||
matériau par primitive), sans changer le contrat de données.
|
||||
- `content/README.md`, `tests/README.md` : le site de démonstration.
|
||||
- Captures : PNG de prévisualisation du showcase sous `docs/briefs/captures/`.
|
||||
|
||||
## 8. Décisions laissées à Olivier
|
||||
|
||||
1. **Résolution** : le showcase est cuit à 1,0 m par cellule (crevasse de 6 m
|
||||
lisible). Les sites de mission restent à 1,5 m sauf décision contraire ; une
|
||||
crevasse de 5 m à 1,5 m par cellule n'a que trois cellules de large.
|
||||
2. **Strates partout ?** Le modificateur est appliqué au canyon seulement.
|
||||
L'appliquer à la falaise sud du Banc du Vigilant est un changement de
|
||||
recette, donc de `.res`, donc une re-cuisson à valider.
|
||||
3. **Sites de mission suivants** (U-Boot sous falaise, océanographique en
|
||||
canyon) : les primitives sont prêtes ; le plan de masse de chaque site
|
||||
reste à proposer et valider séparément.
|
||||
@@ -0,0 +1,56 @@
|
||||
# Formes de relief variées — Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Extend the bake tool with polyline canyons, noise-edged outcrops, boulder fields, polyline escarpments and a strata modifier, then bake a `terrain_showcase` site that demonstrates all five reference landforms and is covered by geometric contract tests.
|
||||
|
||||
**Architecture:** `tools/site_terrain_baker.gd` gains a `Modifier` phase (after the primitive sum, with the local gradient) and a per-primitive material override hook. New primitives are nested classes next to the existing ones. A new recipe `tools/sites/terrain_showcase_recipe.gd` and its baked `content/sites/terrain_showcase*.{tres,res}` are the demonstration site. Nothing under `game/` changes.
|
||||
|
||||
**Tech Stack:** Godot 4.7.2 headless, GDScript, `bash scripts/quality_gate.sh`.
|
||||
|
||||
**Spec:** `docs/briefs/terrain-varied-landforms.md`.
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- Godot binary (Git Bash): `export GODOT_BIN="/c/Users/olivi/Downloads/Godot_v4.7.2-stable_win64.exe/Godot_v4.7.2-stable_win64_console.exe"`; ripgrep: `export PATH="$PATH:/c/Users/olivi/AppData/Local/Programs/Microsoft VS Code/resources/app/node_modules/@vscode/ripgrep/bin"`.
|
||||
- `tools/` depends on `game/domain/` only; nothing under `game/` or `content/` references `res://tools/` (architecture check).
|
||||
- Determinism: every random element is a seeded `FastNoiseLite` or `RandomNumberGenerator`; the same recipe bakes byte-identical arrays.
|
||||
- `content/sites/zone1_vigilant_terrain.res` must not change (re-bake and `git diff --stat` empty for it).
|
||||
- Test runners: `TEST_COUNT` equals the number of `_expect` calls.
|
||||
- Never hand-edit `.tres`/`.res`.
|
||||
- Commits end with `Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>`.
|
||||
|
||||
## Tasks
|
||||
|
||||
### Task 1: Modifier phase and per-primitive material hook
|
||||
- `Primitive.material_at(x, z) -> int` returning `NO_MATERIAL := -1`.
|
||||
- `Modifier.modify(x, z, elevation, gradient: Vector2) -> float`.
|
||||
- `Recipe.modifiers: Array[Modifier]`.
|
||||
- `bake()` order: sum → modifiers (gradient by central differences on the current array) → floor → slope material → primitive material → patches.
|
||||
- Unit tests: a modifier that adds 1 m everywhere shifts the field; a primitive forcing ROCK on a flat plane wins over the slope rule; zone 1 recipe bakes identical to the checked-in `.res`.
|
||||
|
||||
### Task 2: `Canyon` and `Escarpment` (polyline primitives)
|
||||
- Shared static `distance_to_path(point, path) -> float`, `arc_position(point, path) -> Vector2(along_m, total_m)` and `signed_distance_to_path` (sign from the nearest segment's cross product, positive on the right of travel).
|
||||
- Edge noise: `FastNoiseLite` seeded, `TYPE_SIMPLEX_SMOOTH`, frequency 0.05, amplitude `edge_amplitude_m`.
|
||||
- Tests: Canyon depth on the flat floor, zero far away, follows a corner, closes over one half-width at the ends; Escarpment right side lower by `drop_m`, left side unchanged.
|
||||
|
||||
### Task 3: `Outcrop` and `BoulderField`
|
||||
- Outcrop: `e = (sqrt((dx/rx)² + (dz/rz)²) - 1) * mean_radius + noise`, `delta = height * (1 - smoothstep(-wall_width, 0, e))`, rock where `delta > 0.3 * height` and `rock_top`.
|
||||
- BoulderField: `RandomNumberGenerator` seeded; disc sampling `r = radius * sqrt(u)`, angle `TAU * v`; per boulder `h = r_b * height_ratio`, profile `h * sqrt(max(0, 1 - (d / r_b)²))`; combine by `maxf`; early reject outside `radius + max_r`; rock where profile > 0.
|
||||
- Tests: plateau height, rock on top, zero at two radii; boulder field deterministic, zero outside, rock on a boulder, two overlapping boulders do not sum.
|
||||
|
||||
### Task 4: `Strata` modifier
|
||||
- Formula from the brief §4.3. Tests: a 45° plane with 2 m layers and sharpness 0.9 produces at least two flat runs (|gradient| < 0.1) on a transect; a flat plane is unchanged; sharpness 0 is identity.
|
||||
|
||||
### Task 5: `terrain_showcase` recipe, bake, registry
|
||||
- Recipe per brief §5; `bake_site_terrain.gd` RECIPES gains `terrain_showcase`; bake; preview PNG under `docs/briefs/captures/`.
|
||||
- Verify zone 1 re-bake identical.
|
||||
|
||||
### Task 6: Contract and integration tests for the showcase
|
||||
- `tests/contract/run_site_content_tests.gd`: new `_test_showcase_content()` per brief §6.
|
||||
- `tests/integration/run_site_terrain_integration.gd`: showcase loads < 3 s, canyon ray lower than mesa ray, scan reads rock and sand.
|
||||
- Foundation test: `select_dive_site` on the showcase is not required.
|
||||
|
||||
### Task 7: Docs
|
||||
- ADR 0012 amendment, `content/README.md`, `tests/README.md`, technical plan status line, brief status.
|
||||
- Full gate green.
|
||||
Reference in new issue
Block a user