From 23568e882017baaace06e8d9e20b2718363fd58f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Bousqui=C3=A9?= Date: Fri, 18 Sep 2026 14:26:30 +0200 Subject: [PATCH] =?UTF-8?q?docs(resources):=20=C3=89tape=2010=20textures?= =?UTF-8?q?=20=E2=80=94=20bilan,=20README=20et=20ROADMAP=20=C3=A0=20jour?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ROADMAP : Phase 4.1 (Textures) cochée — struct Texture, uvs, bind group shader, Material avec texture diffuse. - README : jalon 8 (diffuse textures) ajouté à la Roadmap ; ligne resource 'texture' + material enrichi dans le tableau des resources du module. - DRAFT Étape 10 : cases 10.1-10.6 cochées (terminé et vérifié le 2026-09-18), point d'étape clôturé, bilan de fin d'étape rédigé (y compris la divergence D2 : multiplication texel * couleur du vertex au lieu de remplacement, et la structure réelle du PipelineCache). --- README.md | 1 + docs/DRAFT.md | 93 ++++++++++++++++++++++++++----------- docs/ROADMAP.md | 8 ++-- lib/src/resources/README.md | 3 +- 4 files changed, 74 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index f5f18ae..af6c51e 100644 --- a/README.md +++ b/README.md @@ -184,3 +184,4 @@ The architecture docs live in `docs/tech/` and are written in **French**. Each d 5. **Typed resource handles** — keep String IDs for the MVP (current design, source of truth in `Scene`); slotmap-based generational handles (`ARCHI_ARENES.md`) are deferred to a later performance pass. 6. **Error unification** — replace `Result<_, String>` in `Scene`/`PipelineCache` with typed errors. 7. ✅ **CPU geometry storage (Étape 8)** — `Mesh` retains a shared `Arc` (CPU source of truth with colors) alongside its GPU buffers; meshes are declared from a `Geometry` via `Mesh::from_geometry`/`Scene::create_mesh(id, geometry, material)` instead of raw `&[Vertex]` arrays. (Done 2026-09-18; `transform` stays on `Entity` — deviation D3.) +8. ✅ **Diffuse textures (Étape 10, Phase 4.1)** — `resources::Texture` (GPU image: device+view+sampler, `Rgba8UnormSrgb`, loaders `from_rgba8`/`from_bytes`/`from_file`) attached to a `Material` as diffuse texture. The `standard` shader samples it via bind group **@2** (shared layout: sampler+texture); UVs are forwarded as vertex attribute location 2. Without a texture the material uses a shared 1×1 white placeholder so lit and unlit rendering are unchanged (no regression). The `cube` example now uses a procedural checkerboard texture. (Done 2026-09-18.) diff --git a/docs/DRAFT.md b/docs/DRAFT.md index b82782b..57a0086 100644 --- a/docs/DRAFT.md +++ b/docs/DRAFT.md @@ -47,58 +47,99 @@ layout pour tous ». ## Plan d'implémentation ### 10.1 — Nouveau type `Texture` + dépendance `image` -- [ ] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`). -- [ ] `resources::texture::Texture { texture: wgpu::Texture, view: wgpu::TextureView, sampler: wgpu::Sampler }`. -- [ ] `Texture::from_bytes(device, queue, &[u8])` (ou `from_file`) : décode via `image`, remplit un +- [x] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`). +- [x] `resources::texture::Texture { texture: wgpu::Texture, view: wgpu::TextureView, sampler: wgpu::Sampler }`. +- [x] `Texture::from_bytes(device, queue, &[u8])` (ou `from_file`) : décode via `image`, remplit un buffer RGBA et upload via `Queue::write_texture` (D3). -- [ ] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2. -- [ ] Enregistrer `pub mod texture` dans `resources/mod.rs`. +- [x] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2. +- [x] Enregistrer `pub mod texture` dans `resources/mod.rs`. ### 10.2 — Bind group layout texture (groupe 2) partagé -- [ ] `create_uniform_bind_group_layouts` retourne `[frame, object, texture]` (3 layouts) ; `texture` +- [x] `create_uniform_bind_group_layouts` retourne `[frame, object, texture]` (3 layouts) ; `texture` = `BindingType::Sampler(Filtering)` (binding 0) + `Texture { sample_type: Float, view_dimension: D2 }` (binding 1), visibilité fragment. -- [ ] `build_pipeline` : ajouter le 3ᵉ layout au `PipelineLayoutDescriptor` (l'unicité du layout est +- [x] `build_pipeline` : ajouter le 3ᵉ layout au `PipelineLayoutDescriptor` (l'unicité du layout est conservée — D1). -- [ ] Mettre à jour les destructures `let [frame_layout, object_layout]` (renderer.rs) pour 3 éléments. +- [x] Mettre à jour les destructures `let [frame_layout, object_layout]` (renderer.rs) pour 3 éléments. ### 10.3 — Shader standard : UV → fragment + échantillonnage -- [ ] `VertexOutput` : ajouter `@location(2) uv: vec2` ; `vs_main` écrit `out.uv = input.uv`. -- [ ] Déclarer `@group(2) @binding(0) var texture_sampler: sampler;` et +- [x] `VertexOutput` : ajouter `@location(2) uv: vec2` ; `vs_main` écrit `out.uv = input.uv`. +- [x] Déclarer `@group(2) @binding(0) var texture_sampler: sampler;` et `@group(2) @binding(1) var diffuse_texture: texture_2d;`. -- [ ] `fs_main` : `let texel = textureSample(diffuse_texture, texture_sampler, in.uv);` — en lit → +- [x] `fs_main` : `let texel = textureSample(diffuse_texture, texture_sampler, in.uv);` — en lit → `texel.rgb * (ambient + diffuse)`, en unlit → `texel` (D2). Mettre à jour la doc du module shader. ### 10.4 — `Material` porte la texture diffuse -- [ ] `Material { texture: Option>, texture_bind_group }` ; le constructeur construit le +- [x] `Material { texture: Option>, texture_bind_group }` ; le constructeur construit le bind group (groupe 2) depuis le layout partagé, avec placeholder si `None` (D1/D4). -- [ ] Méthode `set_texture(...)` qui recrée le bind group si la texture change. -- [ ] Le sampler/placeholder partagé est fourni par la lib (une seule instanciation) pour que tout +- [x] Méthode `set_texture(...)` qui recrée le bind group si la texture change. +- [x] Le sampler/placeholder partagé est fourni par la lib (une seule instanciation) pour que tout Material sans texture lie le blanc. ### 10.5 — API Scene & binding dans `draw_entity` -- [ ] Rendre accessible un layout du groupe 2 aux Materials (via `SceneGpu` / cache) pour construire +- [x] Rendre accessible un layout du groupe 2 aux Materials (via `SceneGpu` / cache) pour construire leurs bind groups. -- [ ] Route déclarative `Scene` : `add_texture(id, tex)` et liaison d'une texture par id à un material +- [x] Route déclarative `Scene` : `add_texture(id, tex)` et liaison d'une texture par id à un material (ex. `add_material_texture` ou paramètre de `add_material_shader`). -- [ ] `draw_entity` : `pass.set_bind_group(2, material.texture_bind_group, &[])`. -- [ ] Vérifier qu'un material *sans* texture continue de fonctionner (placeholder → aucune régression). +- [x] `draw_entity` : `pass.set_bind_group(2, material.texture_bind_group, &[])`. +- [x] Vérifier qu'un material *sans* texture continue de fonctionner (placeholder → aucune régression). ### 10.6 — Exemples + validation -- [ ] `cube` : texturer le cube (image PNG embarquée via `include_bytes!` pour rester autonome, +- [x] `cube` : texturer le cube (image PNG embarquée via `include_bytes!` pour rester autonome, ou un motif procédural RGBA généré en mémoire). -- [ ] `simple`/`manual` : pas de régression (placeholder). -- [ ] `cargo fmt --all`, `cargo check --workspace` (0 warning), `cargo test --workspace` (vert, tests de +- [x] `simple`/`manual` : pas de régression (placeholder). +- [x] `cargo fmt --all`, `cargo check --workspace` (0 warning), `cargo test --workspace` (vert, tests de layout uniform + validation WGSL à jour), `cargo doc` (pas de `missing_docs`). -- [ ] Exécuter `cube` (faces texturées) et `simple` sans erreur backend. +- [x] Exécuter `cube` (faces texturées) et `simple` sans erreur backend. ## Point d'étape - [x] Valider D1–D4 avant implémentation. *(actées le 2026-09-18)* -- [ ] Caser 10.1–10.6, validation verte, exemples OK. -- [ ] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT. -- [ ] Rédiger le bilan et ouvrir la suite (Phase 4.2 Éclairage avancé). +- [x] Caser 10.1–10.6, validation verte, exemples OK. *(terminé et vérifié le 2026-09-18)* +- [x] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT. *(refactor : 440f2df ; docs : à finaliser)* +- [x] Rédiger le bilan et ouvrir la suite (Phase 4.2 Éclairage avancé). *(2026-09-18)* --- -_Fin du DRAFT Étape 10 — à valider avant implémentation._ +## Bilan — Étape 10 : Textures (Phase 4.1) + +**Livrés (commits `refactor` 440f2df puis `docs`) :** +- **10.1** `resources::Texture` — `{ texture, view, sampler }`, format `Rgba8UnormSrgb`, usage + `TEXTURE_BINDING | COPY_DST`, mipmap 1 (YAGNI), sampler `Linear`/`Repeat`. Constructeurs + `from_rgba8`, `from_bytes` (via `image`, features png+jpeg), `from_file`, `white_placeholder` (1×1). + Dépendance `image = "0.25"` ajoutée à `lib/Cargo.toml`. +- **10.2** Layout partagé **groupe 2** (sampler binding 0 + texture binding 1, visibilité fragment). + `build_pipeline` pose les 3 layouts `[frame, object, texture]` sur toutes les pipelines → l'unicité + du layout est conservée (D1). `PipelineCache` détient le layout + le placeholder blanc partagé. +- **10.3** Shader standard : `VertexOutput.uv` (location 2) transmis au fragment ; groupe `@2` + `texture_sampler` (binding 0) + `diffuse_texture` (binding 1). Échantillonnage inconditionnel + (D2) : en lit `base = texel * couleur(vertex)`, en unlit `base = texel`. +- **10.4** `Material.texture: Option>` + `texture_bind_group` construit au constructeur + via le cache (layout partagé + placeholder si aucune texture). +- **10.5** `draw_entity` bind `@group(2)` ; `Scene` : `add_texture` / `get_texture` / + `add_material_texture` ; `init_gpu` prend désormais la `Queue` pour bâtir le placeholder. +- **10.6** Exemple `cube` : nouvelles faces géométriques propres + UV `[0,1]²` par face + texture + damier procédurale (générée en mémoire, pas d'asset externe). + +**Divergence légère vs DRAFT (D2 affiné)** : le plan disait « le texel **remplace** la couleur du +vertex ». À l'implémentation on multiplie (`base = texel.rgb * in.color`) : avec un placeholder blanc +c'est strictement identique pour les materials sans texture, et l'API permet de teinter une texture. +Aucun impact fonctionnel — documenté pour traçabilité. + +**Validation verte** : `cargo fmt --all` propre · `cargo check --workspace` 0 warning · `cargo test +--workspace` 3 tests verts · `cargo doc` sans `missing_docs` (docs manquantes sur le nouveau type +ajoutées). Exécutions réelles de `cube`, `simple`, `manual` sans erreur backend (le fallback +« Shader not found » affiché est le comportement préexistant — répertoire courant ≠ `lib/`). + +**Structure finale en mémoire** (`PipelineCache`) : champ `texture_bind_group_layout` + `placeholder` +(`Arc` blanc 1×1), exposés via les méthodes `texture_bind_group_layout()`, +`placeholder()`, et `texture_bind_group(Option>)` pour créer un groupe 2 depuis une +texture (ou le placeholder). + +**Suite (Phase 4.2 — Éclairage avancé du ROADMAP)** : multi-lumières (directionnelles/ponctuelles), +atténuation, spéculaire Phong raffiné, éventuellement cubemap/Ibl. Ne pas oublier un resize des +textures si on dessert des rendus hors-écran (renvoyé en Phase 4.4 avec le depth). + +--- + +_Fin du DRAFT Étape 10 — étape terminée le 2026-09-18._ diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index a84cb2b..8d7ad29 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -146,10 +146,10 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z } **Objectif** : Qualité visuelle et performances. ### 4.1 Textures -- [ ] Struct `Texture` avec chargement d'image -- [x] Ajouter `uvs: Option>` dans `Geometry` *(déjà en place — prérequis Étape 10, présent dans le code ; seul l'échantillonnage manque)* -- [ ] BindGroup pour les textures dans le shader -- [ ] `Material` supporte une texture diffuse +- [x] Struct `Texture` avec chargement d'image *(Étape 10 : resources::Texture, from_rgba8/bytes/file, Rgba8UnormSrgb, sampler linear/repeat)* +- [x] Ajouter `uvs: Option>` dans `Geometry` *(prérequis Étape 10, déjà présent dans le code — seul l'échantillonnage manquait)* +- [x] BindGroup pour les textures dans le shader *(Étape 10 : groupe @2 sampler+texture sur toutes les pipelines, placeholder blanc)* +- [x] `Material` supporte une texture diffuse *(Étape 10 : Material.texture + texture_bind_group, placeholder si None)* ### 4.2 Éclairage avancé - [ ] Support multi-lumières (directionnelles, ponctuelles) diff --git a/lib/src/resources/README.md b/lib/src/resources/README.md index e1f531d..c4bb415 100644 --- a/lib/src/resources/README.md +++ b/lib/src/resources/README.md @@ -8,7 +8,8 @@ The `resources` module defines three immutable data types that flow through the |------|---------------| | **vertex** | Vertex struct — CPU-side per-attribute tuple (position [f32;3], normal [f32;3], uv [f32;2], color [f32;4]). Must match PipelineCache::build_pipeline() vertex buffer layout byte-for-byte. | | **mesh** | Mesh struct — persistent GPU geometry container with retained CPU `geometry: Arc` (Étape 8), vertex_buffer (wgpu::Buffer), optional index_buffer, and draw call counters. Created via Mesh::from_geometry() which derives Vertex arrays from the Geometry and uploads them to GPU buffers. | -| **material** | Material struct — lightweight appearance descriptor pairing shader_id with a shared RenderPipeline Arc. Multiple Materials referencing the same shader_id point to the identical compiled GPU pipeline. | +| **material** | Material struct — lightweight appearance descriptor pairing shader_id with a shared RenderPipeline Arc. Multiple Materials referencing the same shader_id point to the identical compiled GPU pipeline. Optionally holds a diffuse `Texture` (Étape 10) plus its texture bind group. | +| **texture** | Texture struct *(Étape 10)* — GPU 2D image (device, view, sampler) in `Rgba8UnormSrgb`. Constructors: `from_rgba8` (raw bytes), `from_bytes` (encoded, via the `image` crate: png/jpeg/...), `from_file`, and `white_placeholder` (1x1 white used when no texture is attached). Sampler is linear-filtered with repeat addressing. | ## Interaction with Other Modules