diff --git a/src/render/bind_group.rs b/src/render/bind_group.rs index 7744e97..27a8203 100644 --- a/src/render/bind_group.rs +++ b/src/render/bind_group.rs @@ -1,5 +1,7 @@ use crate::texture::{DitherTextures, FlowmapTexture}; +use std::num::NonZeroU64; +use super::types::Uniforms; use super::Renderer; impl Renderer @@ -24,7 +26,11 @@ impl Renderer entries: &[ wgpu::BindGroupEntry { binding: 0, - resource: uniform_buffer.as_entire_binding(), + resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding { + buffer: uniform_buffer, + offset: 0, + size: NonZeroU64::new(std::mem::size_of::() as u64), + }), }, wgpu::BindGroupEntry { binding: 1, diff --git a/src/render/debug_overlay.rs b/src/render/debug_overlay.rs index 2161c80..4f8adf6 100644 --- a/src/render/debug_overlay.rs +++ b/src/render/debug_overlay.rs @@ -102,32 +102,31 @@ impl DebugOverlay cache: None, }); - let pipeline_snow_light = - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("Snow Light Debug Pipeline"), - layout: Some(&snow_light_layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: Some("vs_main"), - buffers: &[ScreenVertex::desc()], - compilation_options: Default::default(), - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: Some("fs_snow_light"), - targets: &[Some(color_target)], - compilation_options: Default::default(), - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - cull_mode: None, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview_mask: None, - cache: None, - }); + let pipeline_snow_light = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("Snow Light Debug Pipeline"), + layout: Some(&snow_light_layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + buffers: &[ScreenVertex::desc()], + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_snow_light"), + targets: &[Some(color_target)], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + cull_mode: None, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview_mask: None, + cache: None, + }); Self { pipeline_shadow, diff --git a/src/render/mod.rs b/src/render/mod.rs index 5ee1a18..635c9fc 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -6,12 +6,19 @@ mod types; pub use types::{DrawCall, Pipeline, Spotlight, SpotlightRaw, Uniforms, MAX_SPOTLIGHTS}; +use crate::entity::EntityHandle; + use crate::debug::DebugMode; use crate::postprocess::{create_blit_pipeline, create_fullscreen_quad, LowResFramebuffer}; use crate::texture::{DitherTextures, FlowmapTexture}; -use pipeline::{create_debug_lines_pipeline, create_main_pipeline, create_snow_clipmap_pipeline, - create_wireframe_pipeline}; +use pipeline::{ + create_debug_lines_pipeline, create_main_pipeline, create_snow_clipmap_pipeline, + create_wireframe_pipeline, +}; use std::cell::RefCell; +use std::num::NonZeroU64; + +const MAX_DRAW_CALLS: usize = 64; pub struct Renderer { @@ -62,6 +69,8 @@ pub struct Renderer snow_light_accumulation: Option, snow_light_bound: bool, + + pub selected_entity: Option, } impl Renderer @@ -90,8 +99,9 @@ impl Renderer .await .map_err(|_| "Failed to find adapter")?; - let wireframe_supported = - adapter.features().contains(wgpu::Features::POLYGON_MODE_LINE); + let wireframe_supported = adapter + .features() + .contains(wgpu::Features::POLYGON_MODE_LINE); let required_features = if wireframe_supported { wgpu::Features::POLYGON_MODE_LINE @@ -129,7 +139,7 @@ impl Renderer let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Uniform Buffer"), - size: std::mem::size_of::() as wgpu::BufferAddress, + size: (std::mem::size_of::() * MAX_DRAW_CALLS) as wgpu::BufferAddress, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, mapped_at_creation: false, }); @@ -306,8 +316,8 @@ impl Renderer visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, ty: wgpu::BindingType::Buffer { ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: None, + has_dynamic_offset: true, + min_binding_size: NonZeroU64::new(std::mem::size_of::() as u64), }, count: None, }, @@ -463,15 +473,22 @@ impl Renderer let wireframe_pipeline = if wireframe_supported { - Some(create_wireframe_pipeline(&device, config.format, &bind_group_layout)) + Some(create_wireframe_pipeline( + &device, + config.format, + &bind_group_layout, + )) } else { None }; - let debug_lines_pipeline = - Some(create_debug_lines_pipeline(&device, config.format, &bind_group_layout)); + let debug_lines_pipeline = Some(create_debug_lines_pipeline( + &device, + config.format, + &bind_group_layout, + )); let debug_overlay = Some(debug_overlay::DebugOverlay::new(&device, config.format)); @@ -533,6 +550,7 @@ impl Renderer dummy_snow_light_sampler, snow_light_accumulation: None, snow_light_bound: false, + selected_entity: None, }) } @@ -585,6 +603,9 @@ impl Renderer label: Some("Render Encoder"), }); + let uniform_size = std::mem::size_of::() as u64; + let mut uniform_slot: u64 = 0; + for (i, draw_call) in draw_calls.iter().enumerate() { let uniforms = Uniforms::new( @@ -603,8 +624,15 @@ impl Renderer &self.spotlights, debug_mode.as_u32(), ); - self.queue - .write_buffer(&self.uniform_buffer, 0, bytemuck::cast_slice(&[uniforms])); + + let offset = uniform_slot * uniform_size; + uniform_slot += 1; + + self.queue.write_buffer( + &self.uniform_buffer, + offset, + bytemuck::cast_slice(&[uniforms]), + ); { let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -660,7 +688,7 @@ impl Renderer .unwrap_or(&self.standard_pipeline), }; render_pass.set_pipeline(pipeline); - render_pass.set_bind_group(0, &self.bind_group, &[]); + render_pass.set_bind_group(0, &self.bind_group, &[offset as u32]); if let Some(ref displacement_bind_group) = draw_call.displacement_bind_group { @@ -686,7 +714,10 @@ impl Renderer { for draw_call in draw_calls.iter() { - if matches!(draw_call.pipeline, Pipeline::SnowClipmap | Pipeline::DebugLines) + if matches!( + draw_call.pipeline, + Pipeline::SnowClipmap | Pipeline::DebugLines + ) { continue; } @@ -707,9 +738,11 @@ impl Renderer &self.spotlights, 4, ); + let wire_offset = uniform_slot * uniform_size; + uniform_slot += 1; self.queue.write_buffer( &self.uniform_buffer, - 0, + wire_offset, bytemuck::cast_slice(&[uniforms]), ); @@ -742,7 +775,7 @@ impl Renderer }); wire_pass.set_pipeline(wireframe_pipeline); - wire_pass.set_bind_group(0, &self.bind_group, &[]); + wire_pass.set_bind_group(0, &self.bind_group, &[wire_offset as u32]); wire_pass.set_vertex_buffer(0, draw_call.vertex_buffer.slice(..)); if let Some(ref instance_buffer) = draw_call.instance_buffer @@ -764,6 +797,99 @@ impl Renderer } } + if let Some(selected) = self.selected_entity + { + if let Some(ref wireframe_pipeline) = self.wireframe_pipeline + { + for draw_call in draw_calls.iter() + { + if draw_call.entity != Some(selected) + { + continue; + } + + if matches!( + draw_call.pipeline, + Pipeline::SnowClipmap | Pipeline::DebugLines + ) + { + continue; + } + + let uniforms = Uniforms::new( + draw_call.model, + *view, + *projection, + &light_view_projections, + camera_position, + player_position, + self.terrain_height_scale, + time, + self.shadow_bias, + draw_call.tile_scale, + draw_call.enable_dissolve, + draw_call.enable_snow_light, + &self.spotlights, + 4, + ); + let sel_offset = uniform_slot * uniform_size; + uniform_slot += 1; + self.queue.write_buffer( + &self.uniform_buffer, + sel_offset, + bytemuck::cast_slice(&[uniforms]), + ); + + { + let mut sel_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: Some("Selection Wireframe Pass"), + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &self.framebuffer.view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }, + depth_slice: None, + })], + depth_stencil_attachment: Some( + wgpu::RenderPassDepthStencilAttachment { + view: &self.framebuffer.depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Load, + store: wgpu::StoreOp::Store, + }), + stencil_ops: None, + }, + ), + timestamp_writes: None, + occlusion_query_set: None, + multiview_mask: None, + }); + + sel_pass.set_pipeline(wireframe_pipeline); + sel_pass.set_bind_group(0, &self.bind_group, &[sel_offset as u32]); + sel_pass.set_vertex_buffer(0, draw_call.vertex_buffer.slice(..)); + + if let Some(ref instance_buffer) = draw_call.instance_buffer + { + sel_pass.set_vertex_buffer(1, instance_buffer.slice(..)); + } + + sel_pass.set_index_buffer( + draw_call.index_buffer.slice(..), + wgpu::IndexFormat::Uint32, + ); + sel_pass.draw_indexed( + 0..draw_call.num_indices, + 0, + 0..draw_call.num_instances, + ); + } + } + } + } + if debug_mode == DebugMode::ShadowMap { if let Some(ref overlay) = self.debug_overlay @@ -1074,3 +1200,12 @@ pub fn update_spotlights(spotlights: Vec) renderer.spotlights = spotlights; }); } + +pub fn set_selected_entity(entity: Option) +{ + GLOBAL_RENDERER.with(|r| { + let mut renderer = r.borrow_mut(); + let renderer = renderer.as_mut().expect("Renderer not set"); + renderer.selected_entity = entity; + }); +} diff --git a/src/render/types.rs b/src/render/types.rs index 20d5fa5..d42a2cd 100644 --- a/src/render/types.rs +++ b/src/render/types.rs @@ -1,6 +1,8 @@ use bytemuck::{Pod, Zeroable}; use glam::Mat4; +use crate::entity::EntityHandle; + pub const MAX_SPOTLIGHTS: usize = 4; #[repr(C)] @@ -156,4 +158,5 @@ pub struct DrawCall pub enable_dissolve: bool, pub enable_snow_light: bool, pub displacement_bind_group: Option, + pub entity: Option, } diff --git a/src/systems/render.rs b/src/systems/render.rs index ce22765..dafec63 100644 --- a/src/systems/render.rs +++ b/src/systems/render.rs @@ -60,6 +60,7 @@ pub fn render_system(world: &World) -> Vec enable_dissolve: mesh_component.enable_dissolve, enable_snow_light: mesh_component.enable_snow_light, displacement_bind_group: None, + entity: Some(entity), }) }) .collect()