use crate::handle::*; use crate::mesh::*; use crate::pool::{MaterialData, TextureData}; use crate::scene::MeshInstance; pub trait OnLoad { fn load_mesh(&mut self, mesh_data: &MeshData) -> MeshHandle; fn load_texture(&mut self, texture_data: &TextureData) -> TextureHandle; fn load_material(&mut self, material_data: &MaterialData) -> MaterialHandle; } pub struct BasicMesh { pub mesh_handle: MeshHandle, pub material_handle: MaterialHandle, } impl BasicMesh { pub fn instantiate(&self, transform: glam::Mat4) -> MeshInstance { MeshInstance { mesh: self.mesh_handle, material: self.material_handle, transform, } } } pub struct ObjModel { mesh: BasicMesh, } impl ObjModel { pub fn load(mut on_load: impl OnLoad) -> Self { use tobj::*; let model_data = include_bytes!("assets/viking_room/model.obj").to_vec(); let model_data = &mut model_data.as_slice(); let load_options = LoadOptions { triangulate: true, single_index: true, ..Default::default() }; let (models, _mats) = load_obj_buf(model_data, &load_options, |_| unimplemented!()).unwrap(); let mut vertices = Vec::new(); let mut indices = Vec::new(); let m = models.first().unwrap(); let index_base = vertices.len() as u32; for i in 0..m.mesh.positions.len() / 3 { let t = i * 3; vertices.push(Vertex { position: [ m.mesh.positions[t], m.mesh.positions[t + 2], -m.mesh.positions[t + 1], ], normal: [ m.mesh.normals[t], m.mesh.normals[t + 2], -m.mesh.normals[t + 1], ], tex_coords: [m.mesh.texcoords[i * 2], 1.0 - m.mesh.texcoords[i * 2 + 1]], }); } indices.extend(m.mesh.indices.iter().map(|i| i + index_base)); let mesh_data = MeshData { vertices, indices }; let mesh_handle = on_load.load_mesh(&mesh_data); let albedo_data = include_bytes!("assets/viking_room/albedo.png"); let albedo = image::load_from_memory(albedo_data).unwrap(); use image::GenericImageView; let dimensions = albedo.dimensions(); let albedo_rgb = albedo.as_rgb8().unwrap().to_vec(); let mut albedo_rgba = Vec::::new(); for rgb in albedo_rgb.chunks(3) { albedo_rgba.extend_from_slice(rgb); albedo_rgba.push(0xff); } let albedo_data = TextureData { width: dimensions.0, height: dimensions.1, data: albedo_rgba, }; let albedo = on_load.load_texture(&albedo_data); let material_handle = on_load.load_material(&MaterialData { albedo, metallic_roughness: albedo, // TODO better placeholder MR texture }); let mesh = BasicMesh { mesh_handle, material_handle, }; Self { mesh } } pub fn draw(&self, meshes: &mut Vec, transform: glam::Mat4) { meshes.push(self.mesh.instantiate(transform)); } } pub struct GltfModel { meshes: Vec, } impl GltfModel { pub fn load(on_load: impl OnLoad) -> Self { use gltf::*; let model_data = include_bytes!("assets/DamagedHelmet.glb"); let model = Gltf::from_slice(model_data.as_slice()).unwrap(); let loader = GltfLoader::load(&model, on_load); Self { meshes: loader.meshes, } } pub fn draw(&self, meshes: &mut Vec, transform: glam::Mat4) { for mesh in self.meshes.iter() { meshes.push(mesh.instantiate(transform)); } } } pub struct GltfLoader<'a, T: OnLoad> { pub model: &'a gltf::Gltf, pub buffers: Vec>, pub on_load: T, pub meshes: Vec, } impl<'a, T: OnLoad> GltfLoader<'a, T> { pub fn load(model: &'a gltf::Gltf, on_load: T) -> Self { let buffers = Self::load_buffers(model); let mut loader = Self { model, buffers, on_load, meshes: Default::default(), }; for scene in loader.model.scenes() { for node in scene.nodes() { loader.load_node(node); } } loader } pub fn load_buffers(model: &gltf::Gltf) -> Vec> { let mut buffer_data = Vec::>::new(); for buffer in model.buffers() { match buffer.source() { gltf::buffer::Source::Bin => { buffer_data.push(model.blob.as_deref().unwrap().into()); } _ => panic!("URI buffer sources are unsupported"), } } buffer_data } pub fn load_node(&mut self, node: gltf::Node) { if let Some(mesh) = node.mesh() { for primitive in mesh.primitives() { let mesh = self.load_primitive(primitive); self.meshes.push(mesh); } } for child in node.children() { self.load_node(child); } } pub fn load_primitive(&mut self, primitive: gltf::Primitive) -> BasicMesh { let material_handle = self.load_primitive_material(primitive.clone()); let mesh_handle = self.load_primitive_mesh(primitive); BasicMesh { material_handle, mesh_handle, } } pub fn load_primitive_material(&mut self, primitive: gltf::Primitive) -> MaterialHandle { let pbr = primitive.material().pbr_metallic_roughness(); let base_color = pbr.base_color_texture().unwrap().texture(); let albedo = self.load_texture(base_color); let metallic_roughness = pbr.metallic_roughness_texture().unwrap().texture(); let metallic_roughness = self.load_texture(metallic_roughness); self.on_load.load_material(&MaterialData { albedo, metallic_roughness, }) } pub fn load_texture(&mut self, texture: gltf::Texture) -> TextureHandle { let source = texture.source().source(); let view = if let gltf::image::Source::View { view, .. } = source { view } else { panic!("texture must be embedded"); }; let start = view.offset() as usize; let end = (view.offset() + view.length()) as usize; let src = &self.buffers[view.buffer().index()][start..end]; use image::GenericImageView; let image = image::load_from_memory(src).unwrap(); let dimensions = image.dimensions(); let rgba: Vec = if let Some(rgba) = image.as_rgba8() { rgba.to_vec() } else { let rgb = image.as_rgb8().unwrap().to_vec(); let mut rgba = Vec::::new(); for rgb in rgb.chunks(3) { rgba.extend_from_slice(rgb); rgba.push(0xff); } rgba }; self.on_load.load_texture(&TextureData { width: dimensions.0, height: dimensions.1, data: rgba, }) } pub fn load_primitive_mesh(&mut self, primitive: gltf::Primitive) -> MeshHandle { use gltf::mesh::util::{ReadIndices, ReadTexCoords}; if primitive.mode() != gltf::mesh::Mode::Triangles { panic!("glTF primitive must be triangle list"); } let reader = primitive.reader(|buffer| Some(&self.buffers[buffer.index()])); let positions = reader.read_positions().unwrap(); let mut normals = reader.read_normals().unwrap(); let tex_coords = reader.read_tex_coords(0).unwrap(); let mut tex_coords = if let ReadTexCoords::F32(tex_coords) = tex_coords { tex_coords } else { panic!("only f32 texture coordinates are supported") }; let mut vertices = Vec::new(); for position in positions { let normal = normals.next().unwrap(); let tex_coords = tex_coords.next().unwrap(); vertices.push(Vertex { position, normal, tex_coords, }); } let indices = match reader.read_indices().unwrap() { ReadIndices::U32(indices) => indices.collect(), ReadIndices::U16(indices) => indices.map(|i| i as u32).collect(), ReadIndices::U8(indices) => indices.map(|i| i as u32).collect(), }; self.on_load.load_mesh(&MeshData { vertices, indices }) } }