WIP: obj loader mostly done

This commit is contained in:
2023-10-21 04:19:29 +03:00
committed by Illusion
parent 1dd39d9f70
commit 421c7f5db3
9 changed files with 583 additions and 270 deletions
+17 -21
View File
@@ -6,30 +6,25 @@ use crate::{mesh::Vertex, shader::Shader};
pub struct AABB { pub struct AABB {
pub min: glm::Vec3, pub min: glm::Vec3,
pub max: glm::Vec3, pub max: glm::Vec3,
indices_len: u32,
vao: u32,
} }
#[derive(Debug)] impl AABB {
pub struct AABBMesh { pub fn new(min: glm::Vec3, max: glm::Vec3) -> AABB {
pub vertices: Vec<Vertex>,
pub indices: Vec<u32>,
pub vao: u32,
}
impl AABBMesh {
pub fn new(aabb: &AABB) -> AABBMesh {
let mut vao = 0; let mut vao = 0;
let mut vbo = 0; let mut vbo = 0;
let mut ebo = 0; let mut ebo = 0;
let vertices = vec![ let vertices = vec![
Vertex { position: glm::vec3(aabb.min.x, aabb.min.y, aabb.min.z), tex_coords: glm::vec2(0.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(min.x, min.y, min.z), tex_coords: glm::vec2(0.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) },
Vertex { position: glm::vec3(aabb.max.x, aabb.min.y, aabb.min.z), tex_coords: glm::vec2(1.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(max.x, min.y, min.z), tex_coords: glm::vec2(1.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) },
Vertex { position: glm::vec3(aabb.max.x, aabb.max.y, aabb.min.z), tex_coords: glm::vec2(1.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(max.x, max.y, min.z), tex_coords: glm::vec2(1.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) },
Vertex { position: glm::vec3(aabb.min.x, aabb.max.y, aabb.min.z), tex_coords: glm::vec2(0.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(min.x, max.y, min.z), tex_coords: glm::vec2(0.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) },
Vertex { position: glm::vec3(aabb.min.x, aabb.min.y, aabb.max.z), tex_coords: glm::vec2(0.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(min.x, min.y, max.z), tex_coords: glm::vec2(0.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) },
Vertex { position: glm::vec3(aabb.max.x, aabb.min.y, aabb.max.z), tex_coords: glm::vec2(1.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(max.x, min.y, max.z), tex_coords: glm::vec2(1.0, 0.0), normal: glm::vec3(0.0, 0.0, 0.0) },
Vertex { position: glm::vec3(aabb.max.x, aabb.max.y, aabb.max.z), tex_coords: glm::vec2(1.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(max.x, max.y, max.z), tex_coords: glm::vec2(1.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) },
Vertex { position: glm::vec3(aabb.min.x, aabb.max.y, aabb.max.z), tex_coords: glm::vec2(0.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) }, Vertex { position: glm::vec3(min.x, max.y, max.z), tex_coords: glm::vec2(0.0, 1.0), normal: glm::vec3(0.0, 0.0, 0.0) },
]; ];
let indices = vec![ let indices = vec![
@@ -69,9 +64,10 @@ impl AABBMesh {
gl::BindVertexArray(0); gl::BindVertexArray(0);
} }
AABBMesh { AABB {
vertices, min,
indices, max,
indices_len: indices.len() as u32,
vao, vao,
} }
} }
@@ -85,7 +81,7 @@ impl AABBMesh {
// draw Mesh // draw Mesh
gl::BindVertexArray(self.vao); gl::BindVertexArray(self.vao);
gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE); gl::PolygonMode(gl::FRONT_AND_BACK, gl::LINE);
gl::DrawElements(gl::TRIANGLES, self.indices.len() as i32, gl::UNSIGNED_INT, std::ptr::null()); gl::DrawElements(gl::TRIANGLES, self.indices_len as i32, gl::UNSIGNED_INT, std::ptr::null());
// reset stuff to default // reset stuff to default
gl::BindVertexArray(0); gl::BindVertexArray(0);
+31
View File
@@ -0,0 +1,31 @@
mod obj;
use std::path::PathBuf;
use crate::{mesh::Vertex, aabb::AABB};
#[derive(Debug)]
pub struct ObjMesh {
pub name: String,
pub vertices: Vec<Vertex>,
pub indices: Vec<u32>,
}
#[derive(Debug)]
pub struct Object {
pub name: String,
pub meshes: Vec<ObjMesh>,
pub aabb: AABB
}
pub fn load_from_file(path: &PathBuf) -> Result<Object, Box<dyn std::error::Error>> {
// FIXME: don't explode if to_str() fails
let path_str = path.to_str().unwrap();
let file = std::fs::File::open(path_str)?;
// TODO: support both stl and obj based on file extension
// if no extension, then test for binary STL magic bytes
// if no magic bytes, then try to guess based on the first line of text in the file
let obj = obj::load_obj(file)?;
Ok(obj)
}
+248
View File
@@ -0,0 +1,248 @@
use std::io::{BufReader, BufRead};
use crate::{mesh::Vertex, aabb::AABB, importer::ObjMesh, importer::Object};
const BUF_CAP: usize = 1024 * 128; // 128 Kilobytes
enum Token {
Object,
Group,
Vertex,
Normal,
TexCoord,
Face,
Point,
Line,
SmoothShading,
MaterialLib,
MaterialUsage,
}
impl Token {
fn from_str<'a>(s: &'a str) -> Option<Token> {
match s {
"o" => Some(Token::Object),
"g" => Some(Token::Group),
"v" => Some(Token::Vertex),
"vn" => Some(Token::Normal),
"vt" => Some(Token::TexCoord),
"f" => Some(Token::Face),
"p" => Some(Token::Point),
"l" => Some(Token::Line),
"s" => Some(Token::SmoothShading),
"mtllib" => Some(Token::MaterialLib),
"usemtl" => Some(Token::MaterialUsage),
_ => None,
}
}
}
pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
let now = std::time::Instant::now();
let reader = BufReader::with_capacity(BUF_CAP, file);
let mut object_name = String::new();
let mut current_mesh_name = String::new();
let mut temp_vertices = Vec::new();
let mut vertices = Vec::new();
let mut normals = Vec::new();
let mut indices_counter: u32 = 0;
let mut indices = Vec::new();
let mut tex_coords = Vec::new();
let mut meshes = Vec::new();
let mut min_aabb = glm::vec3(std::f32::MAX, std::f32::MAX, std::f32::MAX);
let mut max_aabb = glm::vec3(std::f32::MIN, std::f32::MIN, std::f32::MIN);
for line in reader.lines() {
let line = line?;
// skip empty lines and comments
if line.is_empty() || line.chars().nth(0).is_some_and(|c| c == '#') {
continue;
}
let mut iter = line.split_ascii_whitespace();
let first = iter.next();
if let Some(token) = first {
match Token::from_str(token) {
Some(Token::Object) => {
let name = {
if current_mesh_name.is_empty() {
object_name.clone()
} else {
current_mesh_name.clone()
}
};
if !vertices.is_empty() {
meshes.push(ObjMesh{
name,
vertices: vertices.clone(),
indices: indices.clone(),
});
}
vertices.clear();
indices.clear();
indices_counter = 0;
object_name = iter.next().unwrap_or("").to_string();
}
Some(Token::Group) => {
let name = {
if current_mesh_name.is_empty() {
object_name.clone()
} else {
current_mesh_name.clone()
}
};
if !vertices.is_empty() {
meshes.push(ObjMesh{
name,
vertices: vertices.clone(),
indices: indices.clone(),
});
}
vertices.clear();
indices.clear();
indices_counter = 0;
current_mesh_name = iter.next().unwrap_or("default_mesh").to_string();
}
Some(Token::Vertex) => {
let mut iter = iter
.take(3)
.map(|i| i.parse::<f32>().unwrap());
let x = iter.next().unwrap();
let y = iter.next().unwrap();
let z = iter.next().unwrap();
temp_vertices.push(glm::vec3(x, y, z));
if x < min_aabb.x {
min_aabb.x = x;
}
if y < min_aabb.y {
min_aabb.y = y;
}
if z < min_aabb.z {
min_aabb.z = z;
}
if x > max_aabb.x {
max_aabb.x = x;
}
if y > max_aabb.y {
max_aabb.y = y;
}
if z > max_aabb.z {
max_aabb.z = z;
}
}
Some(Token::Normal) => {
let mut iter = iter
.take(3)
.map(|i| i.parse::<f32>().unwrap());
let x = iter.next().unwrap();
let y = iter.next().unwrap();
let z = iter.next().unwrap();
normals.push(glm::vec3(x, y, z));
}
Some(Token::TexCoord) => {
let mut iter = iter
.take(2)
.map(|i| i.parse::<f32>().unwrap());
let u = iter.next().unwrap();
let v = iter.next().unwrap();
tex_coords.push(glm::vec2(u, v));
}
Some(Token::Face) => {
// TODO: vertex indices can be negative
let face = iter.collect::<Vec<_>>();
let mut calculated_normal = glm::vec3(0.0, 0.0, 0.0);
if normals.is_empty() {
calculated_normal = glm::normalize(glm::cross(
temp_vertices[face[1].parse::<i32>().unwrap() as usize - 1] - temp_vertices[face[0].parse::<i32>().unwrap() as usize - 1],
temp_vertices[face[2].parse::<i32>().unwrap() as usize - 1] - temp_vertices[face[0].parse::<i32>().unwrap() as usize - 1]
));
}
for (i, vert) in face.iter().enumerate() {
if vert.contains("//") {
let mut it = vert.split("//");
let vert = it.next().unwrap().parse::<i32>().unwrap() - 1;
let normal = it.next().unwrap().parse::<i32>().unwrap() - 1;
vertices.push(Vertex{
position: *temp_vertices.get(vert as usize).unwrap(),
normal: *normals.get(normal as usize).unwrap(),
tex_coords: glm::vec2(0.0, 0.0)
});
} else if vert.contains("/") {
let mut it = vert.split("/");
let vertex = it.next().unwrap().parse::<i32>().unwrap() - 1;
let t_coords = it.next().unwrap().parse::<i32>().unwrap() - 1;
let normal = it.next().unwrap().parse::<i32>().unwrap() - 1;
vertices.push(Vertex{
position: *temp_vertices.get(vertex as usize).unwrap(),
normal: *normals.get(normal as usize).unwrap(),
tex_coords: *tex_coords.get(t_coords as usize).unwrap()
});
} else {
let vert = vert.parse::<i32>().unwrap() - 1;
vertices.push(Vertex{
position: *temp_vertices.get(vert as usize).unwrap(),
normal: calculated_normal,
tex_coords: glm::vec2(0.0, 0.0),
});
}
// 2 triangles per face
if i < face.len() - 2 {
indices.push(indices_counter);
indices.push(indices_counter + i as u32 + 1);
indices.push(indices_counter + i as u32 + 2);
}
}
indices_counter += face.len() as u32;
}
Some(Token::MaterialLib) => {
// TODO: handle material file
}
Some(Token::MaterialUsage) => {
// TODO: handle material usage
// this can be used multiple times before f is mentioned
// to apply the material to those faces after it
}
_ => {},
}
}
}
let elapsed = now.elapsed();
println!("Loaded in {}ms", elapsed.as_millis());
let mesh_name = {
if current_mesh_name.is_empty() && !object_name.is_empty() {
object_name.clone()
} else if !current_mesh_name.is_empty() {
current_mesh_name.clone()
} else {
"default_mesh".to_string()
}
};
meshes.push(ObjMesh{
name: mesh_name,
vertices: vertices.clone(),
indices: indices.clone(),
});
let aabb = AABB::new(min_aabb, max_aabb);
Ok(Object{
name: object_name,
meshes,
aabb
})
}
+27
View File
@@ -0,0 +1,27 @@
struct STLHeader {
header: [u8; 80],
num_triangles: u32,
}
struct Vertex {
x: f32,
y: f32,
z: f32,
}
struct Triangle {
normal: [f32; 3],
vertices: [Vertex; 3],
attribute_byte_count: u16,
}
struct STL {
header: STLHeader,
triangles: Vec<Triangle>,
}
impl STL {
pub fn new(stl: Vec<u8>) -> () {
// TODO:
}
}
+1
View File
@@ -10,3 +10,4 @@ pub mod model;
pub mod ui; pub mod ui;
pub mod log; pub mod log;
pub mod aabb; pub mod aabb;
pub mod importer;
+9 -9
View File
@@ -80,7 +80,7 @@ pub struct Mesh {
pub vertices: Vec<Vertex>, pub vertices: Vec<Vertex>,
pub indices: Vec<u32>, pub indices: Vec<u32>,
pub textures: Vec<Texture>, // pub textures: Vec<Texture>,
pub material: Material, pub material: Material,
vao: u32, vao: u32,
@@ -89,7 +89,7 @@ pub struct Mesh {
} }
impl Mesh { impl Mesh {
pub fn new(name: &str, vertices: Vec<Vertex>, indices: Vec<u32>, textures: Vec<Texture>, material: Material, transformation: &mut glm::Mat4) -> Mesh { pub fn new(name: &str, vertices: Vec<Vertex>, indices: Vec<u32>) -> Mesh {
let mut vao = 0; let mut vao = 0;
let mut vbo = 0; let mut vbo = 0;
let mut ebo = 0; let mut ebo = 0;
@@ -122,21 +122,21 @@ impl Mesh {
gl::BindVertexArray(0); gl::BindVertexArray(0);
} }
let (position, rotation, scale) = decompose_mat(transformation); // let (position, rotation, scale) = decompose_mat(transformation);
Mesh { Mesh {
name: name.to_string(), name: name.to_string(),
vertices, vertices,
indices, indices,
textures, // textures,
material, material: Material::new("default".to_string(), glm::vec3(0.4, 0.4, 0.4), glm::vec3(0.7, 0.7, 0.7), glm::vec3(0.1, 0.1, 0.1), 32.0),
vbo, vbo,
vao, vao,
ebo, ebo,
position, position: glm::vec3(0.0, 0.0, 0.0),
rotation, rotation: glm::vec3(0.0, 0.0, 0.0),
scale, scale: glm::vec3(1.0, 1.0, 1.0),
pivot: position, pivot: glm::vec3(0.0, 0.0, 0.0),
} }
} }
+241 -232
View File
@@ -1,4 +1,6 @@
use crate::{mesh::{Mesh, Vertex, Texture, Material, apply_rotation}, shader::Shader, utils, ui::ui, log, aabb}; use std::path::PathBuf;
use crate::{mesh::{Mesh, Vertex, Texture, Material, apply_rotation}, shader::Shader, utils, ui::ui, log, aabb, importer};
use russimp; use russimp;
use anyhow::{Result, anyhow}; use anyhow::{Result, anyhow};
@@ -16,284 +18,291 @@ pub struct Model {
pub name: String, pub name: String,
pub meshes: Vec<Mesh>, pub meshes: Vec<Mesh>,
pub aabb: aabb::AABB, pub aabb: aabb::AABB,
pub aabb_mesh: aabb::AABBMesh,
pub scaling_factor: f32, pub scaling_factor: f32,
} }
fn process_node<'a>( // fn process_node<'a>(
node: &russimp::node::Node, // node: &russimp::node::Node,
scene: &'a russimp::scene::Scene, // scene: &'a russimp::scene::Scene,
meshes: &mut Vec<Mesh>, // meshes: &mut Vec<Mesh>,
dir: &std::path::PathBuf, // dir: &std::path::PathBuf,
loaded_textures: &mut Vec<Texture>, // loaded_textures: &mut Vec<Texture>,
init_trans: &glm::Mat4, // init_trans: &glm::Mat4,
aabb: &mut aabb::AABB, // aabb: &mut aabb::AABB,
scale: &mut f32, // scale: &mut f32,
) -> Vec<Box<dyn std::error::Error>> { // ) -> Vec<Box<dyn std::error::Error>> {
let mut errors = vec![]; // let mut errors = vec![];
let node_trans = glm::mat4( // let node_trans = glm::mat4(
node.transformation.a1, node.transformation.a2, node.transformation.a3, node.transformation.a4, // node.transformation.a1, node.transformation.a2, node.transformation.a3, node.transformation.a4,
node.transformation.b1, node.transformation.b2, node.transformation.b3, node.transformation.b4, // node.transformation.b1, node.transformation.b2, node.transformation.b3, node.transformation.b4,
node.transformation.c1, node.transformation.c2, node.transformation.c3, node.transformation.c4, // node.transformation.c1, node.transformation.c2, node.transformation.c3, node.transformation.c4,
node.transformation.d1, node.transformation.d2, node.transformation.d3, node.transformation.d4, // node.transformation.d1, node.transformation.d2, node.transformation.d3, node.transformation.d4,
); // );
let mut new_trans = *init_trans * node_trans; // let mut new_trans = *init_trans * node_trans;
for i in 0..node.meshes.len() { // for i in 0..node.meshes.len() {
let mesh = &scene.meshes[node.meshes[i] as usize]; // let mesh = &scene.meshes[node.meshes[i] as usize];
let (processed_mesh, mut errs) = process_mesh(mesh, scene, dir, loaded_textures, &mut new_trans, aabb); // let (processed_mesh, mut errs) = process_mesh(mesh, scene, dir, loaded_textures, &mut new_trans, aabb);
errors.append(&mut errs); // errors.append(&mut errs);
meshes.push(processed_mesh); // meshes.push(processed_mesh);
} // }
for child in node.children.borrow().clone().into_iter() { // for child in node.children.borrow().clone().into_iter() {
let mut errs = process_node(&child, scene, meshes, dir, loaded_textures, &new_trans, aabb, scale); // let mut errs = process_node(&child, scene, meshes, dir, loaded_textures, &new_trans, aabb, scale);
errors.append(&mut errs); // errors.append(&mut errs);
} // }
let scale_factor_x = SCALING_FACTOR / (aabb.max.x - aabb.min.x); // let scale_factor_x = SCALING_FACTOR / (aabb.max.x - aabb.min.x);
let scale_factor_y = SCALING_FACTOR / (aabb.max.y - aabb.min.y); // let scale_factor_y = SCALING_FACTOR / (aabb.max.y - aabb.min.y);
let scale_factor_z = SCALING_FACTOR / (aabb.max.z - aabb.min.z); // let scale_factor_z = SCALING_FACTOR / (aabb.max.z - aabb.min.z);
// Use the minimum scaling factor to maintain proportions // // Use the minimum scaling factor to maintain proportions
*scale = scale_factor_x.min(scale_factor_y).min(scale_factor_z); // *scale = scale_factor_x.min(scale_factor_y).min(scale_factor_z);
return errors; // return errors;
} // }
fn process_mesh( // fn process_mesh(
mesh: &russimp::mesh::Mesh, // mesh: &russimp::mesh::Mesh,
scene: &russimp::scene::Scene, // scene: &russimp::scene::Scene,
dir: &std::path::PathBuf, // dir: &std::path::PathBuf,
loaded_textures: &mut Vec<Texture>, // loaded_textures: &mut Vec<Texture>,
transformation: &mut glm::Mat4, // transformation: &mut glm::Mat4,
aabb: &mut aabb::AABB // aabb: &mut aabb::AABB
) -> (Mesh, Vec<Box<dyn std::error::Error>>) { // ) -> (Mesh, Vec<Box<dyn std::error::Error>>) {
let mut vertices = vec![]; // let mut vertices = vec![];
let mut indices = vec![]; // let mut indices = vec![];
let mut textures = vec![]; // let mut textures = vec![];
for i in 0..mesh.vertices.len() { // for i in 0..mesh.vertices.len() {
let pos = glm::vec4(mesh.vertices[i].x, mesh.vertices[i].y, mesh.vertices[i].z, 1.0); // let pos = glm::vec4(mesh.vertices[i].x, mesh.vertices[i].y, mesh.vertices[i].z, 1.0);
let norm = match mesh.normals.len() { // let norm = match mesh.normals.len() {
0 => glm::vec3(0.0, 0.0, 0.0), // 0 => glm::vec3(0.0, 0.0, 0.0),
_ => glm::vec3(mesh.normals[i].x, mesh.normals[i].y, mesh.normals[i].z), // _ => glm::vec3(mesh.normals[i].x, mesh.normals[i].y, mesh.normals[i].z),
}; // };
let t = &mesh.texture_coords[0]; // let t = &mesh.texture_coords[0];
let tex_coords = match t { // let tex_coords = match t {
Some(tex) => { // Some(tex) => {
glm::vec2(tex[i].x, tex[i].y) // glm::vec2(tex[i].x, tex[i].y)
}, // },
None => glm::vec2(0.0, 0.0) // None => glm::vec2(0.0, 0.0)
}; // };
let vertex = Vertex::new(pos.truncate(3), norm, tex_coords); // let vertex = Vertex::new(pos.truncate(3), norm, tex_coords);
if vertex.position.x < aabb.min.x { // if vertex.position.x < aabb.min.x {
aabb.min.x = vertex.position.x; // aabb.min.x = vertex.position.x;
} // }
if vertex.position.y < aabb.min.y { // if vertex.position.y < aabb.min.y {
aabb.min.y = vertex.position.y; // aabb.min.y = vertex.position.y;
} // }
if vertex.position.z < aabb.min.z { // if vertex.position.z < aabb.min.z {
aabb.min.z = vertex.position.z; // aabb.min.z = vertex.position.z;
} // }
if vertex.position.x > aabb.max.x { // if vertex.position.x > aabb.max.x {
aabb.max.x = vertex.position.x; // aabb.max.x = vertex.position.x;
} // }
if vertex.position.y > aabb.max.y { // if vertex.position.y > aabb.max.y {
aabb.max.y = vertex.position.y; // aabb.max.y = vertex.position.y;
} // }
if vertex.position.z > aabb.max.z { // if vertex.position.z > aabb.max.z {
aabb.max.z = vertex.position.z; // aabb.max.z = vertex.position.z;
} // }
vertices.push(vertex); // vertices.push(vertex);
} // }
for i in 0..mesh.faces.len() { // for i in 0..mesh.faces.len() {
for j in 0..mesh.faces[i].0.len() { // for j in 0..mesh.faces[i].0.len() {
indices.push(mesh.faces[i].0[j]); // indices.push(mesh.faces[i].0[j]);
} // }
} // }
let mat = &scene.materials[mesh.material_index as usize]; // let mat = &scene.materials[mesh.material_index as usize];
let material = process_material(mat); // let material = process_material(mat);
let (mut found_textures, errs) = load_material_textures(mat, dir, loaded_textures); // let (mut found_textures, errs) = load_material_textures(mat, dir, loaded_textures);
textures.append(&mut found_textures); // textures.append(&mut found_textures);
let mesh = Mesh::new(mesh.name.as_str(), vertices, indices, textures, material, transformation); // let mesh = Mesh::new(mesh.name.as_str(), vertices, indices, textures, material, transformation);
return (mesh, errs); // return (mesh, errs);
} // }
fn process_material(mat: &russimp::material::Material) -> Material { // fn process_material(mat: &russimp::material::Material) -> Material {
let mat_name = String::from("Default_Mat"); // let mat_name = String::from("Default_Mat");
let ambient = glm::vec3(0.2, 0.2, 0.2); // let ambient = glm::vec3(0.2, 0.2, 0.2);
let diffuse = glm::vec3(0.7, 0.7, 0.7); // let diffuse = glm::vec3(0.7, 0.7, 0.7);
let specular = glm::vec3(0.1, 0.1, 0.1); // let specular = glm::vec3(0.1, 0.1, 0.1);
let shininess = 32.0; // let shininess = 32.0;
// TODO: better way of mapping properties // // TODO: better way of mapping properties
// for property in mat.properties.iter() { // // for property in mat.properties.iter() {
// match property.key.as_str() { // // match property.key.as_str() {
// "$clr.ambient" => { // // "$clr.ambient" => {
// ambient = match &property.data { // // ambient = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => { // // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// glm::vec3(a[0], a[1], a[2]) // // glm::vec3(a[0], a[1], a[2])
// }, // // },
// _ => panic!("Property should not be this type: {}", property.key) // // _ => panic!("Property should not be this type: {}", property.key)
// }; // // };
// }, // // },
// "$clr.diffuse" => { // // "$clr.diffuse" => {
// diffuse = match &property.data { // // diffuse = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => { // // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// glm::vec3(a[0], a[1], a[2]) // // glm::vec3(a[0], a[1], a[2])
// }, // // },
// _ => panic!("Property should not be this type: {}", property.key) // // _ => panic!("Property should not be this type: {}", property.key)
// }; // // };
// }, // // },
// "$clr.specular" => { // // "$clr.specular" => {
// specular = match &property.data { // // specular = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => { // // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// glm::vec3(a[0], a[1], a[2]) // // glm::vec3(a[0], a[1], a[2])
// }, // // },
// _ => panic!("Property should not be this type: {}", property.key) // // _ => panic!("Property should not be this type: {}", property.key)
// } // // }
// } // // }
// "?mat.name" => { // // "?mat.name" => {
// mat_name = match &property.data { // // mat_name = match &property.data {
// russimp::material::PropertyTypeInfo::String(s) => { // // russimp::material::PropertyTypeInfo::String(s) => {
// s.to_string() // // s.to_string()
// }, // // },
// _ => panic!("Property should not be this type: {}", property.key) // // _ => panic!("Property should not be this type: {}", property.key)
// }; // // };
// } // // }
// "$mat.shininess" => { // // "$mat.shininess" => {
// shininess = match &property.data { // // shininess = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => { // // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// a[0] // // a[0]
// }, // // },
// _ => panic!("Property should not be this type: {}", property.key) // // _ => panic!("Property should not be this type: {}", property.key)
// }; // // };
// } // // }
// _ => {}, // // _ => {},
// } // // }
// } // // }
Material::new(mat_name, ambient, diffuse, specular, shininess) // Material::new(mat_name, ambient, diffuse, specular, shininess)
} // }
// TODO: might nuke this cuz we don't need textures I think // // TODO: might nuke this cuz we don't need textures I think
fn load_material_textures( // fn load_material_textures(
mat: &russimp::material::Material, // mat: &russimp::material::Material,
dir: &std::path::PathBuf, // dir: &std::path::PathBuf,
loaded_textures: &mut Vec<Texture> // loaded_textures: &mut Vec<Texture>
) -> (Vec<Texture>, Vec<Box<dyn std::error::Error>>) { // ) -> (Vec<Texture>, Vec<Box<dyn std::error::Error>>) {
let mut textures = vec![]; // let mut textures = vec![];
let mut errors = vec![]; // let mut errors = vec![];
for (typ, tex) in mat.textures.iter() { // for (typ, tex) in mat.textures.iter() {
if SUPPORTED_TEXTURE_TYPES.contains(typ) { // if SUPPORTED_TEXTURE_TYPES.contains(typ) {
let texture = tex.borrow(); // let texture = tex.borrow();
let mut skip = false; // let mut skip = false;
let tex_filename = &texture.filename; // let tex_filename = &texture.filename;
// HACK: fix this // // HACK: fix this
if tex_filename.is_empty() { // if tex_filename.is_empty() {
continue; // continue;
} // }
// println!("texture filename: {}", tex_filename); // // println!("texture filename: {}", tex_filename);
let path = dir.join(tex_filename); // let path = dir.join(tex_filename);
for loaded_tex in &mut *loaded_textures { // for loaded_tex in &mut *loaded_textures {
if loaded_tex.path == path { // if loaded_tex.path == path {
textures.push(loaded_tex.clone()); // textures.push(loaded_tex.clone());
skip = true; // skip = true;
break; // break;
} // }
} // }
if !skip { // if !skip {
match Texture::new(path, *typ) { // match Texture::new(path, *typ) {
Ok(texture) => { // Ok(texture) => {
loaded_textures.push(texture.clone()); // loaded_textures.push(texture.clone());
textures.push(texture); // textures.push(texture);
}, // },
Err(e) => { // Err(e) => {
let err = anyhow!("Error loading texture: {}", e); // let err = anyhow!("Error loading texture: {}", e);
println!("{}", err); // println!("{}", err);
errors.push(err.into()); // errors.push(err.into());
}, // },
} // }
} // }
} // }
} // }
return (textures, errors); // return (textures, errors);
} // }
impl Model { impl Model {
pub fn new(path: &str, state: &mut ui::State) -> Result<Self, Box<dyn std::error::Error>> { pub fn new(obj: importer::Object, state: &mut ui::State) -> Model {
let scene = russimp::scene::Scene::from_file(path, // let scene = russimp::scene::Scene::from_file(path,
vec![ // vec![
russimp::scene::PostProcess::Triangulate, // russimp::scene::PostProcess::Triangulate,
russimp::scene::PostProcess::GenerateNormals, // russimp::scene::PostProcess::GenerateNormals,
russimp::scene::PostProcess::FlipUVs, // russimp::scene::PostProcess::FlipUVs,
]) // ])
.map_err(|e| { // .map_err(|e| {
let e = match e { // let e = match e {
russimp::RussimpError::TextureNotFound => anyhow!("Texture not found"), // russimp::RussimpError::TextureNotFound => anyhow!("Texture not found"),
_ => anyhow!("{}", e) // _ => anyhow!("{}", e)
}; // };
return e; // return e;
})?; // })?;
let root_node = match &scene.root { // let root_node = match &scene.root {
Some(root) => root, // Some(root) => root,
None => return Err("Scene has no root node".into()), // None => return Err("Scene has no root node".into()),
}; // };
if scene.flags & russimp::sys::AI_SCENE_FLAGS_INCOMPLETE == 1 { // if scene.flags & russimp::sys::AI_SCENE_FLAGS_INCOMPLETE == 1 {
return Err("Scene is incomplete")?; // return Err("Scene is incomplete")?;
} // }
let directory = match std::path::Path::new(path).parent() { // let directory = match std::path::Path::new(path).parent() {
Some(dir) => dir, // Some(dir) => dir,
None => return Err("Model path has no parent directory".into()), // None => return Err("Model path has no parent directory".into()),
}.to_path_buf(); // }.to_path_buf();
let mut loaded_textures = vec![]; // let mut loaded_textures = vec![];
let mut meshes = vec![]; // let mut meshes = vec![];
let init_trans_mat = utils::mat_ident(); // let init_trans_mat = utils::mat_ident();
let mut aabb = aabb::AABB{
min: glm::vec3(std::f32::MAX, std::f32::MAX, std::f32::MAX),
max: glm::vec3(std::f32::MIN, std::f32::MIN, std::f32::MIN),
};
let mut scale: f32 = 1.0; let mut scale: f32 = 1.0;
let errors = process_node(&root_node, &scene, &mut meshes, &directory, &mut loaded_textures, &init_trans_mat, &mut aabb, &mut scale); // let errors = process_node(&root_node, &scene, &mut meshes, &directory, &mut loaded_textures, &init_trans_mat, &mut aabb, &mut scale);
for err in errors { // for err in errors {
state.logger.history.push(log::LogMessage::new(log::LogLevel::Warning, &err.to_string())); // state.logger.history.push(log::LogMessage::new(log::LogLevel::Warning, &err.to_string()));
// }
let mut meshes = Vec::new();
for mesh in obj.meshes.into_iter() {
meshes.push(Mesh::new(&mesh.name, mesh.vertices, mesh.indices));
} }
Ok(Model { Model {
id: state.get_next_id(), id: state.get_next_id(),
name: root_node.name.to_owned(), name: obj.name.to_owned(),
aabb_mesh: aabb::AABBMesh::new(&aabb), aabb: obj.aabb,
aabb,
scaling_factor: scale, scaling_factor: scale,
meshes, meshes,
}) }
// Ok(Model {
// id: state.get_next_id(),
// name: root_node.name.to_owned(),
// aabb_mesh: aabb::AABBMesh::new(&aabb),
// aabb,
// scaling_factor: scale,
// meshes,
// })
} }
pub fn draw(&self, shader: &Shader, draw_aabb: bool) { pub fn draw(&self, shader: &Shader, draw_aabb: bool) {
@@ -312,7 +321,7 @@ impl Model {
} }
if draw_aabb { if draw_aabb {
self.aabb_mesh.draw(shader, &model_mat); self.aabb.draw(shader, &model_mat);
} }
} }
+1 -2
View File
@@ -154,8 +154,7 @@ fn draw_transformations(ui: &imgui::Ui, mesh: &mut mesh::Mesh) {
fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) { fn draw_mesh_hierarchy(ui: &imgui::Ui, mesh: &mut mesh::Mesh, i: usize) {
ui.tree_node_config(format!("{}###{}", mesh.name.as_str(), i)).build(|| { ui.tree_node_config(format!("{}###{}", mesh.name.as_str(), i)).build(|| {
ui.text(format!("Vertices: {}", mesh.vertices.len())); ui.text(format!("Vertices: {}", mesh.vertices.len()));
ui.text(format!("Faces: {}", mesh.vertices.len() / 3)); ui.text(format!("Triangles: {}", mesh.indices.len() / 3));
ui.text(format!("Textures: {}", mesh.textures.len()));
ui.tree_node_config(mesh.material.name.as_str()).build(|| { ui.tree_node_config(mesh.material.name.as_str()).build(|| {
ui.text(format!("{}", mesh.material)); ui.text(format!("{}", mesh.material));
}); });
+7 -5
View File
@@ -4,7 +4,7 @@ use glad_gl::gl;
use glm; use glm;
use anyhow::{Result, Context}; use anyhow::{Result, Context};
use crate::{ui, log, model}; use crate::{log, model, ui, importer};
const SUPPORTED_FILE_EXTENSIONS: [&str; 2] = ["obj", "stl"]; const SUPPORTED_FILE_EXTENSIONS: [&str; 2] = ["obj", "stl"];
@@ -62,9 +62,11 @@ pub fn import_models_from_paths(paths: &Vec<PathBuf>, state: &mut ui::ui::State)
continue; continue;
} }
} }
let model = model::Model::new(model_path.to_str().unwrap(), state); let obj_result = importer::load_from_file(model_path);
match model { match obj_result {
Ok(mut m) => { Ok(obj) => {
let mut m = model::Model::new(obj, state);
state.active_model = Some(m.id); state.active_model = Some(m.id);
if let Some(model_name) = filename { if let Some(model_name) = filename {
state.logger.log(&format!("Loaded model \"{}\"", model_name.to_str().unwrap()), log::LogLevel::Info); state.logger.log(&format!("Loaded model \"{}\"", model_name.to_str().unwrap()), log::LogLevel::Info);
@@ -78,7 +80,7 @@ pub fn import_models_from_paths(paths: &Vec<PathBuf>, state: &mut ui::ui::State)
println!("{}", error); println!("{}", error);
state.logger.log(&error, log::LogLevel::Error); state.logger.log(&error, log::LogLevel::Error);
}, }
} }
} }
} }