1use crate::{
2 Error,
3 Rectangle,
4 Context,
5 Image,
6 Color,
7 graphics::{
8 Buffer,
9 BufferType,
10 BufferUsage,
11 },
12};
13use glow::HasContext;
14
15struct DynamicBuffer {
16 vertex_data: Vec<f32>,
17 vertices: u16,
18 indices: Vec<u16>,
19}
20
21impl DynamicBuffer {
22 pub fn new() -> Self {
23 Self {
24 vertex_data: Vec::new(),
25 vertices: 0,
26 indices: Vec::new(),
27 }
28 }
29
30 pub fn push_vertex(&mut self, x: f32, y: f32, u: f32, v: f32) -> u16 {
31 let index = self.vertices;
32 self.vertices += 1;
33
34 self.vertex_data.push(x);
35 self.vertex_data.push(y);
36 self.vertex_data.push(u);
37 self.vertex_data.push(v);
38
39 index
40 }
41
42 pub fn push_quad(&mut self, a: u16, b: u16, c: u16, d: u16) {
43 self.indices.push(a);
44 self.indices.push(b);
45 self.indices.push(c);
46
47 self.indices.push(a);
48 self.indices.push(c);
49 self.indices.push(d);
50 }
51
52 pub fn build(self, gl: &glow::Context) -> Result<(i32, Buffer, Buffer), Error> {
53 use std::mem::size_of;
54 use std::slice::from_raw_parts;
55
56 let vertex_buffer = unsafe {
57 let byte_len = self.vertex_data.len() * size_of::<f32>();
58 let byte_data = from_raw_parts(self.vertex_data.as_ptr() as *const u8, byte_len);
59 Buffer::create(gl, BufferType::VertexBuffer, BufferUsage::DynamicDraw, &byte_data)?
60 };
61
62 let index_buffer = unsafe {
63 let byte_len = self.indices.len() * size_of::<u16>();
64 let byte_data = from_raw_parts(self.indices.as_ptr() as *const u8, byte_len);
65 Buffer::create(gl, BufferType::IndexBuffer, BufferUsage::DynamicDraw, &byte_data)?
66 };
67
68 let count = self.indices.len() as i32;
69
70 Ok((count, vertex_buffer, index_buffer))
71 }
72}
73
74struct SpriteInstance {
75 sprite: Image,
76 source: Rectangle,
77 target: Rectangle,
78 color: Color,
79}
80
81pub struct SpriteBatch {
82 instances: Vec<SpriteInstance>,
83 brute_force: bool,
84}
85
86impl SpriteBatch {
87 pub fn new() -> Self {
88 Self {
89 instances: Vec::new(),
90 brute_force: false,
91 }
92 }
93
94 pub fn draw_sprite(&mut self, sprite: Image, source: Rectangle, target: Rectangle, color: Color) {
95 self.instances.push(SpriteInstance {
97 sprite,
98 source,
99 target,
100 color,
101 });
102 }
103
104 pub fn draw(self, context: &mut Context) -> Result<(), Error> {
105 if self.instances.len() < 1 {
106 return Ok(());
107 }
108
109 let gl = &context.gl;
110 let shader = &context.sprite_shader;
111
112 let canvas_size = Rectangle::new(0, 0, 1280, 720);
113
114 let mut dynamic_buffer = DynamicBuffer::new();
115 for instance in &self.instances {
116
117 let image_size = if let Some((width, height)) = context.images.find_size(instance.sprite) {
119 Rectangle::new(0, 0, width as i32, height as i32)
120 } else {
121 Rectangle::new(0, 0, 1, 1)
122 };
123
124 let (source_left, source_right, source_top, source_bottom) = instance.source.to_rendering_position(&image_size);
125 let (target_left, target_right, target_top, target_bottom) = instance.target.to_rendering_position(&canvas_size);
126
127 let a = dynamic_buffer.push_vertex((target_left - 0.5) * 2.0, (target_bottom - 0.5) * 2.0, source_left, source_top);
128 let b = dynamic_buffer.push_vertex((target_right - 0.5) * 2.0, (target_bottom - 0.5) * 2.0, source_right, source_top);
129 let c = dynamic_buffer.push_vertex((target_right - 0.5) * 2.0, (target_top - 0.5) * 2.0, source_right, source_bottom);
130 let d = dynamic_buffer.push_vertex((target_left - 0.5) * 2.0, (target_top - 0.5) * 2.0, source_left, source_bottom);
131
132 dynamic_buffer.push_quad(a, b, c, d);
133
134 }
135 let (_count, vertex_buffer, index_buffer) = dynamic_buffer.build(gl)?;
136
137 unsafe {
138 gl.enable(glow::BLEND);
139 gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
140
141 gl.use_program(Some(shader.get_inner()));
142
143 let position_attribute = shader.get_attribute_location("position")
144 .ok_or(super::ShaderError::AttributeNotFound("position".to_string()))?;
145 gl.bind_buffer(glow::ARRAY_BUFFER, Some(vertex_buffer.get_inner()));
146 gl.vertex_attrib_pointer_f32(position_attribute, 4, glow::FLOAT, false, 0, 0);
147 gl.enable_vertex_attrib_array(position_attribute);
148
149 gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(index_buffer.get_inner()));
150
151 let color_location = shader.get_uniform_location("color")
152 .ok_or(super::ShaderError::UniformNotFound("color".to_string()))?;
153 let sprite_location = shader.get_uniform_location("sprite")
154 .ok_or(super::ShaderError::UniformNotFound("sprite".to_string()))?;
155
156 let mut draw_calls = 0;
157 let mut skipped = 0;
158
159 if self.brute_force {
160 let mut offset = 0;
162 for instance in &self.instances {
163 if let Some(sprite) = context.images.find_texture(instance.sprite) {
164 gl.active_texture(glow::TEXTURE0);
165 gl.bind_texture(glow::TEXTURE_2D, Some(sprite));
166 gl.uniform_1_i32(Some(sprite_location), 0);
167 let (r, g, b, a) = instance.color.into_normalized();
168 gl.uniform_4_f32(Some(color_location), r, g, b, a);
169 gl.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_SHORT, offset);
170 offset += 6 * std::mem::size_of::<u16>() as i32;
171
172 draw_calls += 1;
173 } else {
174 skipped += 1;
175 }
176 }
177 } else {
178 let mut batch_offset = 0;
184 let mut current_offset = 0;
185 let mut draw_count = 0;
186 let mut last_color = self.instances[0].color;
187 let mut last_sprite = self.instances[0].sprite;
188
189 for instance in &self.instances {
190 if last_color != instance.color || last_sprite != instance.sprite {
192 if let Some(sprite) = context.images.find_texture(last_sprite) {
194 gl.active_texture(glow::TEXTURE0);
195 gl.bind_texture(glow::TEXTURE_2D, Some(sprite));
196 gl.uniform_1_i32(Some(sprite_location), 0);
197 let (r, g, b, a) = last_color.into_normalized();
198 gl.uniform_4_f32(Some(color_location), r, g, b, a);
199 gl.draw_elements(glow::TRIANGLES, draw_count, glow::UNSIGNED_SHORT, batch_offset);
200
201 draw_calls += 1;
202 } else {
203 skipped += 1;
204 }
205
206 draw_count = 0;
208 batch_offset = current_offset;
209 last_color = instance.color;
210 last_sprite = instance.sprite;
211 }
212
213 current_offset += 6 * std::mem::size_of::<u16>() as i32;
215 draw_count += 6;
216 }
217
218 if let Some(sprite) = context.images.find_texture(last_sprite) {
220 gl.active_texture(glow::TEXTURE0);
221 gl.bind_texture(glow::TEXTURE_2D, Some(sprite));
222 gl.uniform_1_i32(Some(sprite_location), 0);
223 let (r, g, b, a) = last_color.into_normalized();
224 gl.uniform_4_f32(Some(color_location), r, g, b, a);
225 gl.draw_elements(glow::TRIANGLES, draw_count, glow::UNSIGNED_SHORT, batch_offset);
226 draw_calls += 1;
227 } else {
228 skipped += 1;
229 }
230 }
231
232 gl.delete_buffer(vertex_buffer.get_inner());
233 gl.delete_buffer(index_buffer.get_inner());
234
235 context.metrics.add_draw_calls(draw_calls);
236 context.metrics.add_sprites_drawn(self.instances.len() - skipped);
237 context.metrics.add_sprites_skipped(skipped);
238 }
239
240 Ok(())
241 }
242}