use super::{Arc, Color, Material, Metal, Point3, Ray, Vec3}; pub struct HitRecord { pub p: Point3, // hit location pub normal: Vec3, // normal of hit location pub mat_ptr: Arc, pub t: f64, // ray steps (distance) from camera to hit pub front_face: bool, // whether object was hitted from front or back } pub struct Sphere { center: Point3, radius: f64, pub mat_ptr: Arc, } pub struct Triangle { a: Point3, b: Point3, c: Point3, normal: Vec3, pub mat_ptr: Arc, } pub trait Hittable: Sync + Send { fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool; } impl HitRecord { fn set_face_normal(&mut self, r: &Ray, outward_normal: Vec3) { self.front_face = true; //Vec3::dot(r.direction(), outward_normal) < 0.0; if self.front_face { self.normal = outward_normal; } else { self.normal = (-1.0) * outward_normal; } } pub fn new(p: Point3, normal: Vec3, m: Arc, t: f64, front_face: bool) -> Self { HitRecord { p: p, normal: normal, mat_ptr: m, t: t, front_face: front_face, } } pub fn empty() -> Self { Self::new( Point3::null(), Vec3::null(), Arc::new(Metal::new(&Color::null(), 0.0)), 0.0, false, ) } } impl Sphere { pub fn new(cen: Point3, r: f64, m: Arc) -> Self { Sphere { center: cen, radius: r, mat_ptr: m, } } } impl Hittable for Sphere { fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool { let oc = r.origin() - self.center; let a = r.direction().length_squared(); // gleiches Ergebnis wie Skalarprodukt let half_b = Vec3::dot(oc, r.direction()); let c = oc.length_squared() - self.radius * self.radius; let discriminant = half_b * half_b - a * c; if discriminant < 0.0 { return false; } let sqrtd = discriminant.sqrt(); let mut root = (-half_b - sqrtd) / a; let normal = (r.at(root) - self.center) / self.radius; if root < t_min || t_max < root || Vec3::dot(normal, r.direction()) > 0.0 { root = (-half_b + sqrtd) / a; let normal = (r.at(root) - self.center) / self.radius; if root < t_min || t_max < root || Vec3::dot(normal, r.direction()) > 0.0 { return false; } } rec.t = root; rec.p = r.at(rec.t); let outward_normal = (rec.p - self.center) / self.radius; rec.set_face_normal(r, outward_normal); rec.mat_ptr = self.mat_ptr.clone(); return true; } } impl Triangle { pub fn new(a: Point3, b: Point3, c: Point3, normal: Vec3, m: Arc) -> Self { Triangle { a, b, c, normal, mat_ptr: m, } } } impl Hittable for Triangle { fn hit(&self, r: &Ray, t_min: f64, t_max: f64, rec: &mut HitRecord) -> bool { let epsilon = 0.0000001; let a_to_b = self.b - self.a; let a_to_c = self.c - self.a; let h = Vec3::cross(r.direction(), a_to_c); let a = Vec3::dot(a_to_b, h); if a > -epsilon && a < epsilon { // ray is parallel to triangle return false; } let f = 1.0 / a; let a_to_origin = r.origin() - self.a; let u = Vec3::dot(a_to_origin, h) * f; if u < 0.0 || u > 1.0 { //check if outside of triangle return false; } let q = Vec3::cross(a_to_origin, a_to_b); let v = Vec3::dot(r.direction(), q) * f; if v < 0.0 || u + v > 1.0 { // intersection outside of triangle return false; } let t = Vec3::dot(a_to_c, q) * f; // where is intersection on ray? if t <= epsilon { // line intersection but no ray intersection return false; } // ray intersection rec.t = t; rec.p = r.at(t); rec.mat_ptr = self.mat_ptr.clone(); rec.normal = self.normal; return true; /* old normal calculation code if normal is not known // triangle normal alg from https://stackoverflow.com/questions/19350792/calculate-normal-of-a-single-triangle-in-3d-space let normal_to_camera = Vec3::new( a_to_b.y() * a_to_c.z() - a_to_b.z() * a_to_c.y(), a_to_b.z() * a_to_c.x() - a_to_b.x() * a_to_c.z(), a_to_b.x() * a_to_c.y() - a_to_b.y() * a_to_c.x(), ); let unit_normal = normal_to_camera / normal_to_camera.length(); println!("From obj: {},{},{} - calculated: {},{},{}", self.normal.x(),self.normal.y(),self.normal.z(),unit_normal.x(),unit_normal.y(),unit_normal.z()); if Vec3::dot(unit_normal, r.direction()) >= 0.0 { // normal towards camera rec.set_face_normal(r, unit_normal); } else { // normal not towards camera, has to be flipped rec.set_face_normal(r, -1.0 * unit_normal); } return true; */ } }