Skip to main content

sable_gpu/
vertex.rs

1//! Vertex type definitions and layouts.
2//!
3//! This module provides the [`Vertex`] trait and common vertex types.
4
5use bytemuck::{Pod, Zeroable};
6
7/// A trait for vertex types that can be used in GPU buffers.
8///
9/// Types implementing this trait must be `Pod` (plain old data) and provide
10/// their vertex buffer layout for use in render pipelines.
11pub trait Vertex: Pod {
12    /// Returns the vertex buffer layout describing the attributes.
13    fn layout() -> wgpu::VertexBufferLayout<'static>;
14}
15
16/// A simple vertex with position and color.
17///
18/// This is the most basic vertex type, suitable for simple colored geometry.
19#[repr(C)]
20#[derive(Debug, Clone, Copy, Pod, Zeroable)]
21pub struct PositionColorVertex {
22    /// Position in 3D space (x, y, z).
23    pub position: [f32; 3],
24    /// RGB color values (r, g, b).
25    pub color: [f32; 3],
26}
27
28impl PositionColorVertex {
29    /// Create a new vertex with position and color.
30    #[must_use]
31    pub const fn new(position: [f32; 3], color: [f32; 3]) -> Self {
32        Self { position, color }
33    }
34}
35
36impl Vertex for PositionColorVertex {
37    fn layout() -> wgpu::VertexBufferLayout<'static> {
38        const ATTRIBUTES: &[wgpu::VertexAttribute] = &[
39            // Position at location 0
40            wgpu::VertexAttribute {
41                format: wgpu::VertexFormat::Float32x3,
42                offset: 0,
43                shader_location: 0,
44            },
45            // Color at location 1
46            wgpu::VertexAttribute {
47                format: wgpu::VertexFormat::Float32x3,
48                offset: std::mem::size_of::<[f32; 3]>() as u64,
49                shader_location: 1,
50            },
51        ];
52
53        wgpu::VertexBufferLayout {
54            array_stride: std::mem::size_of::<PositionColorVertex>() as u64,
55            step_mode: wgpu::VertexStepMode::Vertex,
56            attributes: ATTRIBUTES,
57        }
58    }
59}
60
61/// A vertex with position, color, and texture coordinates.
62#[repr(C)]
63#[derive(Debug, Clone, Copy, Pod, Zeroable)]
64pub struct PositionColorTexVertex {
65    /// Position in 3D space (x, y, z).
66    pub position: [f32; 3],
67    /// RGB color values (r, g, b).
68    pub color: [f32; 3],
69    /// Texture coordinates (u, v).
70    pub tex_coords: [f32; 2],
71}
72
73impl PositionColorTexVertex {
74    /// Create a new vertex with position, color, and texture coordinates.
75    #[must_use]
76    pub const fn new(position: [f32; 3], color: [f32; 3], tex_coords: [f32; 2]) -> Self {
77        Self {
78            position,
79            color,
80            tex_coords,
81        }
82    }
83}
84
85impl Vertex for PositionColorTexVertex {
86    fn layout() -> wgpu::VertexBufferLayout<'static> {
87        const ATTRIBUTES: &[wgpu::VertexAttribute] = &[
88            // Position at location 0
89            wgpu::VertexAttribute {
90                format: wgpu::VertexFormat::Float32x3,
91                offset: 0,
92                shader_location: 0,
93            },
94            // Color at location 1
95            wgpu::VertexAttribute {
96                format: wgpu::VertexFormat::Float32x3,
97                offset: std::mem::size_of::<[f32; 3]>() as u64,
98                shader_location: 1,
99            },
100            // Texture coordinates at location 2
101            wgpu::VertexAttribute {
102                format: wgpu::VertexFormat::Float32x2,
103                offset: (std::mem::size_of::<[f32; 3]>() * 2) as u64,
104                shader_location: 2,
105            },
106        ];
107
108        wgpu::VertexBufferLayout {
109            array_stride: std::mem::size_of::<PositionColorTexVertex>() as u64,
110            step_mode: wgpu::VertexStepMode::Vertex,
111            attributes: ATTRIBUTES,
112        }
113    }
114}
115
116#[cfg(test)]
117mod tests {
118    use super::*;
119
120    #[test]
121    fn test_position_color_vertex_new() {
122        let vertex = PositionColorVertex::new([1.0, 2.0, 3.0], [0.5, 0.6, 0.7]);
123        assert_eq!(vertex.position, [1.0, 2.0, 3.0]);
124        assert_eq!(vertex.color, [0.5, 0.6, 0.7]);
125    }
126
127    #[test]
128    fn test_position_color_vertex_layout() {
129        let layout = PositionColorVertex::layout();
130        assert_eq!(
131            layout.array_stride,
132            std::mem::size_of::<PositionColorVertex>() as u64
133        );
134        assert_eq!(layout.step_mode, wgpu::VertexStepMode::Vertex);
135        assert_eq!(layout.attributes.len(), 2);
136
137        // Position attribute
138        assert_eq!(layout.attributes[0].shader_location, 0);
139        assert_eq!(layout.attributes[0].format, wgpu::VertexFormat::Float32x3);
140        assert_eq!(layout.attributes[0].offset, 0);
141
142        // Color attribute
143        assert_eq!(layout.attributes[1].shader_location, 1);
144        assert_eq!(layout.attributes[1].format, wgpu::VertexFormat::Float32x3);
145        assert_eq!(
146            layout.attributes[1].offset,
147            std::mem::size_of::<[f32; 3]>() as u64
148        );
149    }
150
151    #[test]
152    fn test_position_color_tex_vertex_new() {
153        let vertex = PositionColorTexVertex::new([1.0, 2.0, 3.0], [0.5, 0.6, 0.7], [0.0, 1.0]);
154        assert_eq!(vertex.position, [1.0, 2.0, 3.0]);
155        assert_eq!(vertex.color, [0.5, 0.6, 0.7]);
156        assert_eq!(vertex.tex_coords, [0.0, 1.0]);
157    }
158
159    #[test]
160    fn test_position_color_tex_vertex_layout() {
161        let layout = PositionColorTexVertex::layout();
162        assert_eq!(
163            layout.array_stride,
164            std::mem::size_of::<PositionColorTexVertex>() as u64
165        );
166        assert_eq!(layout.attributes.len(), 3);
167
168        // Texture coordinate attribute
169        assert_eq!(layout.attributes[2].shader_location, 2);
170        assert_eq!(layout.attributes[2].format, wgpu::VertexFormat::Float32x2);
171    }
172
173    #[test]
174    fn test_vertex_size_alignment() {
175        // Ensure proper memory layout for GPU
176        assert_eq!(std::mem::size_of::<PositionColorVertex>(), 24); // 6 * 4 bytes
177        assert_eq!(std::mem::size_of::<PositionColorTexVertex>(), 32); // 8 * 4 bytes
178    }
179
180    #[test]
181    fn test_vertex_is_pod() {
182        // Verify vertices can be safely cast to bytes
183        let vertex = PositionColorVertex::new([1.0, 0.0, 0.0], [1.0, 1.0, 1.0]);
184        let bytes: &[u8] = bytemuck::bytes_of(&vertex);
185        assert_eq!(bytes.len(), 24);
186    }
187}