refactor(resources): activate diffuse textures on all render paths (Étape 10)

Implémente le plan Étape 10 (Phase 4.1 Textures), décisions D1-D4 actées :
- 10.1 : nouveau type resources::Texture (device+view+sampler), format
  Rgba8UnormSrgb, sampler linear/repeat, dep 'image' (png/jpeg). Constructeurs
  from_rgba8 / from_bytes / from_file / white_placeholder.
- 10.2 : create_texture_bind_group_layout (groupe 2 : sampler+texture, fragment).
  build_pipeline pose désormais 3 layouts [frame, object, texture] — « un seul
  layout pour tous » (D1). PipelineCache détient le layout + le placeholder blanc.
- 10.3 : shader standard — UV transmis au fragment (location 2), groupe @2
  texture_sampler + diffuse_texture, échantillonnage inconditionnel
  base = texel * couleur(vertex) (D2) : sans texture (placeholder blanc) pas
  de régression en lit comme en unlit.
- 10.4 : Material gagne texture: Option<Arc<Texture>> + texture_bind_group,
  construit dans le constructeur via le cache (layout partagé + placeholder).
- 10.5 : draw_entity bind @group(2) ; Scene : add_texture / get_texture /
  add_material_texture ; init_gpu accepte la Queue pour bâtir le placeholder.
- exemple cube : géométrie avec UV [0,1]² par face + texture damier procédurale.

Validation : fmt, check 0 warning, tests verts (3 + doc), doc sans missing_docs,
cube/simple/manual lancés sans erreur backend.
This commit is contained in:
Jérôme Bousquié
2026-09-18 14:18:02 +02:00
parent 3de84aa4dc
commit 440f2dffa3
13 changed files with 588 additions and 55 deletions
+11 -6
View File
@@ -389,13 +389,15 @@ fn create_depth_texture(
(depth_texture, depth_view)
}
/// Binds a Material pipeline, the two uniform bind groups, and Mesh buffers into an active render
/// Binds a Material pipeline, the three shared bind groups, and Mesh buffers into an active render
/// pass and issues the draw call. Shared by `Renderer::render` and `Renderer::render_scene`.
/// The frame (group 0) and object (group 1) bind groups are **required** by every pipeline layout
/// (Étape 3 : un seul layout pour tous) — they must be bound even if the shader does not read them.
/// Draws indexed geometry when an index buffer exists, otherwise falls back to a non-indexed draw.
/// Inputs: pass (active render pass), mesh (geometry to draw), material (pipeline to bind),
/// frame_bind_group (shared per-frame uniforms), object_bind_group (per-entity/identity model).
/// The frame (@0), object (@1) and texture (@2) bind groups are **required** by every pipeline layout
/// (Étape 3 : un seul layout pour tous — Étape 10 : groupe texture) — they must be bound even if the
/// shader does not read them. Draws indexed geometry when an index buffer exists, otherwise falls
/// back to a non-indexed draw.
/// Inputs: pass (active render pass), mesh (geometry to draw), material (pipeline + texture bind
/// group to bind), frame_bind_group (shared per-frame uniforms), object_bind_group (per-entity/identity
/// model).
fn draw_entity(
pass: &mut wgpu::RenderPass<'_>,
mesh: &Mesh,
@@ -410,6 +412,9 @@ fn draw_entity(
pass.set_pipeline(&material.pipeline);
pass.set_bind_group(0, frame_bind_group, &[]);
pass.set_bind_group(1, object_bind_group, &[]);
// Étape 10 (DRAFT 10.4) : groupe texture — le Material possède son bind group (placeholder
// blanc s'il n'a pas de texture, D1/D2). Toujours liable car posé sur toutes les pipelines.
pass.set_bind_group(2, &material.texture_bind_group, &[]);
pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
if let Some(index_buffer) = &mesh.index_buffer {
pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16);