ray_tracing_core/geometry/instancing/
flip_normals.rs

1use crate::core::object::Object;
2use crate::core::HitRecord;
3use crate::geometry::{Geometry, Visitor};
4use crate::math::{Ray, AABB};
5use crate::types::{FSize, Vector3};
6use std::error::Error;
7use std::ops::Range;
8use std::sync::Arc;
9
10pub struct FlipNormals {
11    pub id: usize,
12    pub node: Arc<dyn Geometry>,
13}
14
15impl FlipNormals {
16    pub fn new(node: Arc<dyn Geometry>) -> FlipNormals {
17        FlipNormals {
18            id: Object::new_id(),
19            node: node.clone(),
20        }
21    }
22}
23
24impl Geometry for FlipNormals {
25    fn get_id(&self) -> usize {
26        self.id
27    }
28
29    fn bounding_box(&self, time: Range<FSize>) -> Option<AABB> {
30        self.node.bounding_box(time)
31    }
32
33    fn hit(&self, ray: &Ray, t_range: Range<FSize>) -> Option<HitRecord> {
34        match self.node.hit(ray, t_range.clone()) {
35            Some(mut hit_record) => {
36                hit_record.invert_normal();
37                Some(hit_record)
38            }
39            None => None,
40        }
41    }
42
43    fn pdf_value(&self, o: &Vector3, v: &Vector3) -> FSize {
44        self.node.pdf_value(o, v)
45    }
46
47    fn random(&self, o: &Vector3) -> Vector3 {
48        self.node.random(o)
49    }
50
51    fn accept(&self, visitor: &mut dyn Visitor) -> Result<(), Box<dyn Error>> {
52        visitor.visit_instancing_flip_normals(&self)
53    }
54}
55
56#[cfg(test)]
57mod flip_normals_test {
58    use super::*;
59    use crate::geometry::shape::Sphere;
60    use crate::material::{Metal, NoMaterial};
61    use crate::test;
62    use crate::texture::ConstantTexture;
63    use crate::types::ColorRGBA;
64    use crate::types::{Point3, Vector3};
65
66    #[test]
67    fn bounding_box_test() {
68        let s = Sphere::new(Point3::new(0.0, 0.0, 0.0), 1.0, Arc::new(NoMaterial::new()));
69        let i = FlipNormals::new(Arc::new(s));
70        let b = i.bounding_box(0.0..0.0);
71        match b {
72            Some(b) => {
73                test::assert_eq_vector3(&b.min, &Vector3::new(-1.0, -1.0, -1.0), 0.01);
74                test::assert_eq_vector3(&b.max, &Vector3::new(1.0, 1.0, 1.0), 0.01);
75            }
76            _ => assert!(false),
77        }
78    }
79
80    #[test]
81    fn hit_test() {
82        let s = Sphere::new(
83            Point3::new(0.0, 0.0, 0.0),
84            1.0,
85            Arc::new(Metal::new(
86                0.0,
87                Arc::new(ConstantTexture::new(ColorRGBA::new(1.0, 1.0, 1.0, 1.0))),
88            )),
89        );
90        let i = FlipNormals::new(Arc::new(s));
91        let ray1 = Ray::new_ray(Vector3::new(0.0, -5.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
92        let ray2 = Ray::new_ray(Vector3::new(2.0, 0.0, 0.0), Vector3::new(0.0, 1.0, 0.0));
93        match i.hit(&ray1, 0.0..10.0) {
94            Some(hit_record) => {
95                test::assert_eq_vector3(&hit_record.normal, &Vector3::new(0.0, 1.0, 0.0), 0.01)
96            }
97            None => panic!("no result"),
98        }
99        match i.hit(&ray1, 10.0..20.0) {
100            Some(_) => panic!("unexpected hit"),
101            None => (),
102        }
103        match i.hit(&ray2, 0.0..10.0) {
104            Some(_) => panic!("unexpected hit"),
105            None => (),
106        }
107    }
108}