diff --git a/kubi/shaders/world.wgsl b/kubi/shaders/world.wgsl index 2bd7a07..bdbc475 100644 --- a/kubi/shaders/world.wgsl +++ b/kubi/shaders/world.wgsl @@ -1,21 +1,27 @@ -struct Uniform { - position_offset: vec3, +struct CameraUniform { view_proj: mat4x4, }; -@group(1) @binding(0) -var world: Uniform; +struct WorldUniform { + position: vec3, +}; + +@group(0) @binding(0) +var camera: CameraUniform; + +@group(0) @binding(1) +var world: WorldUniform; struct VertexInput { @location(0) position: vec3, @location(1) normal: vec3, - @location(2) uv: vec2, + @location(2) tex_coords: vec2, @location(3) tex_index: u32, } struct VertexOutput { @builtin(position) clip_position: vec4, - @location(0) tex_coords: vec2, + //@location(0) tex_coords: vec2, } @vertex @@ -23,8 +29,8 @@ fn vs_main( model: VertexInput, ) -> VertexOutput { var out: VertexOutput; - out.tex_coords = model.tex_coords; - out.clip_position = camera.view_proj * vec4(model.position, 1.0); + out.clip_position = camera.view_proj * vec4(model.position + world.position, 1.0); + //out.tex_coords = model.tex_coords; return out; } diff --git a/kubi/src/assets.rs b/kubi/src/assets.rs index 8ca33c3..1a44c1f 100644 --- a/kubi/src/assets.rs +++ b/kubi/src/assets.rs @@ -3,7 +3,6 @@ use kubi_shared::block::BlockTexture; use crate::rendering::Renderer; mod texture; -mod shaders; use texture::load_asset_texture_array; diff --git a/kubi/src/assets/shaders.rs b/kubi/src/assets/shaders.rs deleted file mode 100644 index 7eb81eb..0000000 --- a/kubi/src/assets/shaders.rs +++ /dev/null @@ -1,6 +0,0 @@ -macro_rules! include_shader_prefab { - ($name: literal, $vert: literal, $frag: literal, $facade: expr) => { - compile_error!("`include_shader_prefab` is not implemented yet") - } -} -pub(crate) use include_shader_prefab; diff --git a/kubi/src/assets/texture.rs b/kubi/src/assets/texture.rs index 5dde5ac..4ba744b 100644 --- a/kubi/src/assets/texture.rs +++ b/kubi/src/assets/texture.rs @@ -45,9 +45,8 @@ pub fn load_asset_texture_array< NonZeroU32::new(dimensions.1).expect("image dimensions must be non-zero") ); - //Ensure same size (skip if poisoned) - if !img_dim.is_poisoned() { - let mut img_dim = img_dim.lock().unwrap(); + //Ensure same size + if let Ok(mut img_dim) = img_dim.lock() { if let Some(current_size) = img_dim.replace(dim_nonzero) { assert!(dim_nonzero == current_size, "image dimensions do not match"); } @@ -57,7 +56,8 @@ pub fn load_asset_texture_array< }).collect(); //Lock for the final time and retrieve the dimensions - let img_dim = img_dim.lock().unwrap().expect("No images were loaded").clone(); + let img_dim = img_dim.lock().unwrap() + .expect("no images were loaded, cannot create an empty texture array"); let img_dim_vec = UVec2::new(img_dim.0.get(), img_dim.1.get()); (raw_images, img_dim_vec) diff --git a/kubi/src/lib.rs b/kubi/src/lib.rs index 78a9200..47e7b40 100644 --- a/kubi/src/lib.rs +++ b/kubi/src/lib.rs @@ -3,7 +3,7 @@ use shipyard::{ World, Workload, IntoWorkload, UniqueView, UniqueViewMut, - NonSendSync, WorkloadModificator, + WorkloadModificator, SystemModificator }; use winit::{ @@ -12,8 +12,9 @@ use winit::{ }; use glam::vec3; use std::time::Instant; - pub(crate) use kubi_shared::transform; + +pub(crate) mod util; pub(crate) mod rendering; pub(crate) mod world; pub(crate) mod player; @@ -57,7 +58,7 @@ use input::{init_input, process_inputs}; use fly_controller::update_controllers; use rendering::{ Renderer, - RenderTarget, + RenderData, BackgroundColor, init_rendering_internals, world::{draw_world, draw_current_chunk_border}, @@ -128,25 +129,48 @@ fn update() -> Workload { ).into_sequential_workload().run_if(is_ingame), update_networking_late.run_if(is_multiplayer), compute_cameras, + update_camera_uniform, update_state, exit_on_esc, disconnect_on_exit.run_if(is_multiplayer), ).into_sequential_workload() } -//TODO move this to the renderer module -fn render() -> Workload { - ( - ( - update_camera_uniform, - draw_world, - draw_current_chunk_border, - render_selection_box, - render_entities, - ).into_sequential_workload().run_if(is_ingame), - ).into_sequential_workload() +//XXX: move to rendering? +fn render(world: &mut World, data: &mut RenderData) { + //RenderPass0 - draw world + { + let mut render_pass = data.encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("RenderPass0"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &data.view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color::BLACK), + store: true, + }, + })], + depth_stencil_attachment: None, + }); + + if world.run(is_ingame) { + world.run_with_data(draw_world, &mut render_pass); + } + } } +// fn render() -> Workload { +// ( +// ( +// , +// draw_world, +// draw_current_chunk_border, +// render_selection_box, +// render_entities, +// ).into_sequential_workload().run_if(is_ingame), +// ).into_sequential_workload() +// } + fn after_frame_end() -> Workload { ( clear_events, @@ -182,7 +206,6 @@ pub fn kubi_main() { world.add_workload(pre_startup); world.add_workload(startup); world.add_workload(update); - world.add_workload(render); world.add_workload(after_frame_end); //Run pre-startup procedure @@ -238,22 +261,18 @@ pub fn kubi_main() { //Run update workflows world.run_workload(update).unwrap(); - //Start rendering (maybe use custom views for this?) - let target = { - let renderer = world.borrow::>().unwrap(); - renderer.begin() - }; - world.add_unique(target); - - //Run render workflow - world.run_workload(render).unwrap(); - - //Finish rendering + //Start rendering { - let target = world.remove_unique::().unwrap(); - let renderer = world.borrow::>().unwrap(); - renderer.end(target); - } + let mut target = { + let renderer = world.borrow::>().unwrap(); + renderer.begin() + }; + render(&mut world, &mut target); + { + let renderer = world.borrow::>().unwrap(); + renderer.end(target); + } + }; //After frame end world.run_workload(after_frame_end).unwrap(); diff --git a/kubi/src/rendering.rs b/kubi/src/rendering.rs index 9c148f9..e79f7db 100644 --- a/kubi/src/rendering.rs +++ b/kubi/src/rendering.rs @@ -1,4 +1,4 @@ -use shipyard::{Unique, Workload, IntoWorkload, WorkloadModificator}; +use shipyard::{Unique, Workload, IntoWorkload, WorkloadModificator, AllStoragesView}; use winit::{ event_loop::EventLoop, window::{Window, WindowBuilder, Fullscreen}, @@ -25,8 +25,7 @@ pub const OPENGL_TO_WGPU_MATRIX: Mat4 = Mat4::from_cols_array(&[ 0.0, 0.0, 0.5, 1.0, ]); -#[derive(Unique)] -pub struct RenderTarget { +pub struct RenderData { pub output: wgpu::SurfaceTexture, pub view: wgpu::TextureView, pub encoder: wgpu::CommandEncoder, @@ -163,7 +162,7 @@ impl Renderer { } /// Start a new frame - pub fn begin(&self) -> RenderTarget { + pub fn begin(&self) -> RenderData { //Surface texture let output = self.surface.get_current_texture().unwrap(); @@ -171,35 +170,14 @@ impl Renderer { let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default()); //Encoder - let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + let encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: Some("RenderEncoder"), }); - //Begin render pass - { - let _render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: Some("RenderPass"), - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: 0.1, - g: 0.2, - b: 0.3, - a: 1.0, - }), - store: true, - }, - })], - depth_stencil_attachment: None, - }); - } - - RenderTarget { output, view, encoder } + RenderData { output, view, encoder } } - pub fn end(&self, target: RenderTarget) { + pub fn end(&self, target: RenderData) { self.queue.submit([target.encoder.finish()]); target.output.present(); } @@ -229,3 +207,35 @@ pub fn init_rendering_internals() -> Workload { ).into_workload().after_all(compile_shaders), ).into_workload() } + +macro_rules! vertex_attributes { + ( + $T: ident, + $( + $name:ident: $vertex_format:ident + ),* + ) => { + impl $T { + pub const VERTEX_ATTRIBUTES: [::wgpu::VertexAttribute; [$(stringify!($name)),*].len()] = { + let mut offset = 0; + let mut shader_location = 0; + [ + $( + { + let attribute = wgpu::VertexAttribute { + format: ::wgpu::VertexFormat::$vertex_format, + offset, shader_location, + }; + #[allow(unused_assignments)] { + shader_location += 1; + offset += ::wgpu::VertexFormat::$vertex_format.size(); + } + attribute + }, + )* + ] + }; + } + }; +} +pub(crate) use vertex_attributes; diff --git a/kubi/src/rendering/selection_box.rs b/kubi/src/rendering/selection_box.rs index 86386aa..cb64478 100644 --- a/kubi/src/rendering/selection_box.rs +++ b/kubi/src/rendering/selection_box.rs @@ -5,7 +5,7 @@ use crate::{ camera::Camera, }; use super::{ - RenderTarget, + RenderData, primitives::cube::CubePrimitive, }; diff --git a/kubi/src/rendering/world.rs b/kubi/src/rendering/world.rs index 9b80e85..4c9982c 100644 --- a/kubi/src/rendering/world.rs +++ b/kubi/src/rendering/world.rs @@ -1,6 +1,6 @@ use glam::{Vec3, Mat4, Quat, ivec3}; use shipyard::{NonSendSync, UniqueView, UniqueViewMut, View, IntoIter, track, Unique, AllStoragesView}; -use wgpu::util::DeviceExt; +use wgpu::{util::DeviceExt, RenderPass}; use crate::{ camera::Camera, player::MainPlayer, @@ -10,18 +10,25 @@ use crate::{ ChunkStorage, ChunkMeshStorage, chunk::CHUNK_SIZE, - }, settings::GameSettings, + }, settings::GameSettings, util::yolo, }; -use super::{Renderer, RenderTarget, shaders::Shaders, camera_uniform::CameraUniformBuffer}; +use super::{Renderer, RenderData, shaders::Shaders, camera_uniform::CameraUniformBuffer, vertex_attributes}; #[repr(C, packed)] #[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] pub struct ChunkVertex { pub position: [f32; 3], pub normal: [f32; 3], - pub uv: [f32; 2], + pub tex_coords: [f32; 2], pub tex_index: u32, } +vertex_attributes!( + ChunkVertex, + position: Float32x3, + normal: Float32x3, + tex_coords: Float32x2, + tex_index: Uint32 +); #[repr(C, packed)] #[derive(Clone, Copy, Default, bytemuck::Pod, bytemuck::Zeroable)] @@ -44,42 +51,6 @@ pub fn init_gpu_data( let shaders: UniqueView<'_, Shaders> = storages.borrow::>().unwrap(); let camera_uniform = storages.borrow::>().unwrap(); - let pipeline = renderer.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("WorldRenderPipeline"), - layout: Some(&renderer.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("WorldRenderPipelineLayout"), - bind_group_layouts: &[], - push_constant_ranges: &[], - })), - vertex: wgpu::VertexState { - module: &shaders.world, - entry_point: "vs_main", - buffers: &[], - }, - fragment: Some(wgpu::FragmentState { - module: &shaders.world, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format: renderer.config.format, - blend: Some(wgpu::BlendState::REPLACE), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - strip_index_format: None, - front_face: wgpu::FrontFace::Ccw, - cull_mode: Some(wgpu::Face::Back), - polygon_mode: wgpu::PolygonMode::Fill, - unclipped_depth: false, - conservative: false, - }, - //TODO enable depth buffer - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - }); - let uniform_buffer = renderer.device.create_buffer_init( &wgpu::util::BufferInitDescriptor { label: Some("WorldUniformBuffer"), @@ -113,6 +84,7 @@ pub fn init_gpu_data( ], label: Some("WorldBindGroupLayout"), }); + let bind_group = renderer.device.create_bind_group(&wgpu::BindGroupDescriptor { layout: &bind_group_layout, entries: &[ @@ -128,12 +100,56 @@ pub fn init_gpu_data( label: Some("WorldBindGroup"), }); + let pipeline_layout = renderer.device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("WorldRenderPipelineLayout"), + bind_group_layouts: &[&bind_group_layout], + push_constant_ranges: &[], + }); + + let pipeline = renderer.device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("WorldRenderPipeline"), + layout: Some(&pipeline_layout), + vertex: wgpu::VertexState { + module: &shaders.world, + entry_point: "vs_main", + buffers: &[ + wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &ChunkVertex::VERTEX_ATTRIBUTES, + } + ], + }, + fragment: Some(wgpu::FragmentState { + module: &shaders.world, + entry_point: "fs_main", + targets: &[Some(wgpu::ColorTargetState { + format: renderer.config.format, + blend: Some(wgpu::BlendState::REPLACE), + write_mask: wgpu::ColorWrites::ALL, + })], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: Some(wgpu::Face::Back), + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + //TODO enable depth buffer + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + }); + storages.add_unique(GpuData { pipeline, uniform_buffer, bind_group }); } pub fn draw_world( + render_pass: &mut RenderPass, renderer: UniqueView, - mut target: UniqueViewMut, gpu_data: UniqueView, chunks: UniqueView, meshes: UniqueView, @@ -147,10 +163,19 @@ pub fn draw_world( let world_position = position.as_vec3() * CHUNK_SIZE as f32; //TODO culling like in the glium version + //TODO indirect //Draw chunk mesh - - //TODO: i need renderpass here! + for (&position, chunk) in &chunks.chunks { + // //! //TODO THIS IS PROBABLY UB + // AND I DONT FUCKING CARE + let mesh = unsafe { yolo(meshes.get(key).expect("Mesh index pointing to nothing")) }; + let bind_group = unsafe { yolo(&gpu_data.bind_group) }; + render_pass.set_bind_group(0, bind_group, &[]); + render_pass.set_vertex_buffer(0, mesh.vertex_buffer.slice(..)); + render_pass.set_index_buffer(mesh.index_buffer.slice(..), wgpu::IndexFormat::Uint32); + render_pass.draw_indexed(0..mesh.idx_count, 0, 0..1); + } } } } diff --git a/kubi/src/util.rs b/kubi/src/util.rs new file mode 100644 index 0000000..a47f8fa --- /dev/null +++ b/kubi/src/util.rs @@ -0,0 +1,33 @@ +pub unsafe fn yolo<'a, T>(t: &'a T) -> &'static T { + std::mem::transmute(t) +} + +// use shipyard::{World, AllStoragesView, AllStoragesViewMut, System, AllSystem}; + +// pub trait WorldExt { +// fn run_with_data_multiple + AllSystem<(T,), B>, const SIZE: usize>(&mut self, systems: [S; SIZE], data: T); +// } + +// impl<'a> WorldExt for AllStoragesView<'a> { +// fn run_with_data_multiple + AllSystem<(T,), B>, const SIZE: usize>(&mut self, systems: [S; SIZE], data: T) { +// for system in systems { +// self.run_with_data(system, data); +// } +// } +// } + +// impl<'a> WorldExt for AllStoragesViewMut<'a> { +// fn run_with_data_multiple + AllSystem<(T,), B>, const SIZE: usize>(&mut self, systems: [S; SIZE], data: T) { +// for system in systems { +// self.run_with_data(system, data); +// } +// } +// } + +// impl WorldExt for World { +// fn run_with_data_multiple + AllSystem<(T,), B>, const SIZE: usize>(&mut self, systems: [S; SIZE], data: T) { +// for system in systems { +// self.run_with_data(system, data); +// } +// } +// } diff --git a/kubi/src/world.rs b/kubi/src/world.rs index f813b43..6297f7b 100644 --- a/kubi/src/world.rs +++ b/kubi/src/world.rs @@ -18,6 +18,8 @@ use chunk::{Chunk, ChunkMesh, CHUNK_SIZE}; use tasks::ChunkTaskManager; use queue::BlockUpdateQueue; +///XXX: This is no longer required (`wgpu::Buffer` is `Send` + `Sync` unlike glium `IndexBuffer`/`VertexBuffer`) +///but I'd rather keep it as it will make switching to indirect drawing easier. #[derive(Default, Unique)] pub struct ChunkStorage { pub chunks: HashMap diff --git a/kubi/src/world/chunk.rs b/kubi/src/world/chunk.rs index fa7fbb9..b68ed87 100644 --- a/kubi/src/world/chunk.rs +++ b/kubi/src/world/chunk.rs @@ -14,6 +14,7 @@ impl ChunkData { pub struct ChunkMesh { pub vertex_buffer: wgpu::Buffer, pub index_buffer: wgpu::Buffer, + pub idx_count: u32, } #[derive(Clone, Copy, Debug, PartialEq, Eq, Default)] diff --git a/kubi/src/world/loading.rs b/kubi/src/world/loading.rs index 2952efe..54ac7a3 100644 --- a/kubi/src/world/loading.rs +++ b/kubi/src/world/loading.rs @@ -251,6 +251,7 @@ fn process_completed_tasks( let mesh = ChunkMesh { vertex_buffer, index_buffer, + idx_count: indexes.len() as u32 }; if let Some(index) = chunk.mesh_index { meshes.update(index, mesh).expect("Mesh update failed"); diff --git a/kubi/src/world/mesh/builder.rs b/kubi/src/world/mesh/builder.rs index 476d25b..cc303be 100644 --- a/kubi/src/world/mesh/builder.rs +++ b/kubi/src/world/mesh/builder.rs @@ -108,7 +108,7 @@ impl MeshBuilder { self.vertex_buffer.push(ChunkVertex { position: (coord + vert[i]).to_array(), normal: norm.to_array(), - uv: UV_COORDS[i], + tex_coords: UV_COORDS[i], tex_index: texture }); } @@ -131,7 +131,7 @@ impl MeshBuilder { self.vertex_buffer.push(ChunkVertex { position: (coord.as_vec3() + vertices[i]).to_array(), normal: normal_front, - uv: UV_COORDS[i], + tex_coords: UV_COORDS[i], tex_index: front_texture }) } @@ -139,7 +139,7 @@ impl MeshBuilder { self.vertex_buffer.push(ChunkVertex { position: (coord.as_vec3() + vertices[i]).to_array(), normal: normal_back, - uv: UV_COORDS[i], + tex_coords: UV_COORDS[i], tex_index: back_texture }) }