From 0e92480ce8d6babf7619cef568dcdc6277027e9c Mon Sep 17 00:00:00 2001 From: Avery Date: Fri, 18 Apr 2025 22:14:54 +0200 Subject: [PATCH] Mesh and render a single chunk (Broken) --- shaders/chunk.frag | 22 +++ shaders/chunk.vert | 130 ++++++++++++++ src/game/chunk.rs | 104 +++++++++++ src/game/mod.rs | 13 +- src/main.rs | 17 ++ src/render/chunk.rs | 278 ++++++++++++++++++++++++++++++ src/render/cube.rs | 4 + src/render/in_world.rs | 2 +- src/render/loading_world.rs | 2 + src/render/mesher/block_mesher.rs | 109 ++++++++++++ src/render/mesher/chunk_mesher.rs | 30 ++++ src/render/mesher/mod.rs | 40 +++++ src/render/mod.rs | 2 + src/render/skybox.rs | 4 + src/vector.rs | 89 ++++++++++ 15 files changed, 836 insertions(+), 10 deletions(-) create mode 100644 shaders/chunk.frag create mode 100644 shaders/chunk.vert create mode 100644 src/game/chunk.rs create mode 100644 src/render/chunk.rs create mode 100644 src/render/mesher/block_mesher.rs create mode 100644 src/render/mesher/chunk_mesher.rs create mode 100644 src/render/mesher/mod.rs create mode 100644 src/vector.rs diff --git a/shaders/chunk.frag b/shaders/chunk.frag new file mode 100644 index 0000000..3f3287c --- /dev/null +++ b/shaders/chunk.frag @@ -0,0 +1,22 @@ +#version 450 +// vim: ft=c +// clang-format off + +layout(location = 0) out vec4 outColor; + +layout(location = 0) in vec4 normal; +layout(location = 1) in vec4 pos_pre; +layout(location = 2) in vec4 view_orig; + +layout(push_constant, std430) uniform pc { + layout(offset=48) vec4 data; +}; + +void main() { + outColor = vec4(data.rgb*(1.0+dot(normal.xyz, normalize(vec3(-0.7, -0.5, -0.1))))/2.0, 1.0); +//if(pos_post.z <= 0.0) { +// outColor = vec4(1.0); +//} + //outColor = normal.xyz; + //outColor = vec4(vec3(1.0-gl_FragCoord.z), 1.0); +} diff --git a/shaders/chunk.vert b/shaders/chunk.vert new file mode 100644 index 0000000..a8375c7 --- /dev/null +++ b/shaders/chunk.vert @@ -0,0 +1,130 @@ +#version 450 +// vim: ft=c +// clang-format off + +struct PosNorm { + vec4 pos; + vec4 norm; +}; + +layout(std430, set = 0, binding = 0) buffer positions_buffer { + PosNorm posnrm[]; +} pos; + +layout(push_constant, std430) uniform pc { + layout(offset=0) vec4 cam_orig; + layout(offset=16) vec4 cam_rot; + layout(offset=32) uvec2 screen_res; +}; + +layout (location = 0) out vec4 normal; +layout (location = 1) out vec4 pos_pre; +layout (location = 2) out vec4 view_orig; + +const float PI = 3.14159; +// Forgive me for I have sinned +const float TAU = PI*2.0; + +void main() { + // assign outs + normal = pos.posnrm[gl_VertexIndex].norm; + pos_pre = pos.posnrm[gl_VertexIndex].pos; + view_orig = cam_orig; + + // define constants + const float zFar = 100.0; + const float zNear = 0.1; + + // assign the transformee + gl_Position = pos.posnrm[gl_VertexIndex].pos; + + mat4 fix_coordinates = mat4( + -1.0, 0.0, 0.0, 0.0, + 0.0, -1.0, 0.0, 0.0, + 0.0, 0.0, -1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + mat4 view_orig = mat4( + 1.0, 0.0, 0.0, cam_orig.x, + 0.0, 1.0, 0.0, cam_orig.y, + 0.0, 0.0, 1.0, cam_orig.z, + 0.0, 0.0, 0.0, 1.0 + ); + mat4 view_rot_xz = mat4( + cos(cam_rot.y), 0.0, sin(cam_rot.y), 0.0, + 0.0, 1.0, 0.0, 0.0, + -sin(cam_rot.y), 0.0, cos(cam_rot.y), 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + mat4 view_rot_yz = mat4( + 1.0, 0.0, 0.0, 0.0, + 0.0, cos(cam_rot.x), sin(cam_rot.x), 0.0, + 0.0, -sin(cam_rot.x), cos(cam_rot.x), 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + mat4 project_aspect = mat4( + (float(screen_res.y)/float(screen_res.x)), 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + mat4 project_znear = mat4( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, -zNear, + 0.0, 0.0, 0.0, 1.0 + ); + mat4 project_div = mat4( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, tan((70.0/2.0)/360.0*TAU), 0.0 + ); + mat4 project_normal = mat4( + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0/zFar, 0.0, + 0.0, 0.0, 0.0, 1.0 + ); + + // vulkan has inverted screen coordinates + // but we want regular mesh coordinates + // gl_Position.xyz *= -1.0 + gl_Position *= fix_coordinates; + + // apply view's origin transformation + //gl_Position.xyz += cam_orig.xyz; + gl_Position *= view_orig; + + // apply view's xz rotation + //mat2 xz_rot; + //xz_rot[0] = vec2(cos(cam_rot.y), -sin(cam_rot.y)); + //xz_rot[1] = vec2(sin(cam_rot.y), cos(cam_rot.y)); + //gl_Position.xz *= inverse(xz_rot); + gl_Position *= view_rot_xz; + + // apply view's yz rotation + //mat2 yz_rot; + //yz_rot[0] = vec2(cos(cam_rot.x), -sin(cam_rot.x)); + //yz_rot[1] = vec2(sin(cam_rot.x), cos(cam_rot.x)); + //gl_Position.yz *= inverse(yz_rot); + gl_Position *= view_rot_yz; + + // aspect correction + //gl_Position.x *= (1080.0/1920.0); + gl_Position *= project_aspect; + + // z near correction + //gl_Position.z -= zNear; + gl_Position *= project_znear; + + // division by z + // has to be assigned by w so that the hardware performs the division AFTER clipping. + //gl_Position.w = (gl_Position.z*sin(140.0/360.0*TAU)); + gl_Position *= project_div; + + // z normalization + //gl_Position.z /= zFar; + gl_Position *= project_normal; + +} diff --git a/src/game/chunk.rs b/src/game/chunk.rs new file mode 100644 index 0000000..442faff --- /dev/null +++ b/src/game/chunk.rs @@ -0,0 +1,104 @@ +use std::mem::MaybeUninit; + +use crate::{vec3, vector::Vector3}; + +pub struct Chunk { + blocks: [[[Block; 16]; 16]; 16], + pub chunk_coord: Vector3, +} + +impl Chunk { + pub fn new(chunk_coord: Vector3) -> Self { + let mut blocks: [[[MaybeUninit; 16]; 16]; 16] = + unsafe { MaybeUninit::uninit().assume_init() }; + + blocks.iter_mut().for_each(|bs| { + bs.iter_mut().for_each(|bs| { + bs.iter_mut().for_each(|u| { + u.write(Block { + typ: BlockType::Air, + }); + }) + }) + }); + + let blocks = unsafe { + std::mem::transmute::<[[[MaybeUninit; 16]; 16]; 16], [[[Block; 16]; 16]; 16]>( + blocks, + ) + }; + + Chunk { + blocks, + chunk_coord, + } + } + + pub fn set_block(&mut self, block: Block, local_coord: Vector3) { + match local_coord { + Vector3 { + x: x @ 0..16, + y: y @ 0..16, + z: z @ 0..16, + } => self.blocks[x as usize][y as usize][z as usize] = block, + _ => todo!(), + } + } + + pub fn new_init(blocks: [[[Block; 16]; 16]; 16], chunk_coord: Vector3) -> Self { + Self { + blocks, + chunk_coord, + } + } + + pub fn get_block(&self, local_coord: Vector3) -> Option<&Block> { + match local_coord { + Vector3 { + x: x @ 0..16, + y: y @ 0..16, + z: z @ 0..16, + } => Some(&self.blocks[x as usize][y as usize][z as usize]), + _ => None, + } + } + + pub fn blocks(&self) -> impl Iterator)> { + let i = self + .blocks + .iter() + .enumerate() + .map(|(x, bs)| { + bs.iter() + .enumerate() + .map(move |(y, bs)| { + bs.iter() + .enumerate() + .map(move |(z, b)| (b, vec3!(x as i32, y as i32, z as i32))) + }) + .flatten() + }) + .flatten(); + i + } +} + +#[derive(Clone, Copy)] +pub struct Block { + pub typ: BlockType, +} + +impl Block { + pub fn is_transparent(&self) -> bool { + match self.typ { + BlockType::Air => true, + BlockType::Solid => false, + } + } +} + +#[derive(Clone, Copy)] +pub enum BlockType { + Air, + Solid, +} diff --git a/src/game/mod.rs b/src/game/mod.rs index de1f55d..c4957ca 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -8,19 +8,14 @@ use sdl2::{ keyboard::Keycode, }; -use crate::{Game, InWorld, RenderAvailable, input::InputHandler}; +pub mod chunk; -#[derive(Debug, Default)] -pub struct Vector3 { - pub x: f32, - pub y: f32, - pub z: f32, -} +use crate::{Game, InWorld, RenderAvailable, input::InputHandler, vector::Vector3}; #[derive(Debug, Default)] pub struct Camera { - pub origin: Vector3, - pub rotation: Vector3, + pub origin: Vector3, + pub rotation: Vector3, } impl Game { diff --git a/src/main.rs b/src/main.rs index 15a7ac4..53dcbcc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,11 +4,16 @@ mod game; mod input; mod render; +mod vector; + use ash::Device; use ash::Entry; use ash::Instance; use ash::vk; use game::Camera; +use game::chunk::Block; +use game::chunk::BlockType; +use game::chunk::Chunk; use render::SwapchainCtx; use render::loading_world::Component; use render::loading_world::WorldComponent; @@ -129,8 +134,20 @@ fn main() { // Hardcoded loading of a world, skybox + 1 cube let mut game = game.start_load_world(); + let mut chunk = Chunk::new(vec3!(0, 0, 0)); + chunk.set_block( + Block { + typ: BlockType::Solid, + }, + vec3!(1, 1, 1), + ); + + let chunk = game.create_chunk(&chunk); + game.add_component(chunk); + let cube = game.create_cube(); game.add_component(cube); + let game = game.start_world(); // Run the actual game diff --git a/src/render/chunk.rs b/src/render/chunk.rs new file mode 100644 index 0000000..c94a058 --- /dev/null +++ b/src/render/chunk.rs @@ -0,0 +1,278 @@ +use std::collections::HashMap; + +use ash::vk; + +use crate::{Game, LoadingWorld, RenderCtx, game::chunk::Chunk}; + +use super::{ + MemType, + loading_world::WorldComponent, + mesher::{Mesh, PosNorm}, +}; + +impl WorldComponent for ChunkRender { + fn descriptors(&self) -> std::collections::HashMap { + let mut map = HashMap::new(); + map.insert(vk::DescriptorType::STORAGE_BUFFER, 1); + map + } + + fn desc_layout(&self, ctx: &RenderCtx) -> vk::DescriptorSetLayout { + setup_desc_layout(ctx) + } + + fn write_desc_set( + &self, + ctx: &RenderCtx, + desc_set: vk::DescriptorSet, + skybox: (vk::ImageView, vk::Sampler), + ) { + let buf_info = vk::DescriptorBufferInfo::default() + .buffer(self.mesh_buf) + .offset(0) + .range(vk::WHOLE_SIZE); + let buf_infos = [buf_info]; + let buf_desc = vk::WriteDescriptorSet::default() + .dst_set(desc_set) + .dst_binding(0) + .dst_array_element(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .buffer_info(&buf_infos); + + unsafe { + ctx.dev.update_descriptor_sets(&[buf_desc], &[]); + } + } + + fn pipeline(&self) -> (vk::Pipeline, vk::PipelineLayout) { + (self.pipeline, self.pipe_layout) + } + + fn push_constants( + &self, + camera: &crate::game::Camera, + res: [u32; 2], + base_color: [f32; 4], + ) -> Vec<(vk::ShaderStageFlags, u32, Vec)> { + let mut constants = Vec::new(); + let cam_data: Vec = (Vec::::from([ + camera.origin.x, + camera.origin.y, + camera.origin.z, + 0.0, + camera.rotation.x, + camera.rotation.y, + camera.rotation.z, + ])) + .iter() + .map(|f| f.to_ne_bytes()) + .collect::>() + .into_flattened(); + constants.push((vk::ShaderStageFlags::VERTEX, 0, cam_data)); + + let screen_res: Vec = res + .iter() + .map(|f| f.to_ne_bytes()) + .collect::>() + .into_flattened(); + constants.push((vk::ShaderStageFlags::VERTEX, 32, screen_res)); + + let base_color: Vec = (Vec::::from([0.0, 1.0, 1.0, 1.0])) + .iter() + .map(|f| f.to_ne_bytes()) + .collect::>() + .into_flattened(); + constants.push((vk::ShaderStageFlags::FRAGMENT, 48, base_color)); + + constants + } + + fn num_verticies(&self) -> u32 { + self.num_vert as u32 + } +} + +pub struct ChunkRender { + mesh_buf: vk::Buffer, + num_vert: usize, + pipeline: vk::Pipeline, + pipe_layout: vk::PipelineLayout, +} + +impl RenderCtx { + fn create_chunk(&self, pass: vk::RenderPass, chunk: &Chunk) -> ChunkRender { + let mut mesh = Mesh::default(); + chunk.mesh(&mut mesh); + let num_vert = mesh.num_vert(); + let buf = mesh.to_buffer(); + + let mesh_mem = self.mem_alloc( + MemType::HostVisibile, + (buf.len() * size_of::()) as u64, + ); + + let mesh_buf = self.alloc_buf( + mesh_mem, + (buf.len() * size_of::()) as u64, + vk::BufferUsageFlags::TRANSFER_SRC | vk::BufferUsageFlags::STORAGE_BUFFER, + ); + + let mesh_mem = self.buf_to_ptr(mesh_mem, mesh_buf); + unsafe { + std::ptr::copy(buf.as_ptr(), mesh_mem as *mut PosNorm, buf.len()); + } + + let pipe_layout = setup_pipe_layout(self); + let pipeline = setup_pipeline(self, &pass, pipe_layout); + + ChunkRender { + mesh_buf, + num_vert, + pipeline, + pipe_layout, + } + } +} + +impl Game { + /// Create a cube to be rendered in world, this can then be + /// added using [`Game::add_component`] + pub fn create_chunk(&self, chunk: &Chunk) -> ChunkRender { + let Self { + state: LoadingWorld { ctx, pass, .. }, + } = self; + ctx.create_chunk(*pass, chunk) + } +} + +fn load_chunk_shaders(ctx: &RenderCtx) -> (vk::ShaderModule, vk::ShaderModule) { + let shader_vert_shader = ctx.shader_mod_from_file( + "shaders/chunk_vert.spv", + vk::ShaderModuleCreateFlags::empty(), + ); + let shader_frag_shader = ctx.shader_mod_from_file( + "shaders/chunk_frag.spv", + vk::ShaderModuleCreateFlags::empty(), + ); + + (shader_vert_shader, shader_frag_shader) +} + +fn setup_desc_layout(ctx: &RenderCtx) -> vk::DescriptorSetLayout { + let storage_binding = vk::DescriptorSetLayoutBinding::default() + .binding(0) + .descriptor_type(vk::DescriptorType::STORAGE_BUFFER) + .descriptor_count(1) + .stage_flags(vk::ShaderStageFlags::VERTEX); + let layouts = [storage_binding]; + + let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(&layouts); + let layout = unsafe { + ctx.dev + .create_descriptor_set_layout(&layout_info, None) + .expect("Failed to create descriptor set layout") + }; + layout +} + +fn setup_pipe_layout(ctx: &RenderCtx) -> vk::PipelineLayout { + let layout = setup_desc_layout(ctx); + let push_range: [vk::PushConstantRange; 2] = [ + vk::PushConstantRange::default() + .stage_flags(vk::ShaderStageFlags::VERTEX) + .offset(0) + .size(48), // vec4 camera_orig, camera_rot; uvec2 screen_res + vk::PushConstantRange::default() + .stage_flags(vk::ShaderStageFlags::FRAGMENT) + .offset(48) + .size(16), // vec4 base_color + ]; + + let layouts = &[layout]; + + let pipe_layout = vk::PipelineLayoutCreateInfo::default() + .set_layouts(layouts.as_ref()) + .push_constant_ranges(&push_range); + let pipe_layout = unsafe { + ctx.dev + .create_pipeline_layout(&pipe_layout, None) + .expect("Failed to create pipeline layout") + }; + pipe_layout +} + +fn setup_pipeline( + ctx: &RenderCtx, + pass: &vk::RenderPass, + layout: vk::PipelineLayout, +) -> vk::Pipeline { + let (vert_shader, frag_shader) = load_chunk_shaders(ctx); + let shader_stages = ctx.setup_simple_shader_stage(vert_shader, frag_shader); + let vert_input = vk::PipelineVertexInputStateCreateInfo::default(); + let input_assembly = vk::PipelineInputAssemblyStateCreateInfo::default() + .topology(vk::PrimitiveTopology::TRIANGLE_LIST); + let rasterization = vk::PipelineRasterizationStateCreateInfo::default() + .polygon_mode(vk::PolygonMode::FILL) + .cull_mode(vk::CullModeFlags::NONE) + .front_face(vk::FrontFace::CLOCKWISE) + .line_width(1.0); + let multisample = vk::PipelineMultisampleStateCreateInfo::default() + .rasterization_samples(vk::SampleCountFlags::TYPE_1); + let depth_stencil = vk::PipelineDepthStencilStateCreateInfo::default() + .depth_test_enable(true) + .depth_write_enable(true) + .depth_compare_op(vk::CompareOp::LESS) + .depth_bounds_test_enable(true) + .stencil_test_enable(false) + .min_depth_bounds(0.0) + .max_depth_bounds(1.0); + + let blend = vk::PipelineColorBlendAttachmentState::default() + .src_color_blend_factor(vk::BlendFactor::SRC_COLOR) + .dst_color_blend_factor(vk::BlendFactor::SRC_COLOR) + .color_blend_op(vk::BlendOp::ADD) + .src_alpha_blend_factor(vk::BlendFactor::SRC_COLOR) + .dst_alpha_blend_factor(vk::BlendFactor::SRC_COLOR) + .alpha_blend_op(vk::BlendOp::ADD) + .color_write_mask( + vk::ColorComponentFlags::R + | vk::ColorComponentFlags::G + | vk::ColorComponentFlags::B + | vk::ColorComponentFlags::A, + ); + + let blend_attachments = [blend]; + + let color_blend = vk::PipelineColorBlendStateCreateInfo::default() + .attachments(blend_attachments.as_ref()) + .logic_op_enable(false) + .logic_op(vk::LogicOp::COPY) + .blend_constants([1.0, 1.0, 1.0, 1.0]); + + let dynamic = vk::PipelineDynamicStateCreateInfo::default().dynamic_states(&[ + vk::DynamicState::VIEWPORT_WITH_COUNT, + vk::DynamicState::SCISSOR_WITH_COUNT, + ]); + + let pipe_info = vk::GraphicsPipelineCreateInfo::default() + .stages(&shader_stages) + .vertex_input_state(&vert_input) + .input_assembly_state(&input_assembly) + .rasterization_state(&rasterization) + .multisample_state(&multisample) + .depth_stencil_state(&depth_stencil) + .color_blend_state(&color_blend) + .layout(layout) + .dynamic_state(&dynamic) + .render_pass(*pass) + .subpass(0); + + let pipe = unsafe { + ctx.dev + .create_graphics_pipelines(vk::PipelineCache::null(), &[pipe_info], None) + .expect("Failed to create graphics pipeline") + .remove(0) + }; + println!("Created pipeline"); + pipe +} diff --git a/src/render/cube.rs b/src/render/cube.rs index adc79be..adb9d13 100644 --- a/src/render/cube.rs +++ b/src/render/cube.rs @@ -220,6 +220,10 @@ impl WorldComponent for Cube { constants } + + fn num_verticies(&self) -> u32 { + 36 + } } impl RenderCtx { diff --git a/src/render/in_world.rs b/src/render/in_world.rs index 586c965..3dc1141 100644 --- a/src/render/in_world.rs +++ b/src/render/in_world.rs @@ -215,7 +215,7 @@ impl Game { dev.cmd_push_constants(*cmd_buf, layout, f, o, d.as_ref()); }); - dev.cmd_draw(*cmd_buf, 36, 1, 0, 0); + dev.cmd_draw(*cmd_buf, inner.num_verticies(), 1, 0, 0); } { diff --git a/src/render/loading_world.rs b/src/render/loading_world.rs index 2dcc893..ea87e11 100644 --- a/src/render/loading_world.rs +++ b/src/render/loading_world.rs @@ -58,6 +58,8 @@ pub trait WorldComponent { res: [u32; 2], base_color: [f32; 4], ) -> Vec<(vk::ShaderStageFlags, u32, Vec)>; + + fn num_verticies(&self) -> u32; } /// A [`WorldComponent`] and its associated [`vk::DescriptorSet`] diff --git a/src/render/mesher/block_mesher.rs b/src/render/mesher/block_mesher.rs new file mode 100644 index 0000000..271aed5 --- /dev/null +++ b/src/render/mesher/block_mesher.rs @@ -0,0 +1,109 @@ +use std::iter::repeat; + +use crate::{game::chunk::Block, vec3, vector::Vector3}; + +use super::Mesh; + +const TOP_POSITIONS: [Vector3; 6] = [ + vec3!(0.0, 1.0, 0.0), + vec3!(1.0, 1.0, 0.0), + vec3!(0.0, 1.0, 1.0), + vec3!(1.0, 1.0, 1.0), + vec3!(1.0, 1.0, 0.0), + vec3!(0.0, 1.0, 1.0), +]; + +const TOP_NORMAL: Vector3 = vec3!(0.0, 1.0, 0.0); + +const BOTTOM_POSITIONS: [Vector3; 6] = [ + vec3!(0.0, 0.0, 0.0), + vec3!(1.0, 0.0, 0.0), + vec3!(0.0, 0.0, 1.0), + vec3!(1.0, 0.0, 1.0), + vec3!(1.0, 0.0, 0.0), + vec3!(0.0, 0.0, 1.0), +]; + +const BOTTOM_NORMAL: Vector3 = vec3!(0.0, -1.0, 0.0); + +const NORTH_POSITIONS: [Vector3; 6] = [ + vec3!(1.0, 0.0, 0.0), + vec3!(1.0, 1.0, 0.0), + vec3!(1.0, 0.0, 1.0), + vec3!(1.0, 1.0, 1.0), + vec3!(1.0, 1.0, 0.0), + vec3!(1.0, 0.0, 1.0), +]; + +const NORTH_NORMAL: Vector3 = vec3!(1.0, 0.0, 0.0); + +const SOUTH_POSITIONS: [Vector3; 6] = [ + vec3!(0.0, 0.0, 0.0), + vec3!(0.0, 1.0, 0.0), + vec3!(0.0, 0.0, 1.0), + vec3!(0.0, 1.0, 1.0), + vec3!(0.0, 1.0, 0.0), + vec3!(0.0, 0.0, 1.0), +]; + +const SOUTH_NORMAL: Vector3 = vec3!(-1.0, 0.0, 0.0); + +const EAST_POSITIONS: [Vector3; 6] = [ + vec3!(0.0, 0.0, 1.0), + vec3!(1.0, 0.0, 1.0), + vec3!(0.0, 1.0, 1.0), + vec3!(1.0, 1.0, 1.0), + vec3!(1.0, 0.0, 1.0), + vec3!(0.0, 1.0, 1.0), +]; + +const EAST_NORMAL: Vector3 = vec3!(0.0, 0.0, 1.0); + +const WEST_POSITIONS: [Vector3; 6] = [ + vec3!(0.0, 0.0, 0.0), + vec3!(1.0, 0.0, 0.0), + vec3!(0.0, 1.0, 0.0), + vec3!(1.0, 1.0, 0.0), + vec3!(1.0, 0.0, 0.0), + vec3!(0.0, 1.0, 0.0), +]; + +const WEST_NORMAL: Vector3 = vec3!(0.0, 0.0, -1.0); + +const POSITIONS: [&'static [Vector3; 6]; 6] = [ + &TOP_POSITIONS, + &BOTTOM_POSITIONS, + &NORTH_POSITIONS, + &SOUTH_POSITIONS, + &EAST_POSITIONS, + &WEST_POSITIONS, +]; + +const NORMALS: [&'static Vector3; 6] = [ + &TOP_NORMAL, + &BOTTOM_NORMAL, + &NORTH_NORMAL, + &SOUTH_NORMAL, + &EAST_NORMAL, + &WEST_NORMAL, +]; + +pub fn mesh_block(mesh: &mut Mesh, block: &Block, pos: Vector3, sides: [bool; 6]) { + let pos = Into::>::into(pos); + mesh.positions.extend( + POSITIONS + .iter() + .copied() + .zip(sides) + .filter_map(|(s, e)| e.then(|| s.iter().map(|p| *p + pos))) + .flatten(), + ); + mesh.normals.extend( + NORMALS + .iter() + .copied() + .zip(sides) + .filter_map(|(s, e)| e.then(|| repeat(s).take(6))) + .flatten(), + ); +} diff --git a/src/render/mesher/chunk_mesher.rs b/src/render/mesher/chunk_mesher.rs new file mode 100644 index 0000000..8e8298c --- /dev/null +++ b/src/render/mesher/chunk_mesher.rs @@ -0,0 +1,30 @@ +use crate::{game::chunk::Chunk, vec3, vector::Vector3}; + +use super::{Mesh, block_mesher::mesh_block}; + +const NEIGHBOURS: [Vector3; 6] = [ + vec3!(0, 1, 0), + vec3!(0, -1, 0), + vec3!(1, 0, 0), + vec3!(-1, 0, 0), + vec3!(0, 0, 1), + vec3!(0, 0, -1), +]; + +impl Chunk { + pub fn mesh(&self, mesh: &mut Mesh) { + for (block, b_pos) in self.blocks().filter(|(b, _)| !b.is_transparent()) { + let mut sides = [false; 6]; + sides + .iter_mut() + .zip(NEIGHBOURS.iter().copied()) + .for_each(|(f, n)| { + *f = self + .get_block(b_pos + n) + .map(|b| b.is_transparent()) + .unwrap_or(true) + }); + mesh_block(mesh, block, b_pos + (self.chunk_coord * 16), sides); + } + } +} diff --git a/src/render/mesher/mod.rs b/src/render/mesher/mod.rs new file mode 100644 index 0000000..c4901a3 --- /dev/null +++ b/src/render/mesher/mod.rs @@ -0,0 +1,40 @@ +use crate::vector::Vector3; + +mod block_mesher; +mod chunk_mesher; + +#[derive(Default, Debug)] +pub struct Mesh { + positions: Vec>, + normals: Vec>, +} + +#[repr(C)] +pub struct PosNorm { + pos: [f32; 4], + norm: [f32; 4], +} + +impl PosNorm { + fn new(pos: Vector3, norm: Vector3) -> Self { + Self { + pos: [pos.x, pos.y, pos.z, 0.0], + norm: [norm.x, norm.y, norm.z, 0.0], + } + } +} + +impl Mesh { + pub fn to_buffer(self) -> Vec { + let Self { positions, normals } = self; + positions + .into_iter() + .zip(normals.into_iter()) + .map(|(p, n)| PosNorm::new(p, n)) + .collect() + } + + pub fn num_vert(&self) -> usize { + self.positions.len() + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs index 4e3f76f..99eefb1 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -5,10 +5,12 @@ use ash::{ use crate::RenderCtx; +mod chunk; mod cube; pub mod in_world; mod init; pub mod loading_world; +pub mod mesher; pub mod skybox; mod swapchain; diff --git a/src/render/skybox.rs b/src/render/skybox.rs index 4be5777..f133845 100644 --- a/src/render/skybox.rs +++ b/src/render/skybox.rs @@ -84,6 +84,10 @@ impl WorldComponent for Skybox { constants } + + fn num_verticies(&self) -> u32 { + 36 + } } impl Skybox {} diff --git a/src/vector.rs b/src/vector.rs new file mode 100644 index 0000000..97ff5da --- /dev/null +++ b/src/vector.rs @@ -0,0 +1,89 @@ +use std::ops::{Add, Mul, Sub}; + +#[macro_export] +macro_rules! vec3 { + ($x:expr, $y:expr, $z:expr) => { + $crate::vector::Vector3 { + x: $x, + y: $y, + z: $z, + } + }; +} + +#[derive(Debug, Default)] +pub struct Vector3 { + pub x: S, + pub y: S, + pub z: S, +} + +impl Into> for Vector3 { + fn into(self) -> Vector3 { + Vector3 { + x: self.x as f32, + y: self.y as f32, + z: self.z as f32, + } + } +} + +impl Clone for Vector3 { + fn clone(&self) -> Self { + Self { + x: self.x.clone(), + y: self.y.clone(), + z: self.z.clone(), + } + } +} + +impl Copy for Vector3 {} + +impl> Add for Vector3 { + type Output = Self; + + fn add(self, rhs: Self) -> Self::Output { + Self { + x: self.x + rhs.x, + y: self.y + rhs.y, + z: self.z + rhs.z, + } + } +} + +impl> Sub for Vector3 { + type Output = Self; + + fn sub(self, rhs: Self) -> Self::Output { + Self { + x: self.x - rhs.x, + y: self.y - rhs.y, + z: self.z - rhs.z, + } + } +} + +impl> Mul for Vector3 { + type Output = Self; + + fn mul(self, rhs: Self) -> Self::Output { + Self { + x: self.x * rhs.x, + y: self.y * rhs.y, + z: self.z * rhs.z, + } + } +} + +impl + Copy> Mul for Vector3 { + type Output = Self; + + fn mul(self, rhs: S) -> Self::Output { + Self { + x: self.x * rhs, + y: self.y * rhs, + z: self.z * rhs, + } + } +}