use crate::gfx::raster;
#[derive(Debug, Clone)]
pub struct DrawCall {
pub depth: f32,
pub color: u32,
pub mode: u8,
pub alpha: f32,
pub kind: DrawKind,
}
#[derive(Debug, Clone)]
pub enum DrawKind {
Triangle {
x0: f32,
y0: f32,
z0: f32,
x1: f32,
y1: f32,
z1: f32,
x2: f32,
y2: f32,
z2: f32,
},
TriangleG {
x0: f32,
y0: f32,
z0: f32,
c0: u32,
x1: f32,
y1: f32,
z1: f32,
c1: u32,
x2: f32,
y2: f32,
z2: f32,
c2: u32,
bands: u32,
},
Line {
x0: f32,
y0: f32,
z0: f32,
x1: f32,
y1: f32,
z1: f32,
},
}
#[derive(Debug)]
pub struct DepthQueue {
calls: Vec<DrawCall>,
cur_mode: u8,
cur_alpha: f32,
}
impl Default for DepthQueue {
fn default() -> Self {
Self { calls: Vec::new(), cur_mode: 0, cur_alpha: 1.0 }
}
}
impl DepthQueue {
pub fn set_state(&mut self, mode: u8, alpha: f32) {
self.cur_mode = mode;
self.cur_alpha = alpha.clamp(0.0, 1.0);
}
pub fn push_triangle(
&mut self,
depth: f32,
color: u32,
x0: f32,
y0: f32,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
) {
self.calls.push(DrawCall {
depth,
color,
mode: self.cur_mode,
alpha: self.cur_alpha,
kind: DrawKind::Triangle { x0, y0, z0: depth, x1, y1, z1: depth, x2, y2, z2: depth },
});
}
#[allow(clippy::too_many_arguments)]
pub fn push_triangle_zv(
&mut self,
color: u32,
x0: f32,
y0: f32,
z0: f32,
x1: f32,
y1: f32,
z1: f32,
x2: f32,
y2: f32,
z2: f32,
) {
let depth = (z0 + z1 + z2) / 3.0;
self.calls.push(DrawCall {
depth,
color,
mode: self.cur_mode,
alpha: self.cur_alpha,
kind: DrawKind::Triangle { x0, y0, z0, x1, y1, z1, x2, y2, z2 },
});
}
#[allow(clippy::too_many_arguments)]
pub fn push_triangle_g(
&mut self,
depth: f32,
x0: f32,
y0: f32,
c0: u32,
x1: f32,
y1: f32,
c1: u32,
x2: f32,
y2: f32,
c2: u32,
bands: u32,
) {
self.calls.push(DrawCall {
depth,
color: c0,
mode: self.cur_mode,
alpha: self.cur_alpha,
kind: DrawKind::TriangleG {
x0,
y0,
z0: depth,
c0,
x1,
y1,
z1: depth,
c1,
x2,
y2,
z2: depth,
c2,
bands,
},
});
}
#[allow(clippy::too_many_arguments)]
pub fn push_triangle_g_zv(
&mut self,
x0: f32,
y0: f32,
z0: f32,
c0: u32,
x1: f32,
y1: f32,
z1: f32,
c1: u32,
x2: f32,
y2: f32,
z2: f32,
c2: u32,
bands: u32,
) {
let depth = (z0 + z1 + z2) / 3.0;
self.calls.push(DrawCall {
depth,
color: c0,
mode: self.cur_mode,
alpha: self.cur_alpha,
kind: DrawKind::TriangleG { x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2, bands },
});
}
pub fn push_line(&mut self, depth: f32, color: u32, x0: f32, y0: f32, x1: f32, y1: f32) {
self.calls.push(DrawCall {
depth,
color,
mode: self.cur_mode,
alpha: self.cur_alpha,
kind: DrawKind::Line { x0, y0, z0: depth, x1, y1, z1: depth },
});
}
pub fn flush(
mut self,
buf: &mut Vec<u32>,
zbuf: Option<&mut Vec<f32>>,
width: usize,
height: usize,
) {
self.calls.sort_unstable_by(|a, b| {
b.depth
.partial_cmp(&a.depth)
.unwrap_or(std::cmp::Ordering::Equal)
});
match zbuf {
Some(z) => {
if z.len() != width * height {
z.clear();
z.resize(width * height, f32::INFINITY);
} else {
for v in z.iter_mut() {
*v = f32::INFINITY;
}
}
for call in &self.calls {
let blended = call.mode != 0 || call.alpha < 0.999;
match call.kind {
DrawKind::Triangle { x0, y0, z0, x1, y1, z1, x2, y2, z2 } => {
if blended {
raster::fill_triangle_z_blend(
buf, z, width, height, call.color, call.mode, call.alpha, x0,
y0, z0, x1, y1, z1, x2, y2, z2,
);
} else {
raster::fill_triangle_z(
buf, z, width, height, call.color, x0, y0, z0, x1, y1, z1, x2,
y2, z2,
);
}
},
DrawKind::TriangleG {
x0,
y0,
z0,
c0,
x1,
y1,
z1,
c1,
x2,
y2,
z2,
c2,
bands,
} => raster::fill_triangle_gouraud_z(
buf, z, width, height, x0, y0, z0, c0, x1, y1, z1, c1, x2, y2, z2, c2,
bands,
),
DrawKind::Line { x0, y0, x1, y1, .. } => {
if blended {
raster::draw_line_blend(
buf, width, height, call.color, call.mode, call.alpha, x0, y0,
x1, y1,
);
} else {
raster::draw_line(buf, width, height, call.color, x0, y0, x1, y1);
}
},
}
}
},
None => {
for call in &self.calls {
let blended = call.mode != 0 || call.alpha < 0.999;
match call.kind {
DrawKind::Triangle { x0, y0, x1, y1, x2, y2, .. } => {
if blended {
raster::fill_triangle_blend(
buf, width, height, call.color, call.mode, call.alpha, x0, y0,
x1, y1, x2, y2,
);
} else {
raster::fill_triangle(
buf, width, height, call.color, x0, y0, x1, y1, x2, y2,
);
}
},
DrawKind::TriangleG {
x0, y0, c0, x1, y1, c1, x2, y2, c2, bands, ..
} => raster::fill_triangle_gouraud(
buf, width, height, x0, y0, c0, x1, y1, c1, x2, y2, c2, bands,
),
DrawKind::Line { x0, y0, x1, y1, .. } => {
if blended {
raster::draw_line_blend(
buf, width, height, call.color, call.mode, call.alpha, x0, y0,
x1, y1,
);
} else {
raster::draw_line(buf, width, height, call.color, x0, y0, x1, y1);
}
},
}
}
},
}
}
pub fn is_empty(&self) -> bool {
self.calls.is_empty()
}
#[cfg(target_arch = "wasm32")]
pub fn flush_to_webgl(
mut self,
fill_r: f32,
fill_g: f32,
fill_b: f32,
width: usize,
height: usize,
) {
self.calls.sort_unstable_by(|a, b| {
b.depth
.partial_cmp(&a.depth)
.unwrap_or(std::cmp::Ordering::Equal)
});
for call in &self.calls {
match call.kind {
DrawKind::Triangle { x0, y0, x1, y1, x2, y2, .. } => {
crate::gfx::webgl::push_triangle(call.color, x0, y0, x1, y1, x2, y2, call.depth)
},
DrawKind::TriangleG { x0, y0, c0, x1, y1, c1, x2, y2, c2, .. } => {
let avg = {
let r = ((c0 >> 16 & 0xFF) + (c1 >> 16 & 0xFF) + (c2 >> 16 & 0xFF)) / 3;
let g = ((c0 >> 8 & 0xFF) + (c1 >> 8 & 0xFF) + (c2 >> 8 & 0xFF)) / 3;
let b = ((c0 & 0xFF) + (c1 & 0xFF) + (c2 & 0xFF)) / 3;
(r << 16) | (g << 8) | b
};
crate::gfx::webgl::push_triangle(avg, x0, y0, x1, y1, x2, y2, call.depth);
},
DrawKind::Line { x0, y0, x1, y1, .. } => {
crate::gfx::webgl::push_line(call.color, x0, y0, x1, y1, call.depth)
},
}
}
crate::gfx::webgl::flush(fill_r, fill_g, fill_b, width, height);
}
}