Files
shacraft-core/crates/shacraft-server/src/render.rs
T

101 lines
3.3 KiB
Rust

use anyhow::{Result, ensure};
use shacraft_core::Pos;
pub fn capture(min: Pos, max: Pos, mut cells: Vec<(Pos, [u8; 3])>) -> Result<Vec<u8>> {
ensure!((0..3).all(|i| max[i] >= min[i]), "invalid capture bounds");
let size = 512usize;
let mut image = vec![0u8; size * size * 3];
for pixel in image.chunks_exact_mut(3) {
pixel.copy_from_slice(&[24, 36, 48]);
}
let span = (max[0] - min[0] + max[2] - min[2] + 2) as f32;
let vertical = ((max[1] - min[1] + 1) as f32 + span * 0.25).max(1.);
let scale = (460. / span.max(1.)).min(440. / vertical).clamp(0.1, 24.);
cells.sort_by_key(|(p, _)| (p[0] + p[2], p[1]));
for (p, c) in cells {
let x = (p[0] - min[0]) as f32;
let y = (p[1] - min[1]) as f32;
let z = (p[2] - min[2]) as f32;
let cx = 256. + (x - z - (max[0] - min[0] - max[2] + min[2]) as f32 * 0.5) * scale;
let cy = 470. - (y + 1.) * scale - (span * 0.5 - x - z) * scale * 0.5;
let top = [
[cx, cy - scale * 0.5],
[cx + scale, cy],
[cx, cy + scale * 0.5],
[cx - scale, cy],
];
polygon(&mut image, size, top, c);
polygon(
&mut image,
size,
[
[cx - scale, cy],
[cx, cy + scale * 0.5],
[cx, cy + scale * 1.5],
[cx - scale, cy + scale],
],
c.map(|v| (v as f32 * 0.64) as u8),
);
polygon(
&mut image,
size,
[
[cx, cy + scale * 0.5],
[cx + scale, cy],
[cx + scale, cy + scale],
[cx, cy + scale * 1.5],
],
c.map(|v| (v as f32 * 0.82) as u8),
);
}
let mut bytes = Vec::new();
{
let mut encoder = png::Encoder::new(&mut bytes, size as u32, size as u32);
encoder.set_color(png::ColorType::Rgb);
encoder.set_depth(png::BitDepth::Eight);
encoder.write_header()?.write_image_data(&image)?;
}
Ok(bytes)
}
fn polygon(img: &mut [u8], size: usize, p: [[f32; 2]; 4], color: [u8; 3]) {
let minx = p
.iter()
.map(|p| p[0])
.fold(f32::INFINITY, f32::min)
.floor()
.max(0.) as usize;
let maxx = p
.iter()
.map(|p| p[0])
.fold(f32::NEG_INFINITY, f32::max)
.ceil()
.clamp(0., size as f32 - 1.) as usize;
let miny = p
.iter()
.map(|p| p[1])
.fold(f32::INFINITY, f32::min)
.floor()
.max(0.) as usize;
let maxy = p
.iter()
.map(|p| p[1])
.fold(f32::NEG_INFINITY, f32::max)
.ceil()
.clamp(0., size as f32 - 1.) as usize;
for y in miny..=maxy {
for x in minx..=maxx {
let mut signs = [false; 4];
for i in 0..4 {
let a = p[i];
let b = p[(i + 1) % 4];
signs[i] = (b[0] - a[0]) * (y as f32 + 0.5 - a[1])
- (b[1] - a[1]) * (x as f32 + 0.5 - a[0])
>= 0.;
}
if signs.iter().all(|s| *s) || signs.iter().all(|s| !*s) {
let i = (y * size + x) * 3;
img[i..i + 3].copy_from_slice(&color);
}
}
}
}