Add textured OBJ model rendering
This commit is contained in:
128
src/resources.rs
Normal file
128
src/resources.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use crate::{model, texture};
|
||||
use eframe::wgpu;
|
||||
use eframe::wgpu::util::DeviceExt;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
fn resource_path(file_name: &str) -> PathBuf {
|
||||
Path::new(env!("RESOURCE_DIR")).join(file_name)
|
||||
}
|
||||
|
||||
pub fn load_string(file_name: &str) -> anyhow::Result<String> {
|
||||
Ok(std::fs::read_to_string(resource_path(file_name))?)
|
||||
}
|
||||
|
||||
pub fn load_binary(file_name: &str) -> anyhow::Result<Vec<u8>> {
|
||||
Ok(std::fs::read(resource_path(file_name))?)
|
||||
}
|
||||
|
||||
pub fn load_texture(
|
||||
file_name: &str,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
) -> anyhow::Result<texture::Texture> {
|
||||
let data = load_binary(file_name)?;
|
||||
texture::Texture::from_bytes(device, queue, &data, file_name)
|
||||
}
|
||||
|
||||
pub fn load_model(
|
||||
file_name: &str,
|
||||
device: &wgpu::Device,
|
||||
queue: &wgpu::Queue,
|
||||
texture_bind_group_layout: &wgpu::BindGroupLayout,
|
||||
) -> anyhow::Result<model::Model> {
|
||||
let path = resource_path(file_name);
|
||||
let (models, obj_materials) = tobj::load_obj(
|
||||
&path,
|
||||
&tobj::LoadOptions {
|
||||
single_index: true,
|
||||
triangulate: true,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let mut materials = Vec::new();
|
||||
for material in obj_materials? {
|
||||
let diffuse_texture = match material.diffuse_texture.as_deref() {
|
||||
Some(texture_path) => load_texture(texture_path, device, queue)?,
|
||||
None => texture::Texture::from_rgba8(
|
||||
device,
|
||||
queue,
|
||||
&[255, 255, 255, 255],
|
||||
1,
|
||||
1,
|
||||
&material.name,
|
||||
)?,
|
||||
};
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
label: Some(&format!("{} Texture Bind Group", material.name)),
|
||||
layout: texture_bind_group_layout,
|
||||
entries: &[
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 0,
|
||||
resource: wgpu::BindingResource::TextureView(&diffuse_texture.view),
|
||||
},
|
||||
wgpu::BindGroupEntry {
|
||||
binding: 1,
|
||||
resource: wgpu::BindingResource::Sampler(&diffuse_texture.sampler),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
materials.push(model::Material {
|
||||
name: material.name,
|
||||
diffuse_texture,
|
||||
bind_group,
|
||||
});
|
||||
}
|
||||
|
||||
let meshes = models
|
||||
.into_iter()
|
||||
.map(|m| {
|
||||
let vertices = (0..m.mesh.positions.len() / 3)
|
||||
.map(|i| model::ModelVertex {
|
||||
position: [
|
||||
m.mesh.positions[i * 3],
|
||||
m.mesh.positions[i * 3 + 1],
|
||||
m.mesh.positions[i * 3 + 2],
|
||||
],
|
||||
tex_coords: if m.mesh.texcoords.len() >= i * 2 + 2 {
|
||||
[m.mesh.texcoords[i * 2], m.mesh.texcoords[i * 2 + 1]]
|
||||
} else {
|
||||
[0.0, 0.0]
|
||||
},
|
||||
normal: if m.mesh.normals.len() >= i * 3 + 3 {
|
||||
[
|
||||
m.mesh.normals[i * 3],
|
||||
m.mesh.normals[i * 3 + 1],
|
||||
m.mesh.normals[i * 3 + 2],
|
||||
]
|
||||
} else {
|
||||
[0.0, 1.0, 0.0]
|
||||
},
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&format!("{} Vertex Buffer", m.name)),
|
||||
contents: bytemuck::cast_slice(&vertices),
|
||||
usage: wgpu::BufferUsages::VERTEX,
|
||||
});
|
||||
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||
label: Some(&format!("{} Index Buffer", m.name)),
|
||||
contents: bytemuck::cast_slice(&m.mesh.indices),
|
||||
usage: wgpu::BufferUsages::INDEX,
|
||||
});
|
||||
|
||||
model::Mesh {
|
||||
name: m.name,
|
||||
vertex_buffer,
|
||||
index_buffer,
|
||||
num_elements: m.mesh.indices.len() as u32,
|
||||
material: m.mesh.material_id.unwrap_or(0),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
Ok(model::Model { meshes, materials })
|
||||
}
|
||||
Reference in New Issue
Block a user