mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Get PngPngPngJpeg terrain working in the actual game.
This commit is contained in:
parent
64ab1c2151
commit
5861603a34
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -5719,6 +5719,7 @@ dependencies = [
|
|||||||
"flate2",
|
"flate2",
|
||||||
"hashbrown",
|
"hashbrown",
|
||||||
"image",
|
"image",
|
||||||
|
"num-traits",
|
||||||
"serde",
|
"serde",
|
||||||
"specs",
|
"specs",
|
||||||
"specs-idvs",
|
"specs-idvs",
|
||||||
|
@ -1929,7 +1929,7 @@ impl Client {
|
|||||||
fn handle_server_terrain_msg(&mut self, msg: ServerGeneral) -> Result<(), Error> {
|
fn handle_server_terrain_msg(&mut self, msg: ServerGeneral) -> Result<(), Error> {
|
||||||
match msg {
|
match msg {
|
||||||
ServerGeneral::TerrainChunkUpdate { key, chunk } => {
|
ServerGeneral::TerrainChunkUpdate { key, chunk } => {
|
||||||
if let Some(chunk) = chunk.ok().and_then(|c| c.decompress()) {
|
if let Some(chunk) = chunk.ok().and_then(|c| c.to_chunk()) {
|
||||||
self.state.insert_chunk(key, Arc::new(chunk));
|
self.state.insert_chunk(key, Arc::new(chunk));
|
||||||
}
|
}
|
||||||
self.pending_chunks.remove(&key);
|
self.pending_chunks.remove(&key);
|
||||||
|
@ -17,9 +17,11 @@ common = {package = "veloren-common", path = "../../common"}
|
|||||||
bincode = "1.3.3"
|
bincode = "1.3.3"
|
||||||
flate2 = "1.0.20"
|
flate2 = "1.0.20"
|
||||||
image = { version = "0.23.12", default-features = false, features = ["png", "jpeg"] }
|
image = { version = "0.23.12", default-features = false, features = ["png", "jpeg"] }
|
||||||
|
num-traits = "0.2"
|
||||||
sum_type = "0.2.0"
|
sum_type = "0.2.0"
|
||||||
vek = { version = "=0.14.1", features = ["serde"] }
|
vek = { version = "=0.14.1", features = ["serde"] }
|
||||||
tracing = { version = "0.1", default-features = false }
|
tracing = { version = "0.1", default-features = false }
|
||||||
|
#inline_tweak = "1.0.2"
|
||||||
|
|
||||||
# Data structures
|
# Data structures
|
||||||
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }
|
hashbrown = { version = "0.9", features = ["rayon", "serde", "nightly"] }
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
use common::{
|
use common::{
|
||||||
terrain::{chonk::Chonk, Block, BlockKind, SpriteKind},
|
terrain::{chonk::Chonk, Block, BlockKind, SpriteKind},
|
||||||
vol::{BaseVol, IntoVolIterator, ReadVol, RectVolSize, SizedVol, WriteVol},
|
vol::{BaseVol, ReadVol, RectVolSize, WriteVol},
|
||||||
volumes::vol_grid_2d::VolGrid2d,
|
volumes::vol_grid_2d::VolGrid2d,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use image::{ImageBuffer, ImageDecoder, Pixel};
|
||||||
|
use num_traits::cast::FromPrimitive;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{
|
use std::{
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
};
|
};
|
||||||
use tracing::trace;
|
use tracing::{trace, warn};
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
/// Wrapper for compressed, serialized data (for stuff that doesn't use the
|
/// Wrapper for compressed, serialized data (for stuff that doesn't use the
|
||||||
@ -71,7 +72,7 @@ impl<T: for<'a> Deserialize<'a>> CompressedData<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Formula for packing voxel data into a 2d array
|
/// Formula for packing voxel data into a 2d array
|
||||||
pub trait PackingFormula {
|
pub trait PackingFormula: Copy {
|
||||||
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32);
|
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32);
|
||||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32);
|
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32);
|
||||||
}
|
}
|
||||||
@ -79,6 +80,7 @@ pub trait PackingFormula {
|
|||||||
/// A tall, thin image, with no wasted space, but which most image viewers don't
|
/// A tall, thin image, with no wasted space, but which most image viewers don't
|
||||||
/// handle well. Z levels increase from top to bottom, xy-slices are stacked
|
/// handle well. Z levels increase from top to bottom, xy-slices are stacked
|
||||||
/// vertically.
|
/// vertically.
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct TallPacking {
|
pub struct TallPacking {
|
||||||
/// Making the borders go back and forth based on z-parity preserves spatial
|
/// Making the borders go back and forth based on z-parity preserves spatial
|
||||||
/// locality better, but is more confusing to look at
|
/// locality better, but is more confusing to look at
|
||||||
@ -88,6 +90,7 @@ pub struct TallPacking {
|
|||||||
impl PackingFormula for TallPacking {
|
impl PackingFormula for TallPacking {
|
||||||
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) { (dims.x, dims.y * dims.z) }
|
fn dimensions(&self, dims: Vec3<u32>) -> (u32, u32) { (dims.x, dims.y * dims.z) }
|
||||||
|
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
||||||
let i = x;
|
let i = x;
|
||||||
let j0 = if self.flip_y {
|
let j0 = if self.flip_y {
|
||||||
@ -103,6 +106,7 @@ impl PackingFormula for TallPacking {
|
|||||||
/// A grid of the z levels, left to right, top to bottom, like English prose.
|
/// A grid of the z levels, left to right, top to bottom, like English prose.
|
||||||
/// Convenient for visualizing terrain, but wastes space if the number of z
|
/// Convenient for visualizing terrain, but wastes space if the number of z
|
||||||
/// levels isn't a perfect square.
|
/// levels isn't a perfect square.
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct GridLtrPacking;
|
pub struct GridLtrPacking;
|
||||||
|
|
||||||
impl PackingFormula for GridLtrPacking {
|
impl PackingFormula for GridLtrPacking {
|
||||||
@ -111,6 +115,7 @@ impl PackingFormula for GridLtrPacking {
|
|||||||
(dims.x * rootz, dims.y * rootz)
|
(dims.x * rootz, dims.y * rootz)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::many_single_char_names)]
|
||||||
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
fn index(&self, dims: Vec3<u32>, x: u32, y: u32, z: u32) -> (u32, u32) {
|
||||||
let rootz = (dims.z as f64).sqrt().ceil() as u32;
|
let rootz = (dims.z as f64).sqrt().ceil() as u32;
|
||||||
let i = x + (z % rootz) * dims.x;
|
let i = x + (z % rootz) * dims.x;
|
||||||
@ -119,23 +124,36 @@ impl PackingFormula for GridLtrPacking {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait VoxelImageEncoding {
|
pub trait VoxelImageEncoding: Copy {
|
||||||
type Workspace;
|
type Workspace;
|
||||||
type Output;
|
type Output;
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace;
|
fn create(width: u32, height: u32) -> Self::Workspace;
|
||||||
fn put_solid(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>);
|
fn put_solid(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>);
|
||||||
fn put_sprite(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, sprite: SpriteKind, ori: Option<u8>);
|
fn put_sprite(
|
||||||
fn finish(ws: &Self::Workspace) -> Self::Output;
|
ws: &mut Self::Workspace,
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
kind: BlockKind,
|
||||||
|
sprite: SpriteKind,
|
||||||
|
ori: Option<u8>,
|
||||||
|
);
|
||||||
|
fn finish(ws: &Self::Workspace) -> Option<Self::Output>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait VoxelImageDecoding: VoxelImageEncoding {
|
||||||
|
fn start(ws: &Self::Output) -> Option<Self::Workspace>;
|
||||||
|
fn get_block(ws: &Self::Workspace, x: u32, y: u32) -> Block;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct PngEncoding;
|
pub struct PngEncoding;
|
||||||
|
|
||||||
impl VoxelImageEncoding for PngEncoding {
|
impl VoxelImageEncoding for PngEncoding {
|
||||||
type Output = Vec<u8>;
|
type Output = Vec<u8>;
|
||||||
type Workspace = image::ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
type Workspace = ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace {
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::Rgba;
|
||||||
ImageBuffer::<Rgba<u8>, Vec<u8>>::new(width, height)
|
ImageBuffer::<Rgba<u8>, Vec<u8>>::new(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,11 +161,22 @@ impl VoxelImageEncoding for PngEncoding {
|
|||||||
ws.put_pixel(x, y, image::Rgba([rgb.r, rgb.g, rgb.b, 255 - kind as u8]));
|
ws.put_pixel(x, y, image::Rgba([rgb.r, rgb.g, rgb.b, 255 - kind as u8]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_sprite(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, sprite: SpriteKind, ori: Option<u8>) {
|
fn put_sprite(
|
||||||
ws.put_pixel(x, y, image::Rgba([kind as u8, sprite as u8, ori.unwrap_or(0), 255]));
|
ws: &mut Self::Workspace,
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
kind: BlockKind,
|
||||||
|
sprite: SpriteKind,
|
||||||
|
ori: Option<u8>,
|
||||||
|
) {
|
||||||
|
ws.put_pixel(
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
image::Rgba([kind as u8, sprite as u8, ori.unwrap_or(0), 255]),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Self::Output {
|
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
@ -161,19 +190,20 @@ impl VoxelImageEncoding for PngEncoding {
|
|||||||
ws.height(),
|
ws.height(),
|
||||||
image::ColorType::Rgba8,
|
image::ColorType::Rgba8,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.ok()?;
|
||||||
buf
|
Some(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct JpegEncoding;
|
pub struct JpegEncoding;
|
||||||
|
|
||||||
impl VoxelImageEncoding for JpegEncoding {
|
impl VoxelImageEncoding for JpegEncoding {
|
||||||
type Output = Vec<u8>;
|
type Output = Vec<u8>;
|
||||||
type Workspace = image::ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
type Workspace = ImageBuffer<image::Rgba<u8>, Vec<u8>>;
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace {
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
use image::{ImageBuffer, Rgba};
|
use image::Rgba;
|
||||||
ImageBuffer::<Rgba<u8>, Vec<u8>>::new(width, height)
|
ImageBuffer::<Rgba<u8>, Vec<u8>>::new(width, height)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,31 +211,39 @@ impl VoxelImageEncoding for JpegEncoding {
|
|||||||
ws.put_pixel(x, y, image::Rgba([rgb.r, rgb.g, rgb.b, 255 - kind as u8]));
|
ws.put_pixel(x, y, image::Rgba([rgb.r, rgb.g, rgb.b, 255 - kind as u8]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_sprite(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, sprite: SpriteKind, _: Option<u8>) {
|
fn put_sprite(
|
||||||
|
ws: &mut Self::Workspace,
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
kind: BlockKind,
|
||||||
|
sprite: SpriteKind,
|
||||||
|
_: Option<u8>,
|
||||||
|
) {
|
||||||
ws.put_pixel(x, y, image::Rgba([kind as u8, sprite as u8, 255, 255]));
|
ws.put_pixel(x, y, image::Rgba([kind as u8, sprite as u8, 255, 255]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Self::Output {
|
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
||||||
jpeg.encode_image(ws).unwrap();
|
jpeg.encode_image(ws).ok()?;
|
||||||
buf
|
Some(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
pub struct MixedEncoding;
|
pub struct MixedEncoding;
|
||||||
|
|
||||||
impl VoxelImageEncoding for MixedEncoding {
|
impl VoxelImageEncoding for MixedEncoding {
|
||||||
type Output = (Vec<u8>, [usize; 3]);
|
type Output = (Vec<u8>, [usize; 3]);
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
type Workspace = (
|
type Workspace = (
|
||||||
image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
image::ImageBuffer<image::Rgb<u8>, Vec<u8>>,
|
ImageBuffer<image::Rgb<u8>, Vec<u8>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace {
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
use image::ImageBuffer;
|
|
||||||
(
|
(
|
||||||
ImageBuffer::new(width, height),
|
ImageBuffer::new(width, height),
|
||||||
ImageBuffer::new(width, height),
|
ImageBuffer::new(width, height),
|
||||||
@ -221,39 +259,174 @@ impl VoxelImageEncoding for MixedEncoding {
|
|||||||
ws.3.put_pixel(x, y, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
ws.3.put_pixel(x, y, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_sprite(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, sprite: SpriteKind, ori: Option<u8>) {
|
fn put_sprite(
|
||||||
|
ws: &mut Self::Workspace,
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
kind: BlockKind,
|
||||||
|
sprite: SpriteKind,
|
||||||
|
ori: Option<u8>,
|
||||||
|
) {
|
||||||
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
||||||
ws.1.put_pixel(x, y, image::Luma([sprite as u8]));
|
ws.1.put_pixel(x, y, image::Luma([sprite as u8]));
|
||||||
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
||||||
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Self::Output {
|
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let mut indices = [0; 3];
|
let mut indices = [0; 3];
|
||||||
let mut f = |x: &image::ImageBuffer<_, Vec<u8>>, i| {
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
&mut buf,
|
&mut buf,
|
||||||
CompressionType::Fast,
|
CompressionType::Fast,
|
||||||
FilterType::Up,
|
FilterType::Up,
|
||||||
);
|
);
|
||||||
png.encode(
|
png.encode(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
||||||
&*x.as_raw(),
|
.ok()?;
|
||||||
x.width(),
|
|
||||||
x.height(),
|
|
||||||
image::ColorType::L8,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
indices[i] = buf.len();
|
indices[i] = buf.len();
|
||||||
|
Some(())
|
||||||
};
|
};
|
||||||
f(&ws.0, 0);
|
f(&ws.0, 0)?;
|
||||||
f(&ws.1, 1);
|
f(&ws.1, 1)?;
|
||||||
f(&ws.2, 2);
|
f(&ws.2, 2)?;
|
||||||
|
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 10);
|
||||||
jpeg.encode_image(&ws.3).unwrap();
|
jpeg.encode_image(&ws.3).ok()?;
|
||||||
(buf, indices)
|
Some((buf, indices))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn image_from_bytes<'a, I: ImageDecoder<'a>, P: 'static + Pixel<Subpixel = u8>>(
|
||||||
|
decoder: I,
|
||||||
|
) -> Option<ImageBuffer<P, Vec<u8>>> {
|
||||||
|
let (w, h) = decoder.dimensions();
|
||||||
|
let mut buf = vec![0; decoder.total_bytes() as usize];
|
||||||
|
decoder.read_image(&mut buf).ok()?;
|
||||||
|
ImageBuffer::from_raw(w, h, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VoxelImageDecoding for MixedEncoding {
|
||||||
|
fn start((quad, indices): &Self::Output) -> Option<Self::Workspace> {
|
||||||
|
use image::codecs::{jpeg::JpegDecoder, png::PngDecoder};
|
||||||
|
let ranges: [_; 4] = [
|
||||||
|
0..indices[0],
|
||||||
|
indices[0]..indices[1],
|
||||||
|
indices[1]..indices[2],
|
||||||
|
indices[2]..quad.len(),
|
||||||
|
];
|
||||||
|
tracing::info!("{:?} {:?}", ranges, indices);
|
||||||
|
let a = image_from_bytes(PngDecoder::new(&quad[ranges[0].clone()]).ok()?)?;
|
||||||
|
let b = image_from_bytes(PngDecoder::new(&quad[ranges[1].clone()]).ok()?)?;
|
||||||
|
let c = image_from_bytes(PngDecoder::new(&quad[ranges[2].clone()]).ok()?)?;
|
||||||
|
let d = image_from_bytes(JpegDecoder::new(&quad[ranges[3].clone()]).ok()?)?;
|
||||||
|
Some((a, b, c, d))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_block(ws: &Self::Workspace, x: u32, y: u32) -> Block {
|
||||||
|
if let Some(kind) = BlockKind::from_u8(ws.0.get_pixel(x, y).0[0]) {
|
||||||
|
if kind.is_filled() {
|
||||||
|
let rgb = ws.3.get_pixel(x, y);
|
||||||
|
Block::new(kind, Rgb {
|
||||||
|
r: rgb[0],
|
||||||
|
g: rgb[1],
|
||||||
|
b: rgb[2],
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
let mut block = Block::new(kind, Rgb { r: 0, g: 0, b: 0 });
|
||||||
|
if let Some(spritekind) = SpriteKind::from_u8(ws.1.get_pixel(x, y).0[0]) {
|
||||||
|
block = block.with_sprite(spritekind);
|
||||||
|
}
|
||||||
|
if let Some(oriblock) = block.with_ori(ws.2.get_pixel(x, y).0[0]) {
|
||||||
|
block = oriblock;
|
||||||
|
}
|
||||||
|
block
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Block::empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
pub struct QuadPngEncoding;
|
||||||
|
|
||||||
|
impl VoxelImageEncoding for QuadPngEncoding {
|
||||||
|
type Output = CompressedData<(Vec<u8>, [usize; 3])>;
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
type Workspace = (
|
||||||
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
|
ImageBuffer<image::Rgb<u8>, Vec<u8>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
|
(
|
||||||
|
ImageBuffer::new(width, height),
|
||||||
|
ImageBuffer::new(width, height),
|
||||||
|
ImageBuffer::new(width, height),
|
||||||
|
ImageBuffer::new(width, height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put_solid(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>) {
|
||||||
|
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
||||||
|
ws.1.put_pixel(x, y, image::Luma([0]));
|
||||||
|
ws.2.put_pixel(x, y, image::Luma([0]));
|
||||||
|
ws.3.put_pixel(x, y, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put_sprite(
|
||||||
|
ws: &mut Self::Workspace,
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
kind: BlockKind,
|
||||||
|
sprite: SpriteKind,
|
||||||
|
ori: Option<u8>,
|
||||||
|
) {
|
||||||
|
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
||||||
|
ws.1.put_pixel(x, y, image::Luma([sprite as u8]));
|
||||||
|
ws.2.put_pixel(x, y, image::Luma([ori.unwrap_or(0)]));
|
||||||
|
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
|
let mut indices = [0; 3];
|
||||||
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
||||||
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
|
&mut buf,
|
||||||
|
CompressionType::Fast,
|
||||||
|
FilterType::Up,
|
||||||
|
);
|
||||||
|
png.encode(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
||||||
|
.ok()?;
|
||||||
|
indices[i] = buf.len();
|
||||||
|
Some(())
|
||||||
|
};
|
||||||
|
f(&ws.0, 0)?;
|
||||||
|
f(&ws.1, 1)?;
|
||||||
|
f(&ws.2, 2)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
|
&mut buf,
|
||||||
|
CompressionType::Fast,
|
||||||
|
FilterType::Paeth,
|
||||||
|
);
|
||||||
|
png.encode(
|
||||||
|
&*ws.3.as_raw(),
|
||||||
|
ws.3.width(),
|
||||||
|
ws.3.height(),
|
||||||
|
image::ColorType::Rgb8,
|
||||||
|
)
|
||||||
|
.ok()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(CompressedData::compress(&(buf, indices), 4))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -261,7 +434,7 @@ pub fn image_terrain_chonk<S: RectVolSize, M: Clone, P: PackingFormula, VIE: Vox
|
|||||||
vie: VIE,
|
vie: VIE,
|
||||||
packing: P,
|
packing: P,
|
||||||
chonk: &Chonk<Block, S, M>,
|
chonk: &Chonk<Block, S, M>,
|
||||||
) -> VIE::Output {
|
) -> Option<VIE::Output> {
|
||||||
image_terrain(
|
image_terrain(
|
||||||
vie,
|
vie,
|
||||||
packing,
|
packing,
|
||||||
@ -280,7 +453,7 @@ pub fn image_terrain_volgrid<
|
|||||||
vie: VIE,
|
vie: VIE,
|
||||||
packing: P,
|
packing: P,
|
||||||
volgrid: &VolGrid2d<Chonk<Block, S, M>>,
|
volgrid: &VolGrid2d<Chonk<Block, S, M>>,
|
||||||
) -> VIE::Output {
|
) -> Option<VIE::Output> {
|
||||||
let mut lo = Vec3::broadcast(i32::MAX);
|
let mut lo = Vec3::broadcast(i32::MAX);
|
||||||
let mut hi = Vec3::broadcast(i32::MIN);
|
let mut hi = Vec3::broadcast(i32::MIN);
|
||||||
for (pos, chonk) in volgrid.iter() {
|
for (pos, chonk) in volgrid.iter() {
|
||||||
@ -306,8 +479,13 @@ pub fn image_terrain<
|
|||||||
vol: &V,
|
vol: &V,
|
||||||
lo: Vec3<u32>,
|
lo: Vec3<u32>,
|
||||||
hi: Vec3<u32>,
|
hi: Vec3<u32>,
|
||||||
) -> VIE::Output {
|
) -> Option<VIE::Output> {
|
||||||
let dims = hi - lo;
|
tracing::info!("image_terrain: {:?} {:?}", lo, hi);
|
||||||
|
let dims = Vec3::new(
|
||||||
|
hi.x.wrapping_sub(lo.x),
|
||||||
|
hi.y.wrapping_sub(lo.y),
|
||||||
|
hi.z.wrapping_sub(lo.z),
|
||||||
|
);
|
||||||
|
|
||||||
let (width, height) = packing.dimensions(dims);
|
let (width, height) = packing.dimensions(dims);
|
||||||
let mut image = VIE::create(width, height);
|
let mut image = VIE::create(width, height);
|
||||||
@ -317,7 +495,14 @@ pub fn image_terrain<
|
|||||||
let (i, j) = packing.index(dims, x, y, z);
|
let (i, j) = packing.index(dims, x, y, z);
|
||||||
|
|
||||||
let block = *vol
|
let block = *vol
|
||||||
.get(Vec3::new(x + lo.x, y + lo.y, z + lo.z).as_())
|
.get(
|
||||||
|
Vec3::new(
|
||||||
|
x.wrapping_add(lo.x),
|
||||||
|
y.wrapping_add(lo.y),
|
||||||
|
z.wrapping_add(lo.z),
|
||||||
|
)
|
||||||
|
.as_(),
|
||||||
|
)
|
||||||
.unwrap_or(&Block::empty());
|
.unwrap_or(&Block::empty());
|
||||||
match (block.get_color(), block.get_sprite()) {
|
match (block.get_color(), block.get_sprite()) {
|
||||||
(Some(rgb), None) => {
|
(Some(rgb), None) => {
|
||||||
@ -339,69 +524,85 @@ pub fn image_terrain<
|
|||||||
VIE::finish(&image)
|
VIE::finish(&image)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct MixedEncodingDenseSprites;
|
pub fn write_image_terrain<
|
||||||
|
V: BaseVol<Vox = Block> + WriteVol,
|
||||||
impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
P: PackingFormula,
|
||||||
type Output = (Vec<u8>, [usize; 3]);
|
VIE: VoxelImageEncoding + VoxelImageDecoding,
|
||||||
type Workspace = (
|
>(
|
||||||
image::ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
_: VIE,
|
||||||
Vec<u8>,
|
packing: P,
|
||||||
Vec<u8>,
|
vol: &mut V,
|
||||||
image::ImageBuffer<image::Rgb<u8>, Vec<u8>>,
|
data: &VIE::Output,
|
||||||
|
lo: Vec3<u32>,
|
||||||
|
hi: Vec3<u32>,
|
||||||
|
) -> Option<()> {
|
||||||
|
let ws = VIE::start(data)?;
|
||||||
|
let dims = Vec3::new(
|
||||||
|
hi.x.wrapping_sub(lo.x),
|
||||||
|
hi.y.wrapping_sub(lo.y),
|
||||||
|
hi.z.wrapping_sub(lo.z),
|
||||||
);
|
);
|
||||||
|
for z in 0..dims.z {
|
||||||
|
for y in 0..dims.y {
|
||||||
|
for x in 0..dims.x {
|
||||||
|
let (i, j) = packing.index(dims, x, y, z);
|
||||||
|
let block = VIE::get_block(&ws, i, j);
|
||||||
|
if let Err(e) = vol.set(lo.as_() + Vec3::new(x, y, z).as_(), block) {
|
||||||
|
warn!(
|
||||||
|
"Error placing a block into a volume at {:?}: {:?}",
|
||||||
|
(x, y, z),
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(())
|
||||||
|
}
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace {
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
use image::ImageBuffer;
|
pub struct WireChonk<VIE: VoxelImageEncoding, P: PackingFormula, M: Clone, S: RectVolSize> {
|
||||||
(
|
zmin: i32,
|
||||||
ImageBuffer::new(width, height),
|
zmax: i32,
|
||||||
Vec::new(),
|
data: VIE::Output,
|
||||||
Vec::new(),
|
below: Block,
|
||||||
ImageBuffer::new(width, height),
|
above: Block,
|
||||||
)
|
meta: M,
|
||||||
|
vie: VIE,
|
||||||
|
packing: P,
|
||||||
|
size: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<VIE: VoxelImageEncoding + VoxelImageDecoding, P: PackingFormula, M: Clone, S: RectVolSize>
|
||||||
|
WireChonk<VIE, P, M, S>
|
||||||
|
{
|
||||||
|
pub fn from_chonk(vie: VIE, packing: P, chonk: &Chonk<Block, S, M>) -> Option<Self> {
|
||||||
|
let data = image_terrain_chonk(vie, packing, chonk)?;
|
||||||
|
Some(Self {
|
||||||
|
zmin: chonk.get_min_z(),
|
||||||
|
zmax: chonk.get_max_z(),
|
||||||
|
data,
|
||||||
|
below: *chonk
|
||||||
|
.get(Vec3::new(0, 0, chonk.get_min_z().saturating_sub(1)))
|
||||||
|
.ok()?,
|
||||||
|
above: *chonk.get(Vec3::new(0, 0, chonk.get_max_z() + 1)).ok()?,
|
||||||
|
meta: chonk.meta().clone(),
|
||||||
|
vie,
|
||||||
|
packing,
|
||||||
|
size: PhantomData,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn put_solid(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>) {
|
pub fn to_chonk(&self) -> Option<Chonk<Block, S, M>> {
|
||||||
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
let mut chonk = Chonk::new(self.zmin, self.below, self.above, self.meta.clone());
|
||||||
ws.3.put_pixel(x, y, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
write_image_terrain(
|
||||||
}
|
self.vie,
|
||||||
|
self.packing,
|
||||||
fn put_sprite(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, sprite: SpriteKind, ori: Option<u8>) {
|
&mut chonk,
|
||||||
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
&self.data,
|
||||||
ws.1.push(sprite as u8);
|
Vec3::new(0, 0, self.zmin as u32),
|
||||||
ws.2.push(ori.unwrap_or(0));
|
Vec3::new(S::RECT_SIZE.x, S::RECT_SIZE.y, self.zmax as u32),
|
||||||
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
)?;
|
||||||
}
|
Some(chonk)
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Self::Output {
|
|
||||||
let mut buf = Vec::new();
|
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
|
||||||
let mut indices = [0; 3];
|
|
||||||
let mut f = |x: &image::ImageBuffer<_, Vec<u8>>, i| {
|
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
|
||||||
&mut buf,
|
|
||||||
CompressionType::Fast,
|
|
||||||
FilterType::Up,
|
|
||||||
);
|
|
||||||
png.encode(
|
|
||||||
&*x.as_raw(),
|
|
||||||
x.width(),
|
|
||||||
x.height(),
|
|
||||||
image::ColorType::L8,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
indices[i] = buf.len();
|
|
||||||
};
|
|
||||||
f(&ws.0, 0);
|
|
||||||
let mut g = |x: &[u8], i| {
|
|
||||||
buf.extend_from_slice(&*CompressedData::compress(&x, 4).data);
|
|
||||||
indices[i] = buf.len();
|
|
||||||
};
|
|
||||||
|
|
||||||
g(&ws.1, 1);
|
|
||||||
g(&ws.2, 2);
|
|
||||||
|
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
|
||||||
jpeg.encode_image(&ws.3).unwrap();
|
|
||||||
(buf, indices)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,13 @@ pub use self::{
|
|||||||
client::{ClientGeneral, ClientMsg, ClientRegister, ClientType},
|
client::{ClientGeneral, ClientMsg, ClientRegister, ClientType},
|
||||||
compression::{
|
compression::{
|
||||||
CompressedData, GridLtrPacking, JpegEncoding, MixedEncoding, PackingFormula, PngEncoding,
|
CompressedData, GridLtrPacking, JpegEncoding, MixedEncoding, PackingFormula, PngEncoding,
|
||||||
TallPacking, VoxelImageEncoding,
|
TallPacking, VoxelImageEncoding, WireChonk,
|
||||||
},
|
},
|
||||||
ecs_packet::EcsCompPacket,
|
ecs_packet::EcsCompPacket,
|
||||||
server::{
|
server::{
|
||||||
CharacterInfo, DisconnectReason, InviteAnswer, Notification, PlayerInfo, PlayerListUpdate,
|
CharacterInfo, DisconnectReason, InviteAnswer, Notification, PlayerInfo, PlayerListUpdate,
|
||||||
RegisterError, ServerGeneral, ServerInfo, ServerInit, ServerMsg, ServerRegisterAnswer,
|
RegisterError, SerializedTerrainChunk, ServerGeneral, ServerInfo, ServerInit, ServerMsg,
|
||||||
|
ServerRegisterAnswer,
|
||||||
},
|
},
|
||||||
world_msg::WorldMapMsg,
|
world_msg::WorldMapMsg,
|
||||||
};
|
};
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
use super::{world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, PingMsg};
|
use super::{
|
||||||
|
world_msg::EconomyInfo, ClientType, CompressedData, EcsCompPacket, MixedEncoding, PingMsg,
|
||||||
|
TallPacking, WireChonk,
|
||||||
|
};
|
||||||
use crate::sync;
|
use crate::sync;
|
||||||
use common::{
|
use common::{
|
||||||
character::{self, CharacterItem},
|
character::{self, CharacterItem},
|
||||||
@ -6,13 +9,14 @@ use common::{
|
|||||||
outcome::Outcome,
|
outcome::Outcome,
|
||||||
recipe::RecipeBook,
|
recipe::RecipeBook,
|
||||||
resources::TimeOfDay,
|
resources::TimeOfDay,
|
||||||
terrain::{Block, TerrainChunk},
|
terrain::{Block, TerrainChunk, TerrainChunkMeta, TerrainChunkSize},
|
||||||
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
|
trade::{PendingTrade, SitePrices, TradeId, TradeResult},
|
||||||
uid::Uid,
|
uid::Uid,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use tracing::warn;
|
||||||
use vek::*;
|
use vek::*;
|
||||||
|
|
||||||
///This struct contains all messages the server might send (on different
|
///This struct contains all messages the server might send (on different
|
||||||
@ -62,6 +66,35 @@ pub enum ServerInit {
|
|||||||
|
|
||||||
pub type ServerRegisterAnswer = Result<(), RegisterError>;
|
pub type ServerRegisterAnswer = Result<(), RegisterError>;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub enum SerializedTerrainChunk {
|
||||||
|
DeflatedChonk(CompressedData<TerrainChunk>),
|
||||||
|
PngPngPngJpeg(WireChonk<MixedEncoding, TallPacking, TerrainChunkMeta, TerrainChunkSize>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerializedTerrainChunk {
|
||||||
|
pub fn deflate(chunk: &TerrainChunk) -> Self {
|
||||||
|
Self::DeflatedChonk(CompressedData::compress(chunk, 5))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn image(chunk: &TerrainChunk) -> Self {
|
||||||
|
if let Some(wc) = WireChonk::from_chonk(MixedEncoding, TallPacking { flip_y: true }, chunk)
|
||||||
|
{
|
||||||
|
Self::PngPngPngJpeg(wc)
|
||||||
|
} else {
|
||||||
|
warn!("Image encoding failure occurred, falling back to deflate");
|
||||||
|
Self::deflate(chunk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_chunk(&self) -> Option<TerrainChunk> {
|
||||||
|
match self {
|
||||||
|
Self::DeflatedChonk(chonk) => chonk.decompress(),
|
||||||
|
Self::PngPngPngJpeg(wc) => wc.to_chonk(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Messages sent from the server to the client
|
/// Messages sent from the server to the client
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub enum ServerGeneral {
|
pub enum ServerGeneral {
|
||||||
@ -106,7 +139,7 @@ pub enum ServerGeneral {
|
|||||||
// Ingame related AND terrain stream
|
// Ingame related AND terrain stream
|
||||||
TerrainChunkUpdate {
|
TerrainChunkUpdate {
|
||||||
key: Vec2<i32>,
|
key: Vec2<i32>,
|
||||||
chunk: Result<CompressedData<TerrainChunk>, ()>,
|
chunk: Result<SerializedTerrainChunk, ()>,
|
||||||
},
|
},
|
||||||
TerrainBlockUpdates(CompressedData<HashMap<Vec3<i32>, Block>>),
|
TerrainBlockUpdates(CompressedData<HashMap<Vec3<i32>, Block>>),
|
||||||
// Always possible
|
// Always possible
|
||||||
|
@ -6,7 +6,7 @@ use common::{
|
|||||||
vol::RectVolSize,
|
vol::RectVolSize,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
use common_ecs::{Job, Origin, ParMode, Phase, System};
|
||||||
use common_net::msg::{ClientGeneral, CompressedData, ServerGeneral};
|
use common_net::msg::{ClientGeneral, SerializedTerrainChunk, ServerGeneral};
|
||||||
use rayon::iter::ParallelIterator;
|
use rayon::iter::ParallelIterator;
|
||||||
use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage};
|
use specs::{Entities, Join, ParJoin, Read, ReadExpect, ReadStorage};
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
@ -79,7 +79,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
network_metrics.chunks_served_from_memory.inc();
|
network_metrics.chunks_served_from_memory.inc();
|
||||||
client.send(ServerGeneral::TerrainChunkUpdate {
|
client.send(ServerGeneral::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Ok(CompressedData::compress(&chunk, 5)),
|
chunk: Ok(SerializedTerrainChunk::image(&chunk)),
|
||||||
})?
|
})?
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
|
@ -14,7 +14,7 @@ use common::{
|
|||||||
LoadoutBuilder, SkillSetBuilder,
|
LoadoutBuilder, SkillSetBuilder,
|
||||||
};
|
};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use common_net::msg::{CompressedData, ServerGeneral};
|
use common_net::msg::{SerializedTerrainChunk, ServerGeneral};
|
||||||
use common_state::TerrainChanges;
|
use common_state::TerrainChanges;
|
||||||
use comp::Behavior;
|
use comp::Behavior;
|
||||||
use specs::{Join, Read, ReadStorage, Write, WriteExpect};
|
use specs::{Join, Read, ReadStorage, Write, WriteExpect};
|
||||||
@ -224,7 +224,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
new_chunks.into_par_iter().for_each(|(key, chunk)| {
|
new_chunks.into_par_iter().for_each(|(key, chunk)| {
|
||||||
let mut msg = Some(ServerGeneral::TerrainChunkUpdate {
|
let mut msg = Some(ServerGeneral::TerrainChunkUpdate {
|
||||||
key,
|
key,
|
||||||
chunk: Ok(CompressedData::compress(&*chunk, 5)),
|
chunk: Ok(SerializedTerrainChunk::image(&*chunk)),
|
||||||
});
|
});
|
||||||
let mut lazy_msg = None;
|
let mut lazy_msg = None;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use crate::{client::Client, presence::Presence};
|
use crate::{client::Client, presence::Presence};
|
||||||
use common::{comp::Pos, terrain::TerrainGrid};
|
use common::{comp::Pos, terrain::TerrainGrid};
|
||||||
use common_ecs::{Job, Origin, Phase, System};
|
use common_ecs::{Job, Origin, Phase, System};
|
||||||
use common_net::msg::{CompressedData, ServerGeneral};
|
use common_net::msg::{CompressedData, SerializedTerrainChunk, ServerGeneral};
|
||||||
use common_state::TerrainChanges;
|
use common_state::TerrainChanges;
|
||||||
use specs::{Join, Read, ReadExpect, ReadStorage};
|
use specs::{Join, Read, ReadExpect, ReadStorage};
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ impl<'a> System<'a> for Sys {
|
|||||||
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
|
lazy_msg = Some(client.prepare(ServerGeneral::TerrainChunkUpdate {
|
||||||
key: *chunk_key,
|
key: *chunk_key,
|
||||||
chunk: Ok(match terrain.get_key(*chunk_key) {
|
chunk: Ok(match terrain.get_key(*chunk_key) {
|
||||||
Some(chunk) => CompressedData::compress(&chunk, 5),
|
Some(chunk) => SerializedTerrainChunk::image(&chunk),
|
||||||
None => break 'chunk,
|
None => break 'chunk,
|
||||||
}),
|
}),
|
||||||
}));
|
}));
|
||||||
|
@ -9,10 +9,11 @@ use common::{
|
|||||||
};
|
};
|
||||||
use common_net::msg::compression::{
|
use common_net::msg::compression::{
|
||||||
image_terrain, image_terrain_chonk, image_terrain_volgrid, CompressedData, GridLtrPacking,
|
image_terrain, image_terrain_chonk, image_terrain_volgrid, CompressedData, GridLtrPacking,
|
||||||
JpegEncoding, MixedEncoding, MixedEncodingDenseSprites, PackingFormula, PngEncoding,
|
JpegEncoding, MixedEncoding, PackingFormula, PngEncoding, QuadPngEncoding, TallPacking,
|
||||||
TallPacking, VoxelImageEncoding,
|
VoxelImageEncoding,
|
||||||
};
|
};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
|
use image::ImageBuffer;
|
||||||
use std::{
|
use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@ -120,6 +121,7 @@ fn channelize_dyna<M: Clone, A: Access>(
|
|||||||
(blocks, r, g, b, sprites)
|
(blocks, r, g, b, sprites)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct MixedEncodingSparseSprites;
|
pub struct MixedEncodingSparseSprites;
|
||||||
|
|
||||||
impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
||||||
@ -135,7 +137,6 @@ impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fn create(width: u32, height: u32) -> Self::Workspace {
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
use image::ImageBuffer;
|
|
||||||
(
|
(
|
||||||
ImageBuffer::new(width, height),
|
ImageBuffer::new(width, height),
|
||||||
ImageBuffer::new(width, height),
|
ImageBuffer::new(width, height),
|
||||||
@ -161,7 +162,7 @@ impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
|||||||
ws.2.insert(Vec2::new(x, y), (sprite, ori.unwrap_or(0)));
|
ws.2.insert(Vec2::new(x, y), (sprite, ori.unwrap_or(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(ws: &Self::Workspace) -> Self::Output {
|
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
use image::codecs::png::{CompressionType, FilterType};
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
let png = image::codecs::png::PngEncoder::new_with_quality(
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
@ -175,11 +176,81 @@ impl VoxelImageEncoding for MixedEncodingSparseSprites {
|
|||||||
ws.0.height(),
|
ws.0.height(),
|
||||||
image::ColorType::L8,
|
image::ColorType::L8,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.ok()?;
|
||||||
let index = buf.len();
|
let index = buf.len();
|
||||||
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
||||||
jpeg.encode_image(&ws.1).unwrap();
|
jpeg.encode_image(&ws.1).ok()?;
|
||||||
(buf, index, CompressedData::compress(&ws.2, 4))
|
Some((buf, index, CompressedData::compress(&ws.2, 4)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct MixedEncodingDenseSprites;
|
||||||
|
|
||||||
|
impl VoxelImageEncoding for MixedEncodingDenseSprites {
|
||||||
|
type Output = (Vec<u8>, [usize; 3]);
|
||||||
|
type Workspace = (
|
||||||
|
ImageBuffer<image::Luma<u8>, Vec<u8>>,
|
||||||
|
Vec<u8>,
|
||||||
|
Vec<u8>,
|
||||||
|
ImageBuffer<image::Rgb<u8>, Vec<u8>>,
|
||||||
|
);
|
||||||
|
|
||||||
|
fn create(width: u32, height: u32) -> Self::Workspace {
|
||||||
|
(
|
||||||
|
ImageBuffer::new(width, height),
|
||||||
|
Vec::new(),
|
||||||
|
Vec::new(),
|
||||||
|
ImageBuffer::new(width, height),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put_solid(ws: &mut Self::Workspace, x: u32, y: u32, kind: BlockKind, rgb: Rgb<u8>) {
|
||||||
|
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
||||||
|
ws.3.put_pixel(x, y, image::Rgb([rgb.r, rgb.g, rgb.b]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn put_sprite(
|
||||||
|
ws: &mut Self::Workspace,
|
||||||
|
x: u32,
|
||||||
|
y: u32,
|
||||||
|
kind: BlockKind,
|
||||||
|
sprite: SpriteKind,
|
||||||
|
ori: Option<u8>,
|
||||||
|
) {
|
||||||
|
ws.0.put_pixel(x, y, image::Luma([kind as u8]));
|
||||||
|
ws.1.push(sprite as u8);
|
||||||
|
ws.2.push(ori.unwrap_or(0));
|
||||||
|
ws.3.put_pixel(x, y, image::Rgb([0; 3]));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(ws: &Self::Workspace) -> Option<Self::Output> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
use image::codecs::png::{CompressionType, FilterType};
|
||||||
|
let mut indices = [0; 3];
|
||||||
|
let mut f = |x: &ImageBuffer<_, Vec<u8>>, i| {
|
||||||
|
let png = image::codecs::png::PngEncoder::new_with_quality(
|
||||||
|
&mut buf,
|
||||||
|
CompressionType::Fast,
|
||||||
|
FilterType::Up,
|
||||||
|
);
|
||||||
|
png.encode(&*x.as_raw(), x.width(), x.height(), image::ColorType::L8)
|
||||||
|
.ok()?;
|
||||||
|
indices[i] = buf.len();
|
||||||
|
Some(())
|
||||||
|
};
|
||||||
|
f(&ws.0, 0)?;
|
||||||
|
let mut g = |x: &[u8], i| {
|
||||||
|
buf.extend_from_slice(&*CompressedData::compress(&x, 4).data);
|
||||||
|
indices[i] = buf.len();
|
||||||
|
};
|
||||||
|
|
||||||
|
g(&ws.1, 1);
|
||||||
|
g(&ws.2, 2);
|
||||||
|
|
||||||
|
let mut jpeg = image::codecs::jpeg::JpegEncoder::new_with_quality(&mut buf, 1);
|
||||||
|
jpeg.encode_image(&ws.3).ok()?;
|
||||||
|
Some((buf, indices))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,8 +329,8 @@ fn main() {
|
|||||||
));
|
));
|
||||||
|
|
||||||
for (sitename, sitepos) in sites.iter() {
|
for (sitename, sitepos) in sites.iter() {
|
||||||
let mut totals = [0.0; 12];
|
let mut totals = [0.0; 13];
|
||||||
let mut total_timings = [0.0; 9];
|
let mut total_timings = [0.0; 10];
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut volgrid = VolGrid2d::new().unwrap();
|
let mut volgrid = VolGrid2d::new().unwrap();
|
||||||
for (i, spiralpos) in Spiral2d::new()
|
for (i, spiralpos) in Spiral2d::new()
|
||||||
@ -305,7 +376,8 @@ fn main() {
|
|||||||
do_deflate_flate2(&bincode::serialize(&channelize_dyna(&dyna)).unwrap());
|
do_deflate_flate2(&bincode::serialize(&channelize_dyna(&dyna)).unwrap());
|
||||||
|
|
||||||
let jpegchonkgrid_pre = Instant::now();
|
let jpegchonkgrid_pre = Instant::now();
|
||||||
let jpegchonkgrid = image_terrain_chonk(JpegEncoding, GridLtrPacking, &chunk);
|
let jpegchonkgrid =
|
||||||
|
image_terrain_chonk(JpegEncoding, GridLtrPacking, &chunk).unwrap();
|
||||||
let jpegchonkgrid_post = Instant::now();
|
let jpegchonkgrid_post = Instant::now();
|
||||||
|
|
||||||
if false {
|
if false {
|
||||||
@ -320,29 +392,42 @@ fn main() {
|
|||||||
|
|
||||||
let jpegchonktall_pre = Instant::now();
|
let jpegchonktall_pre = Instant::now();
|
||||||
let jpegchonktall =
|
let jpegchonktall =
|
||||||
image_terrain_chonk(JpegEncoding, TallPacking { flip_y: false }, &chunk);
|
image_terrain_chonk(JpegEncoding, TallPacking { flip_y: false }, &chunk)
|
||||||
|
.unwrap();
|
||||||
let jpegchonktall_post = Instant::now();
|
let jpegchonktall_post = Instant::now();
|
||||||
|
|
||||||
let jpegchonkflip_pre = Instant::now();
|
let jpegchonkflip_pre = Instant::now();
|
||||||
let jpegchonkflip =
|
let jpegchonkflip =
|
||||||
image_terrain_chonk(JpegEncoding, TallPacking { flip_y: true }, &chunk);
|
image_terrain_chonk(JpegEncoding, TallPacking { flip_y: true }, &chunk)
|
||||||
|
.unwrap();
|
||||||
let jpegchonkflip_post = Instant::now();
|
let jpegchonkflip_post = Instant::now();
|
||||||
|
|
||||||
let mixedchonk_pre = Instant::now();
|
let mixedchonk_pre = Instant::now();
|
||||||
let mixedchonk =
|
let mixedchonk =
|
||||||
image_terrain_chonk(MixedEncoding, TallPacking { flip_y: true }, &chunk);
|
image_terrain_chonk(MixedEncoding, TallPacking { flip_y: true }, &chunk)
|
||||||
|
.unwrap();
|
||||||
let mixedchonk_post = Instant::now();
|
let mixedchonk_post = Instant::now();
|
||||||
|
|
||||||
let mixeddeflate = CompressedData::compress(&mixedchonk, 1);
|
let mixeddeflate = CompressedData::compress(&mixedchonk, 1);
|
||||||
let mixeddeflate_post = Instant::now();
|
let mixeddeflate_post = Instant::now();
|
||||||
|
|
||||||
let mixeddense_pre = Instant::now();
|
let mixeddense_pre = Instant::now();
|
||||||
let mixeddense =
|
let mixeddense = image_terrain_chonk(
|
||||||
image_terrain_chonk(MixedEncodingDenseSprites, TallPacking { flip_y: true }, &chunk);
|
MixedEncodingDenseSprites,
|
||||||
|
TallPacking { flip_y: true },
|
||||||
|
&chunk,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
let mixeddense_post = Instant::now();
|
let mixeddense_post = Instant::now();
|
||||||
|
|
||||||
|
let quadpng_pre = Instant::now();
|
||||||
|
let quadpng =
|
||||||
|
image_terrain_chonk(QuadPngEncoding, TallPacking { flip_y: true }, &chunk)
|
||||||
|
.unwrap();
|
||||||
|
let quadpng_post = Instant::now();
|
||||||
|
|
||||||
let pngchonk_pre = Instant::now();
|
let pngchonk_pre = Instant::now();
|
||||||
let pngchonk = image_terrain_chonk(PngEncoding, GridLtrPacking, &chunk);
|
let pngchonk = image_terrain_chonk(PngEncoding, GridLtrPacking, &chunk).unwrap();
|
||||||
let pngchonk_post = Instant::now();
|
let pngchonk_post = Instant::now();
|
||||||
|
|
||||||
let n = uncompressed.len();
|
let n = uncompressed.len();
|
||||||
@ -358,6 +443,7 @@ fn main() {
|
|||||||
mixedchonk.0.len() as f32 / n as f32,
|
mixedchonk.0.len() as f32 / n as f32,
|
||||||
mixeddeflate.data.len() as f32 / n as f32,
|
mixeddeflate.data.len() as f32 / n as f32,
|
||||||
mixeddense.0.len() as f32 / n as f32,
|
mixeddense.0.len() as f32 / n as f32,
|
||||||
|
quadpng.data.len() as f32 / n as f32,
|
||||||
pngchonk.len() as f32 / n as f32,
|
pngchonk.len() as f32 / n as f32,
|
||||||
];
|
];
|
||||||
let best_idx = sizes
|
let best_idx = sizes
|
||||||
@ -380,6 +466,7 @@ fn main() {
|
|||||||
(mixedchonk_post - mixedchonk_pre).subsec_nanos(),
|
(mixedchonk_post - mixedchonk_pre).subsec_nanos(),
|
||||||
(mixeddeflate_post - mixedchonk_pre).subsec_nanos(),
|
(mixeddeflate_post - mixedchonk_pre).subsec_nanos(),
|
||||||
(mixeddense_post - mixeddense_pre).subsec_nanos(),
|
(mixeddense_post - mixeddense_pre).subsec_nanos(),
|
||||||
|
(quadpng_post - quadpng_pre).subsec_nanos(),
|
||||||
(pngchonk_post - pngchonk_pre).subsec_nanos(),
|
(pngchonk_post - pngchonk_pre).subsec_nanos(),
|
||||||
];
|
];
|
||||||
trace!(
|
trace!(
|
||||||
@ -408,12 +495,12 @@ fn main() {
|
|||||||
let mut f =
|
let mut f =
|
||||||
File::create(&format!("chonkjpegs/{}_{}.jpg", sitename, count)).unwrap();
|
File::create(&format!("chonkjpegs/{}_{}.jpg", sitename, count)).unwrap();
|
||||||
let jpeg_volgrid =
|
let jpeg_volgrid =
|
||||||
image_terrain_volgrid(JpegEncoding, GridLtrPacking, &volgrid);
|
image_terrain_volgrid(JpegEncoding, GridLtrPacking, &volgrid).unwrap();
|
||||||
f.write_all(&*jpeg_volgrid).unwrap();
|
f.write_all(&*jpeg_volgrid).unwrap();
|
||||||
|
|
||||||
let mixedgrid_pre = Instant::now();
|
let mixedgrid_pre = Instant::now();
|
||||||
let (mixed_volgrid, indices) =
|
let (mixed_volgrid, indices) =
|
||||||
image_terrain_volgrid(MixedEncoding, GridLtrPacking, &volgrid);
|
image_terrain_volgrid(MixedEncoding, GridLtrPacking, &volgrid).unwrap();
|
||||||
let mixedgrid_post = Instant::now();
|
let mixedgrid_post = Instant::now();
|
||||||
let seconds = (mixedgrid_post - mixedgrid_pre).as_secs_f64();
|
let seconds = (mixedgrid_post - mixedgrid_pre).as_secs_f64();
|
||||||
println!(
|
println!(
|
||||||
@ -455,7 +542,8 @@ fn main() {
|
|||||||
println!("Average mixedchonk: {}", totals[8] / count as f32);
|
println!("Average mixedchonk: {}", totals[8] / count as f32);
|
||||||
println!("Average mixeddeflate: {}", totals[9] / count as f32);
|
println!("Average mixeddeflate: {}", totals[9] / count as f32);
|
||||||
println!("Average mixeddense: {}", totals[10] / count as f32);
|
println!("Average mixeddense: {}", totals[10] / count as f32);
|
||||||
println!("Average pngchonk: {}", totals[11] / count as f32);
|
println!("Average quadpng: {}", totals[11] / count as f32);
|
||||||
|
println!("Average pngchonk: {}", totals[12] / count as f32);
|
||||||
println!("");
|
println!("");
|
||||||
println!(
|
println!(
|
||||||
"Average lz4_chonk nanos : {:02}",
|
"Average lz4_chonk nanos : {:02}",
|
||||||
@ -490,9 +578,13 @@ fn main() {
|
|||||||
total_timings[7] / count as f32
|
total_timings[7] / count as f32
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
"Average pngchonk nanos: {:02}",
|
"Average quadpng nanos: {:02}",
|
||||||
total_timings[8] / count as f32
|
total_timings[8] / count as f32
|
||||||
);
|
);
|
||||||
|
println!(
|
||||||
|
"Average pngchonk nanos: {:02}",
|
||||||
|
total_timings[9] / count as f32
|
||||||
|
);
|
||||||
println!("-----");
|
println!("-----");
|
||||||
}
|
}
|
||||||
if i % 256 == 0 {
|
if i % 256 == 0 {
|
||||||
|
Loading…
Reference in New Issue
Block a user