From b68707b92cd464f8cddb5a6b65fd5cb4c16ea3b6 Mon Sep 17 00:00:00 2001 From: shadow cat Date: Tue, 17 Sep 2024 20:29:10 -0400 Subject: [PATCH] chunk gen now tries nodes first, also messed around a lot w rendering --- Cargo.lock | 16 +- src/client/mod.rs | 23 +- src/client/render/mod.rs | 3 +- src/client/render/voxel/ray_oct/mod.rs | 6 +- .../render/voxel/ray_oct/shader/compute.wgsl | 432 ++++++++---------- ...te_transition.wgsl => compute_struct.wgsl} | 219 ++++++--- .../voxel/ray_oct/shader/compute_working.wgsl | 131 ++++-- src/common/component/chunk/mod.rs | 2 +- src/server/chunk/load.rs | 118 +---- src/server/generation/mod.rs | 145 ++++++ src/server/mod.rs | 1 + src/util/mod.rs | 1 + src/util/oct_tree.rs | 68 +-- src/util/timer.rs | 59 +++ 14 files changed, 709 insertions(+), 515 deletions(-) rename src/client/render/voxel/ray_oct/shader/{compute_transition.wgsl => compute_struct.wgsl} (52%) create mode 100644 src/server/generation/mod.rs create mode 100644 src/util/timer.rs diff --git a/Cargo.lock b/Cargo.lock index efdaf09..3c6b5e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -84,9 +84,9 @@ dependencies = [ [[package]] name = "arrayref" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" @@ -405,9 +405,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.18" +version = "1.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ac837cdb5cb22e10a256099b4fc502b1dfe560cb282963a974d7abd80e476" +checksum = "2d74707dde2ba56f86ae90effb3b43ddd369504387e718014de010cec7959800" dependencies = [ "jobserver", "libc", @@ -1029,9 +1029,9 @@ checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" dependencies = [ "libc", ] @@ -2113,9 +2113,9 @@ checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" [[package]] name = "unicode-width" diff --git a/src/client/mod.rs b/src/client/mod.rs index 69ab5cb..b669d6d 100644 --- a/src/client/mod.rs +++ b/src/client/mod.rs @@ -17,8 +17,8 @@ pub use state::*; use system::render::add_grid; use crate::{ - server::Server, common::{ClientMessage, ServerHandle, ServerMessage}, + server::Server, }; use self::{input::Input, render::Renderer, ClientState}; @@ -46,8 +46,9 @@ pub struct Client<'a> { server: ServerHandle, server_id_map: HashMap, systems: ClientSystems, - target: Instant, + frame_target: Instant, frame_time: Duration, + second_target: Instant, the_thing: bool, } @@ -91,8 +92,9 @@ impl Client<'_> { world, server, server_id_map: HashMap::new(), - target: Instant::now(), + frame_target: Instant::now(), frame_time: FRAME_TIME, + second_target: Instant::now(), the_thing: false, } } @@ -124,8 +126,8 @@ impl Client<'_> { } } - if now >= self.target { - self.target += self.frame_time; + if now >= self.frame_target { + self.frame_target += self.frame_time; let mut commands = std::mem::take(&mut self.render_commands); let world_cmds = std::mem::take(&mut self.world.resource_mut::().0); commands.extend(world_cmds); @@ -133,6 +135,17 @@ impl Client<'_> { self.renderer.draw(); } + if now >= self.second_target { + self.second_target += Duration::from_secs(1); + // let timer = self.renderer.timer(); + // println!( + // "avg: {:4?}; max: {:4?}; fps: {:4?}", + // timer.avg(), + // timer.max(), + // timer.per_sec(), + // ); + } + if self.exit { event_loop.exit(); } diff --git a/src/client/render/mod.rs b/src/client/render/mod.rs index 446048c..16381be 100644 --- a/src/client/render/mod.rs +++ b/src/client/render/mod.rs @@ -4,7 +4,7 @@ pub mod voxel; pub use command::*; use super::camera::Camera; -use crate::client::rsc::CLEAR_COLOR; +use crate::{client::rsc::CLEAR_COLOR, util::timer::Timer}; use nalgebra::Vector2; use util::DepthTexture; use voxel::VoxelPipeline; @@ -114,6 +114,7 @@ impl<'a> Renderer<'a> { let view = output .texture .create_view(&wgpu::TextureViewDescriptor::default()); + let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor { label: None, timestamp_writes: None, diff --git a/src/client/render/voxel/ray_oct/mod.rs b/src/client/render/voxel/ray_oct/mod.rs index f8d1f5d..c80f6b4 100644 --- a/src/client/render/voxel/ray_oct/mod.rs +++ b/src/client/render/voxel/ray_oct/mod.rs @@ -34,7 +34,7 @@ pub struct VoxelPipeline { } const RENDER_SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("shader/render.wgsl"); -const COMPUTE_SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("shader/compute.wgsl"); +const COMPUTE_SHADER: wgpu::ShaderModuleDescriptor<'_> = include_wgsl!("shader/compute_working.wgsl"); impl VoxelPipeline { pub fn new(device: &wgpu::Device, config: &wgpu::SurfaceConfiguration) -> Self { @@ -82,7 +82,7 @@ impl VoxelPipeline { pub fn update_shader(&mut self, device: &wgpu::Device) { let Ok(shader) = std::fs::read_to_string( - env!("CARGO_MANIFEST_DIR").to_owned() + "/src/client/render/voxel/ray_oct/shader/compute.wgsl", + env!("CARGO_MANIFEST_DIR").to_owned() + "/src/client/render/voxel/ray_oct/shader/compute_working.wgsl", ) else { println!("Failed to reload shader!"); return; @@ -198,7 +198,7 @@ impl VoxelPipeline { zoom: camera.scale, transform, }; - self.layout.view.update(device, encoder, belt, data) + self.layout.view.update(device, encoder, belt, data); } pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { diff --git a/src/client/render/voxel/ray_oct/shader/compute.wgsl b/src/client/render/voxel/ray_oct/shader/compute.wgsl index 7bc8adc..6c217ba 100644 --- a/src/client/render/voxel/ray_oct/shader/compute.wgsl +++ b/src/client/render/voxel/ray_oct/shader/compute.wgsl @@ -42,34 +42,7 @@ fn main(@builtin(global_invocation_id) cell: vec3) { let pos = view.transform * vec4(pixel_pos, 1.0); let dir = view.transform * vec4(normalize(pixel_pos), 0.0); - let start = start_ray(pos, dir); - var color = vec4(0.0); - if start.hit { - var res = ray_next(start.ray, LEAF_BIT); - var safe = 0; - var normals = start.normals; - while res.data != 0 { - safe += 1; - if safe > 100 {break;} - let data = res.data & LEAF_MASK; - let vcolor = get_color(data); - let diffuse = max(dot(global_lights[0].dir, normals[res.ray.axis]) + 0.1, 0.0); - let ambient = 0.2; - let lighting = max(diffuse, ambient); - let new_color = min(vcolor.xyz * lighting, vec3(1.0)); - color += vec4(new_color.xyz * vcolor.a, vcolor.a) * (1.0 - color.a); - if color.a > FULL_ALPHA { break; } - let old_t = res.ray.t; - res = ray_next(res.ray, res.data); - let dist = res.ray.t - old_t; - if data == 3 { - let a = min(dist / 128.0, 1.0); - color += vec4(vec3(0.0) * a, a) * (1.0 - color.a); - } - } - // color = vec4(dir.xyz * res.ray.t / 2048.0, 1.0); - } - // var color = trace_full(pos, dir); + var color = trace_full(pos, dir); let light_mult = clamp((-dot(dir.xyz, global_lights[0].dir) - 0.99) * 200.0, 0.0, 1.0); let sun_color = light_mult * vec3(1.0, 1.0, 1.0); let sky_bg = vec3(0.3, 0.6, 1.0); @@ -89,210 +62,7 @@ const EPSILON = 0.00000000001; const MAX_ITERS = 2000; // NOTE: CANNOT GO HIGHER THAN 23 due to how floating point // numbers are stored and the bit manipulation used -const MAX_SCALE: u32 = 12; - -struct Ray { - t: f32, - vox_pos: vec3, - t_inc: vec3, - scale: u32, - min_adj: vec3, - child: u32, - axis: u32, - node_start: u32, - group_offset: u32, - inv_dir_bits: u32, - parents: array, -}; - -struct RayResult { - ray: Ray, - data: u32, -} - -struct RayStart { - hit: bool, - ray: Ray, - normals: mat3x3, -} - -fn start_ray(pos_view: vec4, dir_view: vec4) -> RayStart { - let gi = 0; - let group = voxel_groups[gi]; - if group.scale == 0 { - return RayStart(false, Ray(), mat3x3()); - } - let dimensions = vec3(1u << group.scale); - let dim_f = vec3(dimensions); - let dim_i = vec3(dimensions); - - // transform so that group is at 0,0 - let pos = (group.transform_inv * pos_view).xyz; - var dir = (group.transform_inv * dir_view).xyz; - if dir.x == 0 {dir.x = EPSILON;} - if dir.y == 0 {dir.y = EPSILON;} - if dir.z == 0 {dir.z = EPSILON;} - - let dir_if = sign(dir); - let dir_uf = max(dir_if, vec3(0.0)); - - - - // calculate normals - var normals = mat3x3( - (group.transform * vec4(dir_if.x, 0.0, 0.0, 0.0)).xyz, - (group.transform * vec4(0.0, dir_if.y, 0.0, 0.0)).xyz, - (group.transform * vec4(0.0, 0.0, dir_if.z, 0.0)).xyz, - ); - var axis = 0u; - - // find where ray intersects with group - let pos_min = (vec3(1.0) - dir_uf) * dim_f; - // time of intersection; x = td + p, solve for t - var t_min = (pos_min - pos) / dir; - if outside3f(pos, ZERO3F, dim_f) { - // points of intersection - let px = pos + t_min.x * dir; - let py = pos + t_min.y * dir; - let pz = pos + t_min.z * dir; - - // check if point is in bounds - let hit = vec3( - inside2f(px.yz, ZERO2F, dim_f.yz), - inside2f(py.xz, ZERO2F, dim_f.xz), - inside2f(pz.xy, ZERO2F, dim_f.xy), - ) && (t_min > ZERO3F); - if !any(hit) { - return RayStart(false, Ray(), mat3x3()); - } - axis = select(select(2u, 1u, hit.y), 0u, hit.x); - } - t_min *= f32(1u << (MAX_SCALE - group.scale)); - // time to move 1 unit in each direction - let full = f32(1u << MAX_SCALE); - let t_inc = abs(1.0 / dir) * full; - let t_offset = max(max(t_min.x, t_min.y), t_min.z); - let t = max(0.0, t_offset); - - let dir_i = vec3(dir_if); - let dir_u = vec3((dir_i + vec3(1)) / 2); - let dir_bits = vec_to_dir(dir_u); - let inv_dir_bits = 7 - dir_bits; - - let node_start = 1u; - let scale = MAX_SCALE - 1; - let scale_exp2 = 0.5; - let parents = array(); - - var child = 0u; - var vox_pos = vec3(1.0); - let t_center = t_min + scale_exp2 * t_inc; - if t > t_center.x { vox_pos.x = 1.5; child |= 4u; } - if t > t_center.y { vox_pos.y = 1.5; child |= 2u; } - if t > t_center.z { vox_pos.z = 1.5; child |= 1u; } - let min_adj = t_min - t_inc; - - return RayStart( - true, - Ray( - t, - vox_pos, - t_inc, - scale, - min_adj, - child, - axis, - node_start, - group.offset, - inv_dir_bits, - parents, - ), - normals - ); -} - -fn ray_next(ray: Ray, skip: u32) -> RayResult { - let group_offset = ray.group_offset; - let t_inc = ray.t_inc; - let min_adj = ray.min_adj; - let inv_dir_bits = ray.inv_dir_bits; - var scale = ray.scale; - var scale_exp2 = bitcast((scale + 127 - MAX_SCALE) << 23); - var vox_pos = ray.vox_pos; - var t = ray.t; - var node_start = ray.node_start; - var child = ray.child; - var parents = ray.parents; - var axis: u32; - var data = 0u; - loop { - let t_corner = vox_pos * t_inc + min_adj; - let node = voxels[group_offset + node_start + (child ^ inv_dir_bits)]; - if node >= LEAF_BIT { - if node != skip { - data = node; - break; - } - - // move to next time point and determine which axis to move along - let t_next = t_corner + scale_exp2 * t_inc; - t = min(min(t_next.x, t_next.y), t_next.z); - axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z); - let move_dir = 4u >> axis; - - // check if need to pop stack - if (child & move_dir) > 0 { - // calculate new scale; first differing bit after adding - let axis_pos = vox_pos[axis]; - // AWARE - let differing = bitcast(axis_pos) ^ bitcast(axis_pos + scale_exp2); - scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); - scale_exp2 = bitcast((scale + 127 - MAX_SCALE) << 23); - if scale >= MAX_SCALE { break; } - - // restore & recalculate parent - let parent_info = parents[scale]; - node_start = parent_info >> 3; - child = parent_info & 7; - let scale_vec = vec3(scale + 23 - MAX_SCALE); - // remove bits lower than current scale - vox_pos = bitcast>((bitcast>(vox_pos) >> scale_vec) << scale_vec); - } - // move to next child and voxel position - child += move_dir; - vox_pos[axis] += scale_exp2; - } else { - // push current node to stack - parents[scale] = (node_start << 3) + child; - scale -= 1u; - - // calculate child node vars - scale_exp2 *= 0.5; - child = 0u; - let t_center = t_corner + scale_exp2 * t_inc; - if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; } - if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; } - if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; } - node_start += 8 + node; - } - } - return RayResult( - Ray( - t, - vox_pos, - t_inc, - scale, - min_adj, - child, - axis, - node_start, - group_offset, - inv_dir_bits, - parents, - ), - data - ); -} +const MAX_SCALE: u32 = 10; fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { let gi = 0; @@ -326,14 +96,13 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { // find where ray intersects with group let pos_min = (vec3(1.0) - dir_uf) * dim_f; - var pos = pos_start; // time of intersection; x = td + p, solve for t - var t_min = (pos_min - pos) / dir; - if outside3f(pos, ZERO3F, dim_f) { + var t_min = (pos_min - pos_start) / dir; + if outside3f(pos_start, ZERO3F, dim_f) { // points of intersection - let px = pos + t_min.x * dir; - let py = pos + t_min.y * dir; - let pz = pos + t_min.z * dir; + let px = pos_start + t_min.x * dir; + let py = pos_start + t_min.y * dir; + let pz = pos_start + t_min.z * dir; // check if point is in bounds let hit = vec3( @@ -344,10 +113,10 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { if !any(hit) { return vec4(0.0); } - pos = select(select(pz, py, hit.y), px, hit.x); axis = select(select(2u, 1u, hit.y), 0u, hit.x); } - t_min *= f32(1u << (MAX_SCALE - group.scale)); + let t_mult = f32(1u << (MAX_SCALE - group.scale)); + t_min *= t_mult; // time to move 1 unit in each direction let full = f32(1u << MAX_SCALE); let inc_t = abs(1.0 / dir) * full; @@ -362,6 +131,7 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { var node_start = 1u; var scale = MAX_SCALE - 1; var scale_exp2 = 0.5; + var skip = LEAF_BIT; var color = vec4(0.0); var parents = array(); @@ -382,13 +152,25 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { let t_corner = vox_pos * inc_t + min_adj; let node = voxels[group.offset + node_start + (child ^ inv_dir_bits)]; if node >= LEAF_BIT { - if node != LEAF_BIT { - let vcolor = get_color(node & LEAF_MASK); - let diffuse = max(dot(global_lights[0].dir, normals[axis]) + 0.1, 0.0); + if node != skip { + skip = node; + let normal = normals[axis]; + let sun_dir = global_lights[0].dir; + let new_pos = pos_view + dir_view * t / t_mult - vec4(normals[axis] * 0.001, 0.0); + + let light = trace_light(new_pos, vec4(-sun_dir, 0.0)); + let diffuse = max(dot(sun_dir, normal) + 0.1, 0.0); let ambient = 0.2; - let lighting = max(diffuse, ambient); - let new_color = min(vcolor.xyz * lighting, vec3(1.0)); - color += vec4(new_color.xyz * vcolor.a, vcolor.a) * (1.0 - color.a); + let specular = (exp(max( + -(dot(reflect(dir_view.xyz, normal), sun_dir) + 0.90) * 4.0, 0.0 + )) - 1.0) * light; + let lighting = max(diffuse * light.a, ambient); + + let vcolor = get_color(node & LEAF_MASK); + let new_rgb = min(vcolor.xyz * lighting + specular.xyz + light.xyz * vcolor.xyz, vec3(1.0)); + let new_a = min(vcolor.a + specular.a, 1.0); + let new_color = vec4(new_rgb, new_a); + color += vec4(new_color.xyz * new_color.a, new_color.a) * (1.0 - color.a); if color.a > FULL_ALPHA { break; } } @@ -438,6 +220,164 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { return color; } +fn trace_light(pos_view: vec4, dir_view: vec4) -> vec4 { + let gi = 0; + let group = voxel_groups[gi]; + if group.scale == 0 { + return vec4(0.0); + } + let dimensions = vec3(1u << group.scale); + let dim_f = vec3(dimensions); + let dim_i = vec3(dimensions); + + // transform so that group is at 0,0 + let pos_start = (group.transform_inv * pos_view).xyz; + var dir = (group.transform_inv * dir_view).xyz; + if dir.x == 0 {dir.x = EPSILON;} + if dir.y == 0 {dir.y = EPSILON;} + if dir.z == 0 {dir.z = EPSILON;} + + let dir_if = sign(dir); + let dir_uf = max(dir_if, vec3(0.0)); + + + + // calculate normals + var normals = mat3x3( + (group.transform * vec4(dir_if.x, 0.0, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, dir_if.y, 0.0, 0.0)).xyz, + (group.transform * vec4(0.0, 0.0, dir_if.z, 0.0)).xyz, + ); + var axis = 0u; + + // find where ray intersects with group + let pos_min = (vec3(1.0) - dir_uf) * dim_f; + // time of intersection; x = td + p, solve for t + var t_min = (pos_min - pos_start) / dir; + if outside3f(pos_start, ZERO3F, dim_f) { + // points of intersection + let px = pos_start + t_min.x * dir; + let py = pos_start + t_min.y * dir; + let pz = pos_start + t_min.z * dir; + + // check if point is in bounds + let hit = vec3( + inside2f(px.yz, ZERO2F, dim_f.yz), + inside2f(py.xz, ZERO2F, dim_f.xz), + inside2f(pz.xy, ZERO2F, dim_f.xy), + ) && (t_min > ZERO3F); + if !any(hit) { + return vec4(0.0); + } + axis = select(select(2u, 1u, hit.y), 0u, hit.x); + } + let t_mult = f32(1u << (MAX_SCALE - group.scale)); + t_min *= t_mult; + // time to move 1 unit in each direction + let full = f32(1u << MAX_SCALE); + let inc_t = abs(1.0 / dir) * full; + let t_offset = max(max(t_min.x, t_min.y), t_min.z); + var t = max(0.0, t_offset); + var old_t = t; + + let dir_i = vec3(dir_if); + let dir_u = vec3((dir_i + vec3(1)) / 2); + let dir_bits = vec_to_dir(dir_u); + let inv_dir_bits = 7 - dir_bits; + + var node_start = 1u; + var scale = MAX_SCALE - 1; + var scale_exp2 = 0.5; + var mask = vec4(0.0); + var skip = LEAF_BIT; + var parents = array(); + + var child = 0u; + var vox_pos = vec3(1.0); + let t_center = t_min + scale_exp2 * inc_t; + if t > t_center.x { vox_pos.x = 1.5; child |= 4u; } + if t > t_center.y { vox_pos.y = 1.5; child |= 2u; } + if t > t_center.z { vox_pos.z = 1.5; child |= 1u; } + let min_adj = t_min - inc_t; + var data = 0u; + + var iters = 0; + loop { + if iters == MAX_ITERS { + return vec4(1.0, 0.0, 1.0, 1.0); + } + iters += 1; + let t_corner = vox_pos * inc_t + min_adj; + let node = voxels[group.offset + node_start + (child ^ inv_dir_bits)]; + if node >= LEAF_BIT { + if node != skip { + skip = node; + if data == 3 { + let dist = (t - old_t) / t_mult; + let vcolor = vec4(vec3(0.0), min(dist / 12.0, 1.0)); + mask += vec4(vcolor.xyz * vcolor.a, vcolor.a) * (1.0 - mask.a); + } + data = node & LEAF_MASK; + if data != 3 && data != 0 { + let vcolor = get_color(data); + mask += vec4(vcolor.xyz * vcolor.a, vcolor.a) * (1.0 - mask.a); + } + old_t = t; + if mask.a > FULL_ALPHA { break; } + } + + // move to next time point and determine which axis to move along + let t_next = t_corner + scale_exp2 * inc_t; + t = min(min(t_next.x, t_next.y), t_next.z); + axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z); + let move_dir = 4u >> axis; + + // check if need to pop stack + if (child & move_dir) > 0 { + // calculate new scale; first differing bit after adding + let axis_pos = vox_pos[axis]; + // AWARE + let differing = bitcast(axis_pos) ^ bitcast(axis_pos + scale_exp2); + scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); + scale_exp2 = bitcast((scale + 127 - MAX_SCALE) << 23); + if scale >= MAX_SCALE { break; } + + // restore & recalculate parent + let parent_info = parents[scale]; + node_start = parent_info >> 3; + child = parent_info & 7; + let scale_vec = vec3(scale + 23 - MAX_SCALE); + // remove bits lower than current scale + vox_pos = bitcast>((bitcast>(vox_pos) >> scale_vec) << scale_vec); + } + // move to next child and voxel position + child += move_dir; + vox_pos[axis] += scale_exp2; + } else { + // push current node to stack + parents[scale] = (node_start << 3) + child; + scale -= 1u; + + // calculate child node vars + scale_exp2 *= 0.5; + child = 0u; + let t_center = t_corner + scale_exp2 * inc_t; + if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; } + if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; } + if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; } + node_start += 8 + node; + } + } + if data == 3 { + let dist = (t - old_t) / t_mult; + let vcolor = vec4(vec3(0.0), min(dist / 12.0, 1.0)); + mask += vec4(vcolor.xyz * vcolor.a, vcolor.a) * (1.0 - mask.a); + } + mask.a = 1.0 - mask.a; + mask = vec4(mask.a * mask.xyz, mask.a); + return mask; +} + fn dir_to_vec(bits: u32) -> vec3 { return vec3(bits >> 2, (bits & 2) >> 1, bits & 1); } diff --git a/src/client/render/voxel/ray_oct/shader/compute_transition.wgsl b/src/client/render/voxel/ray_oct/shader/compute_struct.wgsl similarity index 52% rename from src/client/render/voxel/ray_oct/shader/compute_transition.wgsl rename to src/client/render/voxel/ray_oct/shader/compute_struct.wgsl index 99a65ab..a56d4e8 100644 --- a/src/client/render/voxel/ray_oct/shader/compute_transition.wgsl +++ b/src/client/render/voxel/ray_oct/shader/compute_struct.wgsl @@ -42,7 +42,56 @@ fn main(@builtin(global_invocation_id) cell: vec3) { let pos = view.transform * vec4(pixel_pos, 1.0); let dir = view.transform * vec4(normalize(pixel_pos), 0.0); - var color = trace_full(pos, dir); + let start = start_ray(pos, dir); + var color = vec4(0.0); + let ambient = 0.2; + + if start.hit { + var res = ray_next(start.ray, LEAF_BIT); + var normals = start.normals; + let specular = (exp(max( + -(dot(reflect(dir.xyz, normals[res.ray.axis]), global_lights[0].dir) + 0.90) * 4.0, 0.0 + )) - 1.0); + while res.data != 0 { + let data = res.data & LEAF_MASK; + let vcolor = get_color(data); + let diffuse = max(dot(global_lights[0].dir, normals[res.ray.axis]) + 0.1, 0.0); + let light = max(diffuse, ambient); + let new_color = min(vcolor.xyz * light, vec3(1.0)); + color += vec4(new_color.xyz * vcolor.a, vcolor.a) * (1.0 - color.a); + if color.a > FULL_ALPHA { break; } + let old_t = res.ray.t; + res = ray_next(res.ray, res.data); + if data == 3 { + let dist = (res.ray.t - old_t) / start.t_mult; + let a = min(dist / 12.0, 1.0); + color += vec4(vec3(0.0) * a, a) * (1.0 - color.a); + } + } + if color.a != 0 { + let pos = pos + dir * res.ray.t / start.t_mult - vec4(normals[res.ray.axis] * 0.001, 0.0); + let dir = vec4(-global_lights[0].dir, 0.0); + let start = start_ray(pos, dir); + res = ray_next(start.ray, LEAF_BIT); + var light = 1.0; + while res.data != 0 { + let data = res.data & LEAF_MASK; + let vcolor = get_color(data); + if data != 3 { light -= vcolor.a * light; } + if light <= 0 { break; } + let old_t = res.ray.t; + res = ray_next(res.ray, res.data); + if data == 3 { + let dist = (res.ray.t - old_t) / start.t_mult; + let a = min(dist / 12.0, 1.0); + light -= a; + } + } + color = vec4(color.xyz * max(light, ambient), color.a) + vec4(vec3(specular * light), 0.0); + } + // color = vec4(pos.xyz / 128.0, 1.0); + } + // var color = trace_full(pos, dir); let light_mult = clamp((-dot(dir.xyz, global_lights[0].dir) - 0.99) * 200.0, 0.0, 1.0); let sun_color = light_mult * vec3(1.0, 1.0, 1.0); let sky_bg = vec3(0.3, 0.6, 1.0); @@ -52,25 +101,56 @@ fn main(@builtin(global_invocation_id) cell: vec3) { textureStore(output, cell.xy, color); } +const LEAF_BIT = 1u << 31u; +const LEAF_MASK = ~LEAF_BIT; + const ZERO3F = vec3(0.0); const ZERO2F = vec2(0.0); const FULL_ALPHA = 0.999; const EPSILON = 0.00000000001; -const MAX_ITERS = 1000; +const MAX_ITERS = 2000; +// NOTE: CANNOT GO HIGHER THAN 23 due to how floating point +// numbers are stored and the bit manipulation used const MAX_SCALE: u32 = 10; -fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { +struct Ray { + t: f32, + vox_pos: vec3, + t_inc: vec3, + scale: u32, + min_adj: vec3, + child: u32, + axis: u32, + node_start: u32, + group_offset: u32, + inv_dir_bits: u32, + parents: array, +}; + +struct RayResult { + ray: Ray, + data: u32, +} + +struct RayStart { + hit: bool, + ray: Ray, + normals: mat3x3, + t_mult: f32, +} + +fn start_ray(pos_view: vec4, dir_view: vec4) -> RayStart { let gi = 0; let group = voxel_groups[gi]; if group.scale == 0 { - return vec4(0.0); + return RayStart(); } let dimensions = vec3(1u << group.scale); let dim_f = vec3(dimensions); let dim_i = vec3(dimensions); // transform so that group is at 0,0 - let pos_start = (group.transform_inv * pos_view).xyz; + let pos = (group.transform_inv * pos_view).xyz; var dir = (group.transform_inv * dir_view).xyz; if dir.x == 0 {dir.x = EPSILON;} if dir.y == 0 {dir.y = EPSILON;} @@ -91,7 +171,6 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { // find where ray intersects with group let pos_min = (vec3(1.0) - dir_uf) * dim_f; - var pos = pos_start; // time of intersection; x = td + p, solve for t var t_min = (pos_min - pos) / dir; if outside3f(pos, ZERO3F, dim_f) { @@ -107,57 +186,81 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { inside2f(pz.xy, ZERO2F, dim_f.xy), ) && (t_min > ZERO3F); if !any(hit) { - return vec4(0.0); + return RayStart(); } - pos = select(select(pz, py, hit.y), px, hit.x); axis = select(select(2u, 1u, hit.y), 0u, hit.x); } - t_min *= f32(1u << (MAX_SCALE - group.scale)); + let t_mult = f32(1u << (MAX_SCALE - group.scale)); + t_min *= t_mult; // time to move 1 unit in each direction let full = f32(1u << MAX_SCALE); - let inc_t = abs(1.0 / dir) * full; + let t_inc = abs(1.0 / dir) * full; let t_offset = max(max(t_min.x, t_min.y), t_min.z); - var t = max(0.0, t_offset); + let t = max(0.0, t_offset); let dir_i = vec3(dir_if); let dir_u = vec3((dir_i + vec3(1)) / 2); let dir_bits = vec_to_dir(dir_u); let inv_dir_bits = 7 - dir_bits; - var node_start = 1u; - var scale = MAX_SCALE - 1; - var scale_exp2 = 0.5; - var color = vec4(0.0); - var parents = array(); + let node_start = 1u; + let scale = MAX_SCALE - 1; + let scale_exp2 = 0.5; + let parents = array(); var child = 0u; - var vox_pos = vec3(0.0); - let t_center = t_min + scale_exp2 * inc_t; - if t > t_center.x { vox_pos.x = 0.5; child |= 4u; } - if t > t_center.y { vox_pos.y = 0.5; child |= 2u; } - if t > t_center.z { vox_pos.z = 0.5; child |= 1u; } + var vox_pos = vec3(1.0); + let t_center = t_min + scale_exp2 * t_inc; + if t > t_center.x { vox_pos.x = 1.5; child |= 4u; } + if t > t_center.y { vox_pos.y = 1.5; child |= 2u; } + if t > t_center.z { vox_pos.z = 1.5; child |= 1u; } + let min_adj = t_min - t_inc; - var iters = 0; + return RayStart( + true, + Ray( + t, + vox_pos, + t_inc, + scale, + min_adj, + child, + axis, + node_start, + group.offset, + inv_dir_bits, + parents, + ), + normals, + t_mult, + ); +} + +fn ray_next(ray: Ray, skip: u32) -> RayResult { + let group_offset = ray.group_offset; + let t_inc = ray.t_inc; + let min_adj = ray.min_adj; + let inv_dir_bits = ray.inv_dir_bits; + var scale = ray.scale; + var scale_exp2 = bitcast((scale + 127 - MAX_SCALE) << 23); + var vox_pos = ray.vox_pos; + var t = ray.t; + var node_start = ray.node_start; + var child = ray.child; + var parents = ray.parents; + var axis: u32; + var data = 0u; loop { - if iters == MAX_ITERS { - return vec4(1.0, 0.0, 1.0, 1.0); - } - iters += 1; - let t_corner = vox_pos * inc_t + t_min; - let node = voxels[group.offset + node_start + (child ^ inv_dir_bits)]; + let t_corner = vox_pos * t_inc + min_adj; + let node = voxels[group_offset + node_start + (child ^ inv_dir_bits)]; if node >= LEAF_BIT { - if node != LEAF_BIT { - let vcolor = get_color(node & LEAF_MASK); - let diffuse = max(dot(global_lights[0].dir, normals[axis]) + 0.1, 0.0); - let ambient = 0.2; - let lighting = max(diffuse, ambient); - let new_color = min(vcolor.xyz * lighting, vec3(1.0)); - color += vec4(new_color.xyz * vcolor.a, vcolor.a) * (1.0 - color.a); - if color.a > FULL_ALPHA { break; } + if node != skip { + data = node; + break; } // move to next time point and determine which axis to move along - let t_next = t_corner + scale_exp2 * inc_t; + let t_next = t_corner + scale_exp2 * t_inc; t = min(min(t_next.x, t_next.y), t_next.z); axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z); let move_dir = 4u >> axis; @@ -165,22 +268,23 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { // check if need to pop stack if (child & move_dir) > 0 { // calculate new scale; first differing bit after adding - let axis_pos = u32(vox_pos[axis] * full); - let differing = axis_pos ^ (axis_pos + u32(scale_exp2 * full)); - scale = u32(firstLeadingBit(differing)); - if scale == MAX_SCALE { break; } + let axis_pos = vox_pos[axis]; + // AWARE + let differing = bitcast(axis_pos) ^ bitcast(axis_pos + scale_exp2); + scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); + scale_exp2 = bitcast((scale + 127 - MAX_SCALE) << 23); + if scale >= MAX_SCALE { break; } // restore & recalculate parent let parent_info = parents[scale]; node_start = parent_info >> 3; child = parent_info & 7; - let scale_vec = vec3(scale); - // remove lower scale bits - vox_pos = vec3((vec3(vox_pos * full) >> scale_vec) << scale_vec) / full; - scale_exp2 = 1.0 / f32(1u << (MAX_SCALE - scale)); + let scale_vec = vec3(scale + 23 - MAX_SCALE); + // remove bits lower than current scale + vox_pos = bitcast>((bitcast>(vox_pos) >> scale_vec) << scale_vec); } // move to next child and voxel position - child ^= move_dir; + child += move_dir; vox_pos[axis] += scale_exp2; } else { // push current node to stack @@ -190,20 +294,31 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { // calculate child node vars scale_exp2 *= 0.5; child = 0u; - let t_center = t_corner + scale_exp2 * inc_t; + let t_center = t_corner + scale_exp2 * t_inc; if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; } if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; } if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; } node_start += 8 + node; } } - // return vec4(f32(iters) / f32(MAX_ITERS), 0.0, 0.0, 1.0); - return color; + return RayResult( + Ray( + t, + vox_pos, + t_inc, + scale, + min_adj, + child, + axis, + node_start, + group_offset, + inv_dir_bits, + parents, + ), + data + ); } -const LEAF_BIT = 1u << 31u; -const LEAF_MASK = ~LEAF_BIT; - fn dir_to_vec(bits: u32) -> vec3 { return vec3(bits >> 2, (bits & 2) >> 1, bits & 1); } diff --git a/src/client/render/voxel/ray_oct/shader/compute_working.wgsl b/src/client/render/voxel/ray_oct/shader/compute_working.wgsl index c7b176d..7e06d77 100644 --- a/src/client/render/voxel/ray_oct/shader/compute_working.wgsl +++ b/src/client/render/voxel/ray_oct/shader/compute_working.wgsl @@ -52,11 +52,16 @@ fn main(@builtin(global_invocation_id) cell: vec3) { textureStore(output, cell.xy, color); } +const LEAF_BIT = 1u << 31u; +const LEAF_MASK = ~LEAF_BIT; + const ZERO3F = vec3(0.0); const ZERO2F = vec2(0.0); const FULL_ALPHA = 0.999; const EPSILON = 0.00000000001; -const MAX_ITERS = 1000; +const MAX_ITERS = 2000; +// NOTE: CANNOT GO HIGHER THAN 23 due to how floating point +// numbers are stored and the bit manipulation used const MAX_SCALE: u32 = 10; fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { @@ -91,16 +96,13 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { // find where ray intersects with group let pos_min = (vec3(1.0) - dir_uf) * dim_f; - let pos_max = dir_uf * dim_f; - var pos = pos_start; // time of intersection; x = td + p, solve for t - let t_min = (pos_min - pos) / dir; - let t_max = (pos_max - pos) / dir; - if outside3f(pos, ZERO3F, dim_f) { + var t_min = (pos_min - pos_start) / dir; + if outside3f(pos_start, ZERO3F, dim_f) { // points of intersection - let px = pos + t_min.x * dir; - let py = pos + t_min.y * dir; - let pz = pos + t_min.z * dir; + let px = pos_start + t_min.x * dir; + let py = pos_start + t_min.y * dir; + let pz = pos_start + t_min.z * dir; // check if point is in bounds let hit = vec3( @@ -111,29 +113,36 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { if !any(hit) { return vec4(0.0); } - pos = select(select(pz, py, hit.y), px, hit.x); axis = select(select(2u, 1u, hit.y), 0u, hit.x); } + let t_mult =f32(1u << (MAX_SCALE - group.scale)); + t_min *= t_mult; // time to move 1 unit in each direction - let inc_t = abs(1.0 / dir); + let full = f32(1u << MAX_SCALE); + let inc_t = abs(1.0 / dir) * full; let t_offset = max(max(t_min.x, t_min.y), t_min.z); var t = max(0.0, t_offset); let dir_i = vec3(dir_if); - let dir_u = vec3((dir_i + vec3(1)) / 2); + let dir_u = vec3(dir_uf); let dir_bits = vec_to_dir(dir_u); let inv_dir_bits = 7 - dir_bits; var node_start = 1u; - var scale = group.scale - 1; - var half_t_span = f32(1u << scale) * inc_t; - var t_center = t_min + half_t_span; + var scale = MAX_SCALE - 1; + var scale_exp2 = 0.5; var color = vec4(0.0); var parents = array(); + var prev = LEAF_BIT; + var old_t = t; - var child = (u32(t > t_center.x) << 2) + (u32(t > t_center.y) << 1) + u32(t > t_center.z); - var child_pos = dir_to_vec(child); - var vox_pos = child_pos * (1u << scale); + var child = 0u; + var vox_pos = vec3(1.0); + let t_center = t_min + scale_exp2 * inc_t; + if t > t_center.x { vox_pos.x = 1.5; child |= 4u; } + if t > t_center.y { vox_pos.y = 1.5; child |= 2u; } + if t > t_center.z { vox_pos.z = 1.5; child |= 1u; } + let min_adj = t_min - inc_t; var iters = 0; loop { @@ -141,20 +150,34 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { return vec4(1.0, 0.0, 1.0, 1.0); } iters += 1; + let t_corner = vox_pos * inc_t + min_adj; let node = voxels[group.offset + node_start + (child ^ inv_dir_bits)]; if node >= LEAF_BIT { - if node != LEAF_BIT { - let vcolor = get_color(node & LEAF_MASK); - let diffuse = max(dot(global_lights[0].dir, normals[axis]) + 0.1, 0.0); - let ambient = 0.2; - let lighting = max(diffuse, ambient); - let new_color = min(vcolor.xyz * lighting, vec3(1.0)); - color += vec4(new_color.xyz * vcolor.a, vcolor.a) * (1.0 - color.a); - if color.a > FULL_ALPHA { break; } + if node != prev { + if node != LEAF_BIT { + let dist = (t - old_t) / t_mult; + old_t = t; + let filt = min(dist / 64.0, 1.0); + if prev == LEAF_BIT + 3 { + color.a += filt * (1.0 - color.a); + if color.a > FULL_ALPHA { break; } + } + let pos = (vox_pos - 1.5) * (dir_if) + 0.5 - scale_exp2 * (1.0 - dir_uf); + // let pos = t / t_mult; + // if true {return vec4(pos, 1.0);} + let vcolor = get_color(node & LEAF_MASK, pos); + let diffuse = max(dot(global_lights[0].dir, normals[axis]) + 0.1, 0.0); + let ambient = 0.2; + let lighting = max(diffuse, ambient); + let new_color = min(vcolor.xyz * lighting, vec3(1.0)); + color += vec4(new_color.xyz * vcolor.a, vcolor.a) * (1.0 - color.a); + if color.a > FULL_ALPHA { break; } + } + prev = node; } // move to next time point and determine which axis to move along - let t_next = t_center + half_t_span * vec3(child_pos); + let t_next = t_corner + scale_exp2 * inc_t; t = min(min(t_next.x, t_next.y), t_next.z); axis = select(select(0u, 1u, t == t_next.y), 2u, t == t_next.z); let move_dir = 4u >> axis; @@ -163,45 +186,44 @@ fn trace_full(pos_view: vec4, dir_view: vec4) -> vec4 { if (child & move_dir) > 0 { // calculate new scale; first differing bit after adding let axis_pos = vox_pos[axis]; - let differing = axis_pos ^ (axis_pos + (1u << scale)); - scale = firstLeadingBit(differing); - if scale == group.scale { break; } + // AWARE + let differing = bitcast(axis_pos) ^ bitcast(axis_pos + scale_exp2); + scale = (bitcast(f32(differing)) >> 23) - 127 - (23 - MAX_SCALE); + scale_exp2 = bitcast((scale + 127 - MAX_SCALE) << 23); + if scale >= MAX_SCALE { break; } // restore & recalculate parent let parent_info = parents[scale]; node_start = parent_info >> 3; child = parent_info & 7; - let scale_vec = vec3(scale + 1); - vox_pos = (vox_pos >> scale_vec) << scale_vec; // remove lower scale bits - half_t_span = f32(1u << scale) * inc_t; - t_center = vec3(vox_pos) * inc_t + t_min + half_t_span; + let scale_vec = vec3(scale + 23 - MAX_SCALE); + // remove bits lower than current scale + vox_pos = bitcast>((bitcast>(vox_pos) >> scale_vec) << scale_vec); } // move to next child and voxel position - child ^= move_dir; - child_pos = dir_to_vec(child); - vox_pos |= child_pos << vec3(scale); - // vox_pos[axis] += (1u << scale); + child += move_dir; + vox_pos[axis] += scale_exp2; } else { // push current node to stack parents[scale] = (node_start << 3) + child; scale -= 1u; // calculate child node vars - half_t_span /= 2.0; - t_center += half_t_span * (vec3(child_pos * 2) - 1.0); - child_pos = vec3(vec3(t) > t_center); - child = (child_pos.x << 2) + (child_pos.y << 1) + child_pos.z; - vox_pos += child_pos * (1u << scale); + scale_exp2 *= 0.5; + child = 0u; + let t_center = t_corner + scale_exp2 * inc_t; + if t > t_center.x { vox_pos.x += scale_exp2; child |= 4u; } + if t > t_center.y { vox_pos.y += scale_exp2; child |= 2u; } + if t > t_center.z { vox_pos.z += scale_exp2; child |= 1u; } node_start += 8 + node; } } + // let fog = min(t / t_mult / 1000.0, 1.0); + // return vec4(color.xyz * (1.0 - fog) + vec3(fog), color.a * (1.0 - fog) + fog); // return vec4(f32(iters) / f32(MAX_ITERS), 0.0, 0.0, 1.0); return color; } -const LEAF_BIT = 1u << 31u; -const LEAF_MASK = ~LEAF_BIT; - fn dir_to_vec(bits: u32) -> vec3 { return vec3(bits >> 2, (bits & 2) >> 1, bits & 1); } @@ -210,19 +232,24 @@ fn vec_to_dir(vec: vec3) -> u32 { return vec.x * 4 + vec.y * 2 + vec.z * 1; } -fn get_color(id: u32) -> vec4 { +fn get_color(id: u32, pos: vec3) -> vec4 { + let random = random(pos); + let random2 = random(pos + vec3(0.0001)); switch id { case 0u: { return vec4(0.0); } case 1u: { - return vec4(0.5, 0.5, 0.5, 1.0); + let color = vec3(0.5, 0.5, 0.5 + random * 0.2) * (random2 * 0.4 + 0.8); + return vec4(color, 1.0); } case 2u: { - return vec4(0.5, 1.0, 0.5, 1.0); + let color = vec3(0.4 + random * 0.2, 0.9, 0.4 + random * 0.2) * (random2 * 0.2 + 0.9); + return vec4(color, 1.0); } case 3u: { - return vec4(0.5, 0.5, 1.0, 0.5); + let color = vec3(0.5, 0.5, 1.0) * (random2 * 0.2 + 0.8); + return vec4(color, 0.5); } default: { return vec4(1.0, 0.0, 0.0, 1.0); @@ -230,6 +257,10 @@ fn get_color(id: u32) -> vec4 { } } +fn random(pos: vec3) -> f32 { + return fract(sin(dot(pos,vec3(12.9898,78.233,25.1279)))*43758.5453123); +} + fn outside3f(v: vec3, low: vec3, high: vec3) -> bool { return any(v < low) || any(v > high); } diff --git a/src/common/component/chunk/mod.rs b/src/common/component/chunk/mod.rs index 3f63f62..2db49b8 100644 --- a/src/common/component/chunk/mod.rs +++ b/src/common/component/chunk/mod.rs @@ -8,7 +8,7 @@ use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{bundle::Bundle, component::Component, entity::Entity, system::Resource}; use nalgebra::Vector3; -pub const SCALE: u32 = 8; +pub const SCALE: u32 = 10; pub const SIDE_LENGTH: usize = 2usize.pow(SCALE); pub const SHAPE: (usize, usize, usize) = (SIDE_LENGTH, SIDE_LENGTH, SIDE_LENGTH); pub const DIMENSIONS: Vector3 = Vector3::new(SIDE_LENGTH, SIDE_LENGTH, SIDE_LENGTH); diff --git a/src/server/chunk/load.rs b/src/server/chunk/load.rs index 508031e..463f8a2 100644 --- a/src/server/chunk/load.rs +++ b/src/server/chunk/load.rs @@ -1,17 +1,10 @@ use std::collections::{HashMap, HashSet}; 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}, - }, + common::component::{ChunkBundle, ChunkData, ChunkMesh, ChunkPos}, server::generation::generate_tree, util:: + thread::{ExitType, ThreadChannel, ThreadHandle} }; pub struct ChunkManager { @@ -114,32 +107,7 @@ fn chunk_loader_main(channel: ThreadChannel) { let tree = ChunkData::from_tree(generate_tree(pos)); let tree_time = std::time::Instant::now() - start; - // let start = std::time::Instant::now(); - // let mut data = generate(pos); - // let data_time = std::time::Instant::now() - start; - // - // let start = std::time::Instant::now(); - // let shape = s![ - // 1..data.len_of(Axis(0)) - 1, - // 1..data.len_of(Axis(1)) - 1, - // 1..data.len_of(Axis(2)) - 1 - // ]; - // let mut slice = data.slice_mut(shape); - // let mut iter = tree.into_iter(); - // slice.assign(&Array3::from_shape_fn(chunk::SHAPE, |_| { - // iter.next().unwrap() - // })); - // let convert_time = std::time::Instant::now() - start; - // - // let start = std::time::Instant::now(); - // let mesh = ChunkMesh::from_data(data.map(|i| COLOR_MAP[*i as usize]).view()); - // let mesh_time = std::time::Instant::now() - start; - // - // println!( - // "data: {:<5?} mesh: {:<5?} convert: {:<5?} tree: {:<5?}", - // data_time, mesh_time, convert_time, tree_time - // ); - println!("gen time: {:<5?}", tree_time); + println!("gen time: {:<5?}; size: {}", tree_time, tree.raw().len()); channel.send(ServerChunkMsg::ChunkGenerated(GeneratedChunk { pos, @@ -154,83 +122,3 @@ fn chunk_loader_main(channel: ThreadChannel) { } } -fn generate(pos: ChunkPos) -> Array3 { - let shape = [chunk::SIDE_LENGTH + 2; 3]; - if pos.y > 0 || pos.y < -1 { - return Array3::from_elem(shape, 0); - } - let posf: Vector3 = (pos.cast() * chunk::SIDE_LENGTH as f32) - Vector3::from_element(1.0); - let (noise, min, max) = 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)| { - generate_at(Vector3::new(x, y, z), posf, &noise, min, max) - }) -} - -fn generate_tree(pos: ChunkPos) -> OctTree { - if pos.y > 0 || pos.y < -1 { - return OctTree::from_leaf(0, 8); - } - let posf: Vector3 = pos.cast() * chunk::SIDE_LENGTH as f32; - let (noise, min, max) = - NoiseBuilder::gradient_2d_offset(posf.x, chunk::SIDE_LENGTH, posf.z, chunk::SIDE_LENGTH) - .with_seed(0) - .with_freq(1.0 / (chunk::SIDE_LENGTH as f32)) - .generate(); - OctTree::from_fn_rec(&mut |p| generate_at(p, posf, &noise, min, max), chunk::SCALE) -} - -fn generate_at(p: Vector3, posf: Vector3, noise: &[f32], min: f32, max: f32) -> u32 { - // 0 air 1 stone 2 "sand" 3 water - let y = p.y as f32 + posf.y; - // highest heights, 0.0 .. 1.0 relative to chunk size - let [water, grass, top] = [0.18, 0.35, 0.5].map(|f| chunk::SIDE_LENGTH as f32 * f); - let n = ((noise[p.x + p.z * chunk::SIDE_LENGTH] - min) / (max - min) * 2.0).exp2() * top * 0.25; - if y < n { - if y < water { - 1 - } else if y < grass { - 2 - } else { - 1 - } - } else if y <= water { - 3 - } else { - 0 - } -} - -const COLOR_MAP: [VoxelColor; 4] = [ - VoxelColor { - r: 0, - g: 0, - b: 0, - a: 0, - }, - VoxelColor { - r: 150, - g: 150, - b: 150, - a: 255, - }, - VoxelColor { - r: 100, - g: 255, - b: 100, - a: 255, - }, - VoxelColor { - r: 100, - g: 100, - b: 255, - a: 200, - }, -]; diff --git a/src/server/generation/mod.rs b/src/server/generation/mod.rs new file mode 100644 index 0000000..c4026fa --- /dev/null +++ b/src/server/generation/mod.rs @@ -0,0 +1,145 @@ +use nalgebra::Vector3; +use simdnoise::NoiseBuilder; + +use crate::{ + common::component::{chunk, ChunkPos}, + util::oct_tree::OctTree, +}; + +pub fn generate_tree(pos: ChunkPos) -> OctTree { + if pos.y > 0 || pos.y < -1 { + return OctTree::from_leaf(0, 8); + } + let posf: Vector3 = pos.cast() * chunk::SIDE_LENGTH as f32; + let noise1 = generate_noise_map(0, 1.0, posf, chunk::SCALE, &mut |v: f32| { + (v * 2.0).exp2() * TOP * 0.25 + }); + let noise2 = generate_noise_map(1, 50.0, posf, chunk::SCALE, &mut |v: f32| v * 20.0 + GRASS); + OctTree::from_fn_rec( + &mut |p| generate_leaf(p, posf, (&noise1.base, &noise2.base)), + &mut |p, lvl| generate_node(p, lvl, posf, (&noise1, &noise2)), + chunk::SCALE, + ) +} + +const WATER: f32 = 0.18 * chunk::SIDE_LENGTH as f32; +const GRASS: f32 = 0.35 * chunk::SIDE_LENGTH as f32; +const TOP: f32 = 0.5 * chunk::SIDE_LENGTH as f32; + +// 0 air 1 stone 2 grass 3 water +fn generate_leaf(p: Vector3, posf: Vector3, noise: (&[f32], &[f32])) -> u32 { + let y = p.y as f32 + posf.y; + let n = noise.0[p.x + p.z * chunk::SIDE_LENGTH]; + let n2 = noise.1[p.x + p.z * chunk::SIDE_LENGTH]; + if y < n { + if y < WATER { + 1 + } else if y < n2 { + 2 + } else { + 1 + } + } else if y <= WATER { + 3 + } else { + 0 + } +} + +// 0 air 1 stone 2 grass 3 water +fn generate_node( + p: Vector3, + scale: u32, + posf: Vector3, + noise: (&NoiseMap, &NoiseMap), +) -> Option { + let side_len = 2usize.pow(scale); + let y = NumRange { + min: p.y as f32 + posf.y, + max: (p.y + side_len - 1) as f32 + posf.y, + }; + let l = scale as usize - 1; + let i = (p.x >> scale) + (p.z >> scale) * (chunk::SIDE_LENGTH / side_len); + let n = &noise.0.levels[l][i]; + let n2 = &noise.1.levels[l][i]; + Some(if y.max < n.min { + if y.max < WATER { + 1 + } else if y.max < n2.min && y.min >= WATER { + 2 + } else if y.min > n2.max { + 1 + } else { + return None; + } + } else if y.max <= WATER && y.min > n.max { + 3 + } else if y.min > WATER && y.min > n.max { + 0 + } else { + return None; + }) +} + +fn generate_noise_map( + seed: i32, + freq: f32, + posf: Vector3, + levels: u32, + adjust: &mut impl FnMut(f32) -> f32, +) -> NoiseMap { + let mut size = 2usize.pow(levels); + let (mut base, min, max) = NoiseBuilder::gradient_2d_offset(posf.x, size, posf.z, size) + .with_seed(seed) + .with_freq(freq / (size as f32)) + .generate(); + for v in &mut base { + *v = adjust((*v - min) / (max - min)); + } + let first_len = base.len() / 4; + let mut first = Vec::with_capacity(first_len); + for y in (0..size).step_by(2) { + for x in (0..size).step_by(2) { + let a = base[x + y * size]; + let b = base[x + 1 + y * size]; + let c = base[x + (y + 1) * size]; + let d = base[x + 1 + (y + 1) * size]; + first.push(NumRange { + min: a.min(b).min(c).min(d), + max: a.max(b).max(c).max(d), + }) + } + } + let mut arr = vec![first]; + for l in 1..levels as usize { + size /= 2; + let prev = &arr[l - 1]; + let mut new = Vec::with_capacity(prev.len() / 4); + for y in (0..size).step_by(2) { + for x in (0..size).step_by(2) { + let a = &prev[x + y * size]; + let b = &prev[x + 1 + y * size]; + let c = &prev[x + (y + 1) * size]; + let d = &prev[x + 1 + (y + 1) * size]; + new.push(NumRange { + min: a.min.min(b.min).min(c.min).min(d.min), + max: a.max.max(b.max).max(c.max).max(d.max), + }) + } + } + arr.push(new); + } + NoiseMap { base, levels: arr } +} + +#[derive(Debug)] +pub struct NoiseMap { + levels: Vec>, + base: Vec, +} + +#[derive(Debug)] +pub struct NumRange { + min: f32, + max: f32, +} diff --git a/src/server/mod.rs b/src/server/mod.rs index be71472..226b411 100644 --- a/src/server/mod.rs +++ b/src/server/mod.rs @@ -3,6 +3,7 @@ mod client; mod rsc; mod system; mod test; +mod generation; pub use client::*; diff --git a/src/util/mod.rs b/src/util/mod.rs index ff0e7bc..3c27c48 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,2 +1,3 @@ pub mod thread; pub mod oct_tree; +pub mod timer; diff --git a/src/util/oct_tree.rs b/src/util/oct_tree.rs index f30651c..ce46180 100644 --- a/src/util/oct_tree.rs +++ b/src/util/oct_tree.rs @@ -1,6 +1,5 @@ use std::fmt::Debug; -use bevy_ecs::system::IntoSystem; use nalgebra::Vector3; use ndarray::ArrayView3; @@ -20,13 +19,13 @@ impl OctNode { pub const fn is_leaf(&self) -> bool { self.0 >= LEAF_BIT } - pub fn is_node(&self) -> bool { + pub const fn is_node(&self) -> bool { self.0 < LEAF_BIT } - pub fn node_data(&self) -> u32 { + pub const fn node_data(&self) -> u32 { self.0 } - pub fn leaf_data(&self) -> u32 { + pub const fn leaf_data(&self) -> u32 { self.0 & !LEAF_BIT } } @@ -57,17 +56,22 @@ impl OctTree { levels, } } - pub fn from_fn_rec(f: &mut impl FnMut(Vector3) -> u32, levels: u32) -> OctTree { - Self::from_fn_offset(f, levels, Vector3::from_element(0)) + pub fn from_fn_rec( + f_leaf: &mut impl FnMut(Vector3) -> u32, + f_node: &mut impl FnMut(Vector3, u32) -> Option, + levels: u32, + ) -> OctTree { + Self::from_fn_offset(f_leaf, f_node, levels, Vector3::from_element(0)) } pub fn from_fn_offset( - f: &mut impl FnMut(Vector3) -> u32, + f_leaf: &mut impl FnMut(Vector3) -> u32, + f_node: &mut impl FnMut(Vector3, u32) -> Option, levels: u32, offset: Vector3, ) -> Self { let mut data = Vec::new(); data.push(OctNode::new_node(0)); - Self::from_fn_offset_inner(f, &mut data, levels, offset); + Self::from_fn_offset_inner(f_leaf, f_node, &mut data, levels, offset); if data.len() == 2 { data.remove(0); } @@ -78,17 +82,18 @@ impl OctTree { } } fn from_fn_offset_inner( - f: &mut impl FnMut(Vector3) -> u32, + f_leaf: &mut impl FnMut(Vector3) -> u32, + f_node: &mut impl FnMut(Vector3, u32) -> Option, accumulator: &mut Vec, level: u32, offset: Vector3, ) { if level == 0 { - accumulator.push(OctNode::new_leaf(f(offset))); + accumulator.push(OctNode::new_leaf(f_leaf(offset))); return; } else if level == 1 { let leaves: [OctNode; 8] = - core::array::from_fn(|i| OctNode::new_leaf(f(offset + CORNERS[i]))); + core::array::from_fn(|i| OctNode::new_leaf(f_leaf(offset + CORNERS[i]))); if leaves[1..].iter().all(|l| *l == leaves[0]) { accumulator.push(leaves[0]); } else { @@ -100,20 +105,21 @@ impl OctTree { accumulator.resize(i + 8, OctNode::new_node(0)); let mut data_start = 0; for (j, corner_offset) in CORNERS.iter().enumerate() { - let sub_start = accumulator.len(); - Self::from_fn_offset_inner( - f, - accumulator, - level - 1, - offset + corner_offset * 2usize.pow(level - 1), - ); - let len = accumulator.len() - sub_start; - if len == 1 { - accumulator[i + j] = accumulator[sub_start]; - accumulator.pop(); + let lvl = level - 1; + let pos = offset + corner_offset * 2usize.pow(lvl); + if let Some(node) = f_node(pos, lvl) { + accumulator[i + j] = OctNode::new_leaf(node); } else { - accumulator[i + j] = OctNode::new_node(data_start as u32); - data_start += len; + let sub_start = accumulator.len(); + Self::from_fn_offset_inner(f_leaf, f_node, accumulator, lvl, pos); + let len = accumulator.len() - sub_start; + if len == 1 { + accumulator[i + j] = accumulator[sub_start]; + accumulator.pop(); + } else { + accumulator[i + j] = OctNode::new_node(data_start as u32); + data_start += len; + } } } if data_start == 0 { @@ -125,10 +131,7 @@ impl OctTree { } } - pub fn from_fn_iter( - f: &mut impl FnMut(Vector3) -> u32, - levels: u32, - ) -> Self { + pub fn from_fn_iter(f: &mut impl FnMut(Vector3) -> u32, levels: u32) -> Self { let mut data = vec![OctNode::new_node(0)]; let mut level: usize = 1; let mut children = Vec::new(); @@ -173,7 +176,7 @@ impl OctTree { } pub fn from_arr(arr: ArrayView3, levels: u32) -> Self { - Self::from_fn_rec(&mut |p| arr[(p.x, p.y, p.z)], levels) + Self::from_fn_rec(&mut |p| arr[(p.x, p.y, p.z)], &mut |_, _| None, levels) } pub fn get(&self, mut pos: Vector3) -> u32 { let mut data_start = 1; @@ -199,7 +202,6 @@ impl OctTree { pub struct OctTreeIter<'a> { queue: Vec, levels: Vec, - pos: usize, cur: u32, run: usize, data: &'a [OctNode], @@ -218,9 +220,8 @@ impl<'a> Iterator for OctTreeIter<'a> { self.run = 8usize.pow(level); self.cur = node.leaf_data(); } else { - let pos = 0; - let add = &self.data[pos..pos + 8]; - self.data = &self.data[pos + DATA_OFFSET..]; + let add = &self.data[..8]; + self.data = &self.data[DATA_OFFSET..]; self.queue.extend(add.iter().rev()); self.levels.resize(self.levels.len() + 8, level - 1); } @@ -234,7 +235,6 @@ impl<'a> IntoIterator for &'a OctTree { fn into_iter(self) -> Self::IntoIter { OctTreeIter { data: &self.data[1..], - pos: 0, cur: 0, levels: vec![self.levels], run: 0, diff --git a/src/util/timer.rs b/src/util/timer.rs new file mode 100644 index 0000000..38635d6 --- /dev/null +++ b/src/util/timer.rs @@ -0,0 +1,59 @@ +use std::time::{Duration, Instant}; + +pub struct Timer { + start: Instant, + pos: usize, + times: Vec>, + durs: Vec>, +} + +impl Timer { + pub fn new(len: usize) -> Self { + Self { + start: Instant::now(), + pos: 0, + durs: vec![None; len], + times: vec![None; len], + } + } + pub fn start(&mut self) { + self.start = Instant::now(); + } + pub fn stop(&mut self) { + let duration = Instant::now() - self.start; + self.durs[self.pos] = Some(duration); + self.times[self.pos] = Some(self.start); + self.pos = (self.pos + 1) % self.times.len(); + } + pub fn avg(&self) -> Duration { + let filtered: Vec<_> = self.durs.iter().filter_map(|d| *d).collect(); + let len = filtered.len(); + if len != 0 { + let total: Duration = filtered.into_iter().sum(); + total / len as u32 + } else { + Duration::ZERO + } + } + pub fn max(&self) -> Duration { + self.durs + .iter() + .filter_map(|d| *d) + .max() + .unwrap_or(Duration::ZERO) + } + pub fn per_sec(&self) -> usize { + let now = Instant::now(); + let mut count = 0; + while count < self.times.len() { + let i = (self.pos + count + 1) % self.times.len(); + let Some(t) = self.times[i] else { break }; + if now - t <= Duration::from_secs(1) { + count += 1; + } else { + break; + } + } + count + } +}