vertex +normals
This commit is contained in:
+63
-4
@@ -2,16 +2,75 @@ use std::sync::Arc;
|
|||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
use winit::window::WindowBuilder;
|
use winit::window::WindowBuilder;
|
||||||
use wsg_lib::context::Context;
|
use wsg_lib::context::Context;
|
||||||
|
use wsg_lib::material::Material;
|
||||||
|
use wsg_lib::mesh::Mesh;
|
||||||
|
use wsg_lib::pipeline_cache::PipelineCache;
|
||||||
|
use wsg_lib::renderer::Renderer;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let event_loop = EventLoop::new().unwrap();
|
let event_loop = EventLoop::new().unwrap();
|
||||||
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
let window = Arc::new(WindowBuilder::new().build(&event_loop).unwrap());
|
||||||
|
|
||||||
// Utilisation de pollster pour le bloc async
|
// Initialisation contexte matériel (device, queue, surface) Utilisation de pollster pour le bloc async
|
||||||
let context = pollster::block_on(Context::new(window.clone()));
|
let context = pollster::block_on(Context::new(window.clone()));
|
||||||
|
|
||||||
match context {
|
// Initialisation des briques de rendu
|
||||||
Ok(_) => println!("Succès : WGPU context initialisé !"),
|
let mut cache = PipelineCache::new();
|
||||||
Err(e) => eprintln!("Erreur lors de l'initialisation : {:?}", e),
|
cache
|
||||||
|
.register_shader("basic", "assets/shaders/basic.wgsl")
|
||||||
|
.unwrap();
|
||||||
|
let renderer = Renderer::new(&context, &cache);
|
||||||
|
|
||||||
|
// Creation d'un material (charge le basic shader via le cache)
|
||||||
|
let material = Material::new(&context.device, context.config.format, "basic", &mut cache);
|
||||||
|
// Creation d'un mesh
|
||||||
|
// 1. Définition des sommets (avec position et couleur pour l'interpolation)
|
||||||
|
let vertices = [
|
||||||
|
Vertex {
|
||||||
|
position: [-0.5, 0.5, 0.0],
|
||||||
|
color: [1.0, 0.0, 0.0],
|
||||||
|
}, // Haut-Gauche (Rouge)
|
||||||
|
Vertex {
|
||||||
|
position: [0.5, 0.5, 0.0],
|
||||||
|
color: [0.0, 1.0, 0.0],
|
||||||
|
}, // Haut-Droite (Vert)
|
||||||
|
Vertex {
|
||||||
|
position: [0.5, -0.5, 0.0],
|
||||||
|
color: [0.0, 0.0, 1.0],
|
||||||
|
}, // Bas-Droite (Bleu)
|
||||||
|
Vertex {
|
||||||
|
position: [-0.5, -0.5, 0.0],
|
||||||
|
color: [1.0, 1.0, 1.0],
|
||||||
|
}, // Bas-Gauche (Blanc)
|
||||||
|
];
|
||||||
|
let indices: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||||
|
let mesh = Mesh::new(&context.device, &vertices, Some(&indices));
|
||||||
|
|
||||||
|
// Render loop
|
||||||
|
event_loop
|
||||||
|
.run(|event, elwt| {
|
||||||
|
match event {
|
||||||
|
winit::event::Event::AboutToWait => {
|
||||||
|
window.request_redraw();
|
||||||
}
|
}
|
||||||
|
winit::event::Event::WindowEvent {
|
||||||
|
event: winit::event::WindowEvent::RedrawRequested,
|
||||||
|
..
|
||||||
|
} => {
|
||||||
|
// Acquisition de la cible de rendu
|
||||||
|
let frame = context.surface.get_current_texture().unwrap();
|
||||||
|
let view = frame
|
||||||
|
.texture
|
||||||
|
.create_view(&wgpu::TextureViewDescriptor::default());
|
||||||
|
|
||||||
|
// Orchestration du rendu
|
||||||
|
renderer.render(&context.device, &context.queue, &view, &mesh, &material);
|
||||||
|
|
||||||
|
// Presentation de l'image
|
||||||
|
frame.present();
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-4
@@ -24,6 +24,7 @@ use std::sync::Arc;
|
|||||||
pub struct PipelineCache {
|
pub struct PipelineCache {
|
||||||
/// Cached pipelines keyed by their shader identifier string.
|
/// Cached pipelines keyed by their shader identifier string.
|
||||||
pipelines: HashMap<String, Arc<wgpu::RenderPipeline>>,
|
pipelines: HashMap<String, Arc<wgpu::RenderPipeline>>,
|
||||||
|
shader_paths: HashMap<String, String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PipelineCache {
|
impl PipelineCache {
|
||||||
@@ -31,8 +32,31 @@ impl PipelineCache {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
pipelines: HashMap::new(),
|
pipelines: HashMap::new(),
|
||||||
|
shader_paths: HashMap::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// Enregistre un chemin de shader associé à un ID.
|
||||||
|
/// Renvoie Ok(id) si réussi, ou une erreur si l'ID existe déjà.
|
||||||
|
pub fn register_shader(&mut self, id: &str, path: &str) -> Result<String, String> {
|
||||||
|
if self.shader_paths.contains_key(id) {
|
||||||
|
return Err(format!("ID '{}' already exists.", id));
|
||||||
|
}
|
||||||
|
self.shader_paths.insert(id.to_string(), path.to_string());
|
||||||
|
Ok(id.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Supprime un ID et son chemin associé.
|
||||||
|
/// Renvoie Ok(id) si réussi, ou une erreur si l'ID est inconnu.
|
||||||
|
pub fn unregister_shader(&mut self, id: &str) -> Result<String, String> {
|
||||||
|
if self.shader_paths.remove(id).is_none() {
|
||||||
|
return Err(format!("ID '{}' does not exist.", id));
|
||||||
|
}
|
||||||
|
// Optionnel : tu pourrais aussi supprimer le pipeline compilé du cache
|
||||||
|
// si tu veux libérer la mémoire GPU immédiatement :
|
||||||
|
self.pipelines.remove(id);
|
||||||
|
|
||||||
|
Ok(id.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves a cached RenderPipeline by shader_id, or creates one on-demand if not present.
|
/// Retrieves a cached RenderPipeline by shader_id, or creates one on-demand if not present.
|
||||||
/// Inputs: device (GPU command source), format (surface texture format for fragment output),
|
/// Inputs: device (GPU command source), format (surface texture format for fragment output),
|
||||||
@@ -50,8 +74,12 @@ impl PipelineCache {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Compile a new pipeline — loads shader and builds the GPU render pipeline
|
// Step 2: Compile a new pipeline — loads shader and builds the GPU render pipeline
|
||||||
// Note: In production you would load shaders specific to shader_id from files or embedded resources.
|
let path = self
|
||||||
let shader = self.load_shader(device, shader_id);
|
.shader_paths
|
||||||
|
.get(shader_id)
|
||||||
|
.map(|s| s.as_str())
|
||||||
|
.unwrap_or(shader_id);
|
||||||
|
let shader = self.load_shader(device, path);
|
||||||
let pipeline = Self::build_pipeline(device, format, &shader);
|
let pipeline = Self::build_pipeline(device, format, &shader);
|
||||||
|
|
||||||
// Step 3: Cache the new pipeline behind Arc and return it
|
// Step 3: Cache the new pipeline behind Arc and return it
|
||||||
@@ -97,11 +125,16 @@ impl PipelineCache {
|
|||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: 12,
|
offset: 12,
|
||||||
shader_location: 1,
|
shader_location: 1,
|
||||||
|
format: wgpu::VertexFormat::Float32x3,
|
||||||
|
}, // normal
|
||||||
|
wgpu::VertexAttribute {
|
||||||
|
offset: 24,
|
||||||
|
shader_location: 2,
|
||||||
format: wgpu::VertexFormat::Float32x2,
|
format: wgpu::VertexFormat::Float32x2,
|
||||||
}, // uv
|
}, // uv
|
||||||
wgpu::VertexAttribute {
|
wgpu::VertexAttribute {
|
||||||
offset: 20,
|
offset: 32,
|
||||||
shader_location: 2,
|
shader_location: 3,
|
||||||
format: wgpu::VertexFormat::Float32x4,
|
format: wgpu::VertexFormat::Float32x4,
|
||||||
}, // color
|
}, // color
|
||||||
],
|
],
|
||||||
|
|||||||
+7
-3
@@ -8,15 +8,19 @@
|
|||||||
//! - `#[repr(C)]` ensures fields are laid out contiguously without Rust padding reordering, matching C ABI.
|
//! - `#[repr(C)]` ensures fields are laid out contiguously without Rust padding reordering, matching C ABI.
|
||||||
//! - `bytemuck::Pod + bytemuck::Zeroable` enables safe `cast_slice()` conversion for GPU buffer uploads.
|
//! - `bytemuck::Pod + bytemuck::Zeroable` enables safe `cast_slice()` conversion for GPU buffer uploads.
|
||||||
|
|
||||||
/// Per-vertex attribute tuple: position (3D), texture coordinate (2D), color (RGBA).
|
/// Per-vertex attribute tuple: position (3D), normal (3D), texture coordinate (2D), color (RGBA).
|
||||||
/// Must match the vertex buffer layout in PipelineCache::build_pipeline() byte-for-byte.
|
/// Must match the vertex buffer layout in PipelineCache::build_pipeline() byte-for-byte.
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
/// XYZ coordinates of this vertex in world space. Offset: 0 bytes.
|
/// XYZ coordinates of this vertex in world space. Offset: 0 bytes.
|
||||||
pub position: [f32; 3],
|
pub position: [f32; 3],
|
||||||
/// UV texture coordinates. Offset: 12 bytes (after 3 × f32 = 12 bytes).
|
/// XYZ coordinates of the vertex normal. Offset: 12 bytes.
|
||||||
|
pub normal: [f32; 3],
|
||||||
|
/// UV texture coordinates. Offset: 24 bytes
|
||||||
pub uv: [f32; 2],
|
pub uv: [f32; 2],
|
||||||
/// RGBA color values. Offset: 20 bytes (after 5 × f32 = 20 bytes).
|
/// RGBA color values. Offset: 32 bytes.
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Default values for stability
|
||||||
|
|||||||
Reference in New Issue
Block a user