use crate::geom::{Mesh, vertex};
use crate::math::{
Mat4x4, Point3,
mat::{Apply, RealToProj},
pt3, splat,
};
use super::{
Model, ModelToWorld,
clip::{ClipVert, Status, view_frustum},
};
#[derive(Clone, Debug)]
pub struct Obj<A> {
pub geom: Mesh<A>,
pub bbox: BBox<Model>,
pub tf: Mat4x4<ModelToWorld>,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct BBox<B: Default>(pub Point3<B>, pub Point3<B>);
impl<A> Obj<A> {
pub fn new(geom: Mesh<A>) -> Self {
Self::with_transform(geom, Mat4x4::identity())
}
pub fn with_transform(geom: Mesh<A>, tf: Mat4x4<ModelToWorld>) -> Self {
let bbox = BBox::of(&geom);
Self { geom, bbox, tf }
}
}
impl<B: Default> BBox<B> {
pub fn of<A>(mesh: &Mesh<A, B>) -> Self {
mesh.verts.iter().map(|v| &v.pos).collect()
}
pub fn extend(&mut self, pt: &Point3<B>) {
let BBox(low, upp) = self;
*low = low.zip_map(*pt, f32::min);
*upp = upp.zip_map(*pt, f32::max);
}
pub fn is_empty(&self) -> bool {
let BBox(low, upp) = self;
(0..3).any(|i| low[i] >= upp[i])
}
pub fn contains(&self, pt: &Point3<B>) -> bool {
let BBox(low, upp) = self;
(0..3).all(|i| low[i] <= pt[i] && pt[i] <= upp[i])
}
#[rustfmt::skip]
pub fn verts(&self) -> [Point3<B>; 8] {
let [x0, y0, z0] = self.0.0;
let [x1, y1, z1] = self.1.0;
[
pt3(x0, y0, z0), pt3(x0, y0, z1), pt3(x0, y1, z0), pt3(x0, y1, z1),
pt3(x1, y0, z0), pt3(x1, y0, z1), pt3(x1, y1, z0), pt3(x1, y1, z1),
]
}
pub fn visibility(&self, tf: &Mat4x4<RealToProj<B>>) -> Status {
view_frustum::status(
&self
.verts()
.map(|p| ClipVert::new(vertex(tf.apply(&p), ()))),
)
}
}
impl<A> Default for Obj<A> {
fn default() -> Self {
Self {
geom: Default::default(),
bbox: Default::default(),
tf: Default::default(),
}
}
}
impl<B: Default> Default for BBox<B> {
fn default() -> Self {
BBox(
splat(f32::INFINITY).to_pt(),
splat(f32::NEG_INFINITY).to_pt(),
)
}
}
impl<'a, B: Default> Extend<&'a Point3<B>> for BBox<B> {
fn extend<I: IntoIterator<Item = &'a Point3<B>>>(&mut self, it: I) {
it.into_iter().for_each(|pt| self.extend(pt));
}
}
impl<'a, B: Default> FromIterator<&'a Point3<B>> for BBox<B> {
fn from_iter<I: IntoIterator<Item = &'a Point3<B>>>(it: I) -> Self {
let mut bbox = BBox::default();
it.into_iter().for_each(|pt| bbox.extend(pt));
bbox
}
}
#[cfg(test)]
mod tests {
use crate::math::pt3;
use super::*;
#[test]
fn bbox_default() {
assert!(BBox::<()>::default().is_empty());
assert!(!BBox::<()>::default().contains(&Point3::origin()));
}
#[test]
fn bbox_extend() {
let mut bbox = BBox::<()>(pt3(-1.0, -2.0, -3.0), pt3(5.0, 3.0, 2.0));
bbox.extend(&pt3(1.0, 1.0, 1.0));
assert_eq!(bbox, BBox(pt3(-1.0, -2.0, -3.0), pt3(5.0, 3.0, 2.0)));
bbox.extend(&pt3(-2.0, 3.0, 3.0));
assert_eq!(bbox, BBox(pt3(-2.0, -2.0, -3.0), pt3(5.0, 3.0, 3.0)));
}
#[test]
fn bbox_is_empty() {
assert!(
BBox::<()>(pt3(-1.0, 0.0, -1.0), pt3(1.0, 0.0, 1.0)).is_empty()
);
assert!(
BBox::<()>(pt3(-1.0, -1.0, 1.0), pt3(1.0, 1.0, -1.0)).is_empty()
);
assert!(
!BBox::<()>(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0)).is_empty()
);
assert!(
!BBox::<()>(pt3(-1.0, 10.0, -1.0), pt3(1.0, f32::INFINITY, 1.0))
.is_empty()
);
}
#[test]
fn bbox_contains() {
assert!(
!BBox::<()>(pt3(-1.0, 0.0, -1.0), pt3(1.0, 0.0, 1.0))
.contains(&pt3(0.0, 1.0, 0.0))
);
assert!(
BBox::<()>(pt3(-1.0, 0.0, -1.0), pt3(1.0, 0.0, 1.0))
.contains(&pt3(0.0, 0.0, 0.0))
);
assert!(
BBox::<()>(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0))
.contains(&pt3(-1.0, 0.0, 0.0))
);
assert!(
BBox::<()>(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0))
.contains(&pt3(0.0, 0.0, 0.0))
);
}
}