use crate::sprite::SpriteBatch;
use crate::vertex::Vertex2D;
const QUAD_INDICES: [u16; 6] = [0, 1, 2, 2, 3, 0];
const QUAD_INDICES_U32: [u32; 6] = [0, 1, 2, 2, 3, 0];
pub const MAX_SPRITES_PER_BATCH: usize = 16383;
#[must_use]
pub fn batch_to_vertices(batch: &SpriteBatch) -> (Vec<Vertex2D>, Vec<u16>) {
let sprite_count = batch.sprites.len();
let vert_cap = match sprite_count.checked_mul(4) {
Some(v) => v,
None => return (Vec::new(), Vec::new()),
};
let idx_cap = match sprite_count.checked_mul(6) {
Some(v) => v,
None => return (Vec::new(), Vec::new()),
};
let mut vertices = Vec::with_capacity(vert_cap);
let mut indices = Vec::with_capacity(idx_cap);
batch_to_vertices_into(batch, &mut vertices, &mut indices);
(vertices, indices)
}
pub fn batch_to_vertices_into(
batch: &SpriteBatch,
vertices: &mut Vec<Vertex2D>,
indices: &mut Vec<u16>,
) {
let sprite_count = batch.sprites.len().min(MAX_SPRITES_PER_BATCH);
vertices.clear();
vertices.reserve(sprite_count.saturating_mul(4));
indices.clear();
indices.reserve(sprite_count.saturating_mul(6));
for (i, sprite) in batch.sprites.iter().take(sprite_count).enumerate() {
emit_quad_u16(sprite, i, vertices, indices);
}
}
#[must_use]
pub fn batch_to_vertices_u32(batch: &SpriteBatch) -> (Vec<Vertex2D>, Vec<u32>) {
let sprite_count = batch.sprites.len();
let vert_cap = match sprite_count.checked_mul(4) {
Some(v) => v,
None => return (Vec::new(), Vec::new()),
};
let idx_cap = match sprite_count.checked_mul(6) {
Some(v) => v,
None => return (Vec::new(), Vec::new()),
};
let mut vertices = Vec::with_capacity(vert_cap);
let mut indices = Vec::with_capacity(idx_cap);
batch_to_vertices_u32_into(batch, &mut vertices, &mut indices);
(vertices, indices)
}
pub fn batch_to_vertices_u32_into(
batch: &SpriteBatch,
vertices: &mut Vec<Vertex2D>,
indices: &mut Vec<u32>,
) {
let sprite_count = batch.sprites.len();
vertices.clear();
vertices.reserve(sprite_count.saturating_mul(4));
indices.clear();
indices.reserve(sprite_count.saturating_mul(6));
for (i, sprite) in batch.sprites.iter().enumerate() {
emit_quad_u32(sprite, i, vertices, indices);
}
}
#[inline]
fn emit_quad_u16(
sprite: &crate::sprite::Sprite,
i: usize,
vertices: &mut Vec<Vertex2D>,
indices: &mut Vec<u16>,
) {
let c = sprite.color.to_array();
debug_assert!(i < 16384, "u16 index overflow: sprite index {i} >= 16384");
let base = (i * 4) as u16;
let (positions, uvs) = sprite_quad(sprite);
for j in 0..4 {
vertices.push(Vertex2D {
position: positions[j],
tex_coords: uvs[j],
color: c,
});
}
for &idx in &QUAD_INDICES {
indices.push(base + idx);
}
}
#[inline]
fn emit_quad_u32(
sprite: &crate::sprite::Sprite,
i: usize,
vertices: &mut Vec<Vertex2D>,
indices: &mut Vec<u32>,
) {
let c = sprite.color.to_array();
let Some(base) = i.checked_mul(4).and_then(|v| u32::try_from(v).ok()) else {
return;
};
let (positions, uvs) = sprite_quad(sprite);
for j in 0..4 {
vertices.push(Vertex2D {
position: positions[j],
tex_coords: uvs[j],
color: c,
});
}
for &idx in &QUAD_INDICES_U32 {
indices.push(base + idx);
}
}
#[inline]
fn sprite_quad(sprite: &crate::sprite::Sprite) -> ([[f32; 2]; 4], [[f32; 2]; 4]) {
let cx = sprite.x + sprite.width * 0.5;
let cy = sprite.y + sprite.height * 0.5;
let hw = sprite.width * 0.5;
let hh = sprite.height * 0.5;
let corners = [[-hw, -hh], [hw, -hh], [hw, hh], [-hw, hh]];
let (sin, cos) = if sprite.rotation != 0.0 {
(sprite.rotation.sin(), sprite.rotation.cos())
} else {
(0.0, 1.0)
};
let uvs = sprite.uv.corners();
let mut positions = [[0.0f32; 2]; 4];
for (j, corner) in corners.iter().enumerate() {
positions[j] = [
corner[0] * cos - corner[1] * sin + cx,
corner[0] * sin + corner[1] * cos + cy,
];
}
(positions, uvs)
}