1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
use rayon::prelude::*;

use crate::math::{Bounds3, Hit, Point3, Ray, Transformable, Vec3};
use crate::shapes::{Shape, Triangle, TriangleMesh, BVH};

/// A mesh consisting of multiple shapes stored in a [`BVH`].
#[derive(Clone, Debug)]
pub struct Mesh<S: Shape> {
    /// The mesh's bounding volume hierarchy.
    pub bvh: BVH<S>,
    /// The mesh's bounding box.
    pub bounds: Bounds3,
}

impl<S: Shape> Mesh<S> {
    /// Create a new [`BVH`] from a [`Vec`] of [`Shape`].
    pub fn new(shapes: Vec<S>) -> Self {
        let bvh = BVH::init(4, shapes);

        Mesh {
            bounds: bvh
                .shapes
                .iter()
                .fold(Bounds3::default(), |b, s| b.include_bounds(s.bounds())),
            bvh,
        }
    }

    /// Calculate the bounding box of the underlying [`BVH`].
    fn calculate_bounds(&self) -> Bounds3 {
        self.bvh
            .shapes
            .iter()
            .fold(Bounds3::default(), |b, s| b.include_bounds(s.bounds()))
    }
}

impl<S: Shape> Shape for Mesh<S> {
    fn intersects(&self, ray: Ray) -> Option<Hit> {
        self.bvh.intersects(ray).map(|(h, _)| h)
    }

    fn bounds(&self) -> Bounds3 {
        self.bounds
    }
}

impl<I: IntoIterator<Item = TriangleMesh>> From<I> for Mesh<Triangle> {
    fn from(value: I) -> Self {
        Mesh::new(value.into_iter().flat_map(|m| m.triangles()).collect())
    }
}

// TODO: make rotate and scale methods use the translation as origin
impl<S: Shape + Transformable + Clone> Transformable for Mesh<S> {
    // TODO: translate_x, translate_y, translate_z methods
    fn translate(mut self, translation: Vec3) -> Self {
        let shapes = self
            .bvh
            .shapes
            .into_par_iter()
            .map(|s| s.translate(translation))
            .collect();

        self.bvh = BVH::init(4, shapes);
        self.bounds = self.calculate_bounds();

        self
    }

    fn rotate(mut self, origin: Point3, axis: Vec3, angle: f64) -> Self {
        let shapes = self
            .bvh
            .shapes
            .into_par_iter()
            .map(|s| s.rotate(origin, axis, angle))
            .collect();

        self.bvh = BVH::init(4, shapes);
        self.bounds = self.calculate_bounds();

        self
    }

    fn rotate_x(self, angle: f64) -> Self {
        self.rotate(Point3::default(), Vec3::X, angle)
    }

    fn rotate_y(self, angle: f64) -> Self {
        self.rotate(Point3::default(), Vec3::Y, angle)
    }

    fn rotate_z(self, angle: f64) -> Self {
        self.rotate(Point3::default(), Vec3::Z, angle)
    }

    fn scale_x(self, factor: f64) -> Self {
        self.scale(Point3::default(), Vec3::new(factor, 1.0, 1.0))
    }

    fn scale_y(self, factor: f64) -> Self {
        self.scale(Point3::default(), Vec3::new(1.0, factor, 1.0))
    }

    fn scale_z(self, factor: f64) -> Self {
        self.scale(Point3::default(), Vec3::new(1.0, 1.0, factor))
    }

    fn scale_xyz(self, scale: Vec3) -> Self {
        self.scale(Point3::default(), scale)
    }

    fn scale(mut self, origin: Point3, scale: Vec3) -> Self {
        let shapes = self
            .bvh
            .shapes
            .into_par_iter()
            .map(|s| s.scale(origin, scale))
            .collect();

        self.bvh = BVH::init(4, shapes);
        self.bounds = self.calculate_bounds();

        self
    }

    // TODO: Implement when I figured out how to preserve the scale
    /// This method is currently ***not*** implemented.
    fn look_at(self, _target: Point3, _view_up: Vec3) -> Self {
        todo!()
    }
}