CHUNK GENERATION IS REAL
This commit is contained in:
124
Cargo.lock
generated
124
Cargo.lock
generated
@@ -24,7 +24,7 @@ version = "0.8.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"getrandom",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
@@ -319,6 +319,17 @@ version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a"
|
||||
|
||||
[[package]]
|
||||
name = "block-mesh"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c0345520b5aa77d7d154bd578a18e7a974350931b5f41f641a7844e951b978f"
|
||||
dependencies = [
|
||||
"ilattice",
|
||||
"ndcopy",
|
||||
"ndshape",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block2"
|
||||
version = "0.5.1"
|
||||
@@ -403,6 +414,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -713,7 +730,7 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"libc",
|
||||
"wasi",
|
||||
@@ -731,6 +748,12 @@ dependencies = [
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glam"
|
||||
version = "0.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca"
|
||||
|
||||
[[package]]
|
||||
name = "glow"
|
||||
version = "0.13.1"
|
||||
@@ -842,6 +865,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df"
|
||||
|
||||
[[package]]
|
||||
name = "ilattice"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f3db9046391c0e0658a18e23877706d16e48cc8b4711702b9f703776410c2b84"
|
||||
dependencies = [
|
||||
"glam",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.2.6"
|
||||
@@ -859,7 +891,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
|
||||
dependencies = [
|
||||
"cesu8",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"combine",
|
||||
"jni-sys",
|
||||
"log",
|
||||
@@ -921,7 +953,7 @@ version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
@@ -931,7 +963,7 @@ version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
@@ -1020,7 +1052,7 @@ dependencies = [
|
||||
"foreign-types",
|
||||
"log",
|
||||
"objc",
|
||||
"paste",
|
||||
"paste 1.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1086,6 +1118,15 @@ dependencies = [
|
||||
"rawpointer",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndcopy"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24f4e41b81e3f7a4b742755be1bb368a6faafc43af555fe135c36b0efe095c59"
|
||||
dependencies = [
|
||||
"ndshape",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk"
|
||||
version = "0.9.0"
|
||||
@@ -1125,6 +1166,15 @@ dependencies = [
|
||||
"jni-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndshape"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "975bce586ad637e27f6dc26ee907c07050686a588695bfd64b7873a9d48a700c"
|
||||
dependencies = [
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nonmax"
|
||||
version = "0.5.5"
|
||||
@@ -1447,19 +1497,38 @@ version = "0.9.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"redox_syscall 0.5.1",
|
||||
"smallvec",
|
||||
"windows-targets 0.52.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880"
|
||||
dependencies = [
|
||||
"paste-impl",
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "paste-impl"
|
||||
version = "0.1.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -1508,12 +1577,14 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bevy_derive",
|
||||
"bevy_ecs",
|
||||
"block-mesh",
|
||||
"bytemuck",
|
||||
"nalgebra",
|
||||
"ndarray",
|
||||
"pollster",
|
||||
"rand",
|
||||
"simba",
|
||||
"simdnoise",
|
||||
"smaa",
|
||||
"wgpu",
|
||||
"winit",
|
||||
@@ -1531,7 +1602,7 @@ version = "3.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"concurrent-queue",
|
||||
"hermit-abi",
|
||||
"pin-project-lite",
|
||||
@@ -1576,6 +1647,12 @@ dependencies = [
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.20+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.82"
|
||||
@@ -1772,10 +1849,29 @@ dependencies = [
|
||||
"approx",
|
||||
"num-complex",
|
||||
"num-traits",
|
||||
"paste",
|
||||
"paste 1.0.15",
|
||||
"wide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdeez"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6ec898e1be717eee4b54a84ff2fc94ecb5a1b992d4ad148ce30575b45745662"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"paste 0.1.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simdnoise"
|
||||
version = "3.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d9f69a3fa031fc2906ffe27aecc55bdd2c9cb95327c4695ea814a083145fa462"
|
||||
dependencies = [
|
||||
"simdeez",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
@@ -1924,7 +2020,7 @@ version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
@@ -1937,7 +2033,7 @@ dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"bytemuck",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"log",
|
||||
"tiny-skia-path",
|
||||
]
|
||||
@@ -2069,7 +2165,7 @@ version = "0.2.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
@@ -2094,7 +2190,7 @@ version = "0.4.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
@@ -2275,7 +2371,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90e37c7b9921b75dfd26dd973fdcbce36f13dfa6e2dc82aece584e0ed48c355c"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"cfg_aliases 0.1.1",
|
||||
"document-features",
|
||||
"js-sys",
|
||||
|
||||
@@ -15,8 +15,10 @@ ndarray = "0.15.6"
|
||||
pollster = "0.3"
|
||||
rand = "0.8.5"
|
||||
simba = "0.8.1"
|
||||
simdnoise = "3.1.6"
|
||||
smaa = "0.14.0"
|
||||
wgpu = "0.20.1"
|
||||
bevy_ecs = "0.13.2"
|
||||
bevy_derive = "0.13.2"
|
||||
winit = {version="0.30.1", features=["serde"]}
|
||||
block-mesh = "0.2.0"
|
||||
|
||||
@@ -5,8 +5,8 @@ use ndarray::Array3;
|
||||
use winit::{dpi::PhysicalPosition, keyboard::KeyCode as Key, window::CursorGrabMode};
|
||||
|
||||
use crate::{
|
||||
sync::ServerMessage,
|
||||
world::component::{VoxelGrid, VoxelGridBundle},
|
||||
common::ServerMessage,
|
||||
common::component::{VoxelGrid, VoxelGridBundle},
|
||||
};
|
||||
|
||||
use super::{render::voxel::VoxelColor, Client};
|
||||
@@ -84,7 +84,7 @@ impl Client<'_> {
|
||||
}
|
||||
|
||||
// camera position
|
||||
let move_dist = 10.0 * dt;
|
||||
let move_dist = 2.0 * 16.0 * dt;
|
||||
if input.pressed(Key::KeyW) {
|
||||
state.camera.pos += *state.camera.forward() * move_dist;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ use system::render::add_grid;
|
||||
|
||||
use crate::{
|
||||
server::Server,
|
||||
sync::{ClientMessage, ServerHandle, ServerMessage},
|
||||
common::{ClientMessage, ServerHandle, ServerMessage},
|
||||
};
|
||||
|
||||
use self::{input::Input, render::Renderer, ClientState};
|
||||
@@ -54,6 +54,7 @@ pub struct Client<'a> {
|
||||
pub struct ClientSystems {
|
||||
render_add_grid: SystemId,
|
||||
render_update_transform: SystemId,
|
||||
render_add_chunk: SystemId,
|
||||
}
|
||||
|
||||
impl Client<'_> {
|
||||
@@ -70,7 +71,7 @@ impl Client<'_> {
|
||||
|
||||
let state = ClientState::new();
|
||||
let server = ServerHandle::spawn(Server::start);
|
||||
server.send(ServerMessage::LoadWorld);
|
||||
server.send(ServerMessage::Join);
|
||||
|
||||
Self {
|
||||
window,
|
||||
@@ -85,6 +86,7 @@ impl Client<'_> {
|
||||
systems: ClientSystems {
|
||||
render_add_grid: world.register_system(add_grid),
|
||||
render_update_transform: world.register_system(system::render::update_transform),
|
||||
render_add_chunk: world.register_system(system::render::add_chunk),
|
||||
},
|
||||
world,
|
||||
server,
|
||||
@@ -110,6 +112,9 @@ impl Client<'_> {
|
||||
self.world
|
||||
.run_system(self.systems.render_update_transform)
|
||||
.expect("WHAT");
|
||||
self.world
|
||||
.run_system(self.systems.render_add_chunk)
|
||||
.expect("WHAT v3");
|
||||
self.world.clear_trackers();
|
||||
|
||||
if self.state.camera.pos.y < -10.0 {
|
||||
@@ -129,8 +134,6 @@ impl Client<'_> {
|
||||
}
|
||||
|
||||
if self.exit {
|
||||
self.server.send(ServerMessage::Stop);
|
||||
self.server.join();
|
||||
event_loop.exit();
|
||||
}
|
||||
}
|
||||
@@ -139,8 +142,12 @@ impl Client<'_> {
|
||||
for msg in self.server.recv() {
|
||||
match msg {
|
||||
ClientMessage::SpawnVoxelGrid(entity, grid) => {
|
||||
let cid = self.world.spawn(grid).id();
|
||||
self.server_id_map.insert(entity, cid);
|
||||
let id = self.world.spawn(grid).id();
|
||||
self.server_id_map.insert(entity, id);
|
||||
}
|
||||
ClientMessage::LoadChunk(entity, chunk) => {
|
||||
let id = self.world.spawn(chunk).id();
|
||||
self.server_id_map.insert(entity, id);
|
||||
}
|
||||
ClientMessage::PosUpdate(e, pos) => {
|
||||
if let Some(id) = self.server_id_map.get(&e) {
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use crate::client::camera::Camera;
|
||||
use crate::{
|
||||
client::camera::Camera,
|
||||
common::component::{ChunkMesh, ChunkPos},
|
||||
};
|
||||
|
||||
use super::{voxel::VoxelColor, Renderer};
|
||||
use bevy_ecs::entity::Entity;
|
||||
@@ -10,6 +13,7 @@ use winit::window::Window;
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum RenderCommand {
|
||||
CreateVoxelGrid(CreateVoxelGrid),
|
||||
AddChunk(AddChunk),
|
||||
UpdateGridTransform(UpdateGridTransform),
|
||||
ViewUpdate(Camera),
|
||||
}
|
||||
@@ -23,6 +27,13 @@ pub struct CreateVoxelGrid {
|
||||
pub grid: Array3<VoxelColor>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct AddChunk {
|
||||
pub id: Entity,
|
||||
pub pos: ChunkPos,
|
||||
pub mesh: ChunkMesh,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct UpdateGridTransform {
|
||||
pub id: Entity,
|
||||
@@ -49,26 +60,28 @@ impl<'a> Renderer<'a> {
|
||||
let mut new_camera = false;
|
||||
for cmd in commands {
|
||||
match cmd {
|
||||
RenderCommand::CreateVoxelGrid(desc) => {
|
||||
self.voxel_pipeline.add_group(
|
||||
&self.device,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
desc,
|
||||
);
|
||||
}
|
||||
RenderCommand::CreateVoxelGrid(desc) => self.voxel_pipeline.add_group(
|
||||
&self.device,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
desc,
|
||||
),
|
||||
RenderCommand::ViewUpdate(camera) => {
|
||||
new_camera = true;
|
||||
self.camera = camera;
|
||||
}
|
||||
RenderCommand::UpdateGridTransform(update) => {
|
||||
self.voxel_pipeline.update_transform(
|
||||
&self.device,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
update,
|
||||
);
|
||||
}
|
||||
RenderCommand::UpdateGridTransform(update) => self.voxel_pipeline.update_transform(
|
||||
&self.device,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
update,
|
||||
),
|
||||
RenderCommand::AddChunk(desc) => self.voxel_pipeline.add_chunk(
|
||||
&self.device,
|
||||
&mut self.encoder,
|
||||
&mut self.staging_belt,
|
||||
desc,
|
||||
),
|
||||
}
|
||||
}
|
||||
if new_camera {
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
mod command;
|
||||
mod util;
|
||||
pub mod voxel;
|
||||
pub mod voxel_poly;
|
||||
|
||||
pub use command::*;
|
||||
use util::Texture;
|
||||
|
||||
use super::camera::Camera;
|
||||
use crate::client::rsc::CLEAR_COLOR;
|
||||
use nalgebra::Vector2;
|
||||
use smaa::{SmaaMode, SmaaTarget};
|
||||
use voxel_poly::VoxelPipeline;
|
||||
use util::Texture;
|
||||
use voxel::VoxelPipeline;
|
||||
use winit::dpi::PhysicalSize;
|
||||
|
||||
pub struct Renderer<'a> {
|
||||
|
||||
@@ -1,239 +1,3 @@
|
||||
mod color;
|
||||
mod grid;
|
||||
mod group;
|
||||
mod light;
|
||||
mod view;
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use bevy_ecs::entity::Entity;
|
||||
pub use color::*;
|
||||
|
||||
use light::GlobalLight;
|
||||
use nalgebra::{Projective3, Transform3, Translation3, Vector2, Vector3};
|
||||
|
||||
use super::UpdateGridTransform;
|
||||
use crate::client::{
|
||||
camera::Camera,
|
||||
render::{
|
||||
util::{ArrBufUpdate, Storage, Uniform},
|
||||
CreateVoxelGrid,
|
||||
},
|
||||
};
|
||||
|
||||
use {group::VoxelGroup, view::View};
|
||||
|
||||
pub struct VoxelPipeline {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
view: Uniform<View>,
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
bind_group: wgpu::BindGroup,
|
||||
voxel_groups: Storage<VoxelGroup>,
|
||||
voxels: Storage<VoxelColor>,
|
||||
global_lights: Storage<GlobalLight>,
|
||||
id_map: HashMap<Entity, (usize, VoxelGroup)>,
|
||||
}
|
||||
|
||||
impl VoxelPipeline {
|
||||
pub fn new(device: &wgpu::Device, format: &wgpu::TextureFormat) -> Self {
|
||||
// shaders
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Tile Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
|
||||
});
|
||||
|
||||
let view = Uniform::init(device, "view", 0);
|
||||
let voxels = Storage::init(device, "voxels", 1);
|
||||
let voxel_groups = Storage::init(device, "voxel groups", 2);
|
||||
let global_lights = Storage::init_with(
|
||||
device,
|
||||
"global lights",
|
||||
3,
|
||||
&[GlobalLight {
|
||||
direction: Vector3::new(-0.5, -4.0, 2.0).normalize(),
|
||||
}],
|
||||
);
|
||||
|
||||
// bind groups
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
view.bind_group_layout_entry(),
|
||||
voxels.bind_group_layout_entry(),
|
||||
voxel_groups.bind_group_layout_entry(),
|
||||
global_lights.bind_group_layout_entry(),
|
||||
],
|
||||
label: Some("tile_bind_group_layout"),
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_group_layout,
|
||||
entries: &[
|
||||
view.bind_group_entry(),
|
||||
voxels.bind_group_entry(),
|
||||
voxel_groups.bind_group_entry(),
|
||||
global_lights.bind_group_entry(),
|
||||
],
|
||||
label: Some("tile_bind_group"),
|
||||
});
|
||||
|
||||
// pipeline
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Tile Pipeline Layout"),
|
||||
bind_group_layouts: &[&bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Voxel Pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: *format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: true,
|
||||
},
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
Self {
|
||||
pipeline: render_pipeline,
|
||||
view,
|
||||
bind_group,
|
||||
bind_group_layout,
|
||||
voxels,
|
||||
voxel_groups,
|
||||
global_lights,
|
||||
id_map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_group(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
CreateVoxelGrid {
|
||||
id,
|
||||
pos,
|
||||
orientation,
|
||||
dimensions,
|
||||
grid,
|
||||
}: CreateVoxelGrid,
|
||||
) {
|
||||
let offset = self.voxels.len();
|
||||
|
||||
let updates = [ArrBufUpdate {
|
||||
offset,
|
||||
data: &grid.as_slice().unwrap(),
|
||||
}];
|
||||
let size = offset + grid.len();
|
||||
self.voxels.update(device, encoder, belt, size, &updates);
|
||||
|
||||
let proj = Projective3::identity()
|
||||
* Translation3::from(pos)
|
||||
* orientation
|
||||
* Translation3::from(-dimensions.cast() / 2.0);
|
||||
let group = VoxelGroup {
|
||||
transform: proj,
|
||||
transform_inv: proj.inverse(),
|
||||
dimensions: dimensions.cast(),
|
||||
offset: offset as u32,
|
||||
};
|
||||
let updates = [ArrBufUpdate {
|
||||
offset: self.voxel_groups.len(),
|
||||
data: &[group],
|
||||
}];
|
||||
let i = self.voxel_groups.len();
|
||||
let size = i + 1;
|
||||
self.voxel_groups
|
||||
.update(device, encoder, belt, size, &updates);
|
||||
|
||||
self.id_map.insert(id, (i, group));
|
||||
|
||||
self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.bind_group_layout,
|
||||
entries: &[
|
||||
self.view.bind_group_entry(),
|
||||
self.voxels.bind_group_entry(),
|
||||
self.voxel_groups.bind_group_entry(),
|
||||
self.global_lights.bind_group_entry(),
|
||||
],
|
||||
label: Some("tile_bind_group"),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_transform(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
update: UpdateGridTransform,
|
||||
) {
|
||||
if let Some((i, group)) = self.id_map.get_mut(&update.id) {
|
||||
let proj = Projective3::identity()
|
||||
* Translation3::from(update.pos)
|
||||
* update.orientation
|
||||
* Translation3::from(-group.dimensions.cast() / 2.0);
|
||||
group.transform = proj;
|
||||
group.transform_inv = proj.inverse();
|
||||
let updates = [ArrBufUpdate {
|
||||
offset: *i,
|
||||
data: &[*group],
|
||||
}];
|
||||
let size = self.voxel_groups.len();
|
||||
self.voxel_groups
|
||||
.update(device, encoder, belt, size, &updates);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_view(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
size: Vector2<u32>,
|
||||
camera: &Camera,
|
||||
) {
|
||||
let transform =
|
||||
Transform3::identity() * Translation3::from(camera.pos) * camera.orientation;
|
||||
let data = View {
|
||||
width: size.x,
|
||||
height: size.y,
|
||||
zoom: camera.scale,
|
||||
transform,
|
||||
};
|
||||
self.view.update(device, encoder, belt, data)
|
||||
}
|
||||
|
||||
pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||
render_pass.draw(0..4, 0..1);
|
||||
}
|
||||
}
|
||||
mod poly;
|
||||
// mod ray;
|
||||
pub use poly::*;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use block_mesh::Voxel;
|
||||
use rand::distributions::{Distribution, Standard};
|
||||
|
||||
#[repr(C)]
|
||||
@@ -46,3 +47,13 @@ impl Distribution<VoxelColor> for Standard {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl block_mesh::Voxel for VoxelColor {
|
||||
fn get_visibility(&self) -> block_mesh::VoxelVisibility {
|
||||
match self.a {
|
||||
0 => block_mesh::VoxelVisibility::Empty,
|
||||
255 => block_mesh::VoxelVisibility::Opaque,
|
||||
_ => block_mesh::VoxelVisibility::Translucent,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
use bytemuck::Zeroable;
|
||||
|
||||
use crate::client::render::voxel::VoxelColor;
|
||||
use super::VoxelColor;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, Zeroable)]
|
||||
@@ -1,21 +1,26 @@
|
||||
mod color;
|
||||
mod face;
|
||||
mod group;
|
||||
mod instance;
|
||||
mod square;
|
||||
mod light;
|
||||
mod view;
|
||||
|
||||
use core::panic;
|
||||
use block_mesh::{ndshape::RuntimeShape, UnitQuadBuffer, RIGHT_HANDED_Y_UP_CONFIG};
|
||||
pub use color::*;
|
||||
pub use face::*;
|
||||
|
||||
use group::FaceGroup;
|
||||
use instance::VoxelFace;
|
||||
use light::GlobalLight;
|
||||
use nalgebra::{Perspective3, Transform3, Translation3, Vector2, Vector3};
|
||||
use view::View;
|
||||
use wgpu::{SurfaceConfiguration, VertexAttribute, VertexFormat};
|
||||
|
||||
use crate::client::camera::Camera;
|
||||
use crate::{
|
||||
client::{camera::Camera, render::AddChunk},
|
||||
common::component::{chunk, ChunkData},
|
||||
};
|
||||
|
||||
use super::{
|
||||
util::{Instances, Texture, Uniform},
|
||||
use super::super::{
|
||||
util::{Instances, Storage, Texture, Uniform},
|
||||
CreateVoxelGrid, UpdateGridTransform,
|
||||
};
|
||||
|
||||
@@ -25,6 +30,7 @@ pub struct VoxelPipeline {
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
bind_groups: Vec<wgpu::BindGroup>,
|
||||
vertices: Vec<Instances<VoxelFace>>,
|
||||
global_lights: Storage<GlobalLight>,
|
||||
}
|
||||
|
||||
const INSTANCE_ATTRS: [wgpu::VertexAttribute; 2] = [
|
||||
@@ -52,12 +58,21 @@ impl VoxelPipeline {
|
||||
let example_faces =
|
||||
Instances::<VoxelFace>::init(device, "voxel groups", 0, &INSTANCE_ATTRS);
|
||||
let example_group = Uniform::<FaceGroup>::init(device, "voxel group", 1);
|
||||
let global_lights = Storage::init_with(
|
||||
device,
|
||||
"global lights",
|
||||
3,
|
||||
&[GlobalLight {
|
||||
direction: Vector3::new(-0.5, -4.0, 2.0).normalize(),
|
||||
}],
|
||||
);
|
||||
|
||||
// bind groups
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
view.bind_group_layout_entry(),
|
||||
example_group.bind_group_layout_entry(),
|
||||
global_lights.bind_group_layout_entry(),
|
||||
],
|
||||
label: Some("tile_bind_group_layout"),
|
||||
});
|
||||
@@ -84,7 +99,7 @@ impl VoxelPipeline {
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: config.format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
blend: Some(wgpu::BlendState::ALPHA_BLENDING),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
@@ -119,6 +134,7 @@ impl VoxelPipeline {
|
||||
bind_group_layout,
|
||||
bind_groups: Vec::new(),
|
||||
vertices: Vec::new(),
|
||||
global_lights,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +154,7 @@ impl VoxelPipeline {
|
||||
size.x as f32 / size.y as f32,
|
||||
std::f32::consts::PI / 2.0,
|
||||
0.1,
|
||||
1000.0,
|
||||
10000.0,
|
||||
);
|
||||
transform = projection.as_matrix() * transform;
|
||||
let data = View {
|
||||
@@ -177,46 +193,31 @@ impl VoxelPipeline {
|
||||
* Translation3::from(pos)
|
||||
* orientation
|
||||
* Translation3::from(-dimensions.cast() / 2.0);
|
||||
for face in 0..6 {
|
||||
let group = FaceGroup {
|
||||
dimensions: dimensions.cast(),
|
||||
transform: proj,
|
||||
face,
|
||||
};
|
||||
let uniform = Uniform::init_with(device, "voxel group", 1, &[group]);
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.bind_group_layout,
|
||||
entries: &[self.view.bind_group_entry(), uniform.bind_group_entry()],
|
||||
label: Some("voxel bind group"),
|
||||
});
|
||||
self.bind_groups.push(bind_group);
|
||||
|
||||
let mut data = Vec::new();
|
||||
let n_offset = match face % 3 {
|
||||
0 => 1,
|
||||
1 => dimensions.z * dimensions.y,
|
||||
2 => dimensions.z,
|
||||
_ => 0,
|
||||
} as i32
|
||||
* ((face as i32 / 3) * 2 - 1);
|
||||
let face_dir = (face as i32 / 3) * 2 - 1;
|
||||
for (i, ((x, y, z), color)) in grid.indexed_iter().enumerate() {
|
||||
let neighbor = match face {
|
||||
0 => if z > 0 {Some((x, y, z - 1))} else {None},
|
||||
2 => if y > 0 {Some((x, y - 1, z))} else {None},
|
||||
1 => if x > 0 {Some((x - 1, y, z))} else {None},
|
||||
3 => if z < dimensions.z - 1 {Some((x, y, z + 1))} else {None},
|
||||
5 => if y < dimensions.y - 1 {Some((x, y + 1, z))} else {None},
|
||||
4 => if x < dimensions.x - 1 {Some((x + 1, y, z))} else {None},
|
||||
_ => panic!("what"),
|
||||
}.map(|p| grid.get(p).unwrap());
|
||||
if color.a > 0 && !neighbor.is_some_and(|c| c.a == color.a) {
|
||||
data.push(VoxelFace {
|
||||
index: i as u32,
|
||||
color: *color,
|
||||
});
|
||||
}
|
||||
}
|
||||
let mut buffer = UnitQuadBuffer::new();
|
||||
let dim: Vector3<u32> = dimensions.cast();
|
||||
let dim = Vector3::new(dim.z, dim.y, dim.x);
|
||||
let shape = RuntimeShape::<u32, 3>::new(dim.into());
|
||||
let slice = grid.as_slice().unwrap();
|
||||
block_mesh::visible_block_faces(
|
||||
slice,
|
||||
&shape,
|
||||
[0; 3],
|
||||
(dim - Vector3::new(1, 1, 1)).into(),
|
||||
&RIGHT_HANDED_Y_UP_CONFIG.faces,
|
||||
&mut buffer,
|
||||
);
|
||||
for (face, group) in buffer.groups.iter().enumerate() {
|
||||
let data: Vec<VoxelFace> = group
|
||||
.iter()
|
||||
.map(|a| {
|
||||
let i = a.minimum[0] + a.minimum[1] * dim.y + a.minimum[2] * dim.y * dim.x;
|
||||
VoxelFace {
|
||||
index: i,
|
||||
color: slice[i as usize],
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
self.vertices.push(Instances::init_with(
|
||||
device,
|
||||
"vvvvv",
|
||||
@@ -224,6 +225,64 @@ impl VoxelPipeline {
|
||||
&INSTANCE_ATTRS,
|
||||
&data,
|
||||
));
|
||||
let group = FaceGroup {
|
||||
dimensions: dimensions.cast(),
|
||||
transform: proj,
|
||||
face: ((8 - face) % 6) as u32,
|
||||
};
|
||||
let uniform = Uniform::init_with(device, "voxel group", 1, &[group]);
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.bind_group_layout,
|
||||
entries: &[
|
||||
self.view.bind_group_entry(),
|
||||
uniform.bind_group_entry(),
|
||||
self.global_lights.bind_group_entry(),
|
||||
],
|
||||
label: Some("voxel bind group"),
|
||||
});
|
||||
self.bind_groups.push(bind_group);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_chunk(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
AddChunk { id, pos, mesh }: AddChunk,
|
||||
) {
|
||||
if mesh.faces.iter().all(|f| f.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
let proj = Transform3::identity()
|
||||
* Translation3::from(pos.cast() * crate::common::component::chunk::SIDE_LENGTH as f32)
|
||||
* Translation3::from(-chunk::DIMENSIONS.cast() / 2.0);
|
||||
|
||||
for (face, meshes) in mesh.faces.iter().enumerate() {
|
||||
self.vertices.push(Instances::init_with(
|
||||
device,
|
||||
"vvvvv",
|
||||
0,
|
||||
&INSTANCE_ATTRS,
|
||||
meshes,
|
||||
));
|
||||
let group = FaceGroup {
|
||||
dimensions: chunk::DIMENSIONS.cast(),
|
||||
transform: proj,
|
||||
face: face as u32,
|
||||
};
|
||||
let uniform = Uniform::init_with(device, "voxel group", 1, &[group]);
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.bind_group_layout,
|
||||
entries: &[
|
||||
self.view.bind_group_entry(),
|
||||
uniform.bind_group_entry(),
|
||||
self.global_lights.bind_group_entry(),
|
||||
],
|
||||
label: Some("voxel bind group"),
|
||||
});
|
||||
self.bind_groups.push(bind_group);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ struct InstanceInput {
|
||||
struct VertexOutput {
|
||||
@builtin(position) clip_position: vec4<f32>,
|
||||
@location(0) color: vec4<f32>,
|
||||
@location(1) normal: vec3<f32>,
|
||||
};
|
||||
|
||||
struct VoxelFace {
|
||||
@@ -28,16 +29,16 @@ struct VoxelGroup {
|
||||
face: u32,
|
||||
};
|
||||
|
||||
struct GlobalLight {
|
||||
dir: vec3<f32>,
|
||||
};
|
||||
|
||||
@group(0) @binding(0)
|
||||
var<uniform> view: View;
|
||||
@group(0) @binding(1)
|
||||
var<uniform> group: VoxelGroup;
|
||||
|
||||
const DIRECTIONS = array(
|
||||
vec3<f32>(1.0, 1.0, 0.0),
|
||||
vec3<f32>(0.0, 1.0, 1.0),
|
||||
vec3<f32>(1.0, 0.0, 1.0),
|
||||
);
|
||||
@group(0) @binding(3)
|
||||
var<storage, read> global_lights: array<GlobalLight>;
|
||||
|
||||
@vertex
|
||||
fn vs_main(
|
||||
@@ -48,14 +49,23 @@ fn vs_main(
|
||||
|
||||
let invert = select(0.0, 1.0, group.face / 3 == 1);
|
||||
let invert_mult = 1.0 - invert * 2.0;
|
||||
let face_axis = group.face % 3;
|
||||
var square_pos = vec2<f32>(
|
||||
f32(vi % 2u),
|
||||
invert + invert_mult * f32(vi / 2u),
|
||||
);
|
||||
var cube_pos = vec3<f32>(invert);
|
||||
square_pos *= invert_mult;
|
||||
cube_pos[(group.face) % 3] += square_pos.x;
|
||||
cube_pos[(group.face + 1) % 3] += square_pos.y;
|
||||
cube_pos[(group.face + 1) % 3] += square_pos.x;
|
||||
cube_pos[(group.face + 2) % 3] += square_pos.y;
|
||||
|
||||
let cube_normal = invert_mult * vec3<f32>(
|
||||
f32(face_axis == 0),
|
||||
f32(face_axis % 2),
|
||||
f32(face_axis / 2),
|
||||
);
|
||||
out.normal = (group.transform * vec4<f32>(cube_normal, 0.0)).xyz;
|
||||
|
||||
var pos = vec4<f32>(
|
||||
cube_pos,
|
||||
1.0,
|
||||
@@ -78,5 +88,9 @@ fn vs_main(
|
||||
fn fs_main(
|
||||
in: VertexOutput,
|
||||
) -> @location(0) vec4<f32> {
|
||||
return in.color;
|
||||
let diffuse = max(dot(global_lights[0].dir, in.normal) + 0.1, 0.0);
|
||||
let ambient = 0.2;
|
||||
let lighting = max(diffuse, ambient);
|
||||
let new_rgb = min(in.color.xyz * lighting, vec3<f32>(1.0));
|
||||
return vec4<f32>(new_rgb, in.color.a);
|
||||
}
|
||||
9
src/client/render/voxel/ray/light.rs
Normal file
9
src/client/render/voxel/ray/light.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use nalgebra::Vector3;
|
||||
|
||||
#[repr(C, align(16))]
|
||||
#[derive(Clone, Copy, PartialEq, bytemuck::Zeroable)]
|
||||
pub struct GlobalLight {
|
||||
pub direction: Vector3<f32>,
|
||||
}
|
||||
|
||||
unsafe impl bytemuck::Pod for GlobalLight {}
|
||||
237
src/client/render/voxel/ray/mod.rs
Normal file
237
src/client/render/voxel/ray/mod.rs
Normal file
@@ -0,0 +1,237 @@
|
||||
mod color;
|
||||
mod grid;
|
||||
mod group;
|
||||
mod light;
|
||||
mod view;
|
||||
|
||||
pub use color::*;
|
||||
|
||||
use super::super::UpdateGridTransform;
|
||||
use crate::client::{
|
||||
camera::Camera,
|
||||
render::{
|
||||
util::{ArrBufUpdate, Storage, Uniform},
|
||||
CreateVoxelGrid,
|
||||
},
|
||||
};
|
||||
use bevy_ecs::entity::Entity;
|
||||
use light::GlobalLight;
|
||||
use nalgebra::{Projective3, Transform3, Translation3, Vector2, Vector3};
|
||||
use std::collections::HashMap;
|
||||
|
||||
use {group::VoxelGroup, view::View};
|
||||
|
||||
pub struct VoxelPipeline {
|
||||
pipeline: wgpu::RenderPipeline,
|
||||
view: Uniform<View>,
|
||||
bind_group_layout: wgpu::BindGroupLayout,
|
||||
bind_group: wgpu::BindGroup,
|
||||
voxel_groups: Storage<VoxelGroup>,
|
||||
voxels: Storage<VoxelColor>,
|
||||
global_lights: Storage<GlobalLight>,
|
||||
id_map: HashMap<Entity, (usize, VoxelGroup)>,
|
||||
}
|
||||
|
||||
impl VoxelPipeline {
|
||||
pub fn new(device: &wgpu::Device, format: &wgpu::TextureFormat) -> Self {
|
||||
// shaders
|
||||
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||
label: Some("Tile Shader"),
|
||||
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
|
||||
});
|
||||
|
||||
let view = Uniform::init(device, "view", 0);
|
||||
let voxels = Storage::init(device, "voxels", 1);
|
||||
let voxel_groups = Storage::init(device, "voxel groups", 2);
|
||||
let global_lights = Storage::init_with(
|
||||
device,
|
||||
"global lights",
|
||||
3,
|
||||
&[GlobalLight {
|
||||
direction: Vector3::new(-0.5, -4.0, 2.0).normalize(),
|
||||
}],
|
||||
);
|
||||
|
||||
// bind groups
|
||||
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
|
||||
entries: &[
|
||||
view.bind_group_layout_entry(),
|
||||
voxels.bind_group_layout_entry(),
|
||||
voxel_groups.bind_group_layout_entry(),
|
||||
global_lights.bind_group_layout_entry(),
|
||||
],
|
||||
label: Some("tile_bind_group_layout"),
|
||||
});
|
||||
|
||||
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &bind_group_layout,
|
||||
entries: &[
|
||||
view.bind_group_entry(),
|
||||
voxels.bind_group_entry(),
|
||||
voxel_groups.bind_group_entry(),
|
||||
global_lights.bind_group_entry(),
|
||||
],
|
||||
label: Some("tile_bind_group"),
|
||||
});
|
||||
|
||||
// pipeline
|
||||
let render_pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
label: Some("Tile Pipeline Layout"),
|
||||
bind_group_layouts: &[&bind_group_layout],
|
||||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: Some("Voxel Pipeline"),
|
||||
layout: Some(&render_pipeline_layout),
|
||||
vertex: wgpu::VertexState {
|
||||
module: &shader,
|
||||
entry_point: "vs_main",
|
||||
buffers: &[],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &shader,
|
||||
entry_point: "fs_main",
|
||||
targets: &[Some(wgpu::ColorTargetState {
|
||||
format: *format,
|
||||
blend: Some(wgpu::BlendState::REPLACE),
|
||||
write_mask: wgpu::ColorWrites::ALL,
|
||||
})],
|
||||
compilation_options: wgpu::PipelineCompilationOptions::default(),
|
||||
}),
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleStrip,
|
||||
strip_index_format: None,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: None,
|
||||
polygon_mode: wgpu::PolygonMode::Fill,
|
||||
unclipped_depth: false,
|
||||
conservative: false,
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: true,
|
||||
},
|
||||
multiview: None,
|
||||
});
|
||||
|
||||
Self {
|
||||
pipeline: render_pipeline,
|
||||
view,
|
||||
bind_group,
|
||||
bind_group_layout,
|
||||
voxels,
|
||||
voxel_groups,
|
||||
global_lights,
|
||||
id_map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_group(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
CreateVoxelGrid {
|
||||
id,
|
||||
pos,
|
||||
orientation,
|
||||
dimensions,
|
||||
grid,
|
||||
}: CreateVoxelGrid,
|
||||
) {
|
||||
let offset = self.voxels.len();
|
||||
|
||||
let updates = [ArrBufUpdate {
|
||||
offset,
|
||||
data: &grid.as_slice().unwrap(),
|
||||
}];
|
||||
let size = offset + grid.len();
|
||||
self.voxels.update(device, encoder, belt, size, &updates);
|
||||
|
||||
let proj = Projective3::identity()
|
||||
* Translation3::from(pos)
|
||||
* orientation
|
||||
* Translation3::from(-dimensions.cast() / 2.0);
|
||||
let group = VoxelGroup {
|
||||
transform: proj,
|
||||
transform_inv: proj.inverse(),
|
||||
dimensions: dimensions.cast(),
|
||||
offset: offset as u32,
|
||||
};
|
||||
let updates = [ArrBufUpdate {
|
||||
offset: self.voxel_groups.len(),
|
||||
data: &[group],
|
||||
}];
|
||||
let i = self.voxel_groups.len();
|
||||
let size = i + 1;
|
||||
self.voxel_groups
|
||||
.update(device, encoder, belt, size, &updates);
|
||||
|
||||
self.id_map.insert(id, (i, group));
|
||||
|
||||
self.bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||
layout: &self.bind_group_layout,
|
||||
entries: &[
|
||||
self.view.bind_group_entry(),
|
||||
self.voxels.bind_group_entry(),
|
||||
self.voxel_groups.bind_group_entry(),
|
||||
self.global_lights.bind_group_entry(),
|
||||
],
|
||||
label: Some("tile_bind_group"),
|
||||
});
|
||||
}
|
||||
|
||||
pub fn update_transform(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
update: UpdateGridTransform,
|
||||
) {
|
||||
if let Some((i, group)) = self.id_map.get_mut(&update.id) {
|
||||
let proj = Projective3::identity()
|
||||
* Translation3::from(update.pos)
|
||||
* update.orientation
|
||||
* Translation3::from(-group.dimensions.cast() / 2.0);
|
||||
group.transform = proj;
|
||||
group.transform_inv = proj.inverse();
|
||||
let updates = [ArrBufUpdate {
|
||||
offset: *i,
|
||||
data: &[*group],
|
||||
}];
|
||||
let size = self.voxel_groups.len();
|
||||
self.voxel_groups
|
||||
.update(device, encoder, belt, size, &updates);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_view(
|
||||
&mut self,
|
||||
device: &wgpu::Device,
|
||||
encoder: &mut wgpu::CommandEncoder,
|
||||
belt: &mut wgpu::util::StagingBelt,
|
||||
size: Vector2<u32>,
|
||||
camera: &Camera,
|
||||
) {
|
||||
let transform =
|
||||
Transform3::identity() * Translation3::from(camera.pos) * camera.orientation;
|
||||
let data = View {
|
||||
width: size.x,
|
||||
height: size.y,
|
||||
zoom: camera.scale,
|
||||
transform,
|
||||
};
|
||||
self.view.update(device, encoder, belt, data)
|
||||
}
|
||||
|
||||
pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
||||
render_pass.set_pipeline(&self.pipeline);
|
||||
render_pass.set_bind_group(0, &self.bind_group, &[]);
|
||||
render_pass.draw(0..4, 0..1);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,9 @@ pub const FPS: u32 = 60;
|
||||
pub const FRAME_TIME: Duration = Duration::from_millis(1000 / FPS as u64);
|
||||
|
||||
pub const CLEAR_COLOR: wgpu::Color = wgpu::Color {
|
||||
r: 0.1,
|
||||
g: 0.1,
|
||||
b: 0.1,
|
||||
r: 0.5,
|
||||
g: 0.8,
|
||||
b: 1.0,
|
||||
a: 1.0,
|
||||
};
|
||||
|
||||
|
||||
@@ -5,15 +5,15 @@ use bevy_ecs::{
|
||||
query::{Added, Changed, Or},
|
||||
system::{Query, ResMut},
|
||||
};
|
||||
use nalgebra::Vector3;
|
||||
use ndarray::Axis;
|
||||
use nalgebra::{AbstractRotation, Rotation3, Vector3};
|
||||
use ndarray::{Array3, Axis};
|
||||
|
||||
use crate::{
|
||||
client::{
|
||||
component::RenderCommands,
|
||||
render::{CreateVoxelGrid, RenderCommand, UpdateGridTransform},
|
||||
render::{voxel::VoxelColor, AddChunk, CreateVoxelGrid, RenderCommand, UpdateGridTransform},
|
||||
},
|
||||
world::component::{Orientation, Pos, VoxelGrid},
|
||||
common::component::{ChunkData, ChunkMesh, ChunkPos, Orientation, Pos, VoxelGrid},
|
||||
};
|
||||
|
||||
pub fn add_grid(
|
||||
@@ -24,16 +24,21 @@ pub fn add_grid(
|
||||
mut renderer: ResMut<RenderCommands>,
|
||||
) {
|
||||
for (id, pos, orientation, grid) in query.iter() {
|
||||
let dims = Vector3::new(
|
||||
grid.len_of(Axis(0)) + 2,
|
||||
grid.len_of(Axis(1)) + 2,
|
||||
grid.len_of(Axis(2)) + 2,
|
||||
);
|
||||
let mut padded = Array3::from_elem((dims.x, dims.y, dims.z), VoxelColor::none());
|
||||
padded
|
||||
.slice_mut(ndarray::s![1..dims.x - 1, 1..dims.y - 1, 1..dims.z - 1])
|
||||
.assign(grid);
|
||||
renderer.push(RenderCommand::CreateVoxelGrid(CreateVoxelGrid {
|
||||
id,
|
||||
pos: **pos,
|
||||
orientation: **orientation,
|
||||
dimensions: Vector3::new(
|
||||
grid.len_of(Axis(0)),
|
||||
grid.len_of(Axis(1)),
|
||||
grid.len_of(Axis(2)),
|
||||
),
|
||||
grid: grid.deref().clone(),
|
||||
dimensions: dims,
|
||||
grid: padded,
|
||||
}));
|
||||
}
|
||||
}
|
||||
@@ -50,3 +55,16 @@ pub fn update_transform(
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_chunk(
|
||||
query: Query<(Entity, &ChunkPos, &ChunkMesh), Or<(Added<ChunkPos>, Added<ChunkMesh>)>>,
|
||||
mut renderer: ResMut<RenderCommands>,
|
||||
) {
|
||||
for (id, pos, mesh) in query.iter() {
|
||||
renderer.push(RenderCommand::AddChunk(AddChunk {
|
||||
id,
|
||||
pos: *pos,
|
||||
mesh: mesh.clone()
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
124
src/common/component/chunk.rs
Normal file
124
src/common/component/chunk.rs
Normal file
@@ -0,0 +1,124 @@
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::{
|
||||
client::render::voxel::{VoxelColor, VoxelFace},
|
||||
util::oct_tree::OctTree,
|
||||
};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{bundle::Bundle, component::Component, entity::Entity, system::Resource};
|
||||
use block_mesh::{ndshape::RuntimeShape, UnitQuadBuffer, RIGHT_HANDED_Y_UP_CONFIG};
|
||||
use nalgebra::Vector3;
|
||||
use ndarray::{s, Array3, Axis};
|
||||
|
||||
pub const SIDE_LENGTH: usize = 16 * 16;
|
||||
pub const SHAPE: (usize, usize, usize) = (SIDE_LENGTH, SIDE_LENGTH, SIDE_LENGTH);
|
||||
pub const DIMENSIONS: Vector3<usize> = Vector3::new(SIDE_LENGTH, SIDE_LENGTH, SIDE_LENGTH);
|
||||
pub const LEN: usize = SHAPE.0 * SHAPE.1 * SHAPE.2;
|
||||
|
||||
#[derive(Debug, Component, Clone, Deref, DerefMut)]
|
||||
pub struct ChunkData {
|
||||
#[deref]
|
||||
data: OctTree<VoxelColor>,
|
||||
}
|
||||
|
||||
impl ChunkData {
|
||||
pub fn empty() -> Self {
|
||||
Self {
|
||||
data: OctTree::Leaf(VoxelColor::none()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_tree(t: OctTree<VoxelColor>) -> Self {
|
||||
Self { data: t }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Component, Default, Deref, DerefMut)]
|
||||
pub struct ChunkPos(pub Vector3<i32>);
|
||||
impl ChunkPos {
|
||||
pub fn new(x: i32, y: i32, z: i32) -> Self {
|
||||
Self(Vector3::new(x, y, z))
|
||||
}
|
||||
}
|
||||
impl From<Vector3<i32>> for ChunkPos {
|
||||
fn from(val: Vector3<i32>) -> Self {
|
||||
ChunkPos(val)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Component)]
|
||||
pub struct ChunkMesh {
|
||||
pub faces: [Vec<VoxelFace>; 6],
|
||||
}
|
||||
|
||||
impl ChunkMesh {
|
||||
pub fn from_data(data: &Array3<VoxelColor>) -> Self {
|
||||
let dim_pad = Vector3::new(
|
||||
data.len_of(Axis(0)) as u32,
|
||||
data.len_of(Axis(1)) as u32,
|
||||
data.len_of(Axis(2)) as u32,
|
||||
);
|
||||
let dim = dim_pad - Vector3::from_element(2);
|
||||
let mut buffer = UnitQuadBuffer::new();
|
||||
let shape = RuntimeShape::<u32, 3>::new(dim_pad.into());
|
||||
let slice = data.as_slice().unwrap();
|
||||
block_mesh::visible_block_faces(
|
||||
slice,
|
||||
&shape,
|
||||
[0; 3],
|
||||
(dim_pad - Vector3::new(1, 1, 1)).into(),
|
||||
&RIGHT_HANDED_Y_UP_CONFIG.faces,
|
||||
&mut buffer,
|
||||
);
|
||||
let faces = [2, 1, 0, 5, 4, 3].map(|f| {
|
||||
buffer.groups[f]
|
||||
.iter()
|
||||
.map(|a| {
|
||||
let i = (a.minimum[0]-1) + (a.minimum[1]-1) * dim.y + (a.minimum[2]-1) * dim.y * dim.x;
|
||||
let i_pad = a.minimum[0] + a.minimum[1] * dim_pad.y + a.minimum[2] * dim_pad.y * dim_pad.x;
|
||||
VoxelFace {
|
||||
index: i,
|
||||
color: slice[i_pad as usize],
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
Self { faces }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Component, Deref, DerefMut)]
|
||||
pub struct LoadedChunks {
|
||||
loaded: HashSet<ChunkPos>,
|
||||
}
|
||||
|
||||
impl LoadedChunks {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
loaded: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource, Deref, DerefMut)]
|
||||
pub struct ChunkMap {
|
||||
#[deref]
|
||||
map: HashMap<ChunkPos, Entity>,
|
||||
pub generating: HashSet<ChunkPos>,
|
||||
}
|
||||
|
||||
impl ChunkMap {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: HashMap::new(),
|
||||
generating: HashSet::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Bundle, Clone)]
|
||||
pub struct ChunkBundle {
|
||||
pub pos: ChunkPos,
|
||||
pub data: ChunkData,
|
||||
pub mesh: ChunkMesh,
|
||||
}
|
||||
@@ -1,19 +1,9 @@
|
||||
use crate::client::render::voxel::VoxelColor;
|
||||
use bevy_ecs::{bundle::Bundle, component::Component};
|
||||
use ndarray::{Array3, ArrayBase, Dim, SliceArg};
|
||||
use std::ops::Range;
|
||||
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{bundle::Bundle, component::Component};
|
||||
use nalgebra::{Rotation3, Vector3};
|
||||
use ndarray::{Array3, ArrayBase, Dim, SliceArg};
|
||||
|
||||
use crate::client::render::voxel::VoxelColor;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Component, Default)]
|
||||
pub struct Synced;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Component, Default, Deref, DerefMut)]
|
||||
pub struct Pos(pub Vector3<f32>);
|
||||
#[derive(Debug, Clone, Copy, Component, Default, Deref, DerefMut)]
|
||||
pub struct Orientation(pub Rotation3<f32>);
|
||||
use super::{Orientation, Pos};
|
||||
|
||||
pub type VoxelGrid = TrackedGrid<VoxelColor>;
|
||||
pub type GridRegion = (Range<usize>, Range<usize>, Range<usize>);
|
||||
@@ -41,27 +31,10 @@ impl<T> TrackedGrid<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pos {
|
||||
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self(Vector3::new(x, y, z))
|
||||
}
|
||||
}
|
||||
impl Orientation {
|
||||
pub fn from_axis_angle<SB: nalgebra::Storage<f32, nalgebra::Const<3>>>(
|
||||
axis: &nalgebra::Unit<nalgebra::Matrix<f32, nalgebra::Const<3>, nalgebra::Const<1>, SB>>,
|
||||
angle: f32,
|
||||
) -> Self {
|
||||
Self(Rotation3::from_axis_angle(axis, angle))
|
||||
}
|
||||
}
|
||||
impl From<Vector3<f32>> for Pos {
|
||||
fn from(val: Vector3<f32>) -> Self {
|
||||
Pos(val)
|
||||
}
|
||||
}
|
||||
impl From<Rotation3<f32>> for Orientation {
|
||||
fn from(val: Rotation3<f32>) -> Self {
|
||||
Orientation(val)
|
||||
impl<T> std::ops::Deref for TrackedGrid<T> {
|
||||
type Target = Array3<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,10 +44,3 @@ pub struct VoxelGridBundle {
|
||||
pub orientation: Orientation,
|
||||
pub grid: VoxelGrid,
|
||||
}
|
||||
|
||||
impl<T> std::ops::Deref for TrackedGrid<T> {
|
||||
type Target = Array3<T>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.data
|
||||
}
|
||||
}
|
||||
62
src/common/component/mod.rs
Normal file
62
src/common/component/mod.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
pub mod chunk;
|
||||
mod grid;
|
||||
|
||||
use chunk::LoadedChunks;
|
||||
pub use chunk::{ChunkBundle, ChunkData, ChunkMap, ChunkMesh, ChunkPos};
|
||||
pub use grid::*;
|
||||
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{bundle::Bundle, component::Component};
|
||||
use nalgebra::{Rotation3, Vector3};
|
||||
|
||||
#[derive(Debug, Clone, Copy, Component, Default, Deref, DerefMut)]
|
||||
pub struct Pos(pub Vector3<f32>);
|
||||
|
||||
impl Pos {
|
||||
pub fn new(x: f32, y: f32, z: f32) -> Self {
|
||||
Self(Vector3::new(x, y, z))
|
||||
}
|
||||
}
|
||||
impl From<Vector3<f32>> for Pos {
|
||||
fn from(val: Vector3<f32>) -> Self {
|
||||
Pos(val)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Component, Default, Deref, DerefMut)]
|
||||
pub struct Orientation(pub Rotation3<f32>);
|
||||
impl Orientation {
|
||||
pub fn from_axis_angle<SB: nalgebra::Storage<f32, nalgebra::Const<3>>>(
|
||||
axis: &nalgebra::Unit<nalgebra::Matrix<f32, nalgebra::Const<3>, nalgebra::Const<1>, SB>>,
|
||||
angle: f32,
|
||||
) -> Self {
|
||||
Self(Rotation3::from_axis_angle(axis, angle))
|
||||
}
|
||||
}
|
||||
impl From<Rotation3<f32>> for Orientation {
|
||||
fn from(val: Rotation3<f32>) -> Self {
|
||||
Orientation(val)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Component)]
|
||||
pub struct Player;
|
||||
|
||||
#[derive(Debug, Clone, Bundle)]
|
||||
pub struct PlayerBundle {
|
||||
pub player: Player,
|
||||
pub loaded_chunks: LoadedChunks,
|
||||
pub pos: Pos,
|
||||
pub orientation: Orientation,
|
||||
}
|
||||
|
||||
impl PlayerBundle {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
player: Player,
|
||||
loaded_chunks: LoadedChunks::new(),
|
||||
pos: Pos::default(),
|
||||
orientation: Orientation::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/common/message.rs
Normal file
28
src/common/message.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use crate::{
|
||||
common::component::{ChunkBundle, Pos, VoxelGridBundle},
|
||||
util::thread::{ExitType, ThreadChannel, ThreadHandle},
|
||||
};
|
||||
use bevy_ecs::entity::Entity;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ServerMessage {
|
||||
Stop,
|
||||
Join,
|
||||
SpawnVoxelGrid(VoxelGridBundle),
|
||||
}
|
||||
|
||||
impl ExitType for ServerMessage {
|
||||
fn exit() -> Self {
|
||||
ServerMessage::Stop
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum ClientMessage {
|
||||
SpawnVoxelGrid(Entity, VoxelGridBundle),
|
||||
LoadChunk(Entity, ChunkBundle),
|
||||
PosUpdate(Entity, Pos),
|
||||
}
|
||||
|
||||
pub type ClientChannel = ThreadChannel<ClientMessage, ServerMessage>;
|
||||
pub type ServerHandle = ThreadHandle<ServerMessage, ClientMessage>;
|
||||
4
src/common/mod.rs
Normal file
4
src/common/mod.rs
Normal file
@@ -0,0 +1,4 @@
|
||||
pub mod component;
|
||||
mod message;
|
||||
|
||||
pub use message::*;
|
||||
@@ -4,9 +4,9 @@ use client::ClientApp;
|
||||
use winit::event_loop::EventLoop;
|
||||
|
||||
mod client;
|
||||
mod world;
|
||||
mod common;
|
||||
mod server;
|
||||
mod sync;
|
||||
mod util;
|
||||
|
||||
fn main() {
|
||||
let event_loop = EventLoop::new().expect("Failed to create event loop");
|
||||
|
||||
182
src/server/chunk/load.rs
Normal file
182
src/server/chunk/load.rs
Normal file
@@ -0,0 +1,182 @@
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
|
||||
use bevy_ecs::{entity::Entity, system::Commands};
|
||||
use nalgebra::Vector3;
|
||||
use ndarray::{s, Array3, Axis};
|
||||
use simdnoise::NoiseBuilder;
|
||||
|
||||
use crate::{
|
||||
client::render::voxel::VoxelColor,
|
||||
common::component::{chunk, ChunkBundle, ChunkData, ChunkMesh, ChunkPos},
|
||||
util::{
|
||||
oct_tree::OctTree,
|
||||
thread::{ExitType, ThreadChannel, ThreadHandle},
|
||||
},
|
||||
};
|
||||
|
||||
pub struct ChunkManager {
|
||||
handles: Vec<ThreadHandle<ChunkLoaderMsg, ServerChunkMsg>>,
|
||||
i: usize,
|
||||
n: usize,
|
||||
map: HashMap<ChunkPos, Entity>,
|
||||
generating: HashSet<ChunkPos>,
|
||||
}
|
||||
|
||||
impl ChunkManager {
|
||||
pub fn new() -> Self {
|
||||
let n = 4;
|
||||
Self {
|
||||
handles: std::iter::repeat_with(|| ThreadHandle::spawn(chunk_loader_main))
|
||||
.take(n)
|
||||
.collect(),
|
||||
i: 0,
|
||||
n,
|
||||
map: HashMap::new(),
|
||||
generating: HashSet::new(),
|
||||
}
|
||||
}
|
||||
pub fn entity_at(&self, pos: &ChunkPos) -> Option<&Entity> {
|
||||
self.map.get(pos)
|
||||
}
|
||||
pub fn is_generating(&self, pos: &ChunkPos) -> bool {
|
||||
self.generating.contains(pos)
|
||||
}
|
||||
pub fn queue(&mut self, pos: ChunkPos) {
|
||||
if !self.is_generating(&pos) {
|
||||
self.handles[self.i].send(ChunkLoaderMsg::Generate(pos));
|
||||
self.i = (self.i + 1) % self.n;
|
||||
self.generating.insert(pos);
|
||||
}
|
||||
}
|
||||
pub fn update(&mut self, commands: &mut Commands) {
|
||||
for msg in self.handles.iter_mut().flat_map(|h| h.recv()) {
|
||||
match msg {
|
||||
ServerChunkMsg::ChunkGenerated(chunk) => {
|
||||
let id = commands
|
||||
.spawn(ChunkBundle {
|
||||
pos: chunk.pos,
|
||||
data: chunk.data,
|
||||
mesh: chunk.mesh,
|
||||
})
|
||||
.id();
|
||||
self.map.insert(chunk.pos, id);
|
||||
self.generating.remove(&chunk.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GeneratedChunk {
|
||||
pub pos: ChunkPos,
|
||||
pub data: ChunkData,
|
||||
pub mesh: ChunkMesh,
|
||||
}
|
||||
|
||||
impl Drop for ChunkManager {
|
||||
fn drop(&mut self) {
|
||||
for h in &mut self.handles {
|
||||
h.send(ChunkLoaderMsg::Exit);
|
||||
h.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ServerChunkMsg {
|
||||
ChunkGenerated(GeneratedChunk),
|
||||
}
|
||||
|
||||
enum ChunkLoaderMsg {
|
||||
Generate(ChunkPos),
|
||||
Exit,
|
||||
}
|
||||
|
||||
impl ExitType for ChunkLoaderMsg {
|
||||
fn exit() -> Self {
|
||||
Self::Exit
|
||||
}
|
||||
}
|
||||
|
||||
fn chunk_loader_main(channel: ThreadChannel<ServerChunkMsg, ChunkLoaderMsg>) {
|
||||
let mut to_generate = VecDeque::new();
|
||||
'outer: loop {
|
||||
let msg = channel.recv_wait();
|
||||
match msg {
|
||||
ChunkLoaderMsg::Generate(pos) => {
|
||||
to_generate.push_back(pos);
|
||||
}
|
||||
ChunkLoaderMsg::Exit => {
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
if let Some(pos) = to_generate.pop_front() {
|
||||
let data = generate(pos);
|
||||
let mesh = ChunkMesh::from_data(&data);
|
||||
let data = if pos.y > 0 || pos.y < -1 {
|
||||
ChunkData::empty()
|
||||
} else {
|
||||
ChunkData::from_tree(OctTree::from_arr(data.slice(s![
|
||||
1..data.len_of(Axis(0)) - 1,
|
||||
1..data.len_of(Axis(1)) - 1,
|
||||
1..data.len_of(Axis(2)) - 1
|
||||
])))
|
||||
};
|
||||
channel.send(ServerChunkMsg::ChunkGenerated(GeneratedChunk {
|
||||
pos,
|
||||
data,
|
||||
mesh,
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn generate(pos: ChunkPos) -> Array3<VoxelColor> {
|
||||
let shape = [chunk::SIDE_LENGTH + 2; 3];
|
||||
if pos.y > 0 {
|
||||
return Array3::from_elem(shape, VoxelColor::none());
|
||||
}
|
||||
if pos.y < -1 {
|
||||
return Array3::from_elem(shape, VoxelColor::none());
|
||||
}
|
||||
let posf: Vector3<f32> = (pos.cast() * chunk::SIDE_LENGTH as f32) - Vector3::from_element(1.0);
|
||||
let (a, b, c, d) = (0.0, 50.0, 100.0, 127.0);
|
||||
let (noise, ..) = NoiseBuilder::gradient_2d_offset(
|
||||
posf.x,
|
||||
chunk::SIDE_LENGTH + 2,
|
||||
posf.z,
|
||||
chunk::SIDE_LENGTH + 2,
|
||||
)
|
||||
.with_seed(0)
|
||||
.with_freq(0.005)
|
||||
.generate();
|
||||
Array3::from_shape_fn(shape, |(x, y, z)| {
|
||||
let y = y as f32 + posf.y;
|
||||
let n = (noise[x + z * (chunk::SIDE_LENGTH + 2)] + 0.022) * (1.0 / 0.044) * d;
|
||||
if y < n.max(b) {
|
||||
if y < b {
|
||||
VoxelColor {
|
||||
r: 100,
|
||||
g: 100,
|
||||
b: 255,
|
||||
a: 255,
|
||||
}
|
||||
} else if y < c {
|
||||
VoxelColor {
|
||||
r: 100,
|
||||
g: 255,
|
||||
b: 100,
|
||||
a: 255,
|
||||
}
|
||||
} else {
|
||||
VoxelColor {
|
||||
r: 150,
|
||||
g: 150,
|
||||
b: 150,
|
||||
a: 255,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VoxelColor::none()
|
||||
}
|
||||
})
|
||||
}
|
||||
2
src/server/chunk/mod.rs
Normal file
2
src/server/chunk/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
mod load;
|
||||
pub use load::*;
|
||||
80
src/server/client.rs
Normal file
80
src/server/client.rs
Normal file
@@ -0,0 +1,80 @@
|
||||
use std::collections::{hash_map, HashMap};
|
||||
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
use bevy_ecs::{component::Component, entity::Entity, system::Resource};
|
||||
|
||||
use crate::common::{ClientChannel, ClientMessage, ServerMessage};
|
||||
|
||||
pub enum ServerClient {
|
||||
Local(ClientChannel),
|
||||
}
|
||||
|
||||
impl ServerClient {
|
||||
pub fn recv(&mut self) -> Vec<ServerMessage> {
|
||||
match self {
|
||||
Self::Local(ch) => ch.recv().collect(),
|
||||
}
|
||||
}
|
||||
pub fn send(&self, msg: ClientMessage) {
|
||||
match self {
|
||||
Self::Local(ch) => ch.send(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deref, DerefMut)]
|
||||
pub struct ServerClients {
|
||||
map: HashMap<Entity, ServerClient>,
|
||||
}
|
||||
|
||||
impl ServerClients {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
map: HashMap::new(),
|
||||
}
|
||||
}
|
||||
pub fn add(&mut self, id: Entity, client: ServerClient) {
|
||||
self.map.insert(id, client);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> IntoIterator for &'a mut ServerClients {
|
||||
type Item = (&'a Entity, &'a mut ServerClient);
|
||||
type IntoIter = hash_map::IterMut<'a, Entity, ServerClient>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.map.iter_mut()
|
||||
}
|
||||
}
|
||||
|
||||
// I don't think it's worth putting a reciever in here rn
|
||||
// and moving that stuff into the ecs but we'll see
|
||||
#[derive(Component)]
|
||||
pub struct ClientComponent {
|
||||
send: Vec<ClientMessage>,
|
||||
}
|
||||
|
||||
impl ClientComponent {
|
||||
pub fn new() -> Self {
|
||||
Self { send: Vec::new() }
|
||||
}
|
||||
pub fn send(&mut self, msg: ClientMessage) {
|
||||
self.send.push(msg);
|
||||
}
|
||||
pub fn take(&mut self) -> Vec<ClientMessage> {
|
||||
std::mem::take(&mut self.send)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct ClientBroadcast(Vec<ClientMessage>);
|
||||
impl ClientBroadcast {
|
||||
pub fn new() -> Self {
|
||||
Self(Vec::new())
|
||||
}
|
||||
pub fn send(&mut self, msg: ClientMessage) {
|
||||
self.0.push(msg);
|
||||
}
|
||||
pub fn take(&mut self) -> Vec<ClientMessage> {
|
||||
std::mem::take(&mut self.0)
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,29 @@
|
||||
mod chunk;
|
||||
mod client;
|
||||
mod rsc;
|
||||
mod system;
|
||||
mod test;
|
||||
|
||||
use crate::{
|
||||
sync::{ClientChannel, ClientMessage, ClientSender, ServerMessage},
|
||||
world::{
|
||||
component::{Orientation, Pos, Synced, VoxelGrid, VoxelGridBundle},
|
||||
generation::generate,
|
||||
pub use client::*;
|
||||
|
||||
use crate::common::{
|
||||
component::{
|
||||
ChunkBundle, ChunkData, ChunkMap, ChunkMesh, ChunkPos, Orientation, PlayerBundle, Pos,
|
||||
VoxelGrid, VoxelGridBundle,
|
||||
},
|
||||
ClientChannel, ClientMessage, ServerMessage,
|
||||
};
|
||||
use bevy_ecs::{entity::Entity, query::With, system::SystemId, world::World};
|
||||
use bevy_ecs::{entity::Entity, system::SystemId, world::World};
|
||||
use chunk::ChunkManager;
|
||||
use client::{ClientBroadcast, ServerClient, ServerClients};
|
||||
use rsc::UPDATE_TIME;
|
||||
use std::time::{Duration, Instant};
|
||||
use test::spawn_test_stuff;
|
||||
|
||||
pub struct Server {
|
||||
update_time: Duration,
|
||||
target: Instant,
|
||||
client: ClientChannel,
|
||||
clients: ServerClients,
|
||||
world: World,
|
||||
systems: ServerSystems,
|
||||
mov: Vec<Entity>,
|
||||
@@ -24,23 +32,27 @@ pub struct Server {
|
||||
|
||||
pub struct ServerSystems {
|
||||
sync_pos: SystemId,
|
||||
sync_chunks: SystemId,
|
||||
}
|
||||
|
||||
impl ServerSystems {
|
||||
pub fn new(world: &mut World) -> Self {
|
||||
Self {
|
||||
sync_pos: world.register_system(system::sync::pos),
|
||||
sync_chunks: world.register_system(system::sync::chunks),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Server {
|
||||
pub fn new(client: ClientChannel) -> Self {
|
||||
pub fn new() -> Self {
|
||||
let mut world = World::new();
|
||||
world.insert_resource(ClientSender(client.sender()));
|
||||
world.insert_resource(ClientBroadcast::new());
|
||||
world.insert_resource(ChunkMap::new());
|
||||
world.insert_non_send_resource(ChunkManager::new());
|
||||
let systems = ServerSystems::new(&mut world);
|
||||
Self {
|
||||
client,
|
||||
clients: ServerClients::new(),
|
||||
world,
|
||||
systems,
|
||||
target: Instant::now(),
|
||||
@@ -50,58 +62,110 @@ impl Server {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_client(client: ClientChannel) -> Self {
|
||||
let mut s = Self::new();
|
||||
s.add_client(ServerClient::Local(client));
|
||||
s
|
||||
}
|
||||
|
||||
pub fn add_client(&mut self, client: ServerClient) {
|
||||
let id = self.world.spawn(ClientComponent::new()).id();
|
||||
self.clients.add(id, client);
|
||||
}
|
||||
|
||||
pub fn start(ch: ClientChannel) {
|
||||
Self::new(ch).run();
|
||||
Self::from_client(ch).run();
|
||||
}
|
||||
|
||||
pub fn run(&mut self) {
|
||||
generate(&mut self.world);
|
||||
spawn_test_stuff(&mut self.world);
|
||||
loop {
|
||||
self.recv();
|
||||
let now = Instant::now();
|
||||
if now >= self.target {
|
||||
self.target += self.update_time;
|
||||
let mut q = self.world.query::<(Entity, &mut Pos)>();
|
||||
for (e, mut p) in q.iter_mut(&mut self.world) {
|
||||
if self.mov.contains(&e) {
|
||||
p.x += 0.1;
|
||||
}
|
||||
}
|
||||
self.world.run_system(self.systems.sync_pos).unwrap();
|
||||
self.world.clear_trackers();
|
||||
self.tick();
|
||||
}
|
||||
if self.stop {
|
||||
break;
|
||||
}
|
||||
self.send();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tick(&mut self) {
|
||||
let mut q = self.world.query::<(Entity, &mut Pos)>();
|
||||
for (e, mut p) in q.iter_mut(&mut self.world) {
|
||||
if self.mov.contains(&e) {
|
||||
p.x += 0.1;
|
||||
}
|
||||
}
|
||||
self.world.run_system(self.systems.sync_pos).unwrap();
|
||||
self.world.run_system(self.systems.sync_chunks).unwrap();
|
||||
self.world.clear_trackers();
|
||||
}
|
||||
|
||||
pub fn recv(&mut self) {
|
||||
for msg in self.client.recv() {
|
||||
match msg {
|
||||
ServerMessage::LoadWorld => {
|
||||
let mut q = self
|
||||
.world
|
||||
.query_filtered::<(Entity, &Pos, &Orientation, &VoxelGrid), With<Synced>>();
|
||||
// ePOG
|
||||
for (e, p, o, g) in q.iter(&self.world) {
|
||||
self.client.send(ClientMessage::SpawnVoxelGrid(
|
||||
e,
|
||||
VoxelGridBundle {
|
||||
pos: *p,
|
||||
orientation: *o,
|
||||
grid: g.clone(),
|
||||
},
|
||||
))
|
||||
for (id, client) in &mut self.clients {
|
||||
for msg in client.recv() {
|
||||
match msg {
|
||||
ServerMessage::Join => {
|
||||
let mut q = self
|
||||
.world
|
||||
.query::<(Entity, &Pos, &Orientation, &VoxelGrid)>();
|
||||
// ePOG
|
||||
for (e, p, o, g) in q.iter(&self.world) {
|
||||
client.send(ClientMessage::SpawnVoxelGrid(
|
||||
e,
|
||||
VoxelGridBundle {
|
||||
pos: *p,
|
||||
orientation: *o,
|
||||
grid: g.clone(),
|
||||
},
|
||||
))
|
||||
}
|
||||
let mut q = self
|
||||
.world
|
||||
.query::<(Entity, &ChunkPos, &ChunkData, &ChunkMesh)>();
|
||||
for (e, p, c, m) in q.iter(&self.world) {
|
||||
client.send(ClientMessage::LoadChunk(
|
||||
e,
|
||||
ChunkBundle {
|
||||
pos: *p,
|
||||
data: c.clone(),
|
||||
mesh: m.clone(),
|
||||
},
|
||||
))
|
||||
}
|
||||
self.world.entity_mut(*id).insert(PlayerBundle::new());
|
||||
}
|
||||
ServerMessage::SpawnVoxelGrid(grid) => {
|
||||
let e = self.world.spawn(grid.clone()).id();
|
||||
self.mov.push(e);
|
||||
self.world
|
||||
.resource_mut::<ClientBroadcast>()
|
||||
.send(ClientMessage::SpawnVoxelGrid(e, grid));
|
||||
}
|
||||
ServerMessage::Stop => {
|
||||
self.stop = true;
|
||||
}
|
||||
}
|
||||
ServerMessage::SpawnVoxelGrid(grid) => {
|
||||
let e = self.world.spawn((grid.clone(), Synced)).id();
|
||||
self.mov.push(e);
|
||||
self.client.send(ClientMessage::SpawnVoxelGrid(e, grid));
|
||||
}
|
||||
ServerMessage::Stop => {
|
||||
self.stop = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn send(&mut self) {
|
||||
let msgs = self.world.resource_mut::<ClientBroadcast>().take();
|
||||
for msg in &msgs {
|
||||
for (_, client) in &mut self.clients {
|
||||
client.send(msg.clone());
|
||||
}
|
||||
}
|
||||
let mut q = self.world.query::<(Entity, &mut ClientComponent)>();
|
||||
for (e, mut c) in q.iter_mut(&mut self.world) {
|
||||
if let Some(sc) = self.clients.get(&e) {
|
||||
for msg in c.take() {
|
||||
sc.send(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,72 @@
|
||||
use bevy_ecs::{entity::Entity, query::Changed, system::{Query, Res}};
|
||||
use bevy_ecs::{
|
||||
entity::Entity,
|
||||
query::{Changed, With},
|
||||
system::{Commands, NonSendMut, Query, ResMut},
|
||||
};
|
||||
use nalgebra::Vector3;
|
||||
|
||||
use crate::{sync::{ClientMessage, ClientSender}, world::component::{Pos, Synced}};
|
||||
use crate::{
|
||||
common::{
|
||||
component::{
|
||||
chunk::{self, ChunkBundle, LoadedChunks},
|
||||
ChunkData, ChunkMesh, ChunkPos, Player, Pos,
|
||||
},
|
||||
ClientMessage,
|
||||
},
|
||||
server::{chunk::ChunkManager, client::ClientBroadcast, ClientComponent},
|
||||
};
|
||||
|
||||
pub fn pos(query: Query<(Entity, &Synced, &Pos), Changed<Pos>>, client: Res<ClientSender>) {
|
||||
for (e, _, pos) in query.iter() {
|
||||
client.send(ClientMessage::PosUpdate(e, *pos));
|
||||
pub fn pos(query: Query<(Entity, &Pos), Changed<Pos>>, mut clients: ResMut<ClientBroadcast>) {
|
||||
for (e, pos) in query.iter() {
|
||||
clients.send(ClientMessage::PosUpdate(e, *pos));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn chunks(
|
||||
mut players: Query<(&Pos, &mut LoadedChunks, &mut ClientComponent), With<Player>>,
|
||||
chunks: Query<(&ChunkPos, &ChunkData, &ChunkMesh)>,
|
||||
mut loader: NonSendMut<ChunkManager>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
for (pos, mut loaded, mut client) in &mut players {
|
||||
let fp = **pos / chunk::SIDE_LENGTH as f32;
|
||||
let player_chunk = Vector3::new(
|
||||
fp.x.floor() as i32,
|
||||
fp.y.floor() as i32,
|
||||
fp.z.floor() as i32,
|
||||
);
|
||||
let radius: i32 = 5;
|
||||
let width = radius * 2 - 1;
|
||||
let mut desired = Vec::new();
|
||||
for i in 0..width.pow(3) {
|
||||
let pos = Vector3::new(i % width, (i / width) % width, i / (width.pow(2)))
|
||||
- Vector3::from_element(radius - 1);
|
||||
let dist = pos.cast::<f32>().norm();
|
||||
if dist < radius as f32 {
|
||||
desired.push((dist, pos));
|
||||
}
|
||||
}
|
||||
desired.sort_by(|(da, ..), (db, ..)| da.total_cmp(db));
|
||||
for (_, pos) in desired {
|
||||
let coords = pos - player_chunk;
|
||||
let pos = ChunkPos(coords);
|
||||
if !loaded.contains(&pos) {
|
||||
if let Some(id) = loader.entity_at(&pos) {
|
||||
let (pos, data, mesh) = chunks.get(*id).unwrap();
|
||||
client.send(ClientMessage::LoadChunk(
|
||||
*id,
|
||||
ChunkBundle {
|
||||
pos: *pos,
|
||||
data: data.clone(),
|
||||
mesh: mesh.clone(),
|
||||
},
|
||||
));
|
||||
loaded.insert(*pos);
|
||||
} else {
|
||||
loader.queue(pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loader.update(&mut commands);
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
use crate::world::component::VoxelGrid;
|
||||
use crate::client::render::voxel::VoxelColor;
|
||||
use crate::common::component::{VoxelGrid, VoxelGridBundle};
|
||||
use bevy_ecs::world::World;
|
||||
use nalgebra::{Rotation3, UnitVector3, Vector3};
|
||||
use ndarray::Array3;
|
||||
|
||||
use crate::client::render::voxel::VoxelColor;
|
||||
|
||||
use super::component::{Synced, VoxelGridBundle};
|
||||
|
||||
pub fn generate(world: &mut World) {
|
||||
pub fn spawn_test_stuff(world: &mut World) {
|
||||
let dim = (15, 10, 10);
|
||||
world.spawn((VoxelGridBundle {
|
||||
world.spawn(VoxelGridBundle {
|
||||
pos: Vector3::new(0.0, 0.0, 20.0).into(),
|
||||
orientation: Rotation3::from_axis_angle(&Vector3::y_axis(), 0.5).into(),
|
||||
grid: VoxelGrid::new(Array3::from_shape_fn(dim, |(x, y, z)| {
|
||||
@@ -33,30 +30,34 @@ pub fn generate(world: &mut World) {
|
||||
VoxelColor::none()
|
||||
}
|
||||
})),
|
||||
}, Synced));
|
||||
});
|
||||
|
||||
let dim = (1000, 2, 1000);
|
||||
world.spawn((VoxelGridBundle {
|
||||
pos: Vector3::new(0.0, -2.1, 0.0).into(),
|
||||
orientation: Rotation3::identity().into(),
|
||||
grid: VoxelGrid::new(Array3::from_shape_fn(dim, |(x, y, z)| {
|
||||
if y == 0 {
|
||||
VoxelColor::random()
|
||||
} else if (y == dim.1 - 1) && (x == 0 || x == dim.0 - 1 || z == 0 || z == dim.2 - 1) {
|
||||
VoxelColor {
|
||||
r: 255,
|
||||
g: 0,
|
||||
b: 255,
|
||||
a: 255,
|
||||
}
|
||||
} else {
|
||||
VoxelColor::none()
|
||||
}
|
||||
})),
|
||||
}, Synced));
|
||||
// let dim = (1000, 2, 1000);
|
||||
// world.spawn((
|
||||
// VoxelGridBundle {
|
||||
// pos: Vector3::new(0.0, -2.1, 0.0).into(),
|
||||
// orientation: Rotation3::identity().into(),
|
||||
// grid: VoxelGrid::new(Array3::from_shape_fn(dim, |(x, y, z)| {
|
||||
// if y == 0 {
|
||||
// VoxelColor::random()
|
||||
// } else if (y == dim.1 - 1) && (x == 0 || x == dim.0 - 1 || z == 0 || z == dim.2 - 1)
|
||||
// {
|
||||
// VoxelColor {
|
||||
// r: 255,
|
||||
// g: 0,
|
||||
// b: 255,
|
||||
// a: 255,
|
||||
// }
|
||||
// } else {
|
||||
// VoxelColor::none()
|
||||
// }
|
||||
// })),
|
||||
// },
|
||||
// Synced,
|
||||
// ));
|
||||
|
||||
let dim = (3, 3, 3);
|
||||
world.spawn((VoxelGridBundle {
|
||||
world.spawn(VoxelGridBundle {
|
||||
pos: Vector3::new(0.0, 0.0, 16.5).into(),
|
||||
orientation: (Rotation3::from_axis_angle(&Vector3::y_axis(), std::f32::consts::PI / 4.0)
|
||||
* Rotation3::from_axis_angle(
|
||||
@@ -70,5 +71,5 @@ pub fn generate(world: &mut World) {
|
||||
b: 255,
|
||||
a: 255,
|
||||
})),
|
||||
}, Synced));
|
||||
});
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
pub mod thread;
|
||||
|
||||
use crate::world::component::{Pos, VoxelGridBundle};
|
||||
use bevy_ecs::{entity::Entity, system::Resource};
|
||||
use std::sync::mpsc::Sender;
|
||||
use thread::{ThreadChannel, ThreadHandle};
|
||||
|
||||
pub enum ServerMessage {
|
||||
Stop,
|
||||
LoadWorld,
|
||||
SpawnVoxelGrid(VoxelGridBundle),
|
||||
}
|
||||
|
||||
pub enum ClientMessage {
|
||||
SpawnVoxelGrid(Entity, VoxelGridBundle),
|
||||
PosUpdate(Entity, Pos),
|
||||
}
|
||||
|
||||
pub type ClientChannel = ThreadChannel<ClientMessage, ServerMessage>;
|
||||
pub type ServerHandle = ThreadHandle<ServerMessage, ClientMessage>;
|
||||
|
||||
#[derive(Resource, Clone)]
|
||||
pub struct ClientSender(pub Sender<ClientMessage>);
|
||||
impl ClientSender {
|
||||
pub fn send(&self, msg: ClientMessage) {
|
||||
self.0.send(msg).expect("YOU HAVE FAILED THE MISSION");
|
||||
}
|
||||
}
|
||||
2
src/util/mod.rs
Normal file
2
src/util/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod thread;
|
||||
pub mod oct_tree;
|
||||
42
src/util/oct_tree.rs
Normal file
42
src/util/oct_tree.rs
Normal file
@@ -0,0 +1,42 @@
|
||||
use std::fmt::Debug;
|
||||
|
||||
use nalgebra::Vector3;
|
||||
use ndarray::{Array3, ArrayView3, Axis};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum OctTree<T> {
|
||||
Leaf(T),
|
||||
Node(Box<[OctTree<T>; 8]>),
|
||||
}
|
||||
|
||||
impl<T: PartialEq + Clone + Debug> OctTree<T> {
|
||||
pub fn from_arr(arr: ArrayView3<T>) -> OctTree<T> {
|
||||
let mut node_arr = arr.map(|x| OctTree::Leaf(x.clone()));
|
||||
while node_arr.len() > 1 {
|
||||
let new_data = node_arr.exact_chunks([2; 3]).into_iter().map(|chunk| {
|
||||
let vec: Vec<OctTree<T>> = chunk.iter().cloned().collect();
|
||||
let vec: [OctTree<T>; 8] = vec.try_into().unwrap();
|
||||
if let OctTree::Leaf(first) = &chunk[[0; 3]] {
|
||||
if vec.iter().all(|n| {
|
||||
if let OctTree::Leaf(d) = n {
|
||||
*d == *first
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}) {
|
||||
return OctTree::Leaf(first.clone())
|
||||
}
|
||||
}
|
||||
OctTree::Node(Box::new(vec))
|
||||
}).collect();
|
||||
node_arr = Array3::from_shape_vec([node_arr.len_of(Axis(0)) / 2; 3], new_data).unwrap();
|
||||
}
|
||||
node_arr[[0; 3]].clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> OctTree<T> {
|
||||
fn get(i: Vector3<usize>) {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,16 @@ use std::{
|
||||
thread::JoinHandle,
|
||||
};
|
||||
|
||||
pub struct ThreadHandle<SendMsg, RecvMsg> {
|
||||
pub channel: ThreadChannel<SendMsg, RecvMsg>,
|
||||
pub handle: Option<JoinHandle<()>>,
|
||||
pub trait ExitType {
|
||||
fn exit() -> Self;
|
||||
}
|
||||
|
||||
impl<SendMsg: Send + 'static, RecvMsg: Send + 'static> ThreadHandle<SendMsg, RecvMsg> {
|
||||
pub struct ThreadHandle<SendMsg: Send + 'static + ExitType, RecvMsg: Send + 'static> {
|
||||
channel: ThreadChannel<SendMsg, RecvMsg>,
|
||||
handle: Option<JoinHandle<()>>,
|
||||
}
|
||||
|
||||
impl<SendMsg: Send + 'static + ExitType, RecvMsg: Send + 'static> ThreadHandle<SendMsg, RecvMsg> {
|
||||
pub fn send(&self, msg: SendMsg) {
|
||||
self.channel.send(msg);
|
||||
}
|
||||
@@ -34,6 +38,13 @@ impl<SendMsg: Send + 'static, RecvMsg: Send + 'static> ThreadHandle<SendMsg, Rec
|
||||
}
|
||||
}
|
||||
|
||||
impl<SendMsg: ExitType + Send, RecvMsg: Send + 'static> Drop for ThreadHandle<SendMsg, RecvMsg> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.channel.send.send(SendMsg::exit());
|
||||
self.join();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThreadChannel<SendMsg, RecvMsg> {
|
||||
send: Sender<SendMsg>,
|
||||
recv: Receiver<RecvMsg>,
|
||||
@@ -50,4 +61,7 @@ impl<SendMsg, RecvMsg> ThreadChannel<SendMsg, RecvMsg> {
|
||||
pub fn recv(&self) -> TryIter<RecvMsg> {
|
||||
self.recv.try_iter()
|
||||
}
|
||||
pub fn recv_wait(&self) -> RecvMsg {
|
||||
self.recv.recv().expect("OOOAAAAAAA")
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
use crate::client::render::voxel::VoxelColor;
|
||||
|
||||
use super::component::TrackedGrid;
|
||||
|
||||
pub struct Chunk {
|
||||
grid: TrackedGrid<VoxelColor>
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
pub mod component;
|
||||
pub mod generation;
|
||||
pub mod chunk;
|
||||
Reference in New Issue
Block a user