logo
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
133
134
135
136
137
138
139
140
141
142
143
144
use macaw::BoundingBox;
use macaw::ColorRgba8;
use macaw::Vec3;

/// Bounding box & sphere of mesh.
///
/// For usability, this is translated to `ffi::MeshBounds`.
/// The Vec3-as-four-floats thing makes it ugly to try to map it directly.
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
pub struct MeshBounds {
    /// Bounding box minimum
    pub bounding_box_min: Vec3,
    /// Bounding box maximum
    pub bounding_box_max: Vec3,
    /// Bounding sphere radius from origin
    pub bounding_sphere_radius: f32,
}

impl MeshBounds {
    /// The bounding box.
    #[inline]
    pub fn bounding_box(&self) -> BoundingBox {
        BoundingBox::from_min_max(self.bounding_box_min, self.bounding_box_max)
    }
}

/// User-friendly mesh buffer contents struct
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
pub struct MeshData {
    /// Debug name,  used to make warnings and errors more readable.
    pub name: String,

    /// List of indices, this can be empty to have a non-indexed mesh
    pub indices: Vec<u32>,

    /// Vertex positions
    pub positions: Vec<[f32; 3]>,
    /// Vertex normals (optional)
    pub normals: Option<Vec<[f32; 3]>>,
    /// Vertex colors (optional)
    pub colors: Option<Vec<ColorRgba8>>,
}

impl MeshData {
    /// Validate mesh buffer contents
    ///
    /// This will for example check that the indices are valid and that we have
    /// the same amount of vertices in the position/normal/color streams.
    pub fn validate(&self) -> Result<(), String> {
        if self.indices.len() % 3 != 0 {
            return Err(
                "Index count not divisible by 3, which is required for triangle lists".to_owned(),
            );
        }

        if !self.indices.is_empty() {
            let max = *self.indices.iter().max().unwrap();
            if max >= self.positions.len() as u32 {
                return Err(format!(
                    "Index out of bounds: {}, vertex count: {}",
                    max,
                    self.positions.len()
                ));
            }
        }
        if let Some(normals) = &self.normals {
            if normals.len() != self.positions.len() {
                return Err(format!(
                    "Mismatch in amount of normals vs positions: {} vs {}",
                    normals.len(),
                    self.positions.len()
                ));
            }
        }
        if let Some(colors) = &self.colors {
            if colors.len() != self.positions.len() {
                return Err(format!(
                    "Mismatch in amount of colors vs positions: {} vs {}",
                    colors.len(),
                    self.positions.len()
                ));
            }
        }
        Ok(())
    }
}

/// Simple mesh representation, layer on to of `MeshData`
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "with_serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
pub struct Mesh {
    /// Bounding box minimum
    pub bounding_box_min: Vec3,
    /// Bounding box maximum
    pub bounding_box_max: Vec3,
    /// bounding sphere radius from origin
    pub bounding_sphere_radius: f32,
    /// Amount of primitives (triangles) in mesh
    pub primitive_count: u64,
    /// Optional mesh data
    pub data: Option<MeshData>,
}

impl Mesh {
    /// Create an empty mesh with no data, centered around 0,0,0
    pub fn empty() -> Self {
        Self {
            bounding_box_min: Vec3::ZERO,
            bounding_box_max: Vec3::ZERO,
            bounding_sphere_radius: 0.0,
            primitive_count: 0,
            data: None,
        }
    }

    /// Validate mesh contents
    pub fn validate(&self) -> Result<(), String> {
        if let Some(data) = &self.data {
            data.validate()
        } else {
            Ok(())
        }
    }

    /// Get bounds of the mesh
    pub fn get_bounds(&self) -> MeshBounds {
        MeshBounds {
            bounding_box_min: self.bounding_box_min,
            bounding_box_max: self.bounding_box_max,
            bounding_sphere_radius: self.bounding_sphere_radius,
        }
    }
}

impl Default for Mesh {
    fn default() -> Self {
        Self::empty()
    }
}