Skip to main content

cvkg_render_gpu/
vertex.rs

1//! Vertex layouts, instance data, and tessellation vertex constructors.
2use lyon::tessellation::{
3    FillVertex, FillVertexConstructor, StrokeVertex, StrokeVertexConstructor,
4};
5
6#[repr(C)]
7#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
8pub struct Vertex {
9    pub position: [f32; 3],
10    pub normal: [f32; 3],
11    pub uv: [f32; 2],
12    pub color: [f32; 4],
13    pub material_id: u32,
14    pub radius: f32,
15    pub slice: [f32; 4],
16    pub logical: [f32; 2],
17    pub size: [f32; 2],
18    pub clip: [f32; 4], // [x, y, width, height]
19    pub tex_index: u32,
20}
21
22/// Vertex format for 3D mesh rendering.
23/// Matches VertexInput3D in WGSL shaders (locations 0-4, 9, 16-21).
24/// This is separate from `Vertex` to avoid layout conflicts with 2D rendering.
25#[repr(C)]
26#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
27pub struct Vertex3D {
28    pub position: [f32; 3],
29    pub normal: [f32; 3],
30    pub uv: [f32; 2],
31    pub color: [f32; 4],
32    /// Tangent vector (xyz) + handedness (w) for normal mapping.
33    /// WGSL location 9 maps to this field directly.
34    pub tangent: [f32; 4],
35}
36
37/// Per-instance data for instanced rendering.
38/// Stores transform data previously duplicated across all vertices of a path/quad.
39#[repr(C)]
40#[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
41pub struct InstanceData {
42    pub translation: [f32; 2],
43    pub scale: [f32; 2],
44    pub rotation: f32,
45    pub blur_radius: f32,
46    /// Per-instance Index of Refraction (IOR) override for custom glass thickness refraction.
47    pub ior_override: f32,
48    /// Per-instance glass effect intensity (0.0 = frosted/minimal, 1.0 = full refraction+specular).
49    pub glass_intensity: f32,
50}
51
52/// Per-instance data for 3D instanced rendering.
53/// Stores a 3×4 model matrix (4th row implied [0,0,0,1]), material overrides,
54/// and UV transform (scale + offset).
55#[repr(C)]
56#[derive(Copy, Clone, Debug, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
57pub struct InstanceData3D {
58    pub model_row0: [f32; 4],
59    pub model_row1: [f32; 4],
60    pub model_row2: [f32; 4],
61    pub material_overrides: [f32; 4],
62    pub uv_scale: [f32; 2],
63    pub uv_offset: [f32; 2],
64}
65
66impl Default for InstanceData3D {
67    fn default() -> Self {
68        Self {
69            model_row0: [1.0, 0.0, 0.0, 0.0],
70            model_row1: [0.0, 1.0, 0.0, 0.0],
71            model_row2: [0.0, 0.0, 1.0, 0.0],
72            material_overrides: [0.0, 0.0, 1.0, 0.0],
73            uv_scale: [1.0, 1.0],
74            uv_offset: [0.0, 0.0],
75        }
76    }
77}
78
79impl InstanceData3D {
80    const ATTRIBUTES: [wgpu::VertexAttribute; 6] = wgpu::vertex_attr_array![
81        16 => Float32x4,
82        17 => Float32x4,
83        18 => Float32x4,
84        19 => Float32x4,
85        20 => Float32x2,
86        21 => Float32x2,
87    ];
88
89    pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
90        wgpu::VertexBufferLayout {
91            array_stride: std::mem::size_of::<InstanceData3D>() as wgpu::BufferAddress,
92            step_mode: wgpu::VertexStepMode::Instance,
93            attributes: &Self::ATTRIBUTES,
94        }
95    }
96}
97
98impl Default for InstanceData {
99    fn default() -> Self {
100        Self {
101            translation: [0.0, 0.0],
102            scale: [1.0, 1.0],
103            rotation: 0.0,
104            blur_radius: 0.0,
105            ior_override: 0.0,
106            glass_intensity: 1.0,
107        }
108    }
109}
110
111impl InstanceData {
112    const ATTRIBUTES: [wgpu::VertexAttribute; 5] = wgpu::vertex_attr_array![
113        11 => Float32x2, // translation
114        12 => Float32x2, // scale
115        13 => Float32,   // rotation
116        14 => Float32,   // blur_radius
117        15 => Float32x2, // ior_override + glass_intensity
118    ];
119
120    pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
121        wgpu::VertexBufferLayout {
122            array_stride: std::mem::size_of::<InstanceData>() as wgpu::BufferAddress,
123            step_mode: wgpu::VertexStepMode::Instance,
124            attributes: &Self::ATTRIBUTES,
125        }
126    }
127}
128
129impl Vertex {
130    const ATTRIBUTES: [wgpu::VertexAttribute; 11] = wgpu::vertex_attr_array![
131        0 => Float32x3, // position
132        1 => Float32x3, // normal
133        2 => Float32x2, // uv
134        3 => Float32x4, // color
135        4 => Uint32,    // mode
136        5 => Float32,   // radius
137        6 => Float32x4, // slice
138        7 => Float32x2, // logical
139        8 => Float32x2, // size
140        9 => Float32x4, // clip
141        10 => Uint32,   // tex_index
142    ];
143
144    pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
145        wgpu::VertexBufferLayout {
146            array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
147            step_mode: wgpu::VertexStepMode::Vertex,
148            attributes: &Self::ATTRIBUTES,
149        }
150    }
151}
152
153/// Vertex buffer layout for 3D meshes.
154/// Matches VertexInput3D WGSL layout (locations 0-4, 9).
155impl Vertex3D {
156    const ATTRIBUTES: [wgpu::VertexAttribute; 5] = wgpu::vertex_attr_array![
157        0 => Float32x3, // position
158        1 => Float32x3, // normal
159        2 => Float32x2, // uv
160        3 => Float32x4, // color
161        9 => Float32x4, // tangent
162    ];
163
164    pub(crate) fn desc() -> wgpu::VertexBufferLayout<'static> {
165        wgpu::VertexBufferLayout {
166            array_stride: std::mem::size_of::<Vertex3D>() as wgpu::BufferAddress,
167            step_mode: wgpu::VertexStepMode::Vertex,
168            attributes: &Self::ATTRIBUTES,
169        }
170    }
171}
172
173pub(crate) struct SceneVertexConstructor {
174    pub(crate) color: [f32; 4],
175}
176
177/// Vertex constructor for stroke tessellation -- includes clip for transform.
178pub(crate) struct CustomStrokeVertexConstructor {
179    pub(crate) color: [f32; 4],
180    pub(crate) clip: [f32; 4],
181    pub(crate) path_length: f32,
182}
183
184impl StrokeVertexConstructor<Vertex> for CustomStrokeVertexConstructor {
185    fn new_vertex(&mut self, vertex: StrokeVertex) -> Vertex {
186        let pos = vertex.position();
187        Vertex {
188            position: [pos.x, pos.y, 0.0],
189            normal: [0.0, 0.0, 1.0],
190            uv: [vertex.advancement(), self.path_length],
191            color: self.color,
192            material_id: 0,
193            radius: 0.0,
194            slice: [0.0, 0.0, 0.0, 1.0],
195            logical: [pos.x, pos.y],
196            size: [1.0, 1.0],
197            clip: self.clip,
198            tex_index: 0,
199        }
200    }
201}
202
203impl FillVertexConstructor<Vertex> for SceneVertexConstructor {
204    fn new_vertex(&mut self, vertex: FillVertex) -> Vertex {
205        Vertex {
206            position: [vertex.position().x, vertex.position().y, 0.0],
207            normal: [0.0, 0.0, 1.0],
208            uv: [0.0, 0.0],
209            color: self.color,
210            material_id: 0,
211            radius: 0.0,
212            slice: [0.0, 0.0, 0.0, 1.0],
213            logical: [vertex.position().x, vertex.position().y],
214            size: [1.0, 1.0],
215            clip: [-f32::INFINITY, -f32::INFINITY, f32::INFINITY, f32::INFINITY],
216            tex_index: 0,
217        }
218    }
219}