299 lines
8.7 KiB
Rust
299 lines
8.7 KiB
Rust
use crate::handle::*;
|
|
use crate::mesh::*;
|
|
use crate::pool::{MaterialData, TextureData};
|
|
use crate::renderer::Renderer;
|
|
use crate::scene::MeshInstance;
|
|
|
|
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(ren: &mut Renderer) -> 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 = ren.mesh_pool.allocate(&ren.device, &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::<u8>::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 = ren
|
|
.texture_pool
|
|
.allocate(&ren.device, &ren.queue, &albedo_data);
|
|
|
|
let material_data = MaterialData { albedo };
|
|
let material_handle =
|
|
ren.material_pool
|
|
.allocate(&ren.device, &ren.texture_pool, &material_data);
|
|
|
|
let mesh = BasicMesh {
|
|
mesh_handle,
|
|
material_handle,
|
|
};
|
|
Self { mesh }
|
|
}
|
|
|
|
pub fn draw(&self, meshes: &mut Vec<MeshInstance>, transform: glam::Mat4) {
|
|
meshes.push(self.mesh.instantiate(transform));
|
|
}
|
|
}
|
|
|
|
pub struct GltfModel {
|
|
meshes: Vec<BasicMesh>,
|
|
}
|
|
|
|
impl GltfModel {
|
|
pub fn load(ren: &mut Renderer) -> 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, ren);
|
|
|
|
Self {
|
|
meshes: loader.meshes,
|
|
}
|
|
}
|
|
|
|
pub fn draw(&self, meshes: &mut Vec<MeshInstance>, transform: glam::Mat4) {
|
|
for mesh in self.meshes.iter() {
|
|
meshes.push(mesh.instantiate(transform));
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct GltfLoader<'a> {
|
|
pub model: &'a gltf::Gltf,
|
|
pub buffers: Vec<Vec<u8>>,
|
|
pub ren: &'a mut Renderer,
|
|
pub meshes: Vec<BasicMesh>,
|
|
}
|
|
|
|
impl<'a> GltfLoader<'a> {
|
|
pub fn load(model: &'a gltf::Gltf, ren: &'a mut Renderer) -> Self {
|
|
let buffers = Self::load_buffers(model);
|
|
|
|
let mut loader = Self {
|
|
model,
|
|
buffers,
|
|
ren,
|
|
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<Vec<u8>> {
|
|
let mut buffer_data = Vec::<Vec<u8>>::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_texture = pbr.base_color_texture().unwrap();
|
|
let base_texture = base_texture.texture().source().source();
|
|
|
|
let base_view = if let gltf::image::Source::View { view, .. } = base_texture {
|
|
view
|
|
} else {
|
|
panic!("texture must be embedded");
|
|
};
|
|
|
|
// TODO put real data in albedo texture
|
|
|
|
let albedo_data = TextureData {
|
|
width: 256,
|
|
height: 256,
|
|
data: vec![0xffu8; 256 * 256 * 4],
|
|
};
|
|
|
|
let albedo =
|
|
self.ren
|
|
.texture_pool
|
|
.allocate(&self.ren.device, &self.ren.queue, &albedo_data);
|
|
|
|
let material_data = MaterialData { albedo };
|
|
|
|
self.ren
|
|
.material_pool
|
|
.allocate(&self.ren.device, &self.ren.texture_pool, &material_data)
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
println!("primitive: {:#?}", primitive);
|
|
|
|
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(),
|
|
};
|
|
|
|
let mesh_data = MeshData { vertices, indices };
|
|
self.ren.mesh_pool.allocate(&self.ren.device, &mesh_data)
|
|
}
|
|
}
|
|
|
|
pub struct GltfPrimitiveAttributes<'a> {
|
|
pub positions: Option<gltf::Accessor<'a>>,
|
|
pub normals: Option<gltf::Accessor<'a>>,
|
|
pub texcoords: Option<gltf::Accessor<'a>>,
|
|
}
|
|
|
|
impl<'a> GltfPrimitiveAttributes<'a> {
|
|
pub fn new(primitive: gltf::Primitive<'a>) -> Self {
|
|
let mut positions = None;
|
|
let mut normals = None;
|
|
let mut texcoords = None;
|
|
|
|
for (semantic, accessor) in primitive.attributes() {
|
|
use gltf::Semantic::*;
|
|
match semantic {
|
|
Positions => positions = Some(accessor),
|
|
Normals => normals = Some(accessor),
|
|
TexCoords(0) => texcoords = Some(accessor),
|
|
_ => println!("unused glTF attribute {:?}", semantic),
|
|
}
|
|
}
|
|
|
|
Self {
|
|
positions,
|
|
normals,
|
|
texcoords,
|
|
}
|
|
}
|
|
}
|