Skip to main content

flow_ngin/data_structures/
model.rs

1//! 3D models: vertices, materials, meshes, and GPU-uploadable data.
2//!
3//! This module provides types for loading and managing 3D models:
4//!
5//! - [`ModelVertex`] holds position, normals, tangents, and texture coordinates
6//! - [`Material`] is the material with diffuse and normal textures and samplers
7//! - [`Mesh`] is a single mesh (vertices, indices, material)
8//! - [`Model`] is a collection of meshes with shared materials
9
10use std::ops::Range;
11
12use crate::{data_structures::texture::{self, create_default_sampler}, resources::pick::pick_layout};
13
14/// Trait for types that describe their GPU vertex layout.
15pub trait Vertex {
16    fn desc() -> wgpu::VertexBufferLayout<'static>;
17}
18
19/// A 3D model vertex with position, texture coordinates, and normal/tangent data.
20///
21/// Used for normal-mapped surfaces. Stores position, UV coordinates, and the
22/// normal + tangent vectors needed for per-pixel normal mapping.
23#[repr(C)]
24#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable, PartialEq)]
25pub struct ModelVertex {
26    pub position: [f32; 3],
27    pub tex_coords: [f32; 2],
28    pub normal: [f32; 3],
29    // for translating normal maps to world space
30    pub tangent: [f32; 3],
31    pub bitangent: [f32; 3],
32}
33
34impl Vertex for ModelVertex {
35    fn desc() -> wgpu::VertexBufferLayout<'static> {
36        use std::mem;
37        wgpu::VertexBufferLayout {
38            array_stride: mem::size_of::<ModelVertex>() as wgpu::BufferAddress,
39            step_mode: wgpu::VertexStepMode::Vertex,
40            attributes: &[
41                wgpu::VertexAttribute {
42                    offset: 0,
43                    // corresponds to the @location in the shader file.
44                    shader_location: 0,
45                    format: wgpu::VertexFormat::Float32x3,
46                },
47                wgpu::VertexAttribute {
48                    offset: mem::size_of::<[f32; 3]>() as wgpu::BufferAddress,
49                    shader_location: 1,
50                    format: wgpu::VertexFormat::Float32x2,
51                },
52                wgpu::VertexAttribute {
53                    offset: mem::size_of::<[f32; 5]>() as wgpu::BufferAddress,
54                    shader_location: 2,
55                    format: wgpu::VertexFormat::Float32x3,
56                },
57                wgpu::VertexAttribute {
58                    offset: mem::size_of::<[f32; 8]>() as wgpu::BufferAddress,
59                    shader_location: 3,
60                    format: wgpu::VertexFormat::Float32x3,
61                },
62                wgpu::VertexAttribute {
63                    offset: mem::size_of::<[f32; 11]>() as wgpu::BufferAddress,
64                    shader_location: 4,
65                    format: wgpu::VertexFormat::Float32x3,
66                },
67            ],
68        }
69    }
70}
71
72#[derive(Clone, Debug)]
73pub struct Material {
74    pub name: String,
75    pub bind_group: wgpu::BindGroup,
76}
77
78impl Material {
79    pub fn new(
80        device: &wgpu::Device,
81        name: &str,
82        diffuse_texture: texture::Texture,
83        normal_texture: texture::Texture,
84        layout: &wgpu::BindGroupLayout,
85    ) -> Result<Self, anyhow::Error> {
86        let diffuse_texture_sampler = diffuse_texture
87            .sampler
88            .ok_or(anyhow::anyhow!("Diffuse texture missing sampler"))?;
89        let normal_texture_sampler = normal_texture
90            .sampler
91            .unwrap_or(create_default_sampler(device));
92        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
93            layout,
94            entries: &[
95                // Must match amount of bind groups in texture_bind_group_layout (wow what a surprise...)
96                wgpu::BindGroupEntry {
97                    binding: 0,
98                    resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
99                },
100                wgpu::BindGroupEntry {
101                    binding: 1,
102                    resource: wgpu::BindingResource::Sampler(&diffuse_texture_sampler),
103                },
104                wgpu::BindGroupEntry {
105                    binding: 2,
106                    resource: wgpu::BindingResource::TextureView(&normal_texture.view),
107                },
108                wgpu::BindGroupEntry {
109                    binding: 3,
110                    resource: wgpu::BindingResource::Sampler(&normal_texture_sampler),
111                },
112            ],
113            label: Some(name),
114        });
115        Ok(Self {
116            name: String::from(name),
117            bind_group,
118        })
119    }
120
121    pub fn new_pick_material(device: &wgpu::Device, name: &str, buffer: wgpu::Buffer) -> Self {
122        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
123            layout: &pick_layout(device),
124            entries: &[
125                // Must match amount of bind groups in texture_bind_group_layout (hmmm not surprising...)
126                wgpu::BindGroupEntry {
127                    binding: 0,
128                    resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
129                        buffer: &buffer,
130                        offset: 0,
131                        size: None,
132                    }),
133                },
134            ],
135            label: Some(name),
136        });
137
138        Self {
139            name: String::from(name),
140            bind_group,
141        }
142    }
143}
144
145#[derive(Clone, Debug)]
146pub struct Mesh {
147    pub name: String,
148    pub vertex_buffer: wgpu::Buffer,
149    pub index_buffer: wgpu::Buffer,
150    pub num_elements: u32,
151    pub material: usize,
152}
153
154#[derive(Debug)]
155pub struct Model {
156    pub meshes: Vec<Mesh>,
157    pub materials: Vec<Material>,
158}
159
160pub trait DrawModel<'a> {
161    fn draw_mesh(
162        &mut self,
163        mesh: &'a Mesh,
164        material: &'a Material,
165        camera_bind_group: &'a wgpu::BindGroup,
166        light_bind_group: &'a wgpu::BindGroup,
167    );
168    fn draw_mesh_instanced(
169        &mut self,
170        mesh: &'a Mesh,
171        material: &'a Material,
172        instances: Range<u32>,
173        camera_bind_group: &'a wgpu::BindGroup,
174        light_bind_group: &'a wgpu::BindGroup,
175    );
176
177    fn draw_model(
178        &mut self,
179        model: &'a Model,
180        camera_bind_group: &'a wgpu::BindGroup,
181        light_bind_group: &'a wgpu::BindGroup,
182    );
183    fn draw_model_instanced(
184        &mut self,
185        model: &'a Model,
186        instances: Range<u32>,
187        camera_bind_group: &'a wgpu::BindGroup,
188        light_bind_group: &'a wgpu::BindGroup,
189    );
190}
191
192impl<'a, 'b> DrawModel<'b> for wgpu::RenderPass<'a>
193where
194    'a: 'b,
195{
196    fn draw_mesh(
197        &mut self,
198        mesh: &'b Mesh,
199        material: &'b Material,
200        camera_bind_group: &'b wgpu::BindGroup,
201        light_bind_group: &'b wgpu::BindGroup,
202    ) {
203        self.draw_mesh_instanced(mesh, material, 0..1, camera_bind_group, light_bind_group);
204    }
205
206    fn draw_mesh_instanced(
207        &mut self,
208        mesh: &'b Mesh,
209        material: &'b Material,
210        instances: Range<u32>,
211        camera_bind_group: &'b wgpu::BindGroup,
212        light_bind_group: &'b wgpu::BindGroup,
213    ) {
214        self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
215        self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
216        self.set_bind_group(0, &material.bind_group, &[]);
217        self.set_bind_group(1, camera_bind_group, &[]);
218        self.set_bind_group(2, light_bind_group, &[]);
219        self.draw_indexed(0..mesh.num_elements, 0, instances);
220    }
221
222    fn draw_model(
223        &mut self,
224        model: &'b Model,
225        camera_bind_group: &'b wgpu::BindGroup,
226        light_bind_group: &'b wgpu::BindGroup,
227    ) {
228        self.draw_model_instanced(model, 0..1, camera_bind_group, light_bind_group);
229    }
230
231    fn draw_model_instanced(
232        &mut self,
233        model: &'b Model,
234        instances: Range<u32>,
235        camera_bind_group: &'b wgpu::BindGroup,
236        light_bind_group: &'b wgpu::BindGroup,
237    ) {
238        for mesh in &model.meshes {
239            let material = &model.materials[mesh.material];
240            self.draw_mesh_instanced(
241                mesh,
242                material,
243                instances.clone(),
244                camera_bind_group,
245                light_bind_group,
246            );
247        }
248    }
249}
250
251pub trait DrawLight<'a> {
252    #[allow(dead_code)]
253    fn draw_light_mesh(
254        &mut self,
255        mesh: &'a Mesh,
256        camera_bind_group: &'a wgpu::BindGroup,
257        light_bind_group: &'a wgpu::BindGroup,
258    );
259    fn draw_light_mesh_instanced(
260        &mut self,
261        mesh: &'a Mesh,
262        instances: Range<u32>,
263        camera_bind_group: &'a wgpu::BindGroup,
264        light_bind_group: &'a wgpu::BindGroup,
265    );
266
267    fn draw_light_model(
268        &mut self,
269        model: &'a Model,
270        camera_bind_group: &'a wgpu::BindGroup,
271        light_bind_group: &'a wgpu::BindGroup,
272    );
273    fn draw_light_model_instanced(
274        &mut self,
275        model: &'a Model,
276        instances: Range<u32>,
277        camera_bind_group: &'a wgpu::BindGroup,
278        light_bind_group: &'a wgpu::BindGroup,
279    );
280}
281
282impl<'a, 'b> DrawLight<'b> for wgpu::RenderPass<'a>
283where
284    'b: 'a,
285{
286    fn draw_light_mesh(
287        &mut self,
288        mesh: &'b Mesh,
289        camera_bind_group: &'b wgpu::BindGroup,
290        light_bind_group: &'b wgpu::BindGroup,
291    ) {
292        self.draw_light_mesh_instanced(mesh, 0..1, camera_bind_group, light_bind_group);
293    }
294
295    fn draw_light_mesh_instanced(
296        &mut self,
297        mesh: &'b Mesh,
298        instances: Range<u32>,
299        camera_bind_group: &'b wgpu::BindGroup,
300        light_bind_group: &'b wgpu::BindGroup,
301    ) {
302        self.set_vertex_buffer(0, mesh.vertex_buffer.slice(..));
303        self.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
304        self.set_bind_group(0, camera_bind_group, &[]);
305        self.set_bind_group(1, light_bind_group, &[]);
306        self.draw_indexed(0..mesh.num_elements, 0, instances);
307    }
308
309    fn draw_light_model(
310        &mut self,
311        model: &'b Model,
312        camera_bind_group: &'b wgpu::BindGroup,
313        light_bind_group: &'b wgpu::BindGroup,
314    ) {
315        self.draw_light_model_instanced(model, 0..1, camera_bind_group, light_bind_group);
316    }
317    fn draw_light_model_instanced(
318        &mut self,
319        model: &'b Model,
320        instances: Range<u32>,
321        camera_bind_group: &'b wgpu::BindGroup,
322        light_bind_group: &'b wgpu::BindGroup,
323    ) {
324        for mesh in &model.meshes {
325            self.draw_light_mesh_instanced(
326                mesh,
327                instances.clone(),
328                camera_bind_group,
329                light_bind_group,
330            );
331        }
332    }
333}