mirror of
https://github.com/IllusionMan1212/3dobs.git
synced 2026-10-24 10:12:35 +00:00
WIP: obj loader mostly done
This commit is contained in:
+17
-21
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
@@ -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:
|
||||
}
|
||||
}
|
||||
@@ -10,3 +10,4 @@ pub mod model;
|
||||
pub mod ui;
|
||||
pub mod log;
|
||||
pub mod aabb;
|
||||
pub mod importer;
|
||||
|
||||
+9
-9
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user