use crate::objects::hit::Hit; use crate::objects::traits::Hittable; use crate::ray::Ray; use crate::{objects::materials::traits::Material, vec3::Vec3}; use std::fmt::Debug; use std::sync::Arc; pub struct Triangle { p1: Vec3, p2: Vec3, p3: Vec3, material: Arc, normal: Vec3, } impl Triangle { pub fn new(p1: Vec3, p2: Vec3, p3: Vec3, material: Arc) -> Self { Self { p1, p2, p3, material, 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 { Some(Hit::new(t, p, normal, material, normal.dot(&-r.dir()) > 0.)) } else { None } } } impl Debug for Triangle { 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("material", &self.material) .finish() } } impl Hittable for Triangle { 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 { // FIXME: might cause ownership issues self.normal } }