1use core::ops::Range;
14
15use platform::BlendMode;
16
17use crate::{
18 geom::Rect,
19 resources::{
20 sprite::{SpriteAsset, SpriteMipLevel},
21 ResourceDatabase, ResourceLoader, SPRITE_CHUNK_DIMENSIONS,
22 },
23};
24
25use super::{DrawQueue, SpriteQuad};
26
27const CHUNK_WIDTH: u16 = SPRITE_CHUNK_DIMENSIONS.0;
28const CHUNK_HEIGHT: u16 = SPRITE_CHUNK_DIMENSIONS.1;
29
30impl SpriteAsset {
31 #[must_use]
38 pub fn draw(
39 &self,
40 dst: Rect,
41 draw_order: u8,
42 draw_queue: &mut DrawQueue,
43 resources: &ResourceDatabase,
44 resource_loader: &mut ResourceLoader,
45 ) -> bool {
46 draw(
47 RenderableSprite {
48 mip_chain: &self.mip_chain,
49 transparent: self.transparent,
50 draw_order,
51 },
52 dst,
53 draw_queue,
54 resources,
55 resource_loader,
56 )
57 }
58}
59
60struct RenderableSprite<'a> {
62 pub mip_chain: &'a [SpriteMipLevel],
66 pub transparent: bool,
69 pub draw_order: u8,
72}
73
74fn draw(
84 src: RenderableSprite,
85 dst: Rect,
86 draw_queue: &mut DrawQueue,
87 resources: &ResourceDatabase,
88 resource_loader: &mut ResourceLoader,
89) -> bool {
90 profiling::function_scope!();
91 let draws_left = draw_queue.sprites.spare_capacity();
92
93 let mut draw_chunk = |chunk_index: u32, dst: Rect, tex: Rect| {
94 profiling::scope!("draw_chunk");
95 if let Some(chunk) = resources.sprite_chunks.get(chunk_index) {
96 let quad = SpriteQuad {
97 position_top_left: (dst.x, dst.y),
98 position_bottom_right: (dst.x + dst.w, dst.y + dst.h),
99 texcoord_top_left: (tex.x, tex.y),
100 texcoord_bottom_right: (tex.x + tex.w, tex.y + tex.h),
101 draw_order: src.draw_order,
102 blend_mode: if src.transparent {
103 BlendMode::Blend
104 } else {
105 BlendMode::None
106 },
107 sprite: chunk.0,
108 };
109
110 draw_queue.sprites.push(quad).unwrap();
111 } else {
112 resource_loader.queue_sprite_chunk(chunk_index, resources);
113 }
114 };
115
116 let rendering_scale_ratio = match &src.mip_chain[0] {
118 SpriteMipLevel::SingleChunkSprite { size, .. }
119 | SpriteMipLevel::MultiChunkSprite { size, .. } => {
120 let width_scale = size.0 / (dst.w * draw_queue.scale_factor) as u16;
121 let height_scale = size.1 / (dst.h * draw_queue.scale_factor) as u16;
122 width_scale.min(height_scale)
123 }
124 };
125
126 let mip_level = rendering_scale_ratio.checked_ilog2().unwrap_or(0) as usize;
132
133 let max_mip = src.mip_chain.len() - 1;
134 let mip = &src.mip_chain[mip_level.min(max_mip)];
135
136 match mip {
137 SpriteMipLevel::SingleChunkSprite {
138 offset,
139 size,
140 sprite_chunk,
141 } => {
142 if draws_left == 0 {
143 return false;
144 }
145
146 let tex_src = Rect {
147 x: offset.0 as f32 / CHUNK_WIDTH as f32,
148 y: offset.1 as f32 / CHUNK_HEIGHT as f32,
149 w: size.0 as f32 / CHUNK_WIDTH as f32,
150 h: size.1 as f32 / CHUNK_HEIGHT as f32,
151 };
152 draw_chunk(*sprite_chunk, dst, tex_src);
153
154 true
155 }
156
157 SpriteMipLevel::MultiChunkSprite {
158 size,
159 sprite_chunks,
160 } => {
161 let chunks_x = size.0.div_ceil(CHUNK_WIDTH - 2) as u32;
162 let chunks_y = size.1.div_ceil(CHUNK_HEIGHT - 2) as u32;
163 assert_eq!(
164 chunks_x * chunks_y,
165 sprite_chunks.end - sprite_chunks.start,
166 "resource database has a corrupt chunk? the amount of chunks does not match the sprite size",
167 );
168
169 if draws_left < (chunks_x * chunks_y) as usize {
170 return false;
171 }
172
173 draw_multi_chunk_sprite(
174 dst,
175 *size,
176 sprite_chunks.clone(),
177 (chunks_x, chunks_y),
178 draw_chunk,
179 );
180
181 true
182 }
183 }
184}
185
186fn draw_multi_chunk_sprite(
187 Rect { x, y, w, h }: Rect,
188 (tex_width, tex_height): (u16, u16),
189 chunks: Range<u32>,
190 (chunks_x, chunks_y): (u32, u32),
191 mut draw: impl FnMut(u32, Rect, Rect),
192) {
193 let scale_x = w / tex_width as f32;
194 let scale_y = h / tex_height as f32;
195
196 let mut tex_x_pos = 0;
197 let mut tex_y_pos = 0;
198 for cy in 0..chunks_y {
199 let curr_chunk_h = (tex_height - tex_y_pos).min(CHUNK_HEIGHT - 2);
200 for cx in 0..chunks_x {
201 let curr_chunk_index = chunks.start + cx + cy * chunks_x;
202 let curr_chunk_w = (tex_width - tex_x_pos).min(CHUNK_WIDTH - 2);
203
204 let dst = Rect {
205 x: x + tex_x_pos as f32 * scale_x,
206 y: y + tex_y_pos as f32 * scale_y,
207 w: curr_chunk_w as f32 * scale_x,
208 h: curr_chunk_h as f32 * scale_y,
209 };
210
211 let tex_src = Rect {
212 x: 1. / CHUNK_WIDTH as f32,
213 y: 1. / CHUNK_HEIGHT as f32,
214 w: curr_chunk_w as f32 / CHUNK_WIDTH as f32,
215 h: curr_chunk_h as f32 / CHUNK_HEIGHT as f32,
216 };
217
218 draw(curr_chunk_index, dst, tex_src);
219
220 tex_x_pos += curr_chunk_w;
221 }
222 tex_y_pos += curr_chunk_h;
223 tex_x_pos = 0;
224 }
225}