feat: implement mesh registry and buffer allocation

This commit is contained in:
alphafetus committed 2026-05-08 16:51:34 -04:00
1 parent 0661494b0f
commit 6fe8ff2af0
1 file changed
+102 -95
+102 -95
View File
@@ -1,14 +1,10 @@
use crate::common::scene::Scene;
use crate::gpu::vertex3::Vertex3;
use std::collections::HashMap; use std::collections::HashMap;
use std::error::Error;
use std::path::Path;
use std::sync::Arc; use std::sync::Arc;
use nalgebra::Vector3;
use uuid::Uuid;
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer}; use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter}; use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
use wavefront::Obj; use wavefront::Obj;
use crate::common::scene::Scene;
use crate::gpu::vertex3::Vertex3;
/// This struct is slightly misleading - it doesn't actually contain the mesh information, but /// This struct is slightly misleading - it doesn't actually contain the mesh information, but
/// the locations of the mesh's information in the overall application's vertex buffer, index buffer, /// the locations of the mesh's information in the overall application's vertex buffer, index buffer,
@@ -24,22 +20,12 @@ use crate::gpu::vertex3::Vertex3;
pub struct Mesh { pub struct Mesh {
pub id: String, pub id: String,
pub vertex_loc: usize, pub vertex_loc: usize,
pub vertex_count: usize,
pub index_loc: usize, pub index_loc: usize,
pub index_count: usize,
} }
impl Mesh { impl Mesh {}
pub fn new(
id: impl Into<String>,
vertex_loc: usize,
index_loc: usize,
) -> Self {
Self {
id: id.into(),
vertex_loc,
index_loc,
}
}
}
/// # Mesh Registry /// # Mesh Registry
/// A wrapper around a series of [`Mesh`] structs, keyed by a unique ID, and the buffers that are /// A wrapper around a series of [`Mesh`] structs, keyed by a unique ID, and the buffers that are
@@ -53,99 +39,120 @@ pub struct MeshRegistry {
} }
impl MeshRegistry { impl MeshRegistry {
pub fn new( /// Creates a `MeshRegistry` from the given [`Scene`].
allocator: Arc<dyn MemoryAllocator>,
) -> Result<Self, Box<dyn Error>> {
let vertex_buffer = Arc::new(Buffer::new_slice::<Vertex3>(
allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::VERTEX_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
1_000_000,
)?);
let index_buffer = Arc::new(Buffer::new_slice::<u32>(
allocator.clone(),
BufferCreateInfo {
usage: BufferUsage::INDEX_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST |
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
3_000_000,
)?);
Ok(Self {
meshes: HashMap::new(),
allocator,
vertex_buffer,
index_buffer,
})
}
/// Creates a `MeshRegistry` from the provided [`Scene`].
/// ///
/// This method will load all the meshes indicated by the scene and allocate the required /// This method will load all the meshes indicated by the scene and allocate the required
/// buffers. This significantly simplifies the allocation process, as the buffers only need to /// buffers. This significantly simplifies the allocation process, as the buffers only need to
/// be allocated once. /// be allocated once.
pub fn from_scene(scene: &Scene, allocator: Arc<dyn MemoryAllocator>) -> Self { pub fn from_scene(scene: &Scene, allocator: Arc<dyn MemoryAllocator>) -> Self {
// First, load all the meshes into a single map. // Load all the meshes into a single map.
println!("Loading meshes from scene"); println!("Loading meshes from scene");
let meshes = scene.mesh_paths.iter().map(|(id, path)| let meshes = scene
(id.clone(), Obj::from_file(path).expect("Couldn't read wavefront file")) .mesh_paths
).collect::<HashMap<String, Obj>>(); .iter()
.map(|(id, path)| {
(
id.clone(),
Obj::from_file(path).expect("Couldn't read wavefront file"),
)
})
.collect::<HashMap<String, Obj>>();
// Second, build two more maps that contain the vertex and index information. let mut verts = Vec::new();
let verts = meshes.iter().map(|(id, obj)| let mut indices = Vec::new();
(id.clone(), obj.vertices().map(|v| v.position().into()).collect()) let mut mesh_list = HashMap::new();
).collect::<HashMap<String, Vec<Vector3<f32>>>>(); meshes.into_iter().for_each(|(id, obj)| {
// Information will be added directly to the verts and indices vectors, and a new Mesh
// struct will be created and inserted into the mesh_list.
let vert_start = verts.len();
let all_verts = obj.vertices();
let vert_len = all_verts.len();
verts.extend(all_verts.map(|v| Vertex3::from(v.position())));
let indices = meshes.iter().map(|(id, obj)| let index_list = obj
(id.clone(), obj.vertices().map(|v| v.position_index()).collect()) .triangles()
).collect::<HashMap<String, Vec<usize>>>(); .flat_map(|t| {
t.iter()
.map(|i| i.position_index() as u32)
.collect::<Vec<u32>>()
})
.collect::<Vec<u32>>();
let index_start = indices.len();
let index_len = index_list.len();
indices.extend(index_list);
// Apply static transformations here. The general idea is that we don't want to have to read // TODO Create bounding boxes for each mesh and associate them with the struct.
// any information from the buffer without the correct information. We do want to keep track mesh_list.insert(
// of what mesh is at what location in the list though. id.clone(),
println!("Applying static mesh transformations"); Mesh {
id,
vertex_loc: vert_start,
vertex_count: vert_len,
index_loc: index_start,
index_count: index_len,
},
);
});
// Get Vector3's as a list of Vertex3's (CPU to GPU) // Allocate buffers for the information.
let vertex_buffer = Self::alloc_vert_buffer(allocator.clone(), verts);
let index_buffer = Self::alloc_index_buffer(allocator.clone(), indices);
// Third, allocate and fill buffers with this information. // Finally, return the struct.
// let vert_buffer = Self::alloc_vert_buffer(allocator, verts.values().collect()); Self {
meshes: mesh_list,
// Fourth, return the struct. vertex_buffer,
index_buffer,
todo!() allocator,
}
} }
/// Allocates and writes to a vertex buffer with the given `Vec<Vertex3>`.
fn alloc_vert_buffer( fn alloc_vert_buffer(
alloc: Arc<dyn MemoryAllocator>, alloc: Arc<dyn MemoryAllocator>,
verts: Vec<Vertex3> verts: Vec<Vertex3>,
) -> Arc<Subbuffer<[Vertex3]>> { ) -> Arc<Subbuffer<[Vertex3]>> {
println!("Allocating vertex buffer"); println!("Allocating vertex buffer");
Arc::new(Buffer::from_iter( Arc::new(
alloc.clone(), Buffer::from_iter(
BufferCreateInfo { alloc.clone(),
usage: BufferUsage::VERTEX_BUFFER, BufferCreateInfo {
..Default::default() usage: BufferUsage::VERTEX_BUFFER,
}, ..Default::default()
AllocationCreateInfo { },
memory_type_filter: MemoryTypeFilter::PREFER_HOST | AllocationCreateInfo {
MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, memory_type_filter: MemoryTypeFilter::PREFER_HOST
..Default::default() | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
}, ..Default::default()
verts },
).expect("Couldn't allocate vertex buffer")) verts,
)
.expect("Couldn't allocate vertex buffer"),
)
}
/// Allocates and writes to an index buffer with the given `Vec<usize>`.
fn alloc_index_buffer(
alloc: Arc<dyn MemoryAllocator>,
indices: Vec<u32>,
) -> Arc<Subbuffer<[u32]>> {
println!("Allocating index buffer");
Arc::new(
Buffer::from_iter(
alloc.clone(),
BufferCreateInfo {
usage: BufferUsage::INDEX_BUFFER,
..Default::default()
},
AllocationCreateInfo {
memory_type_filter: MemoryTypeFilter::PREFER_HOST
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
..Default::default()
},
indices,
)
.expect("Couldn't allocate index buffer"),
)
} }
/// Attempts to find a [`Mesh`] with the given ID, returning `None` if it's not found. /// Attempts to find a [`Mesh`] with the given ID, returning `None` if it's not found.