# DRAFT — Étape 10 : Textures (Phase 4.1) > 📅 **Rédigé le 2026-09-18.** Plan à valider avant implémentation. > Source de vérité = code + README.md. Ce document est vidé à la complétion de l'étape. ## Contexte (état de départ) La Phase 4.1 (Textures) du ROADMAP vise à texturer le rendu. État actuel du code : - **La tuyauterie UV existe déjà** : `Geometry.uvs: Option>` (builder `with_uvs`) et `Vertex.uv` (location 2, offset 24, stride 56) sont déclarés dans `build_pipeline` et lus par le shader `VertexInput`. Seul l'**échantillonnage manque**. - Le shader `standard_shader.wgsl` reçoit l'UV mais ne le transmet pas au fragment et n'échantillonne rien. - **Aucun type `Texture`**, aucun sampler, aucun bind group de texture. `Material` ne porte pas de texture. - Architecture bind group : « un seul layout pour tous » (Étape 3) — les groupes `frame @0` + `object @1` sont posés sur **toutes** les pipelines et bindés à chaque draw (`draw_entity`). **Conclusion** : l'UV est prête ; les pièces manquantes sont un type `Texture`, un bind group de texture (groupe 2) partagé, un échantillonnage dans le shader et le portage sur `Material`. ## Objectif Permettre de texturer un mesh : charger une image → `wgpu::Texture` (+ view + sampler), lier une texture diffuse à un `Material`, échantillonner dans le shader, **sans casser** le pattern « un seul layout pour tous ». ## Décisions (actées le 2026-09-18) - **[x] D1 (intégration bind group — « un seul layout pour tous »)** : on ajoute un **3ᵉ bind group** `@group(2)` (sampler + texture diffuse) posé sur **toutes** les pipelines, avec une **texture blanche 1×1 de secours (placeholder)** utilisée quand un `Material` n'a pas de texture. Cela préserve l'unicité du layout (aucune pipeline multiple), donc un seul flux de rendu. *(alternative écartée : bind group optionnel → casse l'unicité du layout, refactor de toutes les pipelines)*. - **[x] D2 (échantillonnage inconditionnel)** : le fragment shader échantillonne **toujours** la texture diffuse ; le placeholder blanc (texel = 1) reproduit exactement le comportement actuel d'un material sans texture. Donc pas de flag conditionnel → un seul flow de shader. En mode lit, le **texel remplace la couleur du vertex** (`texel.rgb * (ambient + diffuse)`) ; en unlit, le texel tel quel. Symétrique du pattern unlit existant. - **[x] D3 (chargement d'image & format)** : ajouter la dépendance `image` (décodage PNG/JPEG) à `lib/Cargo.toml` ; upload en `TextureFormat::Rgba8UnormSrgb`, usage `TEXTURE_BINDING | COPY_DST`, dimension `D2`, `mip_level_count: 1` (**YAGNI** : pas de génération de mipmaps cette étape), sampler `filter: Linear`, `address_mode: Repeat`. - **[x] D4 (API et portage)** : nouveau type `Texture` (device + view + sampler). `Material` gagne `texture: Option>` et détient son **bind group de texture (groupe 2)**, construit depuis le layout partagé ; sans texture il lie le placeholder. API `Scene` : `add_texture(id, tex)` et liaison d'une texture à un material. `draw_entity` bind `set_bind_group(2, ...)`. ## 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 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`. ### 10.2 — Bind group layout texture (groupe 2) partagé - [ ] `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 conservée — D1). - [ ] 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 `@group(2) @binding(1) var diffuse_texture: texture_2d;`. - [ ] `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 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 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 leurs bind groups. - [ ] 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). ### 10.6 — Exemples + validation - [ ] `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 layout uniform + validation WGSL à jour), `cargo doc` (pas de `missing_docs`). - [ ] 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é). --- _Fin du DRAFT Étape 10 — à valider avant implémentation._