use crate::vector2::Vector2D;
use crate::vector3::Vector3D;
use crate::transform3::Transform3;
use crate::surface3::*;
use crate::bounding_box3::BoundingBox3D;
use crate::ray3::Ray3D;
use std::sync::{RwLock, Arc};
pub struct Triangle3 {
pub points: [Vector3D; 3],
pub normals: [Vector3D; 3],
pub uvs: [Vector2D; 3],
pub surface_data: Surface3Data,
}
impl Triangle3 {
pub fn new_default(transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Triangle3 {
return Triangle3 {
points: [Vector3D::new_default(); 3],
normals: [Vector3D::new_default(); 3],
uvs: [Vector2D::new_default(); 3],
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn new(points: [Vector3D; 3],
normals: [Vector3D; 3],
uvs: [Vector2D; 3],
transform: Option<Transform3>,
is_normal_flipped: Option<bool>) -> Triangle3 {
return Triangle3 {
points,
normals,
uvs,
surface_data: Surface3Data::new(transform, is_normal_flipped),
};
}
pub fn builder() -> Builder {
return Builder::new();
}
pub fn area(&self) -> f64 {
return 0.5 * (self.points[1] - self.points[0]).cross(&(self.points[2] - self.points[0])).length();
}
pub fn get_barycentric_coords(&self, pt: &Vector3D,
b0: &mut f64,
b1: &mut f64,
b2: &mut f64) {
let q01 = (self.points[1] - self.points[0]).cross(&(*pt - self.points[0]));
let q12 = (self.points[2] - self.points[1]).cross(&(*pt - self.points[1]));
let q02 = (self.points[0] - self.points[2]).cross(&(*pt - self.points[2]));
let a = self.area();
*b0 = 0.5 * q12.length() / a;
*b1 = 0.5 * q02.length() / a;
*b2 = 0.5 * q01.length() / a;
}
pub fn face_normal(&self) -> Vector3D {
let ret = (self.points[1] - self.points[0]).cross(&(self.points[2] - self.points[0]));
return ret.normalized();
}
pub fn set_normals_to_face_normal(&mut self) {
self.normals[0] = self.face_normal();
self.normals[1] = self.face_normal();
self.normals[2] = self.face_normal();
}
}
fn closest_point_on_line(v0: &Vector3D, v1: &Vector3D,
pt: &Vector3D) -> Vector3D {
let len_squared = (*v1 - *v0).length_squared();
if len_squared < f64::EPSILON {
return *v0;
}
let t = (*pt - *v0).dot(&(*v1 - *v0)) / len_squared;
if t < 0.0 {
return *v0;
} else if t > 1.0 {
return *v1;
}
return *v0 + (*v1 - *v0) * t;
}
fn closest_normal_on_line(v0: &Vector3D, v1: &Vector3D,
n0: &Vector3D, n1: &Vector3D,
pt: &Vector3D) -> Vector3D {
let len_squared = (*v1 - *v0).length_squared();
if len_squared < f64::EPSILON {
return *n0;
}
let t = (*pt - *v0).dot(&(*v1 - *v0)) / len_squared;
if t < 0.0 {
return *n0;
} else if t > 1.0 {
return *n1;
}
return (*n0 + (*n1 - *n0) * t).normalized();
}
impl Surface3 for Triangle3 {
fn closest_point_local(&self, other_point: &Vector3D) -> Vector3D {
let n = self.face_normal();
let nd = n.dot(&n);
let d = n.dot(&self.points[0]);
let t = (d - n.dot(&other_point)) / nd;
let q = n * t + *other_point;
let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
if n.dot(&q01) < 0.0 {
return closest_point_on_line(&self.points[0], &self.points[1], &q);
}
let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
if n.dot(&q12) < 0.0 {
return closest_point_on_line(&self.points[1], &self.points[2], &q);
}
let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
if n.dot(&q02) < 0.0 {
return closest_point_on_line(&self.points[0], &self.points[2], &q);
}
let a = self.area();
let b0 = 0.5 * q12.length() / a;
let b1 = 0.5 * q02.length() / a;
let b2 = 0.5 * q01.length() / a;
return self.points[0] * b0 + self.points[1] * b1 + self.points[2] * b2;
}
fn bounding_box_local(&self) -> BoundingBox3D {
let mut aabb = BoundingBox3D::new(self.points[0], self.points[1]);
aabb.merge_vec(&self.points[2]);
return aabb;
}
fn closest_intersection_local(&self, ray: &Ray3D) -> SurfaceRayIntersection3 {
let mut intersection = SurfaceRayIntersection3::new();
let n = self.face_normal();
let nd = n.dot(&ray.direction);
if nd < f64::EPSILON {
intersection.is_intersecting = false;
return intersection;
}
let d = n.dot(&self.points[0]);
let t = (d - n.dot(&ray.origin)) / nd;
if t < 0.0 {
intersection.is_intersecting = false;
return intersection;
}
let q = ray.point_at(t);
let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
if n.dot(&q01) <= 0.0 {
intersection.is_intersecting = false;
return intersection;
}
let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
if n.dot(&q12) <= 0.0 {
intersection.is_intersecting = false;
return intersection;
}
let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
if n.dot(&q02) <= 0.0 {
intersection.is_intersecting = false;
return intersection;
}
let a = self.area();
let b0 = 0.5 * q12.length() / a;
let b1 = 0.5 * q02.length() / a;
let b2 = 0.5 * q01.length() / a;
let normal = self.normals[0] * b0 + self.normals[1] * b1 + self.normals[2] * b2;
intersection.is_intersecting = true;
intersection.distance = t;
intersection.point = q;
intersection.normal = normal.normalized();
return intersection;
}
fn closest_normal_local(&self, other_point: &Vector3D) -> Vector3D {
let n = self.face_normal();
let nd = n.dot(&n);
let d = n.dot(&self.points[0]);
let t = (d - n.dot(&other_point)) / nd;
let q = n * t + *other_point;
let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
if n.dot(&q01) < 0.0 {
return closest_normal_on_line(&self.points[0], &self.points[1], &self.normals[0], &self.normals[1],
&q);
}
let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
if n.dot(&q12) < 0.0 {
return closest_normal_on_line(&self.points[1], &self.points[2], &self.normals[1], &self.normals[2],
&q);
}
let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
if n.dot(&q02) < 0.0 {
return closest_normal_on_line(&self.points[0], &self.points[2], &self.normals[0], &self.normals[2],
&q);
}
let a = self.area();
let b0 = 0.5 * q12.length() / a;
let b1 = 0.5 * q02.length() / a;
let b2 = 0.5 * q01.length() / a;
return (self.normals[0] * b0 + self.normals[1] * b1 + self.normals[2] * b2).normalized();
}
fn intersects_local(&self, ray: &Ray3D) -> bool {
let n = self.face_normal();
let nd = n.dot(&ray.direction);
if nd < f64::EPSILON {
return false;
}
let d = n.dot(&self.points[0]);
let t = (d - n.dot(&ray.origin)) / nd;
if t < 0.0 {
return false;
}
let q = ray.point_at(t);
let q01 = (self.points[1] - self.points[0]).cross(&(q - self.points[0]));
if n.dot(&q01) <= 0.0 {
return false;
}
let q12 = (self.points[2] - self.points[1]).cross(&(q - self.points[1]));
if n.dot(&q12) <= 0.0 {
return false;
}
let q02 = (self.points[0] - self.points[2]).cross(&(q - self.points[2]));
if n.dot(&q02) <= 0.0 {
return false;
}
return true;
}
fn view(&self) -> &Surface3Data {
return &self.surface_data;
}
}
pub type Triangle3Ptr = Arc<RwLock<Triangle3>>;
pub struct Builder {
_points: [Vector3D; 3],
_normals: [Vector3D; 3],
_uvs: [Vector2D; 3],
_surface_data: Surface3Data,
}
impl Builder {
pub fn with_points(&mut self, points: [Vector3D; 3]) -> &mut Self {
self._points = points;
return self;
}
pub fn with_normals(&mut self, normals: [Vector3D; 3]) -> &mut Self {
self._normals = normals;
return self;
}
pub fn with_uvs(&mut self, uvs: [Vector2D; 3]) -> &mut Self {
self._uvs = uvs;
return self;
}
pub fn build(&mut self) -> Triangle3 {
return Triangle3::new(
self._points,
self._normals,
self._uvs,
Some(self._surface_data.transform.clone()),
Some(self._surface_data.is_normal_flipped),
);
}
pub fn make_shared(&mut self) -> Triangle3Ptr {
return Triangle3Ptr::new(RwLock::new(self.build()));
}
pub fn new() -> Builder {
return Builder {
_points: [Vector3D::new_default(); 3],
_normals: [Vector3D::new_default(); 3],
_uvs: [Vector2D::new_default(); 3],
_surface_data: Surface3Data::new(None, None),
};
}
}
impl SurfaceBuilderBase3 for Builder {
fn view(&mut self) -> &mut Surface3Data {
return &mut self._surface_data;
}
}