diff --git a/pathtracer/Cargo.toml b/pathtracer/Cargo.toml index dd98d70..4bd7077 100644 --- a/pathtracer/Cargo.toml +++ b/pathtracer/Cargo.toml @@ -28,6 +28,7 @@ rand = "0.7" rayon = "1.3.0" serde_yaml = "0.8" structopt = "0.3" +tobj = "0.1" [dependencies.nalgebra] version = "0.20.0" diff --git a/pathtracer/src/material/mod.rs b/pathtracer/src/material/mod.rs index 63ce29a..879f5e9 100644 --- a/pathtracer/src/material/mod.rs +++ b/pathtracer/src/material/mod.rs @@ -9,7 +9,7 @@ use serde::Deserialize; #[serde(rename_all = "lowercase")] #[allow(missing_docs)] #[enum_dispatch::enum_dispatch] -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub enum MaterialEnum { #[serde(rename = "uniform")] UniformMaterial, diff --git a/pathtracer/src/material/triangle.rs b/pathtracer/src/material/triangle.rs index a9d8adf..72790f2 100644 --- a/pathtracer/src/material/triangle.rs +++ b/pathtracer/src/material/triangle.rs @@ -4,7 +4,7 @@ use crate::Point2D; use serde::Deserialize; /// Represent a material which interpolates between three points. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub struct TriangleMaterial { /// The diffuse components. diffuse: [LinearColor; 3], diff --git a/pathtracer/src/render/mesh.rs b/pathtracer/src/render/mesh.rs index f5ad91c..9d7fe5d 100644 --- a/pathtracer/src/render/mesh.rs +++ b/pathtracer/src/render/mesh.rs @@ -1,11 +1,97 @@ -use super::Object; +use std::convert::TryFrom; +use std::path::PathBuf; + +use nalgebra::Unit; + use serde::Deserialize; +use tobj::{self, load_obj}; + +use super::Object; +use crate::{ + core::{LightProperties, LinearColor}, + material::UniformMaterial, + shape::InterpolatedTriangle, + texture::UniformTexture, + Point, Vector, +}; + /// Represent a mesh of objects. +#[serde(try_from = "Wavefront")] #[derive(Debug, PartialEq, Deserialize)] pub struct Mesh { /// The shapes composing the mesh pub(crate) shapes: Vec, } -// FIXME: wavefront mesh deserialized in mesh +#[derive(Debug, PartialEq, Deserialize)] +pub(crate) struct Wavefront { + pub obj_file: PathBuf, +} + +impl TryFrom for Mesh { + type Error = tobj::LoadError; + + fn try_from(wavefront: Wavefront) -> Result { + let mut shapes = Vec::new(); + + let (models, _materials) = load_obj(&wavefront.obj_file)?; + + for model in models { + let mesh = &model.mesh; + + // mesh.indices contains all vertices. Each group of 3 vertices + // is a triangle, so we iterate over indices 3 by 3. + for i in 0..(mesh.indices.len() / 3) { + let (a, b, c) = ( + mesh.indices[i * 3] as usize, + mesh.indices[i * 3 + 1] as usize, + mesh.indices[i * 3 + 2] as usize, + ); + + // FIXME: world-to-object transformations needed + let pos_a = Point::from_slice(&mesh.positions[(a * 3)..(a * 3 + 2)]); + let pos_b = Point::from_slice(&mesh.positions[(b * 3)..(b * 3 + 2)]); + let pos_c = Point::from_slice(&mesh.positions[(c * 3)..(c * 3 + 2)]); + + // FIXME: normals could be empty + let norm_a = Unit::new_normalize(Vector::new( + mesh.normals[a * 3], + mesh.normals[a * 3 + 1], + mesh.normals[a * 3 + 2], + )); + let norm_b = Unit::new_normalize(Vector::new( + mesh.normals[b * 3], + mesh.normals[b * 3 + 1], + mesh.normals[b * 3 + 2], + )); + let norm_c = Unit::new_normalize(Vector::new( + mesh.normals[c * 3], + mesh.normals[c * 3 + 1], + mesh.normals[c * 3 + 2], + )); + + let t = InterpolatedTriangle::new(pos_a, pos_b, pos_c, norm_a, norm_b, norm_c); + + // FIXME: handle material + if let Some(_) = mesh.material_id { + } else { + // FIXME: should we accept this, and use a default + // Material, or throw a LoadError + shapes.push(Object::new( + t.into(), + UniformMaterial::new(LightProperties::new( + LinearColor::new(1.0, 0.0, 0.0), + LinearColor::new(0.0, 0.0, 0.0), + None, + )) + .into(), + UniformTexture::new(LinearColor::new(0.5, 0.5, 0.5)).into(), + )); + } + } + } + + Ok(Mesh { shapes }) + } +} diff --git a/pathtracer/src/render/object.rs b/pathtracer/src/render/object.rs index deea9a7..ee2b61f 100644 --- a/pathtracer/src/render/object.rs +++ b/pathtracer/src/render/object.rs @@ -12,7 +12,7 @@ use beevee::{ use serde::Deserialize; /// An object being rendered in the scene. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub struct Object { /// The `Object`'s physical shape pub shape: ShapeEnum, diff --git a/pathtracer/src/render/scene.rs b/pathtracer/src/render/scene.rs index 8a46cb9..884322a 100644 --- a/pathtracer/src/render/scene.rs +++ b/pathtracer/src/render/scene.rs @@ -328,6 +328,7 @@ impl From for Scene { .flatten() .collect(); scene.objects.append(&mut flattened_meshes); + Scene::new( scene.camera, scene.lights, diff --git a/pathtracer/src/shape/mod.rs b/pathtracer/src/shape/mod.rs index 69aa181..4094632 100644 --- a/pathtracer/src/shape/mod.rs +++ b/pathtracer/src/shape/mod.rs @@ -14,7 +14,7 @@ use serde::Deserialize; #[serde(rename_all = "lowercase")] #[allow(missing_docs)] #[enum_dispatch::enum_dispatch] -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub enum ShapeEnum { Sphere, Triangle, diff --git a/pathtracer/src/texture/mod.rs b/pathtracer/src/texture/mod.rs index c432fde..8d130ad 100644 --- a/pathtracer/src/texture/mod.rs +++ b/pathtracer/src/texture/mod.rs @@ -9,7 +9,7 @@ use serde::Deserialize; #[serde(rename_all = "lowercase")] #[allow(missing_docs)] #[enum_dispatch::enum_dispatch] -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub enum TextureEnum { #[serde(rename = "uniform")] UniformTexture, diff --git a/pathtracer/src/texture/triangle.rs b/pathtracer/src/texture/triangle.rs index 947bc80..9393ebc 100644 --- a/pathtracer/src/texture/triangle.rs +++ b/pathtracer/src/texture/triangle.rs @@ -4,7 +4,7 @@ use crate::Point2D; use serde::Deserialize; /// Represent a texture which interpolates between three points. -#[derive(Debug, PartialEq, Deserialize)] +#[derive(Debug, Clone, PartialEq, Deserialize)] pub struct TriangleTexture { /// The texture at each point textures: [UniformTexture; 3],