From c4b0ecbadfc0a77c78f3fbd062630243d71abe44 Mon Sep 17 00:00:00 2001 From: zhouzihao <1042181618@qq.com> Date: Mon, 4 Sep 2023 15:42:52 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix-=E4=BF=AE=E6=94=B9=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=95=B0=E6=8D=AE=E7=9A=84=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/mesh_display.rs | 28 +++++++++++++++------------- src/voxel_world/chunk_map.rs | 3 ++- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/client/mesh_display.rs b/src/client/mesh_display.rs index ab9bf40..3c53a4f 100644 --- a/src/client/mesh_display.rs +++ b/src/client/mesh_display.rs @@ -260,15 +260,16 @@ pub fn update_mesh( let volexs: Vec = chunk_map.get_with_neighbor_full_y(chunk_key_y0); match gen_mesh(volexs.to_owned(), material_config.clone()) { Some(render_mesh) => { - let mesh_handle = mesh_manager.mesh_storge.get(&chunk_key_y0).unwrap(); - if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { - // 更新AABB - if let Some(entity) = mesh_manager.entities.get(&chunk_key_y0) { - if let Some(aabb) = render_mesh.compute_aabb() { - commands.entity(*entity).insert(aabb); + if let Some(mesh_handle) = mesh_manager.mesh_storge.get(&chunk_key_y0) { + if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { + // 更新AABB + if let Some(entity) = mesh_manager.entities.get(&chunk_key_y0) { + if let Some(aabb) = render_mesh.compute_aabb() { + commands.entity(*entity).insert(aabb); + } } + *mesh = render_mesh; } - *mesh = render_mesh; } // 没有生成mesh就不管反正后面要生成 } @@ -281,14 +282,15 @@ pub fn update_mesh( }; match gen_mesh_water(pick_water(volexs), material_config) { Some(water_mesh) => { - let mesh_handle = mesh_manager.water_mesh_storge.get(&chunk_key_y0).unwrap(); - if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { - if let Some(entity) = mesh_manager.water_entities.get(&chunk_key_y0) { - if let Some(aabb) = water_mesh.compute_aabb() { - commands.entity(*entity).insert(aabb); + if let Some(mesh_handle) = mesh_manager.water_mesh_storge.get(&chunk_key_y0) { + if let Some(mesh) = mesh_assets.get_mut(mesh_handle) { + if let Some(entity) = mesh_manager.water_entities.get(&chunk_key_y0) { + if let Some(aabb) = water_mesh.compute_aabb() { + commands.entity(*entity).insert(aabb); + } } + *mesh = water_mesh; } - *mesh = water_mesh; } } None => { diff --git a/src/voxel_world/chunk_map.rs b/src/voxel_world/chunk_map.rs index db8be55..7a31609 100644 --- a/src/voxel_world/chunk_map.rs +++ b/src/voxel_world/chunk_map.rs @@ -25,8 +25,9 @@ impl ChunkMap { let nx = &IVec3::new(-1, 0, 0); let pz = &IVec3::new(0, 0, 1); let nz = &IVec3::new(0, 0, -1); + let n_self = &IVec3::new(0, 0, 0); - let offsets = [px, nx, pz, nz]; + let offsets = [px, nx, pz, nz, n_self]; let last_inex = -128 / CHUNK_SIZE + 1; for y_offset in last_inex..=128 / CHUNK_SIZE { From f10b526b5fc5c961f7389858d16de22ce37e361b Mon Sep 17 00:00:00 2001 From: zhouzihao <1042181618@qq.com> Date: Mon, 4 Sep 2023 18:31:38 +0800 Subject: [PATCH 2/3] =?UTF-8?q?dev-=E4=BD=BF=E7=94=A8=E8=B0=83=E8=89=B2?= =?UTF-8?q?=E6=9D=BF=E5=8E=8B=E7=BC=A9=E9=9C=8D=E5=A4=AB=E6=9B=BC=E7=BC=96?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 15 +++++ Cargo.toml | 4 ++ src/client/mesh_display.rs | 9 +-- src/server/async_chunk.rs | 10 ++-- src/server/message_def/chunk_result.rs | 6 +- src/tools/mod.rs | 13 +++++ src/voxel_world/compress.rs | 78 ++++++++++++++++++++++++++ src/voxel_world/mod.rs | 1 + src/voxel_world/voxel.rs | 14 ++++- 9 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 src/voxel_world/compress.rs diff --git a/Cargo.lock b/Cargo.lock index 31679ce..609578f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1311,6 +1311,9 @@ name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +dependencies = [ + "serde", +] [[package]] name = "bitflags" @@ -2473,6 +2476,16 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "huffman-compress" +version = "0.6.1" +source = "git+https://github.com/zzhgithub/rust-huffman-compress?branch=features-serde#6b09ee0290deab72a51b5927b7553fba8c0f37c7" +dependencies = [ + "bit-vec", + "num-traits", + "serde", +] + [[package]] name = "idna" version = "0.4.0" @@ -2703,6 +2716,7 @@ dependencies = [ "bevy_renet", "bevy_sprite3d", "bincode", + "bit-vec", "block-mesh", "clap 4.1.10", "codespan-reporting", @@ -2710,6 +2724,7 @@ dependencies = [ "egui_extras", "futures-lite", "fxhash", + "huffman-compress", "inotify", "lock_api", "ndshape", diff --git a/Cargo.toml b/Cargo.toml index 601fc53..56fa518 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ egui-notify = "0.8.0" rand = "0.8.5" seldom_state = "0.7.0" bevy_sprite3d = "2.5.0" +bit-vec = { version = "0.6", features = ["serde"] } # 解决依赖冲突 num-rational = "^0.4.1" fxhash = "^0.2.1" @@ -49,6 +50,9 @@ simba = "^0.8" bevy_mod_billboard = "0.4.0" notify = "6.1.1" egui_extras = "0.22.0" +huffman-compress = { git = "https://github.com/zzhgithub/rust-huffman-compress", branch = "features-serde", features = [ + "serde", +] } [profile.dev.package.bevy_rapier3d] diff --git a/src/client/mesh_display.rs b/src/client/mesh_display.rs index 3c53a4f..d40e615 100644 --- a/src/client/mesh_display.rs +++ b/src/client/mesh_display.rs @@ -20,13 +20,14 @@ use ndshape::{ConstShape, ConstShape3u32}; use crate::{ common::ClipSpheres, server::message_def::{chunk_result::ChunkResult, ServerChannel}, - tools::get_empty_chunk, + tools::get_all_v_chunk, voxel_world::{ chunk::{ find_chunk_keys_array_by_shpere_y_0, generate_offset_resoure, generate_offset_resoure_min_1, ChunkKey, NeighbourOffest, }, chunk_map::ChunkMap, + compress::uncompress, voxel::Voxel, }, CHUNK_SIZE, CHUNK_SIZE_U32, MATERIAL_RON, VIEW_RADIUS, @@ -165,11 +166,11 @@ pub fn async_chunk_result( let chunk_result: ChunkResult = bincode::deserialize(&message).unwrap(); match chunk_result { ChunkResult::ChunkData { key, data } => { - let task = pool.spawn(async move { (key, data) }); + let task = pool.spawn(async move { (key, uncompress(&data.0, data.1)) }); chunk_sync_task.tasks.push(task); } - ChunkResult::ChunkEmpty(key) => { - let task = pool.spawn(async move { (key, get_empty_chunk()) }); + ChunkResult::ChunkSame((key, voxel)) => { + let task = pool.spawn(async move { (key, get_all_v_chunk(voxel)) }); chunk_sync_task.tasks.push(task); } ChunkResult::ChunkUpdateOne { diff --git a/src/server/async_chunk.rs b/src/server/async_chunk.rs index 7e1aa65..7c4575c 100644 --- a/src/server/async_chunk.rs +++ b/src/server/async_chunk.rs @@ -9,10 +9,10 @@ use crate::{ client::message_def::{chunk_query::ChunkQuery, ClientChannel}, server::{message_def::ServerChannel, object_filing::put_object::put_object}, staff::StaffInfoStroge, - tools::all_empty, voxel_world::{ chunk::ChunkKey, chunk_map::ChunkMap, + compress::compress, map_database::{DbSaveTasks, MapDataBase}, player_state::PlayerOntimeState, voxel::{BasicStone, Voxel, VoxelMaterial}, @@ -65,12 +65,14 @@ pub fn deal_chunk_query_system( } else { voxels = db.find_by_chunk_key(new_key, db_save_task.as_mut()); } - let message = if all_empty(&voxels) { - bincode::serialize(&ChunkResult::ChunkEmpty(new_key)).unwrap() + let (buffer, tree) = compress(voxels.clone()); + let message = if buffer.len() == 0 { + bincode::serialize(&ChunkResult::ChunkSame((new_key, voxels[0]))) + .unwrap() } else { bincode::serialize(&ChunkResult::ChunkData { key: new_key, - data: voxels.clone(), + data: (buffer, tree), }) .unwrap() }; diff --git a/src/server/message_def/chunk_result.rs b/src/server/message_def/chunk_result.rs index e95974b..85d7844 100644 --- a/src/server/message_def/chunk_result.rs +++ b/src/server/message_def/chunk_result.rs @@ -1,4 +1,6 @@ use bevy::prelude::Component; +use bit_vec::BitVec; +use huffman_compress::Tree; use serde::{Deserialize, Serialize}; use crate::voxel_world::{chunk::ChunkKey, voxel::Voxel}; @@ -7,9 +9,9 @@ use crate::voxel_world::{chunk::ChunkKey, voxel::Voxel}; pub enum ChunkResult { ChunkData { key: ChunkKey, - data: Vec, + data: (BitVec, Tree), }, - ChunkEmpty(ChunkKey), + ChunkSame((ChunkKey,Voxel)), ChunkUpdateOne { chunk_key: ChunkKey, pos: [u32; 3], diff --git a/src/tools/mod.rs b/src/tools/mod.rs index a1a3866..f00ccdf 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -35,6 +35,19 @@ pub fn get_empty_chunk() -> Vec { voxels } +/** + * 获取完全空的区块数据 + * Get all empty chunk data + */ +pub fn get_all_v_chunk(voxel: Voxel) -> Vec { + let mut voxels = Vec::new(); + type SampleShape = ConstShape3u32; + for _ in 0..SampleShape::SIZE { + voxels.push(voxel.clone()); + } + voxels +} + /** * 获取cube中心点 属于的 chunkKey和x y z坐标 */ diff --git a/src/voxel_world/compress.rs b/src/voxel_world/compress.rs new file mode 100644 index 0000000..962daba --- /dev/null +++ b/src/voxel_world/compress.rs @@ -0,0 +1,78 @@ +use bevy::utils::HashMap; +use bit_vec::BitVec; +use huffman_compress::{CodeBuilder, Tree}; + +use super::voxel::Voxel; + +pub fn compress(data: Vec) -> (BitVec, Tree) { + let mut weights: HashMap = HashMap::new(); + for &voxel in &data { + let count = weights.entry(voxel).or_insert(0); + *count += 1; + } + + let (book, tree) = CodeBuilder::from_iter(weights).finish(); + let mut buffer = BitVec::new(); + + for voxel in &data { + match book.encode(&mut buffer, voxel) { + Ok(_) => {} + Err(err) => { + println!("{}", err); + } + } + } + (buffer, tree) +} + +pub fn uncompress(buffer: &BitVec, tree: Tree) -> Vec { + tree.decoder(buffer, buffer.len()).collect() +} + +#[test] +fn test() { + let data = vec![ + Voxel::EMPTY, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + ]; + let (buffer, tree) = compress(data.clone()); + + let new_data = uncompress(&buffer, tree); + print!("{}", data.len()); + print!("{}", new_data.len()); + assert_eq!(data, new_data); +} + +#[test] +fn test_serialization() { + let data = vec![ + Voxel::EMPTY, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + Voxel::FILLED, + ]; + let (buffer, tree) = compress(data.clone()); + let tree_s = bincode::serialize(&tree).unwrap(); + let tree_ds: Tree = bincode::deserialize(&tree_s).unwrap(); + + let a = uncompress(&buffer.clone(), tree); + let b = uncompress(&buffer.clone(), tree_ds); + + assert_eq!(a, b); +} diff --git a/src/voxel_world/mod.rs b/src/voxel_world/mod.rs index 199c807..a06f2ef 100644 --- a/src/voxel_world/mod.rs +++ b/src/voxel_world/mod.rs @@ -4,3 +4,4 @@ pub mod map_database; pub mod map_generator; pub mod player_state; pub mod voxel; +pub mod compress; \ No newline at end of file diff --git a/src/voxel_world/voxel.rs b/src/voxel_world/voxel.rs index 98e8c04..4d0226e 100644 --- a/src/voxel_world/voxel.rs +++ b/src/voxel_world/voxel.rs @@ -5,11 +5,23 @@ use serde::{Deserialize, Serialize}; /** * 体素类型 */ -#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, Reflect)] +#[derive(Debug, Clone, Copy, Default, Deserialize, Serialize, Reflect, PartialEq, Eq, Hash)] pub struct Voxel { pub id: u8, } +impl PartialOrd for Voxel { + fn partial_cmp(&self, other: &Self) -> Option { + self.id.partial_cmp(&other.id) + } +} + +impl Ord for Voxel { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.id.cmp(&other.id) + } +} + impl Voxel { pub const EMPTY: Self = Self { id: 0 }; pub const FILLED: Self = Self { id: 1 }; From 908c3a2d2347e946888c6fee07ad8987cb07183e Mon Sep 17 00:00:00 2001 From: zhouzihao <1042181618@qq.com> Date: Mon, 4 Sep 2023 18:51:13 +0800 Subject: [PATCH 3/3] =?UTF-8?q?opz-=E8=B0=83=E6=95=B4=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E9=97=B4=E9=9A=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/mesh_display.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/client/mesh_display.rs b/src/client/mesh_display.rs index d40e615..536d404 100644 --- a/src/client/mesh_display.rs +++ b/src/client/mesh_display.rs @@ -471,10 +471,11 @@ fn cycle_check_mesh( let now: Instant = Instant::now(); let duration: Duration = now - *instant; // 每2s检查一下五秒内没有加载好的数据 - if !state && duration.as_millis() > 5 * 1000 && need_keys.contains(key) { + if !state && duration.as_millis() > 10 * 1000 && need_keys.contains(key) { println!("超时重新请求chunkkey{:?}", key); + // TODO: 这可以检查具体少什么数据? let message = bincode::serialize(&ChunkQuery::GetFullY(*key)).unwrap(); - // todo 对边缘数据不处理! + // 对边缘数据不处理! client.send_message(ClientChannel::ChunkQuery, message); } if duration.as_millis() > 5 * 1000