1use crate::color::Color;
2use crate::math::Aabb;
3use glam::{Vec2, Vec3};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone, Copy, PartialEq)]
9pub struct Vertex {
10 pub position: Vec3,
11 pub normal: Vec3,
12 pub uv: Vec2,
13 pub color: Color,
14 pub tangent: Vec3,
15}
16
17impl Vertex {
18 pub fn new(position: Vec3, normal: Vec3, uv: Vec2) -> Self {
19 Self { position, normal, uv, color: Color::WHITE, tangent: Vec3::X }
20 }
21
22 pub fn with_color(mut self, c: Color) -> Self {
23 self.color = c;
24 self
25 }
26}
27
28#[derive(Debug, Clone)]
31pub struct Mesh {
32 pub vertices: Vec<Vertex>,
33 pub indices: Vec<u32>,
34 pub aabb: Aabb,
35}
36
37impl Mesh {
38 pub fn new(vertices: Vec<Vertex>, indices: Vec<u32>) -> Self {
39 let positions: Vec<Vec3> = vertices.iter().map(|v| v.position).collect();
40 let aabb = if positions.is_empty() {
41 Aabb::new(Vec3::ZERO, Vec3::ZERO)
42 } else {
43 Aabb::from_points(&positions)
44 };
45 Self { vertices, indices, aabb }
46 }
47
48 pub fn triangle_count(&self) -> usize {
49 self.indices.len() / 3
50 }
51
52 pub fn compute_normals(&mut self) {
54 let mut normals = vec![Vec3::ZERO; self.vertices.len()];
55 for tri in self.indices.chunks(3) {
56 let (i0, i1, i2) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
57 let p0 = self.vertices[i0].position;
58 let p1 = self.vertices[i1].position;
59 let p2 = self.vertices[i2].position;
60 let n = (p1 - p0).cross(p2 - p0);
61 normals[i0] += n;
62 normals[i1] += n;
63 normals[i2] += n;
64 }
65 for (v, n) in self.vertices.iter_mut().zip(normals) {
66 v.normal = n.normalize_or_zero();
67 }
68 }
69
70 pub fn compute_tangents(&mut self) {
72 let mut tangents = vec![Vec3::ZERO; self.vertices.len()];
73 for tri in self.indices.chunks(3) {
74 let (i0, i1, i2) = (tri[0] as usize, tri[1] as usize, tri[2] as usize);
75 let p0 = self.vertices[i0].position;
76 let p1 = self.vertices[i1].position;
77 let p2 = self.vertices[i2].position;
78 let uv0 = self.vertices[i0].uv;
79 let uv1 = self.vertices[i1].uv;
80 let uv2 = self.vertices[i2].uv;
81 let e1 = p1 - p0;
82 let e2 = p2 - p0;
83 let du1 = uv1.x - uv0.x;
84 let dv1 = uv1.y - uv0.y;
85 let du2 = uv2.x - uv0.x;
86 let dv2 = uv2.y - uv0.y;
87 let denom = du1 * dv2 - du2 * dv1;
88 if denom.abs() < 1e-8 {
89 continue;
90 }
91 let inv = 1.0 / denom;
92 let t = (e1 * dv2 - e2 * dv1) * inv;
93 tangents[i0] += t;
94 tangents[i1] += t;
95 tangents[i2] += t;
96 }
97 for (v, t) in self.vertices.iter_mut().zip(tangents) {
98 v.tangent = t.normalize_or_zero();
99 }
100 }
101}
102
103pub struct MeshBuilder {
106 vertices: Vec<Vertex>,
107 indices: Vec<u32>,
108}
109
110impl MeshBuilder {
111 pub fn new() -> Self {
112 Self { vertices: Vec::new(), indices: Vec::new() }
113 }
114
115 pub fn add_vertex(&mut self, v: Vertex) -> u32 {
116 let idx = self.vertices.len() as u32;
117 self.vertices.push(v);
118 idx
119 }
120
121 pub fn add_triangle(&mut self, i0: u32, i1: u32, i2: u32) {
122 self.indices.extend_from_slice(&[i0, i1, i2]);
123 }
124
125 pub fn build(self) -> Mesh {
126 Mesh::new(self.vertices, self.indices)
127 }
128}
129
130impl Default for MeshBuilder {
131 fn default() -> Self {
132 Self::new()
133 }
134}
135
136pub fn cube(half: f32) -> Mesh {
140 let h = half;
141 let faces: &[(Vec3, Vec3, Vec3)] = &[
142 (Vec3::Z, Vec3::X, Vec3::Y), (-Vec3::Z, -Vec3::X, Vec3::Y), (Vec3::Y, Vec3::X, -Vec3::Z), (-Vec3::Y, Vec3::X, Vec3::Z), (Vec3::X, -Vec3::Z, Vec3::Y), (-Vec3::X, Vec3::Z, Vec3::Y), ];
150 let mut verts = Vec::new();
151 let mut idx = Vec::new();
152 for &(n, tu, tv) in faces {
153 let base = verts.len() as u32;
154 let center = n * h;
155 let corners = [
156 center - tu * h - tv * h,
157 center + tu * h - tv * h,
158 center + tu * h + tv * h,
159 center - tu * h + tv * h,
160 ];
161 let uvs = [
162 Vec2::new(0.0, 0.0),
163 Vec2::new(1.0, 0.0),
164 Vec2::new(1.0, 1.0),
165 Vec2::new(0.0, 1.0),
166 ];
167 for (p, uv) in corners.iter().zip(uvs.iter()) {
168 verts.push(Vertex {
169 position: *p,
170 normal: n,
171 uv: *uv,
172 color: Color::WHITE,
173 tangent: tu,
174 });
175 }
176 idx.extend_from_slice(&[base, base + 1, base + 2, base, base + 2, base + 3]);
177 }
178 Mesh::new(verts, idx)
179}
180
181pub fn sphere(radius: f32, rings: u32, sectors: u32) -> Mesh {
183 let rings = rings.max(2);
184 let sectors = sectors.max(3);
185 let mut verts = Vec::new();
186 let mut idx = Vec::new();
187
188 for r in 0..=rings {
189 let phi = std::f32::consts::PI * r as f32 / rings as f32;
190 let (sin_phi, cos_phi) = phi.sin_cos();
191 for s in 0..=sectors {
192 let theta = 2.0 * std::f32::consts::PI * s as f32 / sectors as f32;
193 let (sin_t, cos_t) = theta.sin_cos();
194 let x = sin_phi * cos_t;
195 let y = cos_phi;
196 let z = sin_phi * sin_t;
197 let p = Vec3::new(x, y, z);
198 let u = s as f32 / sectors as f32;
199 let v = r as f32 / rings as f32;
200 verts.push(Vertex {
201 position: p * radius,
202 normal: p,
203 uv: Vec2::new(u, v),
204 color: Color::WHITE,
205 tangent: Vec3::new(-sin_t, 0.0, cos_t),
206 });
207 }
208 }
209
210 let w = sectors + 1;
211 for r in 0..rings {
212 for s in 0..sectors {
213 let i0 = r * w + s;
214 let i1 = i0 + 1;
215 let i2 = (r + 1) * w + s;
216 let i3 = i2 + 1;
217 idx.extend_from_slice(&[i0, i2, i1, i1, i2, i3]);
218 }
219 }
220 Mesh::new(verts, idx)
221}
222
223pub fn icosphere(radius: f32, subdivisions: u32) -> Mesh {
225 let t = (1.0 + 5.0_f32.sqrt()) / 2.0;
226 let base_verts: &[[f32; 3]] = &[
227 [-1.0, t, 0.0],
228 [1.0, t, 0.0],
229 [-1.0, -t, 0.0],
230 [1.0, -t, 0.0],
231 [0.0, -1.0, t],
232 [0.0, 1.0, t],
233 [0.0, -1.0, -t],
234 [0.0, 1.0, -t],
235 [t, 0.0, -1.0],
236 [t, 0.0, 1.0],
237 [-t, 0.0, -1.0],
238 [-t, 0.0, 1.0],
239 ];
240 let base_faces: &[[u32; 3]] = &[
241 [0, 11, 5],
242 [0, 5, 1],
243 [0, 1, 7],
244 [0, 7, 10],
245 [0, 10, 11],
246 [1, 5, 9],
247 [5, 11, 4],
248 [11, 10, 2],
249 [10, 7, 6],
250 [7, 1, 8],
251 [3, 9, 4],
252 [3, 4, 2],
253 [3, 2, 6],
254 [3, 6, 8],
255 [3, 8, 9],
256 [4, 9, 5],
257 [2, 4, 11],
258 [6, 2, 10],
259 [8, 6, 7],
260 [9, 8, 1],
261 ];
262
263 let mut points: Vec<Vec3> = base_verts
264 .iter()
265 .map(|v| Vec3::new(v[0], v[1], v[2]).normalize())
266 .collect();
267 let mut faces: Vec<[u32; 3]> = base_faces.to_vec();
268
269 let mut midpoint_cache: HashMap<u64, u32> = HashMap::new();
270
271 let mut get_midpoint = |a: u32, b: u32, pts: &mut Vec<Vec3>| -> u32 {
272 let key = if a < b {
273 ((a as u64) << 32) | b as u64
274 } else {
275 ((b as u64) << 32) | a as u64
276 };
277 if let Some(&idx) = midpoint_cache.get(&key) {
278 return idx;
279 }
280 let mid = (pts[a as usize] + pts[b as usize]).normalize();
281 let idx = pts.len() as u32;
282 pts.push(mid);
283 midpoint_cache.insert(key, idx);
284 idx
285 };
286
287 for _ in 0..subdivisions {
288 let mut new_faces = Vec::with_capacity(faces.len() * 4);
289 for tri in &faces {
290 let m0 = get_midpoint(tri[0], tri[1], &mut points);
291 let m1 = get_midpoint(tri[1], tri[2], &mut points);
292 let m2 = get_midpoint(tri[2], tri[0], &mut points);
293 new_faces.push([tri[0], m0, m2]);
294 new_faces.push([tri[1], m1, m0]);
295 new_faces.push([tri[2], m2, m1]);
296 new_faces.push([m0, m1, m2]);
297 }
298 faces = new_faces;
299 }
300
301 let verts: Vec<Vertex> = points
302 .iter()
303 .map(|&p| {
304 let u = p.z.atan2(p.x) / (2.0 * std::f32::consts::PI) + 0.5;
305 let v = p.y.asin() / std::f32::consts::PI + 0.5;
306 Vertex {
307 position: p * radius,
308 normal: p,
309 uv: Vec2::new(u, v),
310 color: Color::WHITE,
311 tangent: Vec3::new(-p.z, 0.0, p.x).normalize_or_zero(),
312 }
313 })
314 .collect();
315
316 let indices: Vec<u32> = faces.iter().flat_map(|t| t.iter().cloned()).collect();
317 Mesh::new(verts, indices)
318}
319
320pub fn cone(radius: f32, height: f32, segments: u32) -> Mesh {
322 let segments = segments.max(3);
323 let mut verts = Vec::new();
324 let mut idx = Vec::new();
325 let apex = Vec3::new(0.0, height * 0.5, 0.0);
326 let apex_idx = verts.len() as u32;
327 verts.push(Vertex {
328 position: apex,
329 normal: Vec3::Y,
330 uv: Vec2::new(0.5, 0.0),
331 color: Color::WHITE,
332 tangent: Vec3::X,
333 });
334 let base_center_idx = verts.len() as u32;
335 verts.push(Vertex {
336 position: Vec3::new(0.0, -height * 0.5, 0.0),
337 normal: -Vec3::Y,
338 uv: Vec2::new(0.5, 0.5),
339 color: Color::WHITE,
340 tangent: Vec3::X,
341 });
342
343 let first_base = verts.len() as u32;
344 for i in 0..=segments {
345 let angle = 2.0 * std::f32::consts::PI * i as f32 / segments as f32;
346 let (s, c) = angle.sin_cos();
347 let pos = Vec3::new(c * radius, -height * 0.5, s * radius);
348 let side_n = Vec3::new(c * height, radius, s * height).normalize();
349 let uv = Vec2::new(i as f32 / segments as f32, 1.0);
350 verts.push(Vertex {
351 position: pos,
352 normal: side_n,
353 uv,
354 color: Color::WHITE,
355 tangent: Vec3::new(-s, 0.0, c),
356 });
357 }
358 let first_base_cap = verts.len() as u32;
359 for i in 0..=segments {
360 let angle = 2.0 * std::f32::consts::PI * i as f32 / segments as f32;
361 let (s, c) = angle.sin_cos();
362 let pos = Vec3::new(c * radius, -height * 0.5, s * radius);
363 let uv = Vec2::new(c * 0.5 + 0.5, s * 0.5 + 0.5);
364 verts.push(Vertex {
365 position: pos,
366 normal: -Vec3::Y,
367 uv,
368 color: Color::WHITE,
369 tangent: Vec3::X,
370 });
371 }
372 for i in 0..segments {
374 idx.extend_from_slice(&[apex_idx, first_base + i, first_base + i + 1]);
375 }
376 for i in 0..segments {
378 idx.extend_from_slice(&[base_center_idx, first_base_cap + i + 1, first_base_cap + i]);
379 }
380 Mesh::new(verts, idx)
381}
382
383pub fn pyramid(base_half: f32, height: f32) -> Mesh {
385 let h2 = height * 0.5;
386 let b = base_half;
387 let apex = Vec3::new(0.0, h2, 0.0);
388
389 let base_pts = [
390 Vec3::new(-b, -h2, -b),
391 Vec3::new(b, -h2, -b),
392 Vec3::new(b, -h2, b),
393 Vec3::new(-b, -h2, b),
394 ];
395
396 let mut verts = Vec::new();
397 let mut idx = Vec::new();
398
399 for i in 0..4 {
401 let a = base_pts[i];
402 let c = base_pts[(i + 1) % 4];
403 let n = (c - a).cross(apex - a).normalize();
404 let base = verts.len() as u32;
405 verts.push(Vertex {
406 position: apex,
407 normal: n,
408 uv: Vec2::new(0.5, 0.0),
409 color: Color::WHITE,
410 tangent: Vec3::X,
411 });
412 verts.push(Vertex {
413 position: a,
414 normal: n,
415 uv: Vec2::new(0.0, 1.0),
416 color: Color::WHITE,
417 tangent: Vec3::X,
418 });
419 verts.push(Vertex {
420 position: c,
421 normal: n,
422 uv: Vec2::new(1.0, 1.0),
423 color: Color::WHITE,
424 tangent: Vec3::X,
425 });
426 idx.extend_from_slice(&[base, base + 1, base + 2]);
427 }
428
429 let bn = -Vec3::Y;
431 let base_start = verts.len() as u32;
432 for (i, &p) in base_pts.iter().enumerate() {
433 let u = if i == 1 || i == 2 { 1.0 } else { 0.0 };
434 let v = if i == 2 || i == 3 { 1.0 } else { 0.0 };
435 verts.push(Vertex {
436 position: p,
437 normal: bn,
438 uv: Vec2::new(u, v),
439 color: Color::WHITE,
440 tangent: Vec3::X,
441 });
442 }
443 idx.extend_from_slice(&[
444 base_start,
445 base_start + 2,
446 base_start + 1,
447 base_start,
448 base_start + 3,
449 base_start + 2,
450 ]);
451
452 Mesh::new(verts, idx)
453}
454
455pub fn cylinder(radius: f32, height: f32, segments: u32) -> Mesh {
457 let segments = segments.max(3);
458 let h2 = height * 0.5;
459 let mut verts = Vec::new();
460 let mut idx = Vec::new();
461
462 let side_start = 0u32;
464 for i in 0..=segments {
465 let angle = 2.0 * std::f32::consts::PI * i as f32 / segments as f32;
466 let (s, c) = angle.sin_cos();
467 let n = Vec3::new(c, 0.0, s);
468 let u = i as f32 / segments as f32;
469 verts.push(Vertex {
470 position: Vec3::new(c * radius, h2, s * radius),
471 normal: n,
472 uv: Vec2::new(u, 0.0),
473 color: Color::WHITE,
474 tangent: Vec3::new(-s, 0.0, c),
475 });
476 verts.push(Vertex {
477 position: Vec3::new(c * radius, -h2, s * radius),
478 normal: n,
479 uv: Vec2::new(u, 1.0),
480 color: Color::WHITE,
481 tangent: Vec3::new(-s, 0.0, c),
482 });
483 }
484 for i in 0..segments {
485 let b = side_start + i * 2;
486 idx.extend_from_slice(&[b, b + 2, b + 1, b + 1, b + 2, b + 3]);
487 }
488
489 for (cap_y, cap_n, flip) in [(h2, Vec3::Y, false), (-h2, -Vec3::Y, true)] {
491 let center = verts.len() as u32;
492 verts.push(Vertex {
493 position: Vec3::new(0.0, cap_y, 0.0),
494 normal: cap_n,
495 uv: Vec2::new(0.5, 0.5),
496 color: Color::WHITE,
497 tangent: Vec3::X,
498 });
499 let first = verts.len() as u32;
500 for i in 0..=segments {
501 let angle = 2.0 * std::f32::consts::PI * i as f32 / segments as f32;
502 let (s, c) = angle.sin_cos();
503 verts.push(Vertex {
504 position: Vec3::new(c * radius, cap_y, s * radius),
505 normal: cap_n,
506 uv: Vec2::new(c * 0.5 + 0.5, s * 0.5 + 0.5),
507 color: Color::WHITE,
508 tangent: Vec3::X,
509 });
510 }
511 for i in 0..segments {
512 if flip {
513 idx.extend_from_slice(&[center, first + i, first + i + 1]);
514 } else {
515 idx.extend_from_slice(&[center, first + i + 1, first + i]);
516 }
517 }
518 }
519
520 Mesh::new(verts, idx)
521}
522
523pub fn torus(major_radius: f32, tube_radius: f32, major_segs: u32, tube_segs: u32) -> Mesh {
525 let major_segs = major_segs.max(3);
526 let tube_segs = tube_segs.max(3);
527 let mut verts = Vec::new();
528 let mut idx = Vec::new();
529
530 for i in 0..=major_segs {
531 let u = 2.0 * std::f32::consts::PI * i as f32 / major_segs as f32;
532 let (su, cu) = u.sin_cos();
533 let center = Vec3::new(cu * major_radius, 0.0, su * major_radius);
534 let radial = Vec3::new(cu, 0.0, su);
535 for j in 0..=tube_segs {
536 let v = 2.0 * std::f32::consts::PI * j as f32 / tube_segs as f32;
537 let (sv, cv) = v.sin_cos();
538 let pos = center + (radial * cv + Vec3::Y * sv) * tube_radius;
539 let n = (radial * cv + Vec3::Y * sv).normalize();
540 verts.push(Vertex {
541 position: pos,
542 normal: n,
543 uv: Vec2::new(i as f32 / major_segs as f32, j as f32 / tube_segs as f32),
544 color: Color::WHITE,
545 tangent: Vec3::new(-su, 0.0, cu),
546 });
547 }
548 }
549
550 let w = tube_segs + 1;
551 for i in 0..major_segs {
552 for j in 0..tube_segs {
553 let i0 = i * w + j;
554 let i1 = i0 + 1;
555 let i2 = (i + 1) * w + j;
556 let i3 = i2 + 1;
557 idx.extend_from_slice(&[i0, i1, i2, i1, i3, i2]);
558 }
559 }
560 Mesh::new(verts, idx)
561}
562
563pub fn plane(half_size: f32, subdivisions: u32) -> Mesh {
565 let n = subdivisions.max(1) + 1;
566 let mut verts = Vec::new();
567 let mut idx = Vec::new();
568 let step = half_size * 2.0 / subdivisions.max(1) as f32;
569 for row in 0..n {
570 for col in 0..n {
571 let x = -half_size + col as f32 * step;
572 let z = -half_size + row as f32 * step;
573 let u = col as f32 / (n - 1) as f32;
574 let v = row as f32 / (n - 1) as f32;
575 verts.push(Vertex {
576 position: Vec3::new(x, 0.0, z),
577 normal: Vec3::Y,
578 uv: Vec2::new(u, v),
579 color: Color::WHITE,
580 tangent: Vec3::X,
581 });
582 }
583 }
584 for row in 0..n - 1 {
585 for col in 0..n - 1 {
586 let i0 = row * n + col;
587 let i1 = i0 + 1;
588 let i2 = (row + 1) * n + col;
589 let i3 = i2 + 1;
590 idx.extend_from_slice(&[i0, i2, i1, i1, i2, i3]);
591 }
592 }
593 Mesh::new(verts, idx)
594}