diff --git a/Cargo.toml b/Cargo.toml index c106661..115d30b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,8 @@ tiff = "0.10.3" wgpu = {version = "27.0.1", features = ["spirv"]} winit = "0.30.12" +[build-dependencies] +spirv-builder = {git = "https://github.com/rust-gpu/rust-gpu"} + [profile.release] opt-level = 3 diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..e075a73 --- /dev/null +++ b/build.rs @@ -0,0 +1,11 @@ +use spirv_builder::Capability; +use spirv_builder::MetadataPrintout; +use spirv_builder::SpirvBuilder; + +fn main() -> Result<(), Box> +{ + SpirvBuilder::new("shaders", "spirv-unknown-spv1.6") + .print_metadata(MetadataPrintout::Full) + .build()?; + Ok(()) +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..06deb6d --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,7 @@ +[toolchain] +channel = "nightly-2025-06-30" +components = ["rust-src", "rustc-dev", "llvm-tools"] +# commit_hash = 35f6036521777bdc0dcea1f980be4c192962a168 + +# Whenever changing the nightly channel, update the commit hash above, and +# change `REQUIRED_RUST_TOOLCHAIN` in `crates/rustc_codegen_spirv/build.rs` too. diff --git a/shaders/.gitignore b/shaders/.gitignore new file mode 100644 index 0000000..96ef6c0 --- /dev/null +++ b/shaders/.gitignore @@ -0,0 +1,2 @@ +/target +Cargo.lock diff --git a/shaders/Cargo.toml b/shaders/Cargo.toml new file mode 100644 index 0000000..6c10abf --- /dev/null +++ b/shaders/Cargo.toml @@ -0,0 +1,10 @@ +[lib] +crate-type = ["dylib"] + +[package] +name = "shaders" +version = "0.1.0" +edition = "2024" + +[dependencies] +spirv-std = {git = "https://github.com/rust-gpu/rust-gpu"} diff --git a/shaders/src/lib.rs b/shaders/src/lib.rs new file mode 100644 index 0000000..6bb1008 --- /dev/null +++ b/shaders/src/lib.rs @@ -0,0 +1,23 @@ +#![cfg_attr(target_arch = "spirv", no_std)] +use spirv_std::glam::Vec4; +use spirv_std::glam::vec2; +use spirv_std::glam::vec4; +use spirv_std::spirv; + +#[spirv(fragment)] +pub fn main_fs(output: &mut Vec4) +{ + *output = vec4(0.2, 1.0, 0.2, 1.0); +} + +#[spirv(vertex)] +pub fn main_vs(#[spirv(vertex_id)] in_id: &mut u32, #[spirv(position)] out_pos: &mut Vec4) +{ + let positions = [vec2(-1., 3.), vec2(-1., 1.), vec2(3., 1.)]; + *out_pos = Vec4::new( + positions[*in_id as usize].x, + positions[*in_id as usize].y, + 0., + 1., + ); +} diff --git a/shaders/bak.wgsl b/shaders_old/bak.wgsl similarity index 100% rename from shaders/bak.wgsl rename to shaders_old/bak.wgsl diff --git a/shaders/chunk.wgsl b/shaders_old/chunk.wgsl similarity index 100% rename from shaders/chunk.wgsl rename to shaders_old/chunk.wgsl diff --git a/shaders/cube.wgsl b/shaders_old/cube.wgsl similarity index 100% rename from shaders/cube.wgsl rename to shaders_old/cube.wgsl diff --git a/shaders/lighting.wgsl b/shaders_old/lighting.wgsl similarity index 100% rename from shaders/lighting.wgsl rename to shaders_old/lighting.wgsl diff --git a/shaders/voxel_rt.wgsl b/shaders_old/voxel_rt.wgsl similarity index 100% rename from shaders/voxel_rt.wgsl rename to shaders_old/voxel_rt.wgsl diff --git a/src/hm_renderer.rs b/src/hm_renderer.rs deleted file mode 100644 index 22c546e..0000000 --- a/src/hm_renderer.rs +++ /dev/null @@ -1,166 +0,0 @@ -use std::fs::File; -use std::io::BufReader; -use std::path::Path; - -use itertools::Itertools; -use tiff::decoder::Decoder; -use wgpu::Device; -use wgpu::RenderPass; -use wgpu::TextureFormat; - -use crate::state::Camera; -use crate::voxel_renderer::VoxelRenderer; - -pub struct HeightMapRenderer -{ - width: u32, - height: u32, - - heightmap: Vec, - height_min: f32, - height_max: f32, - - alive_chunks: Vec>, - eye_pos: cgmath::Vector3, - - voxel_renderer: VoxelRenderer, -} - -impl HeightMapRenderer -{ - pub fn from_image( - path: impl AsRef, - device: &Device, - surface_format: TextureFormat, - ) -> Self - { - let file = File::open(path).unwrap(); - let mut decoder = Decoder::new(BufReader::new(file)).unwrap(); - let (width, height) = decoder.dimensions().unwrap(); - - let decoded = decoder.read_image().unwrap(); - let pixels = match decoded - { - tiff::decoder::DecodingResult::F32(decoded_pixels) => decoded_pixels, - _ => - { - panic!("Unsuported image format."); - } - }; - - Self { - width, - height, - - height_min: pixels.iter().copied().reduce(f32::min).unwrap_or(0.), - height_max: pixels.iter().copied().reduce(f32::max).unwrap_or(0.), - - heightmap: pixels, - alive_chunks: vec![], - eye_pos: cgmath::Vector3::new(0., 0., 0.), - - voxel_renderer: VoxelRenderer::new(device, surface_format), - } - } - - pub fn set_eye_pos(&mut self, eye_pos: cgmath::Vector3, device: &Device) - { - self.eye_pos = eye_pos; - // self.voxel_renderer.set_chunks( - // device, - // &[ - // cgmath::Vector3::new(-1, 0, -1), - // cgmath::Vector3::new(-1, 0, 1), - // cgmath::Vector3::new(1, 0, -1), - // cgmath::Vector3::new(1, 0, 1), - // ], - // ); - // return; - // Compute alive chunks - - let eye_chunk = cgmath::Vector3::new( - eye_pos.x.floor() as i32, - eye_pos.y.floor() as i32, - eye_pos.z.floor() as i32, - ); - let mut alive_chunks = vec![]; - for ((x, y), z) in (-10..=10) - .cartesian_product(-10..=10) - .cartesian_product(-10..=10) - { - let chunk = eye_chunk + cgmath::Vector3::new(x, y, z); - if chunk.x >= 0 && chunk.z >= 0 - { - if chunk.y == -1 - { - alive_chunks.push(chunk); - } - } - } - - self.alive_chunks = alive_chunks; - if !self.alive_chunks.is_empty() - { - self.voxel_renderer.set_chunks(device, &self.alive_chunks); - } - return; - - let mut alive_chunks = vec![]; - for ((x, y), z) in (-10..=10) - .cartesian_product(-10..=10) - .cartesian_product(-10..=10) - { - let chunk = eye_chunk + cgmath::Vector3::new(x, y, z); - if chunk.x >= 0 - && chunk.x < (self.width / 256) as i32 - && chunk.z >= 0 - && chunk.z < (self.height / 256) as i32 - { - if chunk.y == -1 - { - alive_chunks.push(chunk); - println!("{}, {}, {}", chunk.x, chunk.y, chunk.z); - } - continue; - - let chunk_voxel_height = y * 256; - let submit = [ - (chunk.x, chunk.z), - (chunk.x + 1, chunk.z), - (chunk.x, chunk.z + 1), - (chunk.x + 1, chunk.z + 1), - ] - .iter() - .map(|(chunk_x, chunk_z)| { - let voxel_x = chunk_x * 256; - let voxel_z = chunk_z * 256; - - let altitude = - self.heightmap[voxel_x as usize + voxel_z as usize * self.width as usize]; - let voxel_height = - map(altitude, self.height_min, self.height_max, 0., 100. * 256.); - //println!("{}", voxel_height); - (chunk_voxel_height as f32) < voxel_height - }) - .reduce(|a, b| a || b) - .unwrap_or(false); - if submit - { - alive_chunks.push(chunk); - } - } - } - } - - pub fn render(&mut self, render_pass: &mut RenderPass, camera: &Camera) - { - if !self.alive_chunks.is_empty() - {} - self.voxel_renderer.render(render_pass, camera); - } -} - -fn map(x: f32, x_min: f32, x_max: f32, y_min: f32, y_max: f32) -> f32 -{ - ((x - x_min) / (x_max - x_min)) * (y_max - y_min) + y_min -} diff --git a/src/lib.rs b/src/lib.rs index e34277c..af77405 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,11 +2,8 @@ #![feature(generic_const_exprs)] pub mod egui_renderer; -pub mod hm_renderer; pub mod state; pub mod voxel; -pub mod voxel_renderer; - use std::sync::Arc; use winit::application::ApplicationHandler; diff --git a/src/state.rs b/src/state.rs index 0d1dc35..803523d 100644 --- a/src/state.rs +++ b/src/state.rs @@ -15,6 +15,7 @@ use wgpu::Device; use wgpu::Extent3d; use wgpu::FeaturesWGPU; use wgpu::FeaturesWebGPU; +use wgpu::RenderPipeline; use wgpu::TextureDescriptor; use wgpu::TextureFormat; use wgpu::TextureUsages; @@ -26,7 +27,6 @@ use winit::keyboard::KeyCode; use winit::window::Window; use crate::egui_renderer::EguiState; -use crate::voxel_renderer::VoxelRenderer; pub struct State { @@ -46,8 +46,7 @@ pub struct State last_frame: Instant, depth_buffer: TextureView, - - voxel_renderer: VoxelRenderer, + test_pipeline: RenderPipeline, } #[derive(Clone, Copy)] @@ -72,6 +71,8 @@ impl State { pub async fn new(window: Arc) -> State { + let shaders = wgpu::include_spirv!(env!("shaders.spv")); + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { backends: Backends::VULKAN, ..wgpu::InstanceDescriptor::default() @@ -107,9 +108,52 @@ impl State let cap = surface.get_capabilities(&adapter); let surface_format = cap.formats[0]; + let shader_module = device.create_shader_module(shaders); + let test_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("chunk_pipeline"), + layout: None, + vertex: wgpu::VertexState { + module: &shader_module, + entry_point: Some("vs_main"), + buffers: &[], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader_module, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + format: surface_format, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + ..Default::default() + }, + depth_stencil: Some(wgpu::DepthStencilState { + format: TextureFormat::Depth24Plus, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, + stencil: wgpu::StencilState::default(), + bias: wgpu::DepthBiasState::default(), + }), + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + + cache: None, + }); + let state = State { egui_state: EguiState::new(&device, surface_format, &window), + test_pipeline, + window, queue, size, @@ -139,7 +183,6 @@ impl State TextureFormat::Depth24PlusStencil8, TextureUsages::RENDER_ATTACHMENT, ), - voxel_renderer: VoxelRenderer::new(&device, surface_format), device, }; @@ -249,6 +292,9 @@ impl State timestamp_writes: None, occlusion_query_set: None, }); + + hm_pass.set_pipeline(&self.test_pipeline); + hm_pass.draw(0..3, 0..1); } // Egui Pass diff --git a/src/voxel_renderer.rs b/src/voxel_renderer.rs deleted file mode 100644 index 998e072..0000000 --- a/src/voxel_renderer.rs +++ /dev/null @@ -1,153 +0,0 @@ -use cgmath::EuclideanSpace; -use cgmath::SquareMatrix; -use crevice::std430::AsStd430; -use wgpu::Buffer; -use wgpu::BufferDescriptor; -use wgpu::BufferUsages; -use wgpu::Device; -use wgpu::PushConstantRange; -use wgpu::RenderPass; -use wgpu::RenderPipeline; -use wgpu::ShaderStages; -use wgpu::TextureFormat; -use wgpu::VertexAttribute; -use wgpu::VertexBufferLayout; -use wgpu::VertexFormat; -use wgpu::include_wgsl; -use wgpu::util::BufferInitDescriptor; -use wgpu::util::DeviceExt; - -use crate::state::Camera; - -pub struct VoxelRenderer -{ - chunk_instances_capacity: u32, - chunk_instances: Buffer, - chunk_pipeline: RenderPipeline, -} - -impl VoxelRenderer -{ - pub fn new(device: &Device, surface_format: TextureFormat) -> Self - { - let chunk_shader_module = - device.create_shader_module(include_wgsl!("../shaders/chunk.wgsl")); - let chunk_pipeline_layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("Render3D Mesh Pipeline Layout"), - bind_group_layouts: &[], - push_constant_ranges: &[PushConstantRange { - stages: ShaderStages::VERTEX, - range: 0..ChunkPushConstants::std430_size_static() as u32, - }], - }); - - let chunk_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("chunk_pipeline"), - layout: Some(&chunk_pipeline_layout), - vertex: wgpu::VertexState { - module: &chunk_shader_module, - entry_point: Some("vertex_main"), - buffers: &[VertexBufferLayout { - array_stride: size_of::() as u64 * 3, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &[VertexAttribute { - format: VertexFormat::Sint32x3, - offset: 0, - shader_location: 0, - }], - }], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &chunk_shader_module, - entry_point: Some("fragment_main"), - targets: &[Some(wgpu::ColorTargetState { - format: surface_format, - blend: None, - write_mask: wgpu::ColorWrites::ALL, - })], - compilation_options: wgpu::PipelineCompilationOptions::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - ..Default::default() - }, - depth_stencil: Some(wgpu::DepthStencilState { - format: TextureFormat::Depth24Plus, - depth_write_enabled: true, - depth_compare: wgpu::CompareFunction::Less, - stencil: wgpu::StencilState::default(), - bias: wgpu::DepthBiasState::default(), - }), - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - - cache: None, - }); - - Self { - chunk_instances_capacity: 1, - chunk_instances: device.create_buffer(&BufferDescriptor { - label: Some("chunk_instances_buffer"), - size: size_of::() as u64, - usage: BufferUsages::VERTEX, - mapped_at_creation: false, - }), - - chunk_pipeline, - } - } - - pub fn set_chunks(&mut self, device: &Device, chunks: &[cgmath::Vector3]) - { - self.chunk_instances = device.create_buffer_init(&BufferInitDescriptor { - label: Some("chunk_instances_buffer"), - contents: unsafe { - std::slice::from_raw_parts( - chunks.as_ptr() as *const u8, - std::mem::size_of_val(chunks), - ) - }, - usage: BufferUsages::VERTEX, - }); - self.chunk_instances_capacity = chunks.len() as u32; - } - - pub fn render(&mut self, render_pass: &mut RenderPass, camera: &Camera) - { - render_pass.set_pipeline(&self.chunk_pipeline); - - //renderpass.set_vertex_buffer(0, self.positions_buffer.slice(..)); - render_pass.set_push_constants( - ShaderStages::VERTEX, - 0, - ChunkPushConstants { - view_projection: camera.view_proj(), - transform: cgmath::Matrix4::identity(), - eye_position: camera.eye.to_vec(), - } - .as_std430() - .as_bytes(), - ); - render_pass.set_vertex_buffer(0, self.chunk_instances.slice(..)); - render_pass.draw(0..(6 * 2 * 3), 0..(self.chunk_instances_capacity)); - } -} - -struct ChunkInstance -{ - location: cgmath::Vector3, -} - -#[derive(crevice::std430::AsStd430)] -pub struct ChunkPushConstants -{ - view_projection: cgmath::Matrix4, - transform: cgmath::Matrix4, - eye_position: cgmath::Vector3, -}