Skip to main content

engvis_core/
mesh.rs

1use crate::aabb::Aabb;
2
3/// GPU-ready vertex for PBR rendering
4#[repr(C)]
5#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
6pub struct MeshVertex {
7    pub position: [f32; 3],
8    pub normal: [f32; 3],
9    pub uv: [f32; 2],
10    pub tangent: [f32; 4],
11}
12
13impl MeshVertex {
14    pub const SIZE: usize = std::mem::size_of::<Self>();
15    pub const ATTRIB_COUNT: usize = 4;
16}
17
18/// A sub-mesh references a material index
19#[derive(Debug, Clone)]
20pub struct SubMesh {
21    pub material_index: usize,
22    pub index_offset: u32,
23    pub index_count: u32,
24}
25
26/// A complete mesh with vertices and indices
27#[derive(Debug, Clone)]
28pub struct Mesh {
29    pub name: String,
30    pub vertices: Vec<MeshVertex>,
31    pub indices: Vec<u32>,
32    pub sub_meshes: Vec<SubMesh>,
33    pub aabb: Aabb,
34}
35
36impl Mesh {
37    /// Extract edge indices from triangle indices for edge (line) rendering.
38    /// For each triangle (i0, i1, i2), generates 3 edges: (i0, i1), (i1, i2), (i2, i0).
39    pub fn extract_edge_indices(&self) -> Vec<u32> {
40        let mut edge_indices = Vec::with_capacity(self.indices.len() * 2);
41        for tri in self.indices.chunks(3) {
42            if tri.len() == 3 {
43                edge_indices.extend_from_slice(&[tri[0], tri[1], tri[1], tri[2], tri[2], tri[0]]);
44            }
45        }
46        edge_indices
47    }
48}
49
50/// Create a unit cube mesh with normals, UVs, and tangents
51pub fn create_cube_mesh() -> Mesh {
52    let mut vertices = Vec::new();
53    let mut indices = Vec::new();
54    let mut aabb = Aabb::empty();
55
56    // Each face: 4 vertices with position, normal, uv, tangent
57    let faces: [([f32; 3], [f32; 3], [f32; 4]); 6] = [
58        // +Y (top)
59        ([0.0, 1.0, 0.0], [0.0, 1.0, 0.0], [1.0, 0.0, 0.0, 1.0]),
60        // -Y (bottom)
61        ([0.0, -1.0, 0.0], [0.0, -1.0, 0.0], [-1.0, 0.0, 0.0, 1.0]),
62        // +X (right)
63        ([1.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 0.0, -1.0, 1.0]),
64        // -X (left)
65        ([-1.0, 0.0, 0.0], [-1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 1.0]),
66        // +Z (front)
67        ([0.0, 0.0, 1.0], [0.0, 0.0, 1.0], [1.0, 0.0, 0.0, 1.0]),
68        // -Z (back)
69        ([0.0, 0.0, -1.0], [0.0, 0.0, -1.0], [-1.0, 0.0, 0.0, 1.0]),
70    ];
71
72    let face_vertices: [[([f32; 3], [f32; 2]); 4]; 6] = [
73        // +Y top
74        [
75            ([-0.5, 0.5, -0.5], [0.0, 0.0]),
76            ([0.5, 0.5, -0.5], [1.0, 0.0]),
77            ([0.5, 0.5, 0.5], [1.0, 1.0]),
78            ([-0.5, 0.5, 0.5], [0.0, 1.0]),
79        ],
80        // -Y bottom
81        [
82            ([-0.5, -0.5, 0.5], [0.0, 0.0]),
83            ([0.5, -0.5, 0.5], [1.0, 0.0]),
84            ([0.5, -0.5, -0.5], [1.0, 1.0]),
85            ([-0.5, -0.5, -0.5], [0.0, 1.0]),
86        ],
87        // +X right
88        [
89            ([0.5, -0.5, -0.5], [0.0, 0.0]),
90            ([0.5, -0.5, 0.5], [1.0, 0.0]),
91            ([0.5, 0.5, 0.5], [1.0, 1.0]),
92            ([0.5, 0.5, -0.5], [0.0, 1.0]),
93        ],
94        // -X left
95        [
96            ([-0.5, -0.5, 0.5], [0.0, 0.0]),
97            ([-0.5, -0.5, -0.5], [1.0, 0.0]),
98            ([-0.5, 0.5, -0.5], [1.0, 1.0]),
99            ([-0.5, 0.5, 0.5], [0.0, 1.0]),
100        ],
101        // +Z front
102        [
103            ([0.5, -0.5, 0.5], [0.0, 0.0]),
104            ([-0.5, -0.5, 0.5], [1.0, 0.0]),
105            ([-0.5, 0.5, 0.5], [1.0, 1.0]),
106            ([0.5, 0.5, 0.5], [0.0, 1.0]),
107        ],
108        // -Z back
109        [
110            ([-0.5, -0.5, -0.5], [0.0, 0.0]),
111            ([0.5, -0.5, -0.5], [1.0, 0.0]),
112            ([0.5, 0.5, -0.5], [1.0, 1.0]),
113            ([-0.5, 0.5, -0.5], [0.0, 1.0]),
114        ],
115    ];
116
117    for (face_idx, (normal, _, tangent)) in faces.iter().enumerate() {
118        let base = vertices.len() as u32;
119        for (pos, uv) in &face_vertices[face_idx] {
120            vertices.push(MeshVertex {
121                position: *pos,
122                normal: *normal,
123                uv: *uv,
124                tangent: *tangent,
125            });
126            aabb.extend_pos(*pos);
127        }
128        indices.extend_from_slice(&[
129            base, base + 2, base + 1,
130            base, base + 3, base + 2,
131        ]);
132    }
133
134    let index_count = indices.len() as u32;
135    Mesh {
136        name: "Cube".to_string(),
137        vertices,
138        indices,
139        sub_meshes: vec![SubMesh {
140            material_index: 0,
141            index_offset: 0,
142            index_count,
143        }],
144        aabb,
145    }
146}
147
148impl Aabb {
149    fn extend_pos(&mut self, pos: [f32; 3]) {
150        let v = glam::Vec3::from(pos);
151        self.min = self.min.min(v);
152        self.max = self.max.max(v);
153    }
154}