LOD: fige les jumeaux de fente (slit twins) + blend UV linéaire (le fold est supprimé)

- weld: Δ UV = 0.5 exact n'est plus soudé (ambigu: fente à sa plus large
  vs saut légitime — la colonne u=1 du cône vs le chart disque du cap
  tombait exactement dessus et mélangeait les charts)
- collapse: les UV se blendent linéairement. Le fold par coordonnée
  (Δ entier → 0) figeait l'UV du sommet sur les vertices de base
  (bande de rayures, signalée par l'utilisateur) — il était inutile:
  les jumeaux de fente sont gelés, aucun repli ne traverse la fente
- welded renvoie un struct Welded (clippy)
- test cône: dual-chart (fan latéral bilinéaire + disque du cap),
  surface latérale r = λ; test seam/span réécrit selon la sémantique finale
- doc: gpu-driven.md, ARCHI_CPU_GPU.md (box LOD), DRAFT.md (D10) alignés
  sur la sémantique finale (gel des jumeaux, blend linéaire, seuil strict)
- AGENTS.md: gotcha 'ne jamais folder un saut entier de tuile'

102 tests passent, demo lance et rend sans erreur.
This commit is contained in:
Jérôme Bousquié
2026-09-23 16:43:08 +02:00
parent 40fac63590
commit 004761252b
5 changed files with 328 additions and 114 deletions
+10 -5
View File
@@ -43,11 +43,16 @@ Ce document sert de spécification technique et de trame d'implémentation pour
> le GPU n'effectue que le mappage niveau → ligne de la table LOD du mesh. Les niveaux d'un mesh sont
> générés par **quadric edge collapse** (Garland–Heckbert) au setup (`Geometry::decimated` : les
> arêtes au coût quadrique minimal sont repliées en premier ; soudure **consciente des attributs**
> — un doublon ne fusionne que si UV proches (≤ ½ tuile) ET normales proches (dot > 0.9) — ; un mesh
> sans couture reste fermé, sur un mesh couturé les lèvres de couture sont protégées (pas de trous,
> pas de « books ») ; UVs/couleurs/normales interpolés au repli, normales **héritées** de la source
> (jamais recalculées — l'éclairage reste identique au niveau 0 quelle que soit l'orientation source),
> jamais à travers une seam UV ; rebase u16) et **empilés dans les buffers vertex/index du mesh** (offsets en
> — un doublon ne fusionne que si UV strictement < ½ tuile par coordonnée (un Δ = ½ exact est
> ambigu : fente à sa plus large vs saut légitime) ET normales proches (dot > 0.9) ; les paires
> refusées à UV écart d'entier sont **enregistrées** (jumeaux de fente) — ; un mesh sans couture
> reste fermé (pas de trous, pas de « books »), et sur un mesh couturé les jumeaux de fente sont
> **gelés** (toute arête qui y touche est exclue de la file — la fente zéro-largeur reste fermée à
> tous les niveaux) ; UVs/couleurs/normales **blendés linéairement** au repli (le chart est
> bilinéaire → exact au nouveau point — jamais de fold de tuile, qui figeait l'UV d'un sommet sur
> les vertices de base), normales **héritées** de la source (jamais recalculées — l'éclairage reste
> identique au niveau 0 quelle que soit l'orientation source) ; rebase u16) et **empilés dans les
> buffers vertex/index du mesh** (offsets en
> unités d'élément, pas d'octet — c'est ce qu'exigent les arguments `drawIndirect*` de WebGPU ; plafond u16 :
> 65 535 sommets/mesh, 4 niveaux max). LOD activé par défaut ; `set_lod_enabled(false)` restaure un rendu
> bit-à-bit identique au pré-LOD (niveau 0 partout = comptes complets). Détail : `docs/user/gpu-driven.md`