1pub mod sprite;
6
7use platform::{BlendMode, DrawSettings2D, Platform, SpriteRef, TextureFilter, Vertex2D};
8
9use crate::{allocators::LinearAllocator, collections::FixedVec};
10
11#[derive(Debug)]
16pub struct SpriteQuad {
17 pub position_top_left: (f32, f32),
20 pub position_bottom_right: (f32, f32),
23 pub texcoord_top_left: (f32, f32),
26 pub texcoord_bottom_right: (f32, f32),
29 pub draw_order: u8,
32 pub blend_mode: BlendMode,
35 pub sprite: SpriteRef,
38}
39
40impl SpriteQuad {
41 fn draw_call_identifier(&self) -> (SpriteRef, BlendMode, u8) {
42 (self.sprite, self.blend_mode, self.draw_order)
43 }
44}
45
46pub struct DrawQueue<'frm> {
52 pub sprites: FixedVec<'frm, SpriteQuad>,
54 pub scale_factor: f32,
57}
58
59impl<'frm> DrawQueue<'frm> {
60 pub fn new(
62 allocator: &'frm LinearAllocator,
63 max_quads: usize,
64 scale_factor: f32,
65 ) -> Option<DrawQueue<'frm>> {
66 Some(DrawQueue {
67 sprites: FixedVec::new(allocator, max_quads)?,
68 scale_factor,
69 })
70 }
71
72 pub fn dispatch_draw(&mut self, allocator: &LinearAllocator, platform: &dyn Platform) {
75 'draw_quads: {
76 if self.sprites.is_empty() {
77 break 'draw_quads;
78 }
79
80 self.sprites.sort_unstable_by(|a, b| {
81 a.draw_order
82 .cmp(&b.draw_order)
83 .then_with(|| a.sprite.cmp(&b.sprite))
84 .then_with(|| a.blend_mode.cmp(&b.blend_mode))
85 });
86
87 let mut max_draw_call_length = 0;
88 {
89 let mut prev_draw_call_id = None;
90 let mut current_draw_call_length = 0;
91 for quad in self.sprites.iter() {
92 let current_draw_call_id = Some(quad.draw_call_identifier());
93 if current_draw_call_id == prev_draw_call_id {
94 current_draw_call_length += 1;
95 } else {
96 max_draw_call_length = max_draw_call_length.max(current_draw_call_length);
97 prev_draw_call_id = current_draw_call_id;
98 current_draw_call_length = 1;
99 }
100 }
101 max_draw_call_length = max_draw_call_length.max(current_draw_call_length);
102 }
103
104 let Some(mut vertices) = FixedVec::new(allocator, max_draw_call_length * 4) else {
105 break 'draw_quads;
106 };
107 let Some(mut indices) = FixedVec::new(allocator, max_draw_call_length * 6) else {
108 break 'draw_quads;
109 };
110
111 let mut quad_i = 0;
112 while quad_i < self.sprites.len() {
113 let current_draw_call_id = self.sprites[quad_i].draw_call_identifier();
115 while quad_i < self.sprites.len() {
116 let quad = &self.sprites[quad_i];
117 if quad.draw_call_identifier() != current_draw_call_id {
118 break;
119 }
120
121 let (x0, y0) = quad.position_top_left;
122 let (x1, y1) = quad.position_bottom_right;
123 let (u0, v0) = quad.texcoord_top_left;
124 let (u1, v1) = quad.texcoord_bottom_right;
125 let vert_offset = vertices.len() as u32;
126 let _ = vertices.push(Vertex2D::new(x0, y0, u0, v0));
127 let _ = vertices.push(Vertex2D::new(x0, y1, u0, v1));
128 let _ = vertices.push(Vertex2D::new(x1, y1, u1, v1));
129 let _ = vertices.push(Vertex2D::new(x1, y0, u1, v0));
130 let _ = indices.push(vert_offset);
131 let _ = indices.push(vert_offset + 1);
132 let _ = indices.push(vert_offset + 2);
133 let _ = indices.push(vert_offset);
134 let _ = indices.push(vert_offset + 2);
135 let _ = indices.push(vert_offset + 3);
136
137 quad_i += 1;
138 }
139
140 let (sprite, blend_mode, _) = current_draw_call_id;
142 platform.draw_2d(
143 &vertices,
144 &indices,
145 DrawSettings2D {
146 sprite: Some(sprite),
147 blend_mode,
148 texture_filter: TextureFilter::Linear,
149 clip_area: None,
150 },
151 );
152 vertices.clear();
153 indices.clear();
154 }
155 }
156 }
157}