1use crate::geom::{Mesh, vertex};
2use crate::math::{
3 Mat4x4, Point3,
4 mat::{Apply, RealToProj},
5 pt3, splat,
6};
7
8use super::{
9 Model, ModelToWorld,
10 clip::{ClipVert, Status, view_frustum},
11};
12
13#[derive(Clone, Debug)]
14pub struct Obj<A> {
15 pub geom: Mesh<A>,
16 pub bbox: BBox<Model>,
17 pub tf: Mat4x4<ModelToWorld>,
18}
19
20#[derive(Copy, Clone, Debug, PartialEq)]
23pub struct BBox<B: Default>(pub Point3<B>, pub Point3<B>);
24
25impl<A> Obj<A> {
26 pub fn new(geom: Mesh<A>) -> Self {
27 Self::with_transform(geom, Mat4x4::identity())
28 }
29 pub fn with_transform(geom: Mesh<A>, tf: Mat4x4<ModelToWorld>) -> Self {
30 let bbox = BBox::of(&geom);
31 Self { geom, bbox, tf }
32 }
33}
34
35impl<B: Default> BBox<B> {
36 pub fn of<A>(mesh: &Mesh<A, B>) -> Self {
37 mesh.verts.iter().map(|v| &v.pos).collect()
38 }
39
40 pub fn extend(&mut self, pt: &Point3<B>) {
42 let BBox(low, upp) = self;
43 *low = low.zip_map(*pt, f32::min);
44 *upp = upp.zip_map(*pt, f32::max);
45 }
46
47 pub fn is_empty(&self) -> bool {
48 let BBox(low, upp) = self;
49 (0..3).any(|i| low[i] >= upp[i])
50 }
51
52 pub fn contains(&self, pt: &Point3<B>) -> bool {
54 let BBox(low, upp) = self;
55 (0..3).all(|i| low[i] <= pt[i] && pt[i] <= upp[i])
56 }
57
58 #[rustfmt::skip]
59 pub fn verts(&self) -> [Point3<B>; 8] {
60 let [x0, y0, z0] = self.0.0;
61 let [x1, y1, z1] = self.1.0;
62 [
63 pt3(x0, y0, z0), pt3(x0, y0, z1), pt3(x0, y1, z0), pt3(x0, y1, z1),
64 pt3(x1, y0, z0), pt3(x1, y0, z1), pt3(x1, y1, z0), pt3(x1, y1, z1),
65 ]
66 }
67
68 pub fn visibility(&self, tf: &Mat4x4<RealToProj<B>>) -> Status {
82 view_frustum::status(
83 &self
84 .verts()
85 .map(|p| ClipVert::new(vertex(tf.apply(&p), ()))),
86 )
87 }
88}
89
90impl<A> Default for Obj<A> {
91 fn default() -> Self {
93 Self {
94 geom: Default::default(),
95 bbox: Default::default(),
96 tf: Default::default(),
97 }
98 }
99}
100
101impl<B: Default> Default for BBox<B> {
102 fn default() -> Self {
104 BBox(
105 splat(f32::INFINITY).to_pt(),
106 splat(f32::NEG_INFINITY).to_pt(),
107 )
108 }
109}
110
111impl<'a, B: Default> Extend<&'a Point3<B>> for BBox<B> {
112 fn extend<I: IntoIterator<Item = &'a Point3<B>>>(&mut self, it: I) {
113 it.into_iter().for_each(|pt| self.extend(pt));
114 }
115}
116
117impl<'a, B: Default> FromIterator<&'a Point3<B>> for BBox<B> {
118 fn from_iter<I: IntoIterator<Item = &'a Point3<B>>>(it: I) -> Self {
119 let mut bbox = BBox::default();
120 it.into_iter().for_each(|pt| bbox.extend(pt));
121 bbox
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use crate::math::pt3;
128
129 use super::*;
130
131 #[test]
132 fn bbox_default() {
133 assert!(BBox::<()>::default().is_empty());
134 assert!(!BBox::<()>::default().contains(&Point3::origin()));
135 }
136
137 #[test]
138 fn bbox_extend() {
139 let mut bbox = BBox::<()>(pt3(-1.0, -2.0, -3.0), pt3(5.0, 3.0, 2.0));
140
141 bbox.extend(&pt3(1.0, 1.0, 1.0));
142 assert_eq!(bbox, BBox(pt3(-1.0, -2.0, -3.0), pt3(5.0, 3.0, 2.0)));
143
144 bbox.extend(&pt3(-2.0, 3.0, 3.0));
145 assert_eq!(bbox, BBox(pt3(-2.0, -2.0, -3.0), pt3(5.0, 3.0, 3.0)));
146 }
147
148 #[test]
149 fn bbox_is_empty() {
150 assert!(
151 BBox::<()>(pt3(-1.0, 0.0, -1.0), pt3(1.0, 0.0, 1.0)).is_empty()
152 );
153 assert!(
154 BBox::<()>(pt3(-1.0, -1.0, 1.0), pt3(1.0, 1.0, -1.0)).is_empty()
155 );
156 assert!(
157 !BBox::<()>(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0)).is_empty()
158 );
159 assert!(
160 !BBox::<()>(pt3(-1.0, 10.0, -1.0), pt3(1.0, f32::INFINITY, 1.0))
161 .is_empty()
162 );
163 }
164
165 #[test]
166 fn bbox_contains() {
167 assert!(
168 !BBox::<()>(pt3(-1.0, 0.0, -1.0), pt3(1.0, 0.0, 1.0))
169 .contains(&pt3(0.0, 1.0, 0.0))
170 );
171 assert!(
172 BBox::<()>(pt3(-1.0, 0.0, -1.0), pt3(1.0, 0.0, 1.0))
173 .contains(&pt3(0.0, 0.0, 0.0))
174 );
175 assert!(
176 BBox::<()>(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0))
177 .contains(&pt3(-1.0, 0.0, 0.0))
178 );
179 assert!(
180 BBox::<()>(pt3(-1.0, -1.0, -1.0), pt3(1.0, 1.0, 1.0))
181 .contains(&pt3(0.0, 0.0, 0.0))
182 );
183 }
184}