Files
raytracing/src/objects/triangle.rs
2026-05-02 14:02:06 +02:00

92 lines
2.2 KiB
Rust

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<dyn Material>,
normal: Vec3,
}
impl Triangle {
pub fn new(p1: Vec3, p2: Vec3, p3: Vec3, material: Arc<dyn Material>) -> 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<dyn Material>,
normal: Vec3,
r: &Ray,
) -> Option<Hit> {
// 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<Hit> {
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
}
}