1use alloc::vec;
2use alloc::vec::Vec;
3use he3d_math::scalar::{cos, sin};
4use he3d_math::{Vec2, Vec3};
5
6use crate::Error;
7
8pub const MAX_VERTEX_COUNT: usize = 16_777_216;
11
12#[derive(Clone, Debug, PartialEq)]
20pub struct Mesh {
21 vertices: Vec<Vec3>,
22 uvs: Vec<Vec2>,
23 triangle_normals: Vec<Vec3>,
24}
25
26impl Mesh {
27 pub fn new(vertices: Vec<Vec3>, uvs: Vec<Vec2>) -> Result<Self, Error> {
30 if vertices.is_empty() || vertices.len() > MAX_VERTEX_COUNT {
31 return Err(Error::InvalidSize);
32 }
33 if vertices.len() % 3 != 0 {
34 return Err(Error::InvalidData);
35 }
36 if uvs.len() != vertices.len() {
37 return Err(Error::BufferMismatch);
38 }
39 let mut mesh = Self {
40 vertices,
41 uvs,
42 triangle_normals: Vec::new(),
43 };
44 mesh.recalculate_normals();
45 Ok(mesh)
46 }
47
48 pub fn from_vertices(vertices: Vec<Vec3>) -> Result<Self, Error> {
51 let count = vertices.len();
52 if count == 0 || count > MAX_VERTEX_COUNT {
53 return Err(Error::InvalidSize);
54 }
55 if count % 3 != 0 {
56 return Err(Error::InvalidData);
57 }
58 Self::new(vertices, vec![Vec2::ZERO; count])
59 }
60
61 pub fn vertex_count(&self) -> usize {
62 self.vertices.len()
63 }
64
65 pub fn triangle_count(&self) -> usize {
66 self.vertices.len() / 3
67 }
68
69 pub fn is_valid(&self) -> bool {
70 !self.vertices.is_empty() && self.vertices.len() % 3 == 0
71 }
72
73 pub fn vertices(&self) -> &[Vec3] {
74 &self.vertices
75 }
76
77 pub fn uvs(&self) -> &[Vec2] {
78 &self.uvs
79 }
80
81 pub fn triangle_normals(&self) -> &[Vec3] {
82 &self.triangle_normals
83 }
84
85 pub fn recalculate_normals(&mut self) {
88 let triangle_count = self.vertices.len() / 3;
89 self.triangle_normals.clear();
90 self.triangle_normals.reserve(triangle_count);
91 for tri in self.vertices.chunks_exact(3) {
92 let e1 = tri[1] - tri[0];
93 let e2 = tri[2] - tri[0];
94 self.triangle_normals.push(e1.cross(e2).normalize_or_zero());
95 }
96 }
97
98 pub fn triangle(width: f32, height: f32) -> Result<Self, Error> {
101 if !positive_finite(width) || !positive_finite(height) {
102 return Err(Error::InvalidSize);
103 }
104 let hx = width * 0.5;
105 let hy = height * 0.5;
106 let vertices = vec![
107 Vec3::new(-hx, -hy, 0.0),
108 Vec3::new(0.0, hy, 0.0),
109 Vec3::new(hx, -hy, 0.0),
110 ];
111 let uvs = vec![
112 Vec2::new(0.0, 0.0),
113 Vec2::new(0.5, 1.0),
114 Vec2::new(1.0, 0.0),
115 ];
116 Self::new(vertices, uvs)
117 }
118
119 pub fn plane(width: f32, depth: f32) -> Result<Self, Error> {
122 if !positive_finite(width) || !positive_finite(depth) {
123 return Err(Error::InvalidSize);
124 }
125 let hx = width * 0.5;
126 let hz = depth * 0.5;
127 let vertices = vec![
128 Vec3::new(-hx, 0.0, -hz),
129 Vec3::new(hx, 0.0, hz),
130 Vec3::new(hx, 0.0, -hz),
131 Vec3::new(-hx, 0.0, -hz),
132 Vec3::new(-hx, 0.0, hz),
133 Vec3::new(hx, 0.0, hz),
134 ];
135 let uvs = vec![
136 Vec2::new(0.0, 0.0),
137 Vec2::new(1.0, 1.0),
138 Vec2::new(1.0, 0.0),
139 Vec2::new(0.0, 0.0),
140 Vec2::new(0.0, 1.0),
141 Vec2::new(1.0, 1.0),
142 ];
143 Self::new(vertices, uvs)
144 }
145
146 pub fn cube(width: f32, height: f32, depth: f32) -> Result<Self, Error> {
149 if !positive_finite(width) || !positive_finite(height) || !positive_finite(depth) {
150 return Err(Error::InvalidSize);
151 }
152 let hx = width * 0.5;
153 let hy = height * 0.5;
154 let hz = depth * 0.5;
155 let p = [
156 Vec3::new(-hx, -hy, -hz),
157 Vec3::new(hx, -hy, -hz),
158 Vec3::new(hx, hy, -hz),
159 Vec3::new(-hx, hy, -hz),
160 Vec3::new(-hx, -hy, hz),
161 Vec3::new(hx, -hy, hz),
162 Vec3::new(hx, hy, hz),
163 Vec3::new(-hx, hy, hz),
164 ];
165 let (uv0, uv1, uv2, uv3) = (
166 Vec2::new(0.0, 0.0),
167 Vec2::new(1.0, 0.0),
168 Vec2::new(1.0, 1.0),
169 Vec2::new(0.0, 1.0),
170 );
171
172 let mut vertices = Vec::with_capacity(36);
173 let mut uvs = Vec::with_capacity(36);
174 let mut emit = |a: usize, b: usize, c: usize, uv: [Vec2; 3]| {
175 push_tri(&mut vertices, &mut uvs, p[a], p[b], p[c], uv);
176 };
177 emit(4, 5, 6, [uv0, uv1, uv2]);
178 emit(4, 6, 7, [uv0, uv2, uv3]);
179 emit(1, 0, 3, [uv0, uv1, uv2]);
180 emit(1, 3, 2, [uv0, uv2, uv3]);
181 emit(0, 4, 7, [uv0, uv1, uv2]);
182 emit(0, 7, 3, [uv0, uv2, uv3]);
183 emit(5, 1, 2, [uv0, uv1, uv2]);
184 emit(5, 2, 6, [uv0, uv2, uv3]);
185 emit(3, 7, 6, [uv0, uv1, uv2]);
186 emit(3, 6, 2, [uv0, uv2, uv3]);
187 emit(0, 1, 5, [uv0, uv1, uv2]);
188 emit(0, 5, 4, [uv0, uv2, uv3]);
189
190 Self::new(vertices, uvs)
191 }
192
193 pub fn sphere(radius: f32, segments: u32, rings: u32) -> Result<Self, Error> {
199 if !positive_finite(radius) {
200 return Err(Error::InvalidSize);
201 }
202 let segments = segments.max(3);
203 let rings = rings.max(2);
204 let total = (segments as usize)
205 .checked_mul(rings as usize)
206 .and_then(|v| v.checked_mul(6))
207 .ok_or(Error::Overflow)?;
208 if total == 0 || total > MAX_VERTEX_COUNT {
209 return Err(Error::InvalidSize);
210 }
211
212 let tau = core::f32::consts::TAU;
213 let pi = core::f32::consts::PI;
214 let mut vertices = Vec::with_capacity(total);
215 let mut uvs = Vec::with_capacity(total);
216
217 for ring in 0..rings {
218 let v0 = ring as f32 / rings as f32;
219 let v1 = (ring + 1) as f32 / rings as f32;
220 let lat0 = (v0 - 0.5) * pi;
221 let lat1 = (v1 - 0.5) * pi;
222 let y0 = sin(lat0) * radius;
223 let y1 = sin(lat1) * radius;
224 let r0 = cos(lat0) * radius;
225 let r1 = cos(lat1) * radius;
226
227 for segment in 0..segments {
228 let u0 = segment as f32 / segments as f32;
229 let u1 = (segment + 1) as f32 / segments as f32;
230 let lon0 = u0 * tau;
231 let lon1 = u1 * tau;
232
233 let p00 = Vec3::new(sin(lon0) * r0, y0, cos(lon0) * r0);
234 let p01 = Vec3::new(sin(lon1) * r0, y0, cos(lon1) * r0);
235 let p10 = Vec3::new(sin(lon0) * r1, y1, cos(lon0) * r1);
236 let p11 = Vec3::new(sin(lon1) * r1, y1, cos(lon1) * r1);
237
238 push_tri(
239 &mut vertices,
240 &mut uvs,
241 p00,
242 p11,
243 p10,
244 [Vec2::new(u0, v0), Vec2::new(u1, v1), Vec2::new(u0, v1)],
245 );
246 push_tri(
247 &mut vertices,
248 &mut uvs,
249 p00,
250 p01,
251 p11,
252 [Vec2::new(u0, v0), Vec2::new(u1, v0), Vec2::new(u1, v1)],
253 );
254 }
255 }
256
257 Self::new(vertices, uvs)
258 }
259}
260
261fn positive_finite(value: f32) -> bool {
262 value.is_finite() && value > 0.0
263}
264
265fn push_tri(
266 vertices: &mut Vec<Vec3>,
267 uvs: &mut Vec<Vec2>,
268 a: Vec3,
269 b: Vec3,
270 c: Vec3,
271 uv: [Vec2; 3],
272) {
273 vertices.extend([a, b, c]);
274 uvs.extend(uv);
275}
276
277#[cfg(test)]
278mod tests {
279 use alloc::vec;
280
281 use super::*;
282
283 #[test]
284 fn triangle_is_valid() {
285 let mesh = Mesh::triangle(1.0, 1.0).unwrap();
286 assert_eq!(mesh.vertex_count(), 3);
287 assert_eq!(mesh.triangle_count(), 1);
288 assert!(mesh.is_valid());
289 }
290
291 #[test]
292 fn cube_has_twelve_triangles() {
293 let mesh = Mesh::cube(1.0, 1.0, 1.0).unwrap();
294 assert_eq!(mesh.vertex_count(), 36);
295 assert_eq!(mesh.triangle_count(), 12);
296 }
297
298 #[test]
299 fn rejects_empty_mesh() {
300 assert_eq!(Mesh::new(Vec::new(), Vec::new()), Err(Error::InvalidSize));
301 }
302
303 #[test]
304 fn from_vertices_empty_is_invalid_size() {
305 assert_eq!(Mesh::from_vertices(Vec::new()), Err(Error::InvalidSize));
306 }
307
308 #[test]
309 fn rejects_non_triangle_vertex_count() {
310 let v = vec![Vec3::ZERO, Vec3::X, Vec3::Y, Vec3::Z];
311 assert_eq!(Mesh::from_vertices(v), Err(Error::InvalidData));
312 }
313
314 #[test]
315 fn rejects_uv_length_mismatch() {
316 let v = vec![Vec3::ZERO, Vec3::X, Vec3::Y];
317 let uv = vec![Vec2::ZERO];
318 assert_eq!(Mesh::new(v, uv), Err(Error::BufferMismatch));
319 }
320
321 #[test]
322 fn rejects_zero_or_negative_primitive_sizes() {
323 assert_eq!(Mesh::triangle(0.0, 1.0), Err(Error::InvalidSize));
324 assert_eq!(Mesh::plane(-1.0, 1.0), Err(Error::InvalidSize));
325 assert_eq!(Mesh::cube(1.0, 1.0, f32::NAN), Err(Error::InvalidSize));
326 assert_eq!(Mesh::sphere(0.0, 8, 4), Err(Error::InvalidSize));
327 }
328
329 #[test]
330 fn sphere_rejects_overflow() {
331 assert!(Mesh::sphere(1.0, u32::MAX, u32::MAX).is_err());
332 }
333
334 #[test]
335 fn sphere_vertex_count_matches_tessellation() {
336 let segments = 16u32;
337 let rings = 8u32;
338 let mesh = Mesh::sphere(0.5, segments, rings).unwrap();
339 assert_eq!(mesh.vertex_count(), (segments * rings * 6) as usize);
340 assert_eq!(mesh.triangle_normals().len(), mesh.triangle_count());
341 }
342}