mirror of
https://github.com/IllusionMan1212/3dobs.git
synced 2026-10-24 10:12:35 +00:00
importer/obj: basic material handling. dont panic when encountering v/vt components
This commit is contained in:
@@ -26,5 +26,6 @@
|
|||||||
- only select the last one if we removed the selected model
|
- only select the last one if we removed the selected model
|
||||||
- [x] Use filename for the root node
|
- [x] Use filename for the root node
|
||||||
- [x] Ship shaders directly in the executable
|
- [x] Ship shaders directly in the executable
|
||||||
|
- [ ] Have the program accept model files as CLI args
|
||||||
|
|
||||||
- [ ] Bring back the regular camera movement (can maybe be a switch between them) (for later)
|
- [ ] Bring back the regular camera movement (can maybe be a switch between them) (for later)
|
||||||
|
|||||||
+69
-2
@@ -1,14 +1,81 @@
|
|||||||
mod obj;
|
mod obj;
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::{path::PathBuf, collections::HashMap};
|
||||||
|
|
||||||
use crate::{mesh::Vertex, aabb::AABB};
|
use crate::{mesh::Vertex, aabb::AABB};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TextureType {
|
||||||
|
Ambient,
|
||||||
|
Diffuse,
|
||||||
|
Specular,
|
||||||
|
SpecularHighlight,
|
||||||
|
Bump,
|
||||||
|
Displacement,
|
||||||
|
Decal,
|
||||||
|
Reflection,
|
||||||
|
Emissive
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Material {
|
||||||
|
pub name: String,
|
||||||
|
pub ambient_color: glm::Vec3,
|
||||||
|
pub diffuse_color: glm::Vec3,
|
||||||
|
pub specular_color: glm::Vec3,
|
||||||
|
pub specular_exponent: f32,
|
||||||
|
pub opacity: f32,
|
||||||
|
pub textures: HashMap<String, TextureType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Material {
|
||||||
|
fn new(name: String, ambient: glm::Vec3, diffuse: glm::Vec3, specular: glm::Vec3, shininess: f32, opacity: f32, textures: HashMap<String, TextureType>) -> Self {
|
||||||
|
Self {
|
||||||
|
name,
|
||||||
|
ambient_color: ambient,
|
||||||
|
diffuse_color: diffuse,
|
||||||
|
specular_color: specular,
|
||||||
|
specular_exponent: shininess,
|
||||||
|
opacity,
|
||||||
|
textures
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Material {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: "default_mat".to_string(),
|
||||||
|
ambient_color: glm::vec3(0.4, 0.4, 0.4),
|
||||||
|
diffuse_color: glm::vec3(0.7, 0.7, 0.7),
|
||||||
|
specular_color: glm::vec3(0.1, 0.1, 0.1),
|
||||||
|
specular_exponent: 32.0,
|
||||||
|
opacity: 1.0,
|
||||||
|
textures: HashMap::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for Material {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Ambient: {:?}\nDiffuse: {:?}\nSpecular: {:?}\nShininess: {}\nOpacity: {}",
|
||||||
|
self.ambient_color,
|
||||||
|
self.diffuse_color,
|
||||||
|
self.specular_color,
|
||||||
|
self.specular_exponent,
|
||||||
|
self.opacity
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ObjMesh {
|
pub struct ObjMesh {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub vertices: Vec<Vertex>,
|
pub vertices: Vec<Vertex>,
|
||||||
pub indices: Vec<u32>,
|
pub indices: Vec<u32>,
|
||||||
|
pub material: Option<Material>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@@ -26,6 +93,6 @@ pub fn load_from_file(path: &PathBuf) -> Result<Object, Box<dyn std::error::Erro
|
|||||||
// if no extension, then test for binary STL magic bytes
|
// 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
|
// if no magic bytes, then try to guess based on the first line of text in the file
|
||||||
|
|
||||||
let obj = obj::load_obj(file)?;
|
let obj = obj::load_obj(path, file)?;
|
||||||
Ok(obj)
|
Ok(obj)
|
||||||
}
|
}
|
||||||
|
|||||||
+201
-35
@@ -1,10 +1,10 @@
|
|||||||
use std::io::{BufReader, BufRead};
|
use std::{io::{BufReader, BufRead}, collections::HashMap, path::PathBuf};
|
||||||
|
|
||||||
use crate::{mesh::Vertex, aabb::AABB, importer::ObjMesh, importer::Object};
|
use crate::{mesh::Vertex, aabb::AABB, importer::ObjMesh, importer::Object, importer::Material};
|
||||||
|
|
||||||
const BUF_CAP: usize = 1024 * 128; // 128 Kilobytes
|
const BUF_CAP: usize = 1024 * 128; // 128 Kilobytes
|
||||||
|
|
||||||
enum Token {
|
enum ObjToken {
|
||||||
Object,
|
Object,
|
||||||
Group,
|
Group,
|
||||||
Vertex,
|
Vertex,
|
||||||
@@ -18,26 +18,141 @@ enum Token {
|
|||||||
MaterialUsage,
|
MaterialUsage,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Token {
|
impl ObjToken {
|
||||||
fn from_str<'a>(s: &'a str) -> Option<Token> {
|
fn from_str<'a>(s: &'a str) -> Option<ObjToken> {
|
||||||
match s {
|
match s {
|
||||||
"o" => Some(Token::Object),
|
"o" => Some(ObjToken::Object),
|
||||||
"g" => Some(Token::Group),
|
"g" => Some(ObjToken::Group),
|
||||||
"v" => Some(Token::Vertex),
|
"v" => Some(ObjToken::Vertex),
|
||||||
"vn" => Some(Token::Normal),
|
"vn" => Some(ObjToken::Normal),
|
||||||
"vt" => Some(Token::TexCoord),
|
"vt" => Some(ObjToken::TexCoord),
|
||||||
"f" => Some(Token::Face),
|
"f" => Some(ObjToken::Face),
|
||||||
"p" => Some(Token::Point),
|
"p" => Some(ObjToken::Point),
|
||||||
"l" => Some(Token::Line),
|
"l" => Some(ObjToken::Line),
|
||||||
"s" => Some(Token::SmoothShading),
|
"s" => Some(ObjToken::SmoothShading),
|
||||||
"mtllib" => Some(Token::MaterialLib),
|
"mtllib" => Some(ObjToken::MaterialLib),
|
||||||
"usemtl" => Some(Token::MaterialUsage),
|
"usemtl" => Some(ObjToken::MaterialUsage),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
enum MtlToken {
|
||||||
|
NewMaterial,
|
||||||
|
AmbientColor,
|
||||||
|
DiffuseColor,
|
||||||
|
SpecularColor,
|
||||||
|
Emissive,
|
||||||
|
SpecularExponent,
|
||||||
|
Refraction,
|
||||||
|
Opacity,
|
||||||
|
AmbientTexture,
|
||||||
|
DiffuseTexture,
|
||||||
|
SpecularTexture,
|
||||||
|
SpecularHighlightTexture,
|
||||||
|
EmissiveTexture,
|
||||||
|
BumpTexture,
|
||||||
|
DisplacementTexture,
|
||||||
|
DecalTexture,
|
||||||
|
ReflectionTexture,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MtlToken {
|
||||||
|
fn from_str(s: &str) -> Option<MtlToken> {
|
||||||
|
match s {
|
||||||
|
"newmtl" => Some(MtlToken::NewMaterial),
|
||||||
|
"Ka" => Some(MtlToken::AmbientColor),
|
||||||
|
"Kd" => Some(MtlToken::DiffuseColor),
|
||||||
|
"Ks" => Some(MtlToken::SpecularColor),
|
||||||
|
"Ke" => Some(MtlToken::Emissive),
|
||||||
|
"Ns" => Some(MtlToken::SpecularExponent),
|
||||||
|
"Ni" => Some(MtlToken::Refraction),
|
||||||
|
"d" => Some(MtlToken::Opacity),
|
||||||
|
"map_Ka" => Some(MtlToken::AmbientTexture),
|
||||||
|
"map_Kd" => Some(MtlToken::DiffuseTexture),
|
||||||
|
"map_Ks" => Some(MtlToken::SpecularTexture),
|
||||||
|
"map_Ns" => Some(MtlToken::SpecularHighlightTexture),
|
||||||
|
"map_Ke" => Some(MtlToken::EmissiveTexture),
|
||||||
|
"map_bump" => Some(MtlToken::BumpTexture),
|
||||||
|
"map_d" => Some(MtlToken::DisplacementTexture),
|
||||||
|
"decal" => Some(MtlToken::DecalTexture),
|
||||||
|
"refl" => Some(MtlToken::ReflectionTexture),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_mtl(path: &PathBuf) -> Result<HashMap<String, Material>, Box<dyn std::error::Error>> {
|
||||||
|
let file = std::fs::File::open(path)?;
|
||||||
|
let reader = BufReader::with_capacity(BUF_CAP, file);
|
||||||
|
let mut material_name = String::new();
|
||||||
|
let mut materials: HashMap<String, Material> = HashMap::new();
|
||||||
|
let mut material;
|
||||||
|
let mut ambient = glm::vec3(0.0, 0.0, 0.0);
|
||||||
|
let mut diffuse = glm::vec3(0.0, 0.0, 0.0);
|
||||||
|
let mut specular = glm::vec3(0.0, 0.0, 0.0);
|
||||||
|
let mut shininess = 32.0;
|
||||||
|
let mut opacity = 1.0;
|
||||||
|
|
||||||
|
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 MtlToken::from_str(token) {
|
||||||
|
Some(MtlToken::NewMaterial) => {
|
||||||
|
if !material_name.is_empty() {
|
||||||
|
material = Material::new(material_name.clone(), ambient, diffuse, specular, shininess, opacity, HashMap::new());
|
||||||
|
materials.insert(material_name, material);
|
||||||
|
}
|
||||||
|
|
||||||
|
material_name = iter.next().unwrap().to_string();
|
||||||
|
}
|
||||||
|
Some(MtlToken::AmbientColor) => {
|
||||||
|
let r = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
let g = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
let b = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
ambient = glm::vec3(r, g, b);
|
||||||
|
}
|
||||||
|
Some(MtlToken::DiffuseColor) => {
|
||||||
|
let r = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
let g = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
let b = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
diffuse = glm::vec3(r, g, b);
|
||||||
|
}
|
||||||
|
Some(MtlToken::SpecularColor) => {
|
||||||
|
let r = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
let g = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
let b = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
specular = glm::vec3(r, g, b);
|
||||||
|
}
|
||||||
|
Some(MtlToken::SpecularExponent) => {
|
||||||
|
shininess = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
}
|
||||||
|
Some(MtlToken::Opacity) => {
|
||||||
|
opacity = iter.next().unwrap().parse::<f32>().unwrap();
|
||||||
|
}
|
||||||
|
Some(MtlToken::DiffuseTexture) => {
|
||||||
|
// TODO: textures
|
||||||
|
}
|
||||||
|
_ => { println!("Unhandled material token: {}", token) },
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
material = Material::new(material_name.clone(), ambient, diffuse, specular, shininess, opacity, HashMap::new());
|
||||||
|
|
||||||
|
materials.insert(material_name, material);
|
||||||
|
|
||||||
|
Ok(materials)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load_obj(obj_path: &PathBuf, file: std::fs::File) -> Result<Object, Box<dyn std::error::Error>> {
|
||||||
let now = std::time::Instant::now();
|
let now = std::time::Instant::now();
|
||||||
let reader = BufReader::with_capacity(BUF_CAP, file);
|
let reader = BufReader::with_capacity(BUF_CAP, file);
|
||||||
let mut object_name = String::new();
|
let mut object_name = String::new();
|
||||||
@@ -49,6 +164,8 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
let mut indices = Vec::new();
|
let mut indices = Vec::new();
|
||||||
let mut tex_coords = Vec::new();
|
let mut tex_coords = Vec::new();
|
||||||
let mut meshes = Vec::new();
|
let mut meshes = Vec::new();
|
||||||
|
let mut materials: HashMap<String, Material> = HashMap::new();
|
||||||
|
let mut current_material: Option<Material> = None;
|
||||||
let mut min_aabb = glm::vec3(std::f32::MAX, std::f32::MAX, std::f32::MAX);
|
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);
|
let mut max_aabb = glm::vec3(std::f32::MIN, std::f32::MIN, std::f32::MIN);
|
||||||
|
|
||||||
@@ -62,8 +179,8 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
let mut iter = line.split_ascii_whitespace();
|
let mut iter = line.split_ascii_whitespace();
|
||||||
let first = iter.next();
|
let first = iter.next();
|
||||||
if let Some(token) = first {
|
if let Some(token) = first {
|
||||||
match Token::from_str(token) {
|
match ObjToken::from_str(token) {
|
||||||
Some(Token::Object) => {
|
Some(ObjToken::Object) => {
|
||||||
let name = {
|
let name = {
|
||||||
if current_mesh_name.is_empty() {
|
if current_mesh_name.is_empty() {
|
||||||
object_name.clone()
|
object_name.clone()
|
||||||
@@ -76,6 +193,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
name,
|
name,
|
||||||
vertices: vertices.clone(),
|
vertices: vertices.clone(),
|
||||||
indices: indices.clone(),
|
indices: indices.clone(),
|
||||||
|
material: current_material.clone()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
vertices.clear();
|
vertices.clear();
|
||||||
@@ -84,7 +202,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
|
|
||||||
object_name = iter.next().unwrap_or("").to_string();
|
object_name = iter.next().unwrap_or("").to_string();
|
||||||
}
|
}
|
||||||
Some(Token::Group) => {
|
Some(ObjToken::Group) => {
|
||||||
let name = {
|
let name = {
|
||||||
if current_mesh_name.is_empty() {
|
if current_mesh_name.is_empty() {
|
||||||
object_name.clone()
|
object_name.clone()
|
||||||
@@ -97,6 +215,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
name,
|
name,
|
||||||
vertices: vertices.clone(),
|
vertices: vertices.clone(),
|
||||||
indices: indices.clone(),
|
indices: indices.clone(),
|
||||||
|
material: current_material.clone()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
vertices.clear();
|
vertices.clear();
|
||||||
@@ -105,7 +224,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
|
|
||||||
current_mesh_name = iter.next().unwrap_or("default_mesh").to_string();
|
current_mesh_name = iter.next().unwrap_or("default_mesh").to_string();
|
||||||
}
|
}
|
||||||
Some(Token::Vertex) => {
|
Some(ObjToken::Vertex) => {
|
||||||
let mut iter = iter
|
let mut iter = iter
|
||||||
.take(3)
|
.take(3)
|
||||||
.map(|i| i.parse::<f32>().unwrap());
|
.map(|i| i.parse::<f32>().unwrap());
|
||||||
@@ -138,7 +257,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
Some(Token::Normal) => {
|
Some(ObjToken::Normal) => {
|
||||||
let mut iter = iter
|
let mut iter = iter
|
||||||
.take(3)
|
.take(3)
|
||||||
.map(|i| i.parse::<f32>().unwrap());
|
.map(|i| i.parse::<f32>().unwrap());
|
||||||
@@ -147,7 +266,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
let z = iter.next().unwrap();
|
let z = iter.next().unwrap();
|
||||||
normals.push(glm::vec3(x, y, z));
|
normals.push(glm::vec3(x, y, z));
|
||||||
}
|
}
|
||||||
Some(Token::TexCoord) => {
|
Some(ObjToken::TexCoord) => {
|
||||||
let mut iter = iter
|
let mut iter = iter
|
||||||
.take(2)
|
.take(2)
|
||||||
.map(|i| i.parse::<f32>().unwrap());
|
.map(|i| i.parse::<f32>().unwrap());
|
||||||
@@ -155,16 +274,20 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
let v = iter.next().unwrap();
|
let v = iter.next().unwrap();
|
||||||
tex_coords.push(glm::vec2(u, v));
|
tex_coords.push(glm::vec2(u, v));
|
||||||
}
|
}
|
||||||
Some(Token::Face) => {
|
Some(ObjToken::Face) => {
|
||||||
// TODO: vertex indices can be negative
|
// TODO: vertex indices can be negative
|
||||||
|
|
||||||
let face = iter.collect::<Vec<_>>();
|
let face = iter.collect::<Vec<_>>();
|
||||||
let mut calculated_normal = glm::vec3(0.0, 0.0, 0.0);
|
let mut calculated_normal = glm::vec3(0.0, 0.0, 0.0);
|
||||||
|
|
||||||
if normals.is_empty() {
|
if normals.is_empty() {
|
||||||
|
let part0 = face[0].split("/").next().unwrap();
|
||||||
|
let part1 = face[1].split("/").next().unwrap();
|
||||||
|
let part2 = face[2].split("/").next().unwrap();
|
||||||
|
|
||||||
calculated_normal = glm::normalize(glm::cross(
|
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[part1.parse::<i32>().unwrap() as usize - 1] - temp_vertices[part0.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]
|
temp_vertices[part2.parse::<i32>().unwrap() as usize - 1] - temp_vertices[part0.parse::<i32>().unwrap() as usize - 1]
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,7 +301,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
normal: *normals.get(normal as usize).unwrap(),
|
normal: *normals.get(normal as usize).unwrap(),
|
||||||
tex_coords: glm::vec2(0.0, 0.0)
|
tex_coords: glm::vec2(0.0, 0.0)
|
||||||
});
|
});
|
||||||
} else if vert.contains("/") {
|
} else if vert.matches("/").count() == 2 {
|
||||||
let mut it = vert.split("/");
|
let mut it = vert.split("/");
|
||||||
let vertex = it.next().unwrap().parse::<i32>().unwrap() - 1;
|
let vertex = it.next().unwrap().parse::<i32>().unwrap() - 1;
|
||||||
let t_coords = it.next().unwrap().parse::<i32>().unwrap() - 1;
|
let t_coords = it.next().unwrap().parse::<i32>().unwrap() - 1;
|
||||||
@@ -188,6 +311,15 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
normal: *normals.get(normal as usize).unwrap(),
|
normal: *normals.get(normal as usize).unwrap(),
|
||||||
tex_coords: *tex_coords.get(t_coords as usize).unwrap()
|
tex_coords: *tex_coords.get(t_coords as usize).unwrap()
|
||||||
});
|
});
|
||||||
|
} else if vert.matches("/").count() == 1 {
|
||||||
|
let mut it = vert.split("/");
|
||||||
|
let vertex = it.next().unwrap().parse::<i32>().unwrap() - 1;
|
||||||
|
let t_coords = it.next().unwrap().parse::<i32>().unwrap() - 1;
|
||||||
|
vertices.push(Vertex{
|
||||||
|
position: *temp_vertices.get(vertex as usize).unwrap(),
|
||||||
|
normal: calculated_normal,
|
||||||
|
tex_coords: *tex_coords.get(t_coords as usize).unwrap()
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
let vert = vert.parse::<i32>().unwrap() - 1;
|
let vert = vert.parse::<i32>().unwrap() - 1;
|
||||||
vertices.push(Vertex{
|
vertices.push(Vertex{
|
||||||
@@ -207,15 +339,48 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
|
|
||||||
indices_counter += face.len() as u32;
|
indices_counter += face.len() as u32;
|
||||||
}
|
}
|
||||||
Some(Token::MaterialLib) => {
|
Some(ObjToken::MaterialLib) => {
|
||||||
// TODO: handle material file
|
let material_path = obj_path.parent().unwrap().join(iter.next().unwrap_or(""));
|
||||||
|
let new_materials = parse_mtl(&material_path);
|
||||||
|
match new_materials {
|
||||||
|
Ok(m) => {
|
||||||
|
materials.extend(m);
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Failed to parse mtl file {:?}: {}", material_path, e);
|
||||||
}
|
}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
_ => {},
|
}
|
||||||
|
Some(ObjToken::MaterialUsage) => {
|
||||||
|
// Split into meshes by material usage
|
||||||
|
let 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()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !vertices.is_empty() {
|
||||||
|
meshes.push(ObjMesh{
|
||||||
|
name,
|
||||||
|
vertices: vertices.clone(),
|
||||||
|
indices: indices.clone(),
|
||||||
|
material: current_material.clone()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
vertices.clear();
|
||||||
|
indices.clear();
|
||||||
|
indices_counter = 0;
|
||||||
|
|
||||||
|
let mat_name = iter.next().unwrap_or("").to_string();
|
||||||
|
current_material = materials.get(&mat_name).cloned();
|
||||||
|
}
|
||||||
|
Some(ObjToken::Line) | Some(ObjToken::Point) => {
|
||||||
|
// we don't handle lines or points
|
||||||
|
}
|
||||||
|
_ => { println!("Unhandled obj token: {}", token) },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -236,6 +401,7 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
name: mesh_name,
|
name: mesh_name,
|
||||||
vertices: vertices.clone(),
|
vertices: vertices.clone(),
|
||||||
indices: indices.clone(),
|
indices: indices.clone(),
|
||||||
|
material: current_material
|
||||||
});
|
});
|
||||||
|
|
||||||
let aabb = AABB::new(min_aabb, max_aabb);
|
let aabb = AABB::new(min_aabb, max_aabb);
|
||||||
@@ -243,6 +409,6 @@ pub fn load_obj(file: std::fs::File) -> Result<Object, Box<dyn std::error::Error
|
|||||||
Ok(Object{
|
Ok(Object{
|
||||||
name: object_name,
|
name: object_name,
|
||||||
meshes,
|
meshes,
|
||||||
aabb
|
aabb,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-33
@@ -1,7 +1,7 @@
|
|||||||
use glad_gl::gl;
|
use glad_gl::gl;
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{shader::Shader, utils};
|
use crate::{shader::Shader, utils, importer::Material};
|
||||||
|
|
||||||
fn decompose_mat(matrix: &mut glm::Mat4) -> (glm::Vec3, glm::Vec3, glm::Vec3) {
|
fn decompose_mat(matrix: &mut glm::Mat4) -> (glm::Vec3, glm::Vec3, glm::Vec3) {
|
||||||
let pos = glm::vec3(matrix.c0.w, matrix.c1.w, matrix.c2.w);
|
let pos = glm::vec3(matrix.c0.w, matrix.c1.w, matrix.c2.w);
|
||||||
@@ -89,7 +89,7 @@ pub struct Mesh {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Mesh {
|
impl Mesh {
|
||||||
pub fn new(name: &str, vertices: Vec<Vertex>, indices: Vec<u32>) -> Mesh {
|
pub fn new(name: &str, vertices: Vec<Vertex>, indices: Vec<u32>, material: Option<Material>) -> 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;
|
||||||
@@ -129,7 +129,7 @@ impl Mesh {
|
|||||||
vertices,
|
vertices,
|
||||||
indices,
|
indices,
|
||||||
// textures,
|
// 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),
|
material: material.unwrap_or_default(),
|
||||||
vbo,
|
vbo,
|
||||||
vao,
|
vao,
|
||||||
ebo,
|
ebo,
|
||||||
@@ -151,10 +151,10 @@ impl Mesh {
|
|||||||
let model_mat = glm::ext::translate(&model_mat, glm::vec3(self.position.x, self.position.y, self.position.z));
|
let model_mat = glm::ext::translate(&model_mat, glm::vec3(self.position.x, self.position.y, self.position.z));
|
||||||
shader.set_mat4fv("model", &model_mat);
|
shader.set_mat4fv("model", &model_mat);
|
||||||
|
|
||||||
shader.set_3fv("material.ambient", self.material.ambient);
|
shader.set_3fv("material.ambient", self.material.ambient_color);
|
||||||
shader.set_3fv("material.diffuse", self.material.diffuse);
|
shader.set_3fv("material.diffuse", self.material.diffuse_color);
|
||||||
shader.set_3fv("material.specular", self.material.specular);
|
shader.set_3fv("material.specular", self.material.specular_color);
|
||||||
shader.set_float("material.shininess", self.material.shininess);
|
shader.set_float("material.shininess", self.material.specular_exponent);
|
||||||
|
|
||||||
// for i in 0..self.textures.len() {
|
// for i in 0..self.textures.len() {
|
||||||
// unsafe {
|
// unsafe {
|
||||||
@@ -248,29 +248,3 @@ impl Texture {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Material {
|
|
||||||
pub name: String,
|
|
||||||
pub ambient: glm::Vec3,
|
|
||||||
pub diffuse: glm::Vec3,
|
|
||||||
pub specular: glm::Vec3,
|
|
||||||
pub shininess: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Material {
|
|
||||||
pub fn new(name: String, ambient: glm::Vec3, diffuse: glm::Vec3, specular: glm::Vec3, shininess: f32) -> Self {
|
|
||||||
Material {
|
|
||||||
name,
|
|
||||||
ambient,
|
|
||||||
diffuse,
|
|
||||||
specular,
|
|
||||||
shininess,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::fmt::Display for Material {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
||||||
write!(f, "Ambient: {:?}\nDiffuse: {:?}\nSpecular: {:?}\nShininess: {}", self.ambient, self.diffuse, self.specular, self.shininess)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+3
-5
@@ -1,10 +1,8 @@
|
|||||||
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};
|
||||||
|
|
||||||
|
use crate::{mesh::{Mesh, Vertex, Texture, apply_rotation}, shader::Shader, utils, ui::ui, log, aabb, importer};
|
||||||
|
|
||||||
const SUPPORTED_TEXTURE_TYPES: [russimp::material::TextureType; 2] = [
|
const SUPPORTED_TEXTURE_TYPES: [russimp::material::TextureType; 2] = [
|
||||||
russimp::material::TextureType::Diffuse,
|
russimp::material::TextureType::Diffuse,
|
||||||
russimp::material::TextureType::Specular,
|
russimp::material::TextureType::Specular,
|
||||||
@@ -285,7 +283,7 @@ impl Model {
|
|||||||
let mut meshes = Vec::new();
|
let mut meshes = Vec::new();
|
||||||
|
|
||||||
for mesh in obj.meshes.into_iter() {
|
for mesh in obj.meshes.into_iter() {
|
||||||
meshes.push(Mesh::new(&mesh.name, mesh.vertices, mesh.indices));
|
meshes.push(Mesh::new(&mesh.name, mesh.vertices, mesh.indices, mesh.material));
|
||||||
}
|
}
|
||||||
|
|
||||||
Model {
|
Model {
|
||||||
|
|||||||
Reference in New Issue
Block a user