Skip to main content

astrelis_geometry/
vertex.rs

1//! Vertex formats for tessellated geometry.
2//!
3//! Defines the vertex formats used after tessellation.
4
5use astrelis_render::wgpu;
6use bytemuck::{Pod, Zeroable};
7
8/// Vertex for filled geometry.
9///
10/// Simple 2D position vertex for tessellated fills.
11#[repr(C)]
12#[derive(Copy, Clone, Debug, Pod, Zeroable)]
13pub struct FillVertex {
14    /// Position in 2D space
15    pub position: [f32; 2],
16}
17
18impl FillVertex {
19    /// Create a new fill vertex.
20    pub fn new(x: f32, y: f32) -> Self {
21        Self { position: [x, y] }
22    }
23
24    /// Get the WGPU vertex buffer layout.
25    pub fn vertex_layout() -> wgpu::VertexBufferLayout<'static> {
26        wgpu::VertexBufferLayout {
27            array_stride: std::mem::size_of::<Self>() as u64,
28            step_mode: wgpu::VertexStepMode::Vertex,
29            attributes: &[wgpu::VertexAttribute {
30                offset: 0,
31                shader_location: 0,
32                format: wgpu::VertexFormat::Float32x2,
33            }],
34        }
35    }
36}
37
38/// Vertex for stroked geometry.
39///
40/// Includes position and normal for stroke expansion.
41#[repr(C)]
42#[derive(Copy, Clone, Debug, Pod, Zeroable)]
43pub struct StrokeVertex {
44    /// Position in 2D space (on the path centerline)
45    pub position: [f32; 2],
46    /// Normal vector (perpendicular to path)
47    pub normal: [f32; 2],
48    /// Distance along the path (for dash patterns)
49    pub distance: f32,
50    /// Side indicator (-1 or 1 for left/right of path)
51    pub side: f32,
52}
53
54impl StrokeVertex {
55    /// Create a new stroke vertex.
56    pub fn new(x: f32, y: f32, nx: f32, ny: f32, distance: f32, side: f32) -> Self {
57        Self {
58            position: [x, y],
59            normal: [nx, ny],
60            distance,
61            side,
62        }
63    }
64
65    /// Get the WGPU vertex buffer layout.
66    pub fn vertex_layout() -> wgpu::VertexBufferLayout<'static> {
67        wgpu::VertexBufferLayout {
68            array_stride: std::mem::size_of::<Self>() as u64,
69            step_mode: wgpu::VertexStepMode::Vertex,
70            attributes: &[
71                wgpu::VertexAttribute {
72                    offset: 0,
73                    shader_location: 0,
74                    format: wgpu::VertexFormat::Float32x2,
75                },
76                wgpu::VertexAttribute {
77                    offset: 8,
78                    shader_location: 1,
79                    format: wgpu::VertexFormat::Float32x2,
80                },
81                wgpu::VertexAttribute {
82                    offset: 16,
83                    shader_location: 2,
84                    format: wgpu::VertexFormat::Float32,
85                },
86                wgpu::VertexAttribute {
87                    offset: 20,
88                    shader_location: 3,
89                    format: wgpu::VertexFormat::Float32,
90                },
91            ],
92        }
93    }
94}
95
96/// Output from tessellation: vertices and indices.
97#[derive(Debug, Clone, Default)]
98pub struct TessellatedMesh<V> {
99    /// Vertex data
100    pub vertices: Vec<V>,
101    /// Index data (triangles)
102    pub indices: Vec<u32>,
103}
104
105impl<V> TessellatedMesh<V> {
106    /// Create a new empty mesh.
107    pub fn new() -> Self {
108        Self {
109            vertices: Vec::new(),
110            indices: Vec::new(),
111        }
112    }
113
114    /// Create a mesh with given vertices and indices.
115    pub fn from_data(vertices: Vec<V>, indices: Vec<u32>) -> Self {
116        Self { vertices, indices }
117    }
118
119    /// Check if the mesh is empty.
120    pub fn is_empty(&self) -> bool {
121        self.vertices.is_empty() || self.indices.is_empty()
122    }
123
124    /// Get the number of vertices.
125    pub fn vertex_count(&self) -> usize {
126        self.vertices.len()
127    }
128
129    /// Get the number of indices.
130    pub fn index_count(&self) -> usize {
131        self.indices.len()
132    }
133
134    /// Get the number of triangles.
135    pub fn triangle_count(&self) -> usize {
136        self.indices.len() / 3
137    }
138
139    /// Clear all data.
140    pub fn clear(&mut self) {
141        self.vertices.clear();
142        self.indices.clear();
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_fill_vertex_size() {
152        assert_eq!(std::mem::size_of::<FillVertex>(), 8);
153    }
154
155    #[test]
156    fn test_stroke_vertex_size() {
157        assert_eq!(std::mem::size_of::<StrokeVertex>(), 24);
158    }
159
160    #[test]
161    fn test_empty_mesh() {
162        let mesh: TessellatedMesh<FillVertex> = TessellatedMesh::new();
163        assert!(mesh.is_empty());
164        assert_eq!(mesh.vertex_count(), 0);
165        assert_eq!(mesh.triangle_count(), 0);
166    }
167}