diff --git a/docs/DRAFT.md b/docs/DRAFT.md index 894dfe5..5f66f44 100644 --- a/docs/DRAFT.md +++ b/docs/DRAFT.md @@ -4,8 +4,134 @@ > **Son contenu est effacé au début de chaque nouvelle étape.** La source de vérité de l'état est > le code + README.md ; les autres docs `docs/*` restent stables. -> **État.** Prêt pour l'étape suivante. L'Étape 7 (consolidation : `PipelineCache` → `Scene`, -> `Mesh` → `Material`, matériau par défaut) est terminée et vérifiée le 2026-09-17 ; le détail des -> cases cochées est conservé dans l'historique git de ce fichier et les statuts dans `docs/PLAN.md` -> + `docs/ROADMAP.md`. Candidat suivant suggéré : ROADMAP 1.2 (refactor `Mesh` → `Arc`), -> reporté à ce stade. +> **État.** Étape 8 en préparation — refactor du stockage CPU des données géométriques : +> `Mesh` contient `geometry: Arc`. Étape 7 terminée et vérifiée le 2026-09-17. + +--- + +# Étape 8 — Stockage CPU : `Mesh` contient `Arc` + +## Objectif + +Donner à `Mesh` une source de vérité **CPU partagée** pour sa géométrie, en plus de ses buffers GPU. +Le ROADMAP 1.2 demande `Mesh { geometry: Arc, vertex_buffer, index_buffer, ... }` : +on met en œuvre la partie *stockage CPU* (`geometry: Arc`), en **déviant sur le champ +`transform`** (voir Décisions D3). + +Valeurs apportées : +- **Réutilisation mémoire** : plusieurs meshes peuvent partager le même `Arc` (ex. deux + meshes cube différents partagent une géométrie cube). +- **Base pour les phases suivantes** : bounding box (culling, Phase 3), compute transforms, accès UV + pour les textures (Phase 4), accès normales pour des calculs CPU. +- **Nettoyage du modèle de données** : `Geometry` existe aujourd'hui (`lib/src/math/geometry.rs`) + mais n'est **référencé nulle part** dans le code. Il devient le type canonique côté CPU. + +## État de départ vérifié (2026-09-17) + +- `Geometry` (lib/src/math/geometry.rs) : `{ positions: Vec<[f32;3]>, normals, uvs, + indices }` — **défini et ré-exporté mais jamais utilisé** (aucune construction, aucun champ lu). +- `Vertex` (lib/src/resources/vertex.rs) : tuple CPU `position/normal/uv/color` → **contrat GPU** + (stride 56 o, `VertexBufferLayout` construit sur ses offsets, standard shader lit `color` unlit). +- `Mesh` (lib/src/resources/mesh.rs) : `{ vertex_buffer, index_buffer, num_vertices, + num_indices, material: Option> }`, construit via `Mesh::new(device, vertices, indices)` + / `Mesh::with_material`. Il n'**utilise pas** `Geometry` : il prend des `&[Vertex]` et les uploade. +- `Scene::create_mesh(id, vertices: &[Vertex], indices, material)` (lib/src/scene/scene.rs) uploade + via `Mesh::new` puis lie le matériau. Les buffers GPU restent la seule donnée : aucune rétention CPU. +- `Entity` = `{ mesh_id, transform }` ; `iter_entities` renvoie `(&str, &Arc, &Transform)` + (lib/src/scene/scene.rs). Un mesh est **partagé** par plusieurs entités à des transforms différents. +- Le ROADMAP 1.2 liste `transform: Transform` sur `Mesh` → **déviation justifiée en D3**. + +## Décisions / compromis + +| # | Question | Options | Décision retenue | Justification | +|---|----------|---------|------------------|---------------| +| D1 | Format CPU stocké | (a) garder `Geometry` en tableaux éclatés `positions/normals/uvs/indices` ; (b) y ajouter `colors` ; (c) ranger un `Vec` | **(a)+(b) : `Geometry` en tableaux + `colors`, et conversion `Geometry -> Vec`** — **validé 2026-09-18** | Respecte le contrat GPU (`Vertex`) sans duplication conceptuelle : `Geometry` = données CPU pures, `Vertex` = format d'upload interleaved. Le shader lit `color` ⇒ il faut porter la couleur dans `Geometry`. | +| D2 | Où `Geometry` vit / qui le ré-exporte | (a) reste en `math` ; (b) déplacé en `resources` | **(a) reste en `math`**, ré-exporté de `lib.rs` | Cohérent : c'est une structure de *données* pure, comme `Transform`/`Camera`. Exposer via `math::Geometry` (déjà le cas) + une ré-export `resources` optionnelle à la convenance. | +| D3 | Champ `transform` sur `Mesh` | (a) l'ajouter (ROADMAP littéral) ; (b) le laisser sur `Entity` | **(b) : `transform` reste sur `Entity`** — **validé 2026-09-18** | Un `Mesh` est **partagé** par plusieurs entités à des transforms différents (modèle instancé). Mettre un `transform` unique sur `Mesh` casserait ce modèle (Étape 4/7). **Déviation documentée** au ROADMAP 1.2. | +| D4 | API de création | (a) `Mesh::new(device, vertices, indices)` actuel ; (b) `Mesh::new(device, geometry)` ; garder ou non surcharge | **(b) : `Mesh::from_geometry(device, Arc)` ; suppression de l'ancienne voie `&[Vertex]` dans `Mesh` et `Scene`** | Une seule source canonique. Les examples (non contraignants) sont réécrits pour construire une `Geometry`. `Vertex` reste utilisé en interne pour l'upload. | +| D5 | Rétention CPU + GPU | (a) ne garder que GPU ; (b) garder CPU `Arc` **et** les buffers GPU pré-uploadés | **(b) : `Mesh` garde `geometry: Arc` ET `vertex_buffer`/`index_buffer`** | Pas de re-upload par frame (perf) ; le `Arc` sert les phases futures (bbox, compute, textures). Double stockage assumé. | +| D6 | Convertisseur `Geometry -> Vertex` | (a) méthode `Geometry::to_vertices()` ; (b) impl `From<&Geometry>` | **(a) `Geometry::to_vertices()`** (ou `into_vertices`) | Explicite, avec règles de remplissage documentées (normales/UV/couleur par défaut si absents). | + +## Étape 8.1 — Étendre `Geometry` (couleur + validation) — **proposition validée 2026-09-18** + +- [ ] Dans `lib/src/math/geometry.rs` : + - [ ] ajouter `colors: Option>` en champ optionnel (parallèle à `normals`/`uvs`). + - [ ] documenter les invariants : `positions` obligatoire ; `normals`, `uvs`, `colors`, + `indices` optionnels mais doivent avoir la même longueur que `positions` quand présents. + - [ ] ajouter un constructeur ergonomique, ex. `Geometry::new(positions, indices) -> + Geometry` (positions/valeurs par défaut) et un builder fluent + `.with_normals(..)/.with_uvs(..)/.with_colors(..)` retournant `Self`. + - [ ] ajouter une **validation** `Geometry::validate() -> Result<(), GeometryError>` (ou un + `Self::from_...` vérifiant les longueurs) — erreur si les tableaux optionnels ont une + longueur différente de `positions`. + +## Étape 8.2 — Convertisseur `Geometry` → `Vec` + +- [ ] Dans `lib/src/math/geometry.rs` (ou un petit trait dédié), implémenter : + - [ ] `Geometry::to_vertices() -> Vec` qui zip `positions`/`normals`/`uvs`/`colors` + avec des valeurs par défaut : normale `[0,0,1]`, uv `[0,0]`, couleur blanche `[1,1,1,1]`. + - [ ] documenter clairement ces défauts (i.e. une géométrie sans normales via Phong sera plate). + - [ ] (optionnel) `Geometry::indices()` accesseur sûr (clone ou slice) pour l'upload. + +## Étape 8.3 — `Mesh` contient `Arc` et construit ses buffers + +- [ ] Dans `lib/src/resources/mesh.rs` : + - [ ] ajouter le champ `geometry: Arc`. + - [ ] remplacer/ajouter `Mesh::from_geometry(device: &wgpu::Device, geometry: Arc, + material: Option>) -> Mesh` : + - [ ] `let vertices = geometry.to_vertices(); let indices = geometry.indices;` + - [ ] upload `vertex_buffer` (stride = `size_of::()`, `create_buffer_init`), + - [ ] upload `index_buffer` si `indices` présent (index u16). + - [ ] conserver `num_vertices`/`num_indices` (dérivés de la géométrie) pour `render`. + - [ ] accesseurs publics : `geometry() -> &Arc`, `material()`, `set_material()`. + - [ ] **supprimer** les anciennes voies `Mesh::new(device, vertices: &[Vertex], ...)` / + `Mesh::with_material(...)` (remplacées). Mettre à jour la doc `resources/mod.rs` (ligne + « `mesh::new()` uploads Vertex arrays... ») pour refléter `Geometry`. + +## Étape 8.4 — Adapter `Scene` / l'API déclarative + +- [ ] Dans `lib/src/scene/scene.rs` : + - [ ] changer `create_mesh(id, vertices: &[Vertex], indices, material)` → + `create_mesh(id, geometry: Geometry, material: Option<&str>) -> Result<...>` : + - [ ] il construit `Arc`, appelle `Mesh::from_geometry(self.device(), arc, mat)`. + - [ ] mettre à jour la doc de `Scene` / `resources` sur le rôle de `Geometry`. + - [ ] ré-export : exposer `Geometry` à la racine (déjà via `math::Geometry`) et, à la convenance, + depuis `wsg_lib::resources` pour les exemples. + +## Étape 8.5 — Réécrire les exemples (non contraignants) sur `Geometry` + +- [ ] `lib/examples/cube.rs` : + - [ ] remplacer `cube_vertices() -> Vec` / `cube_indices()` par un builder de + `Geometry` (ou `Geometry::new(...).with_normals(...).with_indices(...)`) ; couleur blanche + par défaut → vérifier le rendu Phong inchangé. + - [ ] appeler `scene.create_mesh("cube_mesh", geometry, Some("cube_material"))`. +- [ ] `lib/examples/simple.rs` : + - [ ] construire une `Geometry` (positions ± couleurs par sommet pour le quad unlit) ; + - [ ] `scene.create_mesh("quad", geometry, None)` (matériau par défaut). +- [ ] `lib/examples/manual.rs` (exemple bas-niveau, utilise `Mesh::new(renderer.device(), + &vertices, ...)`) : + - [ ] réécrire sur `Mesh::from_geometry(device, Arc, None)`. +- [ ] mettre à jour les en-têtes / commentaires des exemples (références à `Vertex` en public). + +## Étape 8.6 — Validation + +- [ ] `cargo fmt --all` (aucun diff résiduel). +- [ ] `cargo check --workspace` puis `cargo build --workspace` **sans warning** (veiller à la + régularité des tableaux dans les exemples). +- [ ] `cargo test --workspace` (zéro test à ce stade, mais compilation clean). +- [ ] `cargo doc --no-deps` **sans warning `missing_docs`** (la crate est en `#![warn(missing_docs)]`). +- [ ] Lancer les 3 exemples (simple, cube, manual) et constater l'absence de panic / rendu visé. +- [ ] Mettre à jour `README.md` (extraits de code `Mesh::new` → `Geometry`, section architecture) + et les statuts `docs/PLAN.md` + `docs/ROADMAP.md` (cocher le refactor 1.2 « stockage CPU » ; + laisser `transform` documenté comme déviation en D3). + +## Point d'étape + +- [ ] Caser le refactor : `Mesh.geometry: Arc` + buffers dérivés, API `create_mesh` + basée `Geometry`, exemples réécrits, validation verte, docs à jour. +- [ ] Deux commits séparés comme d'habitude : un `refactor(...)` (8.1–8.5) puis un `docs(...)` + (8.6). Rédiger un court bilan et ouvrir la question du prochain chantier. + +--- + +_Fin du DRAFT Étape 8 — à valider avant implémentation._ diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 7e6c8d1..5eb3146 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -53,17 +53,24 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z } - [x] `positions: Vec<[f32; 3]>` (obligatoire) - [x] `indices: Option>` (optionnel) - [x] `normals: Option>` (pour Phong) — plus `uvs: Option>` + - [ ] `colors: Option>` — **décidé en DRAFT Étape 8 (D1)** : le shader lit la couleur + unlit, il faut la porter dans `Geometry` ; conversion `Geometry -> Vec` pour l'upload. - [ ] Refactorer `Mesh` pour contenir : - [ ] `geometry: Arc` - [ ] `vertex_buffer: wgpu::Buffer` - [ ] `index_buffer: Option` - - [ ] `transform: Transform` (état CPU) - [x] Ajouter un mesh de test (cube unitaire) en exemple — **fait** (helper `cube_geometry` dans l'exemple `cube`, Étape 5, 2026-09-17) > **Note (2026-09-17, DRAFT Étape 7)** : le refactor « Mesh contient `Arc` » ci-dessus reste > **reporté** (il porte sur le *stockage CPU* des données géométriques). En revanche le volet *matériau* > de `Mesh` a été fait en Étape 7 : `Mesh.material: Option>` (cf. PLAN Phase 2, gestion des > matériaux), indépendant de la structure `Geometry`. +> +> **Décision **D3** (2026-09-18, DRAFT Étape 8)** : le ROADMAP listait `transform: Transform` sur `Mesh`. +> **Déviation validée : `transform` reste sur `Entity` et n'est PAS ajouté à `Mesh`.** Un mesh est +> **partagé** par plusieurs entités à des transforms différents (modèle instancé, Étape 4/7) : un +> `transform` unique sur `Mesh` casserait ce modèle. L'Étape 8 met donc en œuvre le refactor de +> *stockage CPU* (`Mesh.geometry: Arc` + buffers dérivés) **sans** le champ `transform`. ### 1.3 Shader Phong Minimal - [x] Créer `standard_shader.wgsl` (Étape 2, 2026-09-16) :