1use astrelis_core::math::Vec2;
8use astrelis_render::{Color, wgpu};
9use bytemuck::{Pod, Zeroable};
10
11#[repr(C)]
17#[derive(Copy, Clone, Debug, Pod, Zeroable)]
18pub struct QuadInstance {
19 pub position: [f32; 2],
21 pub size: [f32; 2],
23 pub color: [f32; 4],
25 pub border_radius: f32,
27 pub border_thickness: f32,
29 pub z_depth: f32,
31 pub _padding: f32,
33}
34
35impl QuadInstance {
36 pub fn filled(position: Vec2, size: Vec2, color: Color, z_depth: f32) -> Self {
38 Self {
39 position: position.into(),
40 size: size.into(),
41 color: color.into(),
42 border_radius: 0.0,
43 border_thickness: 0.0,
44 z_depth,
45 _padding: 0.0,
46 }
47 }
48
49 pub fn rounded(
51 position: Vec2,
52 size: Vec2,
53 color: Color,
54 border_radius: f32,
55 z_depth: f32,
56 ) -> Self {
57 Self {
58 position: position.into(),
59 size: size.into(),
60 color: color.into(),
61 border_radius,
62 border_thickness: 0.0,
63 z_depth,
64 _padding: 0.0,
65 }
66 }
67
68 pub fn bordered(
70 position: Vec2,
71 size: Vec2,
72 color: Color,
73 border_thickness: f32,
74 border_radius: f32,
75 z_depth: f32,
76 ) -> Self {
77 Self {
78 position: position.into(),
79 size: size.into(),
80 color: color.into(),
81 border_radius,
82 border_thickness,
83 z_depth,
84 _padding: 0.0,
85 }
86 }
87
88 pub fn vertex_layout() -> wgpu::VertexBufferLayout<'static> {
90 use wgpu::*;
91 VertexBufferLayout {
92 array_stride: std::mem::size_of::<Self>() as u64,
93 step_mode: VertexStepMode::Instance,
94 attributes: &[
95 VertexAttribute {
97 offset: 0,
98 shader_location: 2,
99 format: VertexFormat::Float32x2,
100 },
101 VertexAttribute {
103 offset: 8,
104 shader_location: 3,
105 format: VertexFormat::Float32x2,
106 },
107 VertexAttribute {
109 offset: 16,
110 shader_location: 4,
111 format: VertexFormat::Float32x4,
112 },
113 VertexAttribute {
115 offset: 32,
116 shader_location: 5,
117 format: VertexFormat::Float32,
118 },
119 VertexAttribute {
121 offset: 36,
122 shader_location: 6,
123 format: VertexFormat::Float32,
124 },
125 VertexAttribute {
127 offset: 40,
128 shader_location: 7,
129 format: VertexFormat::Float32,
130 },
131 ],
132 }
133 }
134}
135
136#[repr(C)]
146#[derive(Copy, Clone, Debug, Pod, Zeroable)]
147pub struct TextInstance {
148 pub position: [f32; 2],
150 pub size: [f32; 2],
152 pub atlas_uv_min: [f32; 2],
154 pub atlas_uv_max: [f32; 2],
156 pub color: [f32; 4],
158 pub z_depth: f32,
160 pub _padding: [f32; 3],
162}
163
164impl TextInstance {
165 pub fn new(
167 position: Vec2,
168 size: Vec2,
169 atlas_uv_min: [f32; 2],
170 atlas_uv_max: [f32; 2],
171 color: Color,
172 z_depth: f32,
173 ) -> Self {
174 Self {
175 position: position.into(),
176 size: size.into(),
177 atlas_uv_min,
178 atlas_uv_max,
179 color: color.into(),
180 z_depth,
181 _padding: [0.0; 3],
182 }
183 }
184
185 pub fn vertex_layout() -> wgpu::VertexBufferLayout<'static> {
187 use wgpu::*;
188 VertexBufferLayout {
189 array_stride: std::mem::size_of::<Self>() as u64,
190 step_mode: VertexStepMode::Instance,
191 attributes: &[
192 VertexAttribute {
194 offset: 0,
195 shader_location: 2,
196 format: VertexFormat::Float32x2,
197 },
198 VertexAttribute {
200 offset: 8,
201 shader_location: 3,
202 format: VertexFormat::Float32x2,
203 },
204 VertexAttribute {
206 offset: 16,
207 shader_location: 4,
208 format: VertexFormat::Float32x2,
209 },
210 VertexAttribute {
212 offset: 24,
213 shader_location: 5,
214 format: VertexFormat::Float32x2,
215 },
216 VertexAttribute {
218 offset: 32,
219 shader_location: 6,
220 format: VertexFormat::Float32x4,
221 },
222 VertexAttribute {
224 offset: 48,
225 shader_location: 7,
226 format: VertexFormat::Float32,
227 },
228 ],
229 }
230 }
231}
232
233#[repr(C)]
238#[derive(Copy, Clone, Debug, Pod, Zeroable)]
239pub struct ImageInstance {
240 pub position: [f32; 2],
242 pub size: [f32; 2],
244 pub uv_min: [f32; 2],
246 pub uv_max: [f32; 2],
248 pub tint: [f32; 4],
250 pub border_radius: f32,
252 pub texture_index: u32,
254 pub z_depth: f32,
256 pub _padding: f32,
258}
259
260impl ImageInstance {
261 pub fn new(position: Vec2, size: Vec2, z_depth: f32) -> Self {
263 Self {
264 position: position.into(),
265 size: size.into(),
266 uv_min: [0.0, 0.0],
267 uv_max: [1.0, 1.0],
268 tint: [1.0, 1.0, 1.0, 1.0],
269 border_radius: 0.0,
270 texture_index: 0,
271 z_depth,
272 _padding: 0.0,
273 }
274 }
275
276 pub fn with_uv(
278 position: Vec2,
279 size: Vec2,
280 uv_min: [f32; 2],
281 uv_max: [f32; 2],
282 z_depth: f32,
283 ) -> Self {
284 Self {
285 position: position.into(),
286 size: size.into(),
287 uv_min,
288 uv_max,
289 tint: [1.0, 1.0, 1.0, 1.0],
290 border_radius: 0.0,
291 texture_index: 0,
292 z_depth,
293 _padding: 0.0,
294 }
295 }
296
297 pub fn with_tint(position: Vec2, size: Vec2, tint: Color, z_depth: f32) -> Self {
299 Self {
300 position: position.into(),
301 size: size.into(),
302 uv_min: [0.0, 0.0],
303 uv_max: [1.0, 1.0],
304 tint: tint.into(),
305 border_radius: 0.0,
306 texture_index: 0,
307 z_depth,
308 _padding: 0.0,
309 }
310 }
311
312 pub fn tint(mut self, color: Color) -> Self {
314 self.tint = color.into();
315 self
316 }
317
318 pub fn border_radius(mut self, radius: f32) -> Self {
320 self.border_radius = radius;
321 self
322 }
323
324 pub fn texture_index(mut self, index: u32) -> Self {
326 self.texture_index = index;
327 self
328 }
329
330 pub fn z_depth(mut self, z_depth: f32) -> Self {
332 self.z_depth = z_depth;
333 self
334 }
335
336 pub fn vertex_layout() -> wgpu::VertexBufferLayout<'static> {
338 use wgpu::*;
339 VertexBufferLayout {
340 array_stride: std::mem::size_of::<Self>() as u64,
341 step_mode: VertexStepMode::Instance,
342 attributes: &[
343 VertexAttribute {
345 offset: 0,
346 shader_location: 2,
347 format: VertexFormat::Float32x2,
348 },
349 VertexAttribute {
351 offset: 8,
352 shader_location: 3,
353 format: VertexFormat::Float32x2,
354 },
355 VertexAttribute {
357 offset: 16,
358 shader_location: 4,
359 format: VertexFormat::Float32x2,
360 },
361 VertexAttribute {
363 offset: 24,
364 shader_location: 5,
365 format: VertexFormat::Float32x2,
366 },
367 VertexAttribute {
369 offset: 32,
370 shader_location: 6,
371 format: VertexFormat::Float32x4,
372 },
373 VertexAttribute {
375 offset: 48,
376 shader_location: 7,
377 format: VertexFormat::Float32,
378 },
379 VertexAttribute {
381 offset: 52,
382 shader_location: 8,
383 format: VertexFormat::Uint32,
384 },
385 VertexAttribute {
387 offset: 56,
388 shader_location: 9,
389 format: VertexFormat::Float32,
390 },
391 ],
392 }
393 }
394}
395
396#[repr(C)]
401#[derive(Copy, Clone, Debug, Pod, Zeroable)]
402pub struct QuadVertex {
403 pub position: [f32; 2],
405 pub uv: [f32; 2],
407}
408
409impl QuadVertex {
410 pub const fn new(position: [f32; 2], uv: [f32; 2]) -> Self {
412 Self { position, uv }
413 }
414
415 pub const fn unit_quad() -> [QuadVertex; 6] {
417 [
418 QuadVertex::new([0.0, 0.0], [0.0, 0.0]),
420 QuadVertex::new([1.0, 0.0], [1.0, 0.0]),
421 QuadVertex::new([1.0, 1.0], [1.0, 1.0]),
422 QuadVertex::new([0.0, 0.0], [0.0, 0.0]),
424 QuadVertex::new([1.0, 1.0], [1.0, 1.0]),
425 QuadVertex::new([0.0, 1.0], [0.0, 1.0]),
426 ]
427 }
428
429 pub fn vertex_layout() -> wgpu::VertexBufferLayout<'static> {
431 use wgpu::*;
432 VertexBufferLayout {
433 array_stride: std::mem::size_of::<Self>() as u64,
434 step_mode: VertexStepMode::Vertex,
435 attributes: &[
436 VertexAttribute {
438 offset: 0,
439 shader_location: 0,
440 format: VertexFormat::Float32x2,
441 },
442 VertexAttribute {
444 offset: 8,
445 shader_location: 1,
446 format: VertexFormat::Float32x2,
447 },
448 ],
449 }
450 }
451}
452
453#[cfg(test)]
454mod tests {
455 use super::*;
456
457 #[test]
458 fn test_quad_instance_size() {
459 let size = std::mem::size_of::<QuadInstance>();
461 assert_eq!(size % 16, 0, "QuadInstance should be 16-byte aligned");
462 }
463
464 #[test]
465 fn test_text_instance_size() {
466 let size = std::mem::size_of::<TextInstance>();
467 assert_eq!(size, 64, "TextInstance should be 64 bytes");
468 assert_eq!(size % 16, 0, "TextInstance should be 16-byte aligned");
469 }
470
471 #[test]
472 fn test_quad_vertex_size() {
473 let size = std::mem::size_of::<QuadVertex>();
474 assert_eq!(size, 16, "QuadVertex should be 16 bytes");
475 }
476
477 #[test]
478 fn test_unit_quad_vertices() {
479 let vertices = QuadVertex::unit_quad();
480 assert_eq!(vertices.len(), 6);
481
482 assert_eq!(vertices[0].position, [0.0, 0.0]);
484 assert_eq!(vertices[1].position, [1.0, 0.0]);
485 assert_eq!(vertices[2].position, [1.0, 1.0]);
486
487 assert_eq!(vertices[3].position, [0.0, 0.0]);
489 assert_eq!(vertices[4].position, [1.0, 1.0]);
490 assert_eq!(vertices[5].position, [0.0, 1.0]);
491 }
492
493 #[test]
494 fn test_quad_instance_creation() {
495 let instance = QuadInstance::filled(
496 Vec2::new(10.0, 20.0),
497 Vec2::new(100.0, 50.0),
498 Color::RED,
499 0.5,
500 );
501
502 assert_eq!(instance.position, [10.0, 20.0]);
503 assert_eq!(instance.size, [100.0, 50.0]);
504 assert_eq!(instance.border_thickness, 0.0);
505 assert_eq!(instance.z_depth, 0.5);
506 }
507
508 #[test]
509 fn test_text_instance_creation() {
510 let instance = TextInstance::new(
511 Vec2::new(5.0, 15.0),
512 Vec2::new(10.0, 12.0),
513 [0.1, 0.2],
514 [0.3, 0.4],
515 Color::WHITE,
516 0.75,
517 );
518
519 assert_eq!(instance.position, [5.0, 15.0]);
520 assert_eq!(instance.size, [10.0, 12.0]);
521 assert_eq!(instance.atlas_uv_min, [0.1, 0.2]);
522 assert_eq!(instance.atlas_uv_max, [0.3, 0.4]);
523 assert_eq!(instance.z_depth, 0.75);
524 }
525
526 #[test]
527 fn test_image_instance_creation() {
528 let instance = ImageInstance::new(Vec2::new(100.0, 200.0), Vec2::new(50.0, 60.0), 0.25);
529
530 assert_eq!(instance.position, [100.0, 200.0]);
531 assert_eq!(instance.size, [50.0, 60.0]);
532 assert_eq!(instance.z_depth, 0.25);
533 assert_eq!(instance.uv_min, [0.0, 0.0]);
534 assert_eq!(instance.uv_max, [1.0, 1.0]);
535 }
536
537 #[test]
538 fn test_image_instance_size() {
539 let size = std::mem::size_of::<ImageInstance>();
540 assert_eq!(size, 64, "ImageInstance should be 64 bytes");
541 assert_eq!(size % 16, 0, "ImageInstance should be 16-byte aligned");
542 }
543}