docs(resources): Étape 10 textures — bilan, README et ROADMAP à jour
- 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).
This commit is contained in:
@@ -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.
|
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.
|
6. **Error unification** — replace `Result<_, String>` in `Scene`/`PipelineCache` with typed errors.
|
||||||
7. ✅ **CPU geometry storage (Étape 8)** — `Mesh` retains a shared `Arc<Geometry>` (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.)
|
7. ✅ **CPU geometry storage (Étape 8)** — `Mesh` retains a shared `Arc<Geometry>` (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.)
|
||||||
|
|||||||
+67
-26
@@ -47,58 +47,99 @@ layout pour tous ».
|
|||||||
## Plan d'implémentation
|
## Plan d'implémentation
|
||||||
|
|
||||||
### 10.1 — Nouveau type `Texture` + dépendance `image`
|
### 10.1 — Nouveau type `Texture` + dépendance `image`
|
||||||
- [ ] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`).
|
- [x] Ajouter `image` à `lib/Cargo.toml` (features `png`, `jpeg`).
|
||||||
- [ ] `resources::texture::Texture { texture: wgpu::Texture, view: wgpu::TextureView, sampler: wgpu::Sampler }`.
|
- [x] `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] `Texture::from_bytes(device, queue, &[u8])` (ou `from_file`) : décode via `image`, remplit un
|
||||||
buffer RGBA et upload via `Queue::write_texture` (D3).
|
buffer RGBA et upload via `Queue::write_texture` (D3).
|
||||||
- [ ] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2.
|
- [x] `Texture::white_placeholder(device, queue)` : 1×1 blanc, pour D1/D2.
|
||||||
- [ ] Enregistrer `pub mod texture` dans `resources/mod.rs`.
|
- [x] Enregistrer `pub mod texture` dans `resources/mod.rs`.
|
||||||
|
|
||||||
### 10.2 — Bind group layout texture (groupe 2) partagé
|
### 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 }`
|
= `BindingType::Sampler(Filtering)` (binding 0) + `Texture { sample_type: Float, view_dimension: D2 }`
|
||||||
(binding 1), visibilité fragment.
|
(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).
|
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
|
### 10.3 — Shader standard : UV → fragment + échantillonnage
|
||||||
- [ ] `VertexOutput` : ajouter `@location(2) uv: vec2<f32>` ; `vs_main` écrit `out.uv = input.uv`.
|
- [x] `VertexOutput` : ajouter `@location(2) uv: vec2<f32>` ; `vs_main` écrit `out.uv = input.uv`.
|
||||||
- [ ] Déclarer `@group(2) @binding(0) var texture_sampler: sampler;` et
|
- [x] Déclarer `@group(2) @binding(0) var texture_sampler: sampler;` et
|
||||||
`@group(2) @binding(1) var diffuse_texture: texture_2d<f32>;`.
|
`@group(2) @binding(1) var diffuse_texture: texture_2d<f32>;`.
|
||||||
- [ ] `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.
|
`texel.rgb * (ambient + diffuse)`, en unlit → `texel` (D2). Mettre à jour la doc du module shader.
|
||||||
|
|
||||||
### 10.4 — `Material` porte la texture diffuse
|
### 10.4 — `Material` porte la texture diffuse
|
||||||
- [ ] `Material { texture: Option<Arc<Texture>>, texture_bind_group }` ; le constructeur construit le
|
- [x] `Material { texture: Option<Arc<Texture>>, texture_bind_group }` ; le constructeur construit le
|
||||||
bind group (groupe 2) depuis le layout partagé, avec placeholder si `None` (D1/D4).
|
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.
|
- [x] 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] Le sampler/placeholder partagé est fourni par la lib (une seule instanciation) pour que tout
|
||||||
Material sans texture lie le blanc.
|
Material sans texture lie le blanc.
|
||||||
|
|
||||||
### 10.5 — API Scene & binding dans `draw_entity`
|
### 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.
|
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`).
|
(ex. `add_material_texture` ou paramètre de `add_material_shader`).
|
||||||
- [ ] `draw_entity` : `pass.set_bind_group(2, material.texture_bind_group, &[])`.
|
- [x] `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] Vérifier qu'un material *sans* texture continue de fonctionner (placeholder → aucune régression).
|
||||||
|
|
||||||
### 10.6 — Exemples + validation
|
### 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).
|
ou un motif procédural RGBA généré en mémoire).
|
||||||
- [ ] `simple`/`manual` : pas de régression (placeholder).
|
- [x] `simple`/`manual` : pas de régression (placeholder).
|
||||||
- [ ] `cargo fmt --all`, `cargo check --workspace` (0 warning), `cargo test --workspace` (vert, tests de
|
- [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`).
|
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
|
## Point d'étape
|
||||||
|
|
||||||
- [x] Valider D1–D4 avant implémentation. *(actées le 2026-09-18)*
|
- [x] Valider D1–D4 avant implémentation. *(actées le 2026-09-18)*
|
||||||
- [ ] Caser 10.1–10.6, validation verte, exemples OK.
|
- [x] Caser 10.1–10.6, validation verte, exemples OK. *(terminé et vérifié le 2026-09-18)*
|
||||||
- [ ] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT.
|
- [x] Deux commits séparés : `refactor(...)` (10.1–10.5) puis `docs(...)` (10.6) + README/DRAFT. *(refactor : 440f2df ; docs : à finaliser)*
|
||||||
- [ ] Rédiger le bilan et ouvrir la suite (Phase 4.2 Éclairage avancé).
|
- [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<Arc<Texture>>` + `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<Texture>` blanc 1×1), exposés via les méthodes `texture_bind_group_layout()`,
|
||||||
|
`placeholder()`, et `texture_bind_group(Option<Arc<Texture>>)` 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._
|
||||||
|
|||||||
+4
-4
@@ -146,10 +146,10 @@ generated: { by: human:jerome, at: 2026-07-31T00:00:00Z }
|
|||||||
**Objectif** : Qualité visuelle et performances.
|
**Objectif** : Qualité visuelle et performances.
|
||||||
|
|
||||||
### 4.1 Textures
|
### 4.1 Textures
|
||||||
- [ ] Struct `Texture` avec chargement d'image
|
- [x] Struct `Texture` avec chargement d'image *(Étape 10 : resources::Texture, from_rgba8/bytes/file, Rgba8UnormSrgb, sampler linear/repeat)*
|
||||||
- [x] Ajouter `uvs: Option<Vec<[f32; 2]>>` dans `Geometry` *(déjà en place — prérequis Étape 10, présent dans le code ; seul l'échantillonnage manque)*
|
- [x] Ajouter `uvs: Option<Vec<[f32; 2]>>` dans `Geometry` *(prérequis Étape 10, déjà présent dans le code — seul l'échantillonnage manquait)*
|
||||||
- [ ] BindGroup pour les textures dans le shader
|
- [x] BindGroup pour les textures dans le shader *(Étape 10 : groupe @2 sampler+texture sur toutes les pipelines, placeholder blanc)*
|
||||||
- [ ] `Material` supporte une texture diffuse
|
- [x] `Material` supporte une texture diffuse *(Étape 10 : Material.texture + texture_bind_group, placeholder si None)*
|
||||||
|
|
||||||
### 4.2 Éclairage avancé
|
### 4.2 Éclairage avancé
|
||||||
- [ ] Support multi-lumières (directionnelles, ponctuelles)
|
- [ ] Support multi-lumières (directionnelles, ponctuelles)
|
||||||
|
|||||||
@@ -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. |
|
| **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<Geometry>` (É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. |
|
| **mesh** | Mesh struct — persistent GPU geometry container with retained CPU `geometry: Arc<Geometry>` (É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
|
## Interaction with Other Modules
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user