feat: implement MeshLoader

This loader abstracts mesh loading logic into a series of individual file handlers. Each handler is chosen based on the extension of the file to be loaded.
This commit is contained in:
alphafetus committed 2026-08-07 23:46:12 -04:00
1 parent b7e31732e6
commit ba9b677545
9 files changed
+157 -48

No files matched your search

+1 -1
View File
@@ -5,7 +5,7 @@ use nalgebra::{Matrix4, Vector3, Vector4};
/// Contains all information about an instance of a mesh, specifically its translation, rotation,
/// and scale. Note that the rotation is a [`Vector4`], and not a `Quaternion`.
///
/// This may be taken from a scene instance by calling the `Instance::from` method on the
/// This may be taken from a scene instance by calling the [`MeshInstance::from`] method on the
/// corresponding [`SceneInstance`]. This is important, as the `Vector4` is not serializable.
///
/// Before passing this instance into the shaders, construct a transformation matrix by calling
+10 -16
View File
@@ -5,6 +5,7 @@ use std::sync::Arc;
use vulkano::buffer::{Buffer, BufferCreateInfo, BufferUsage, Subbuffer};
use vulkano::memory::allocator::{AllocationCreateInfo, MemoryAllocator, MemoryTypeFilter};
use wavefront::Obj;
use crate::loaders::mesh_loader::{MeshInfo, MeshLoader};
/// 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,
@@ -47,16 +48,18 @@ impl MeshRegistry {
pub fn from_scene(scene: &Scene, allocator: Arc<dyn MemoryAllocator>) -> Self {
// Load all the meshes into a single map.
println!("Loading meshes from scene");
let mesh_loader = MeshLoader::new();
let meshes = scene
.mesh_paths
.iter()
.map(|(id, path)| {
(
id.clone(),
Obj::from_file(path).expect("Couldn't read wavefront file"),
mesh_loader.load_mesh(path).expect("Couldn't read wavefront file"),
)
})
.collect::<HashMap<String, Obj>>();
.collect::<HashMap<String, MeshInfo>>();
let mut buf_verts = Vec::new();
let mut buf_indices = Vec::new();
@@ -65,27 +68,18 @@ impl MeshRegistry {
// 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 = buf_verts.len();
let all_verts = obj.positions();
let all_norms = obj.normals();
let all_verts = obj.vertices;
let all_norms = obj.normals;
let obj_num_verts = all_verts.len();
all_verts.iter().zip(all_norms.iter())
.zip(obj.uvs().iter())
.zip(obj.uvs.iter())
.for_each(|((pos, norm), uv)| {
buf_verts.push(Vertex3::new(*pos, *norm, *uv));
});
// buf_verts.extend(all_verts.map(|v| Vertex3::new(v.position(), v.normal().unwrap_or_default())));
let index_list = obj
.triangles()
.flat_map(|t| {
t.iter()
.map(|i| i.position_index() as u32)
.collect::<Vec<u32>>()
})
.collect::<Vec<u32>>();
let index_start = buf_indices.len();
let obj_num_idx = index_list.len();
buf_indices.extend(index_list);
let obj_num_idx = obj.indices.len();
buf_indices.extend(obj.indices);
println!("Loaded mesh {} with {} vertices and {} indices", id, obj_num_verts, obj_num_idx);
+11
View File
@@ -0,0 +1,11 @@
use std::path::Path;
use anyhow::Result;
use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo};
pub struct GltfLoader;
impl MeshFileLoader for GltfLoader {
fn load_mesh(&self, path: &Path) -> Result<MeshInfo> {
todo!()
}
}
+54
View File
@@ -0,0 +1,54 @@
use std::collections::HashMap;
use std::path::Path;
use anyhow::Result;
use thiserror::Error;
use crate::loaders::obj_loader::ObjLoader;
use super::gltf_loader::*;
pub struct MeshInfo {
pub vertices: Vec<[f32; 3]>,
pub normals: Vec<[f32; 3]>,
pub indices: Vec<u32>,
pub uvs: Vec<[f32; 3]>,
}
pub trait MeshFileLoader {
fn load_mesh(&self, path: &Path) -> Result<MeshInfo>;
}
pub struct MeshLoader {
loaders: HashMap<String, Box<dyn MeshFileLoader>>
}
#[derive(Error, Debug)]
pub enum MeshLoaderError {
#[error("file extension is missing")]
MissingExtension,
#[error("extension {0} is not supported")]
ExtensionNotSupported(String)
}
impl MeshLoader {
pub fn new() -> Self {
let mut loaders = HashMap::new();
loaders.insert("gltf".into(), Box::new(GltfLoader) as Box<dyn MeshFileLoader>);
loaders.insert("obj".into(), Box::new(ObjLoader));
Self {
loaders
}
}
/// Loads a mesh and returns a `Result` containing the information.
pub fn load_mesh<P: AsRef<Path>>(&self, path: &P) -> Result<MeshInfo> {
let ext = path.as_ref().extension()
.and_then(|ext| ext.to_str())
.ok_or(MeshLoaderError::MissingExtension)?;
println!("Found extension {}, continuing with loading", ext);
self.loaders.get(ext)
.ok_or(MeshLoaderError::ExtensionNotSupported(ext.to_string()))?
.load_mesh(path.as_ref())
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod gltf_loader;
pub mod mesh_loader;
mod obj_loader;
+29
View File
@@ -0,0 +1,29 @@
use std::path::Path;
use wavefront::Obj;
use crate::gpu::vertex3::Vertex3;
use crate::loaders::mesh_loader::{MeshFileLoader, MeshInfo};
pub struct ObjLoader;
impl MeshFileLoader for ObjLoader {
fn load_mesh(&self, path: &Path) -> anyhow::Result<MeshInfo> {
let obj_info = Obj::from_file(path)
.expect("Couldn't read wavefront file");
let index_list = obj_info
.triangles()
.flat_map(|t| {
t.iter()
.map(|i| i.position_index() as u32)
.collect::<Vec<u32>>()
})
.collect::<Vec<u32>>();
Ok(MeshInfo {
vertices: obj_info.positions().to_vec(),
normals: obj_info.normals().to_vec(),
indices: index_list,
uvs: obj_info.uvs().to_vec()
})
}
}
+1
View File
@@ -1,6 +1,7 @@
mod app;
mod common;
pub mod gpu;
pub mod loaders;
// pub mod math;
use crate::app::App;