WIP: obj loader mostly done

This commit is contained in:
2023-10-19 02:04:42 +02:00
parent 8b5143d6cb
commit 6c2c8665c3
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 min: glm::Vec3,
pub max: glm::Vec3,
indices_len: u32,
vao: u32,
}
#[derive(Debug)]
pub struct AABBMesh {
pub vertices: Vec<Vertex>,
pub indices: Vec<u32>,
pub vao: u32,
}
impl AABBMesh {
pub fn new(aabb: &AABB) -> AABBMesh {
impl AABB {
pub fn new(min: glm::Vec3, max: glm::Vec3) -> AABB {
let mut vao = 0;
let mut vbo = 0;
let mut ebo = 0;
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(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(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(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(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(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(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(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, 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(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(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(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(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(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(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(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![
@@ -69,9 +64,10 @@ impl AABBMesh {
gl::BindVertexArray(0);
}
AABBMesh {
vertices,
indices,
AABB {
min,
max,
indices_len: indices.len() as u32,
vao,
}
}
@@ -85,7 +81,7 @@ impl AABBMesh {
// draw Mesh
gl::BindVertexArray(self.vao);
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
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 log;
pub mod aabb;
pub mod importer;
+9 -9
View File
@@ -80,7 +80,7 @@ pub struct Mesh {
pub vertices: Vec<Vertex>,
pub indices: Vec<u32>,
pub textures: Vec<Texture>,
// pub textures: Vec<Texture>,
pub material: Material,
vao: u32,
@@ -89,7 +89,7 @@ pub struct 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 vbo = 0;
let mut ebo = 0;
@@ -122,21 +122,21 @@ impl Mesh {
gl::BindVertexArray(0);
}
let (position, rotation, scale) = decompose_mat(transformation);
// let (position, rotation, scale) = decompose_mat(transformation);
Mesh {
name: name.to_string(),
vertices,
indices,
textures,
material,
// textures,
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,
vao,
ebo,
position,
rotation,
scale,
pivot: position,
position: glm::vec3(0.0, 0.0, 0.0),
rotation: glm::vec3(0.0, 0.0, 0.0),
scale: glm::vec3(1.0, 1.0, 1.0),
pivot: glm::vec3(0.0, 0.0, 0.0),
}
}
+254 -245
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 anyhow::{Result, anyhow};
@@ -16,284 +18,291 @@ pub struct Model {
pub name: String,
pub meshes: Vec<Mesh>,
pub aabb: aabb::AABB,
pub aabb_mesh: aabb::AABBMesh,
pub scaling_factor: f32,
}
fn process_node<'a>(
node: &russimp::node::Node,
scene: &'a russimp::scene::Scene,
meshes: &mut Vec<Mesh>,
dir: &std::path::PathBuf,
loaded_textures: &mut Vec<Texture>,
init_trans: &glm::Mat4,
aabb: &mut aabb::AABB,
scale: &mut f32,
) -> Vec<Box<dyn std::error::Error>> {
let mut errors = vec![];
// fn process_node<'a>(
// node: &russimp::node::Node,
// scene: &'a russimp::scene::Scene,
// meshes: &mut Vec<Mesh>,
// dir: &std::path::PathBuf,
// loaded_textures: &mut Vec<Texture>,
// init_trans: &glm::Mat4,
// aabb: &mut aabb::AABB,
// scale: &mut f32,
// ) -> Vec<Box<dyn std::error::Error>> {
// let mut errors = vec![];
let node_trans = glm::mat4(
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.c1, node.transformation.c2, node.transformation.c3, node.transformation.c4,
node.transformation.d1, node.transformation.d2, node.transformation.d3, node.transformation.d4,
);
let mut new_trans = *init_trans * node_trans;
// let node_trans = glm::mat4(
// 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.c1, node.transformation.c2, node.transformation.c3, node.transformation.c4,
// node.transformation.d1, node.transformation.d2, node.transformation.d3, node.transformation.d4,
// );
// let mut new_trans = *init_trans * node_trans;
for i in 0..node.meshes.len() {
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);
errors.append(&mut errs);
meshes.push(processed_mesh);
}
// for i in 0..node.meshes.len() {
// 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);
// errors.append(&mut errs);
// meshes.push(processed_mesh);
// }
for child in node.children.borrow().clone().into_iter() {
let mut errs = process_node(&child, scene, meshes, dir, loaded_textures, &new_trans, aabb, scale);
errors.append(&mut errs);
}
// for child in node.children.borrow().clone().into_iter() {
// let mut errs = process_node(&child, scene, meshes, dir, loaded_textures, &new_trans, aabb, scale);
// errors.append(&mut errs);
// }
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_z = SCALING_FACTOR / (aabb.max.z - aabb.min.z);
// 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_z = SCALING_FACTOR / (aabb.max.z - aabb.min.z);
// Use the minimum scaling factor to maintain proportions
*scale = scale_factor_x.min(scale_factor_y).min(scale_factor_z);
// // Use the minimum scaling factor to maintain proportions
// *scale = scale_factor_x.min(scale_factor_y).min(scale_factor_z);
return errors;
}
// return errors;
// }
fn process_mesh(
mesh: &russimp::mesh::Mesh,
scene: &russimp::scene::Scene,
dir: &std::path::PathBuf,
loaded_textures: &mut Vec<Texture>,
transformation: &mut glm::Mat4,
aabb: &mut aabb::AABB
) -> (Mesh, Vec<Box<dyn std::error::Error>>) {
let mut vertices = vec![];
let mut indices = vec![];
let mut textures = vec![];
// fn process_mesh(
// mesh: &russimp::mesh::Mesh,
// scene: &russimp::scene::Scene,
// dir: &std::path::PathBuf,
// loaded_textures: &mut Vec<Texture>,
// transformation: &mut glm::Mat4,
// aabb: &mut aabb::AABB
// ) -> (Mesh, Vec<Box<dyn std::error::Error>>) {
// let mut vertices = vec![];
// let mut indices = vec![];
// let mut textures = vec![];
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);
// 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 norm = match mesh.normals.len() {
0 => glm::vec3(0.0, 0.0, 0.0),
_ => glm::vec3(mesh.normals[i].x, mesh.normals[i].y, mesh.normals[i].z),
};
let t = &mesh.texture_coords[0];
let tex_coords = match t {
Some(tex) => {
glm::vec2(tex[i].x, tex[i].y)
},
None => glm::vec2(0.0, 0.0)
};
let vertex = Vertex::new(pos.truncate(3), norm, tex_coords);
if vertex.position.x < aabb.min.x {
aabb.min.x = vertex.position.x;
}
if vertex.position.y < aabb.min.y {
aabb.min.y = vertex.position.y;
}
if vertex.position.z < aabb.min.z {
aabb.min.z = vertex.position.z;
}
if vertex.position.x > aabb.max.x {
aabb.max.x = vertex.position.x;
}
if vertex.position.y > aabb.max.y {
aabb.max.y = vertex.position.y;
}
if vertex.position.z > aabb.max.z {
aabb.max.z = vertex.position.z;
}
vertices.push(vertex);
}
for i in 0..mesh.faces.len() {
for j in 0..mesh.faces[i].0.len() {
indices.push(mesh.faces[i].0[j]);
}
}
let mat = &scene.materials[mesh.material_index as usize];
let material = process_material(mat);
let (mut found_textures, errs) = load_material_textures(mat, dir, loaded_textures);
textures.append(&mut found_textures);
let mesh = Mesh::new(mesh.name.as_str(), vertices, indices, textures, material, transformation);
return (mesh, errs);
}
fn process_material(mat: &russimp::material::Material) -> Material {
let mat_name = String::from("Default_Mat");
let ambient = glm::vec3(0.2, 0.2, 0.2);
let diffuse = glm::vec3(0.7, 0.7, 0.7);
let specular = glm::vec3(0.1, 0.1, 0.1);
let shininess = 32.0;
// TODO: better way of mapping properties
// for property in mat.properties.iter() {
// match property.key.as_str() {
// "$clr.ambient" => {
// ambient = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => {
// glm::vec3(a[0], a[1], a[2])
// },
// _ => panic!("Property should not be this type: {}", property.key)
// let norm = match mesh.normals.len() {
// 0 => glm::vec3(0.0, 0.0, 0.0),
// _ => glm::vec3(mesh.normals[i].x, mesh.normals[i].y, mesh.normals[i].z),
// };
// let t = &mesh.texture_coords[0];
// let tex_coords = match t {
// Some(tex) => {
// glm::vec2(tex[i].x, tex[i].y)
// },
// "$clr.diffuse" => {
// diffuse = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => {
// glm::vec3(a[0], a[1], a[2])
// },
// _ => panic!("Property should not be this type: {}", property.key)
// None => glm::vec2(0.0, 0.0)
// };
// let vertex = Vertex::new(pos.truncate(3), norm, tex_coords);
// if vertex.position.x < aabb.min.x {
// aabb.min.x = vertex.position.x;
// }
// if vertex.position.y < aabb.min.y {
// aabb.min.y = vertex.position.y;
// }
// if vertex.position.z < aabb.min.z {
// aabb.min.z = vertex.position.z;
// }
// if vertex.position.x > aabb.max.x {
// aabb.max.x = vertex.position.x;
// }
// if vertex.position.y > aabb.max.y {
// aabb.max.y = vertex.position.y;
// }
// if vertex.position.z > aabb.max.z {
// aabb.max.z = vertex.position.z;
// }
// vertices.push(vertex);
// }
// for i in 0..mesh.faces.len() {
// for j in 0..mesh.faces[i].0.len() {
// indices.push(mesh.faces[i].0[j]);
// }
// }
// let mat = &scene.materials[mesh.material_index as usize];
// let material = process_material(mat);
// let (mut found_textures, errs) = load_material_textures(mat, dir, loaded_textures);
// textures.append(&mut found_textures);
// let mesh = Mesh::new(mesh.name.as_str(), vertices, indices, textures, material, transformation);
// return (mesh, errs);
// }
// fn process_material(mat: &russimp::material::Material) -> Material {
// let mat_name = String::from("Default_Mat");
// let ambient = glm::vec3(0.2, 0.2, 0.2);
// let diffuse = glm::vec3(0.7, 0.7, 0.7);
// let specular = glm::vec3(0.1, 0.1, 0.1);
// let shininess = 32.0;
// // TODO: better way of mapping properties
// // for property in mat.properties.iter() {
// // match property.key.as_str() {
// // "$clr.ambient" => {
// // ambient = match &property.data {
// // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// // glm::vec3(a[0], a[1], a[2])
// // },
// // _ => panic!("Property should not be this type: {}", property.key)
// // };
// // },
// // "$clr.diffuse" => {
// // diffuse = match &property.data {
// // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// // glm::vec3(a[0], a[1], a[2])
// // },
// // _ => panic!("Property should not be this type: {}", property.key)
// // };
// // },
// // "$clr.specular" => {
// // specular = match &property.data {
// // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// // glm::vec3(a[0], a[1], a[2])
// // },
// // _ => panic!("Property should not be this type: {}", property.key)
// // }
// // }
// // "?mat.name" => {
// // mat_name = match &property.data {
// // russimp::material::PropertyTypeInfo::String(s) => {
// // s.to_string()
// // },
// // _ => panic!("Property should not be this type: {}", property.key)
// // };
// // }
// // "$mat.shininess" => {
// // shininess = match &property.data {
// // russimp::material::PropertyTypeInfo::FloatArray(a) => {
// // a[0]
// // },
// // _ => panic!("Property should not be this type: {}", property.key)
// // };
// // }
// // _ => {},
// // }
// // }
// Material::new(mat_name, ambient, diffuse, specular, shininess)
// }
// // TODO: might nuke this cuz we don't need textures I think
// fn load_material_textures(
// mat: &russimp::material::Material,
// dir: &std::path::PathBuf,
// loaded_textures: &mut Vec<Texture>
// ) -> (Vec<Texture>, Vec<Box<dyn std::error::Error>>) {
// let mut textures = vec![];
// let mut errors = vec![];
// for (typ, tex) in mat.textures.iter() {
// if SUPPORTED_TEXTURE_TYPES.contains(typ) {
// let texture = tex.borrow();
// let mut skip = false;
// let tex_filename = &texture.filename;
// // HACK: fix this
// if tex_filename.is_empty() {
// continue;
// }
// // println!("texture filename: {}", tex_filename);
// let path = dir.join(tex_filename);
// for loaded_tex in &mut *loaded_textures {
// if loaded_tex.path == path {
// textures.push(loaded_tex.clone());
// skip = true;
// break;
// }
// }
// if !skip {
// match Texture::new(path, *typ) {
// Ok(texture) => {
// loaded_textures.push(texture.clone());
// textures.push(texture);
// },
// "$clr.specular" => {
// specular = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => {
// glm::vec3(a[0], a[1], a[2])
// Err(e) => {
// let err = anyhow!("Error loading texture: {}", e);
// println!("{}", err);
// errors.push(err.into());
// },
// _ => panic!("Property should not be this type: {}", property.key)
// }
// }
// "?mat.name" => {
// mat_name = match &property.data {
// russimp::material::PropertyTypeInfo::String(s) => {
// s.to_string()
// },
// _ => panic!("Property should not be this type: {}", property.key)
// };
// }
// "$mat.shininess" => {
// shininess = match &property.data {
// russimp::material::PropertyTypeInfo::FloatArray(a) => {
// a[0]
// },
// _ => panic!("Property should not be this type: {}", property.key)
// };
// }
// _ => {},
// }
// }
Material::new(mat_name, ambient, diffuse, specular, shininess)
}
// TODO: might nuke this cuz we don't need textures I think
fn load_material_textures(
mat: &russimp::material::Material,
dir: &std::path::PathBuf,
loaded_textures: &mut Vec<Texture>
) -> (Vec<Texture>, Vec<Box<dyn std::error::Error>>) {
let mut textures = vec![];
let mut errors = vec![];
for (typ, tex) in mat.textures.iter() {
if SUPPORTED_TEXTURE_TYPES.contains(typ) {
let texture = tex.borrow();
let mut skip = false;
let tex_filename = &texture.filename;
// HACK: fix this
if tex_filename.is_empty() {
continue;
}
// println!("texture filename: {}", tex_filename);
let path = dir.join(tex_filename);
for loaded_tex in &mut *loaded_textures {
if loaded_tex.path == path {
textures.push(loaded_tex.clone());
skip = true;
break;
}
}
if !skip {
match Texture::new(path, *typ) {
Ok(texture) => {
loaded_textures.push(texture.clone());
textures.push(texture);
},
Err(e) => {
let err = anyhow!("Error loading texture: {}", e);
println!("{}", err);
errors.push(err.into());
},
}
}
}
}
return (textures, errors);
}
// return (textures, errors);
// }
impl Model {
pub fn new(path: &str, state: &mut ui::State) -> Result<Self, Box<dyn std::error::Error>> {
let scene = russimp::scene::Scene::from_file(path,
vec![
russimp::scene::PostProcess::Triangulate,
russimp::scene::PostProcess::GenerateNormals,
russimp::scene::PostProcess::FlipUVs,
])
.map_err(|e| {
let e = match e {
russimp::RussimpError::TextureNotFound => anyhow!("Texture not found"),
_ => anyhow!("{}", e)
};
return e;
})?;
pub fn new(obj: importer::Object, state: &mut ui::State) -> Model {
// let scene = russimp::scene::Scene::from_file(path,
// vec![
// russimp::scene::PostProcess::Triangulate,
// russimp::scene::PostProcess::GenerateNormals,
// russimp::scene::PostProcess::FlipUVs,
// ])
// .map_err(|e| {
// let e = match e {
// russimp::RussimpError::TextureNotFound => anyhow!("Texture not found"),
// _ => anyhow!("{}", e)
// };
// return e;
// })?;
let root_node = match &scene.root {
Some(root) => root,
None => return Err("Scene has no root node".into()),
};
// let root_node = match &scene.root {
// Some(root) => root,
// None => return Err("Scene has no root node".into()),
// };
if scene.flags & russimp::sys::AI_SCENE_FLAGS_INCOMPLETE == 1 {
return Err("Scene is incomplete")?;
}
// if scene.flags & russimp::sys::AI_SCENE_FLAGS_INCOMPLETE == 1 {
// return Err("Scene is incomplete")?;
// }
let directory = match std::path::Path::new(path).parent() {
Some(dir) => dir,
None => return Err("Model path has no parent directory".into()),
}.to_path_buf();
// let directory = match std::path::Path::new(path).parent() {
// Some(dir) => dir,
// None => return Err("Model path has no parent directory".into()),
// }.to_path_buf();
let mut loaded_textures = vec![];
let mut meshes = vec![];
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 loaded_textures = vec![];
// let mut meshes = vec![];
// let init_trans_mat = utils::mat_ident();
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 {
state.logger.history.push(log::LogMessage::new(log::LogLevel::Warning, &err.to_string()));
// for err in errors {
// 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(),
name: root_node.name.to_owned(),
aabb_mesh: aabb::AABBMesh::new(&aabb),
aabb,
name: obj.name.to_owned(),
aabb: obj.aabb,
scaling_factor: scale,
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) {
@@ -312,7 +321,7 @@ impl Model {
}
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) {
ui.tree_node_config(format!("{}###{}", mesh.name.as_str(), i)).build(|| {
ui.text(format!("Vertices: {}", mesh.vertices.len()));
ui.text(format!("Faces: {}", mesh.vertices.len() / 3));
ui.text(format!("Textures: {}", mesh.textures.len()));
ui.text(format!("Triangles: {}", mesh.indices.len() / 3));
ui.tree_node_config(mesh.material.name.as_str()).build(|| {
ui.text(format!("{}", mesh.material));
});
+7 -5
View File
@@ -4,7 +4,7 @@ use glad_gl::gl;
use glm;
use anyhow::{Result, Context};
use crate::{ui, log, model};
use crate::{log, model, ui, importer};
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;
}
}
let model = model::Model::new(model_path.to_str().unwrap(), state);
match model {
Ok(mut m) => {
let obj_result = importer::load_from_file(model_path);
match obj_result {
Ok(obj) => {
let mut m = model::Model::new(obj, state);
state.active_model = Some(m.id);
if let Some(model_name) = filename {
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);
state.logger.log(&error, log::LogLevel::Error);
},
}
}
}
}