ajout mesh.rs

This commit is contained in:
Jérôme Bousquié
2026-07-05 15:51:37 +02:00
parent 7cc1fb845f
commit 22edac6ad5
3 changed files with 82 additions and 0 deletions
+45
View File
@@ -0,0 +1,45 @@
La nouvelle vision architecturale
Pour comprendre ce changement, imagine que tu veux peindre 10 tableaux différents.
Avant (Ton code actuel) : Chaque tableau possède sa propre cuisine, son propre chef (le pipeline), et son propre matériel de peinture. C'est inefficace.
Après (La nouvelle structure) : Tu as un Atelier (le Renderer) qui contient des Recettes (le Material / Shader) et tu apportes tes Toiles (le Mesh). Tu peux utiliser la même recette pour 50 toiles différentes sans effort.
Voici la nouvelle répartition des responsabilités :
1. Le Mesh (La géométrie)
Il ne sait pas comment il est affiché, il sait seulement ce qu'il est.
Contenu : Il possède les données brutes (le VertexBuffer et éventuellement un IndexBuffer pour optimiser le dessin).
Rôle : Il est passif. C'est juste un conteneur de données prêtes à être envoyées à la carte graphique.
2. Le Material (Le look)
Il définit l'apparence de l'objet.
Contenu : Il possède le RenderPipeline. Le pipeline contient tout ce qui est "lourd" à créer (le shader compilé, les états de fusion, le mode de tracé).
Rôle : Il est réutilisable. Si tu as 10 objets en métal, ils partagent tous le même Material (donc le même pipeline).
3. Le Renderer (L'Orchestrateur)
Il devient beaucoup plus léger et efficace.
Contenu : Il ne possède plus de buffers en dur. Il possède une méthode render qui accepte un Mesh ET un Material.
Rôle : Il fait la liaison au moment de l'appel :
Rust
// Schématiquement
renderer.render_pass.set_pipeline(&material.pipeline); // On change de recette
renderer.render_pass.set_vertex_buffer(0, &mesh.buffer); // On pose la toile
renderer.render_pass.draw(...);
Pourquoi cette structure est-elle meilleure ?
Réutilisation (Performance) : Si tu as 100 objets avec le même shader, tu ne compiles ton shader qu'une seule fois. Tu gagnes énormément en vitesse de chargement et en occupation mémoire.
Modularité : Tu peux créer un Mesh "Cercle" et un Mesh "Carré". Tu peux créer un Material "Rouge" et un Material "Bleu". Tu peux combiner n'importe quel Mesh avec n'importe quel Material sans changer une ligne de code.
Nettoyage : Ton renderer.rs devient un orchestrateur pur au lieu d'être un fourre-tout.
La note technique : "Pipeline Cache"
Dans cette nouvelle structure, tu devras faire attention à un détail : le couplage entre le Mesh et le Material.
Si ton Mesh a une structure de Vertex différente de ce que le Material (Shader) attend, le rendu sera invalide. Le Material doit donc garantir qu'il est capable de traiter le format de données fourni par le Mesh.
+1
View File
@@ -1,6 +1,7 @@
pub mod conf; pub mod conf;
pub mod context; pub mod context;
pub mod error; pub mod error;
pub mod mesh;
pub mod renderer; pub mod renderer;
pub mod vertex; pub mod vertex;
+36
View File
@@ -0,0 +1,36 @@
use crate::vertex::Vertex;
use wgpu::util::DeviceExt;
pub struct Mesh {
pub vertex_buffer: wgpu::Buffer,
pub index_buffer: Option<wgpu::Buffer>,
pub num_vertices: u32,
pub num_indices: u32,
}
impl Mesh {
pub fn new(device: &wgpu::Device, vertices: &[Vertex], indices: Option<&[u16]>) -> Self {
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Mesh Vertex Buffer"),
contents: bytemuck::cast_slice(vertices),
usage: wgpu::BufferUsages::VERTEX,
});
let (index_buffer, num_indices) = if let Some(data) = indices {
let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Mesh Index Buffer"),
usage: wgpu::BufferUsages::INDEX,
contents: bytemuck::cast_slice(data),
});
(Some(buffer), data.len() as u32)
} else {
(None, 0)
};
Self {
vertex_buffer,
index_buffer,
num_vertices: vertices.len() as u32,
num_indices,
}
}
}