From f1ac226dbb605ada614a99359a2e7751dbbf7348 Mon Sep 17 00:00:00 2001 From: djairoh Date: Sat, 2 May 2026 04:46:15 +0200 Subject: [PATCH] ft: quad support --- src/main.rs | 3 +- src/objects.rs | 1 + src/objects/quad.rs | 84 ++++++++++++++++++++++++++++++++++++++++ src/objects/triangle.rs | 82 +++++++++++++++++++++++---------------- src/scenes/raw_camera.rs | 2 +- src/scenes/scene.rs | 35 ++++++++++++++++- src/vec3.rs | 28 ++++++++++++-- 7 files changed, 193 insertions(+), 42 deletions(-) create mode 100644 src/objects/quad.rs diff --git a/src/main.rs b/src/main.rs index 09fc42f..0575b7a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,8 +27,7 @@ fn main() { pretty_env_logger::init(); // TODO: use cli arg for scenefile - let json_file = "./scenes/withTriangle.json"; - // let json_file = "./scenes/scene.json"; + let json_file = "./scenes/scene.json"; let json_str = fs::read_to_string(json_file).expect("Reading specified scene file failed!"); let mut scene: Scene = serde_json::from_str(&json_str).unwrap(); scene.render(); diff --git a/src/objects.rs b/src/objects.rs index b7b495c..c3057c4 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -2,4 +2,5 @@ pub mod hit; pub mod materials; pub mod sphere; pub mod triangle; +pub mod quad; pub mod traits; diff --git a/src/objects/quad.rs b/src/objects/quad.rs new file mode 100644 index 0000000..dc1b1f1 --- /dev/null +++ b/src/objects/quad.rs @@ -0,0 +1,84 @@ +use crate::objects::hit::Hit; +use crate::objects::sphere::Sphere; +use crate::objects::traits::Hittable; +use crate::objects::triangle::Triangle; +use crate::ray::Ray; +use crate::{objects::materials::traits::Material, vec3::Vec3}; +use log::info; +use std::fmt::Debug; +use std::sync::Arc; + +pub struct Quad { + p1: Vec3, + p2: Vec3, + p3: Vec3, + p4: Vec3, + material: Arc, + normal: Vec3, +} + +impl Quad { + pub fn new(p1: Vec3, p2: Vec3, p3: Vec3, p4: Vec3, material: Arc) -> Self { + Self { + p1, + p2, + p3, + p4, + material, + normal: (p2 - p1).cross(&(p4 - p1)).get_unit(), + } + } + pub fn corner_spheres(&self) -> Vec { + let mut out: Vec = vec![]; + out.push(Sphere::new(self.p1, 1., self.material.clone())); + out.push(Sphere::new(self.p2, 1., self.material.clone())); + out.push(Sphere::new(self.p3, 1., self.material.clone())); + out.push(Sphere::new(self.p4, 1., self.material.clone())); + return out; + + } +} + +impl Debug for Quad { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Triangle") + .field("p1", &self.p1) + .field("p2", &self.p2) + .field("p3", &self.p3) + .field("p4", &self.p4) + .field("material", &self.material) + .finish() + } +} + +impl Hittable for Quad { + fn hit(&self, r: &Ray) -> Option { + let isct1 = Triangle::hit( + self.p1, + self.p2, + self.p4, + self.material.clone(), + self.normal, + r, + ); + let isct2 = Triangle::hit( + self.p2, + self.p3, + self.p4, + self.material.clone(), + self.normal, + r, + ); + if isct1.is_some() { + return isct1; + } + if isct2.is_some() { + } + return isct2; + } + + fn normal_at(&self, _p: &Vec3) -> Vec3 { + // FIXME: might cause ownership issues + return self.normal; + } +} diff --git a/src/objects/triangle.rs b/src/objects/triangle.rs index 7d41ef8..8b389bf 100644 --- a/src/objects/triangle.rs +++ b/src/objects/triangle.rs @@ -1,8 +1,9 @@ +use log::{info, warn}; + use crate::objects::hit::Hit; use crate::objects::traits::Hittable; use crate::ray::Ray; use crate::{objects::materials::traits::Material, vec3::Vec3}; -use is_close::default; use std::fmt::Debug; use std::sync::Arc; @@ -21,7 +22,43 @@ impl Triangle { p2, p3, material, - normal: (p2 - p1).cross(&(p3 - p1)), + normal: (p2 - p1).cross(&(p3 - p1)).get_unit(), + } + } + + pub fn hit( + p1: Vec3, + p2: Vec3, + p3: Vec3, + material: Arc, + normal: Vec3, + r: &Ray, + ) -> Option { + // check if ray parallel to plane + let dot = normal.dot(r.dir()); + if dot == 0.0 { + return None; + } + + let d = (-normal).dot(&p1); + // hitpoint on plane + let t = -(normal.dot(&(r.origin() + d))) / dot; + // hits behind camera + if t < 0. { + return None; + }; + + let p = r.at(t); + let a4 = (p3 - p1).cross(&(p2 - p1)).length(); + let a3 = (p3 - p).cross(&(p2 - p)).length(); + let a2 = (p3 - p).cross(&(p1 - p)).length(); + let a1 = (p2 - p).cross(&(p1 - p)).length(); + + let diff = (a4 - a1 - a2 - a3).abs(); + if diff < 0.001 { + return Some(Hit::new(t, p, normal, material, normal.dot(&-r.dir()) < 0.)); + } else { + return None; } } } @@ -38,38 +75,15 @@ impl Debug for Triangle { } impl Hittable for Triangle { - fn hit(&self, r: &Ray) -> Option { - // check if ray parallel to plane - let dot = self.normal.dot(r.dir()); - if default().is_close(dot, 0.) { - return None; - } - - let d = (-self.normal).dot(&self.p1); - // hitpoint on plane - let t = -(self.normal.dot(&(r.origin() + d))) / dot; - // hits behind camera - if t < 0. { - return None; - }; - - let p = *r.origin() + t * r.dir(); - let a4 = (self.p3 - self.p1).cross(&(self.p2 - self.p1)).length(); - let a3 = (self.p3 - p).cross(&(self.p2 - p)).length(); - let a2 = (self.p3 - p).cross(&(self.p1 - p)).length(); - let a1 = (self.p2 - p).cross(&(self.p1 - p)).length(); - - let mut normal_copy = self.normal.clone(); - let diff = (a4 - a1 - a2 - a3).abs(); - if diff < 0.01 { - if self.normal.dot(&-r.dir()) < 0. { - normal_copy *= -1.; // TODO: vec3 * integer function - } - return Some(Hit::new(t, p, self.normal, self.material.clone(), true)); // TODO: - // front_face calculation; have to change with if check up there iggg - } else { - return None; - } + fn hit(&self, r: &Ray) -> Option { + Triangle::hit( + self.p1, + self.p2, + self.p3, + self.material.clone(), + self.normal, + r, + ) } fn normal_at(&self, _p: &Vec3) -> Vec3 { diff --git a/src/scenes/raw_camera.rs b/src/scenes/raw_camera.rs index 92fc6fb..c8b2e98 100644 --- a/src/scenes/raw_camera.rs +++ b/src/scenes/raw_camera.rs @@ -5,7 +5,7 @@ use crate::{camera::Camera, vec3::Vec3}; #[derive(Deserialize)] pub struct RawCamera { // output - image_width: u32, + image_width: u32, image_height: u32, // raytracing diff --git a/src/scenes/scene.rs b/src/scenes/scene.rs index c98dce1..5321243 100644 --- a/src/scenes/scene.rs +++ b/src/scenes/scene.rs @@ -1,5 +1,5 @@ -use std::sync::Arc; use std::fmt::Debug; +use std::sync::Arc; use log::warn; use serde::Deserialize; @@ -12,6 +12,7 @@ use crate::{ lambertian::{Lambertian, Metal}, traits::Material, }, + quad::Quad, sphere::Sphere, traits::Hittable, triangle::Triangle, @@ -28,7 +29,11 @@ pub struct Scene { impl Debug for Scene { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Scene").field("camera", &self.camera).field("materials", &self.materials).field("objects", &self.objects).finish() + f.debug_struct("Scene") + .field("camera", &self.camera) + .field("materials", &self.materials) + .field("objects", &self.objects) + .finish() } } @@ -100,11 +105,21 @@ struct RawTriangle { pub material: u32, } +#[derive(Deserialize)] +struct RawQuad { + pub p1: Vec3, + pub p2: Vec3, + pub p3: Vec3, + pub p4: Vec3, + pub material: u32, +} + #[derive(Deserialize)] #[serde(tag = "type", rename_all = "lowercase")] enum HittableDef { Sphere(RawSphere), Triangle(RawTriangle), + Quad(RawQuad), } impl HittableDef { @@ -140,6 +155,22 @@ impl HittableDef { materials.get(t.material as usize).unwrap().clone(), ))) } + HittableDef::Quad(q) => { + if q.material as usize >= materials.len() { + warn!( + "Quad specified nonexistent material {}; skipping...", + q.material + ); + return None; + } + Some(Arc::new(Quad::new( + q.p1, + q.p2, + q.p3, + q.p4, + materials.get(q.material as usize).unwrap().clone(), + ))) + } } } } diff --git a/src/vec3.rs b/src/vec3.rs index 94f887b..582988a 100644 --- a/src/vec3.rs +++ b/src/vec3.rs @@ -1,13 +1,13 @@ use core::f32::math::sqrt; use is_close::default; use rand::RngExt; -use serde::Deserialize; +use serde::{Deserialize, Serialize, Serializer}; use std::{ fmt::Display, ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; -#[derive(Copy, Clone, Debug, Deserialize)] +#[derive(Copy, Clone, Debug)] pub struct Vec3 { x: f32, y: f32, @@ -156,6 +156,29 @@ impl Vec3 { } } +impl Serialize for Vec3 { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + ([self.x, self.y, self.z] as [f32; 3]).serialize(serializer) + } +} + +impl<'de> Deserialize<'de> for Vec3 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + let arr = <[f32; 3]>::deserialize(deserializer)?; + Ok(Self { + x: arr[0], + y: arr[1], + z: arr[2], + }) + } +} + impl Default for Vec3 { fn default() -> Self { Self { @@ -222,7 +245,6 @@ impl Add for &Vec3 { } } - impl Add for Vec3 { type Output = Self;