diff options
| author | Kai Stevenson <kai@kaistevenson.com> | 2026-08-25 00:32:16 -0700 |
|---|---|---|
| committer | Kai Stevenson <kai@kaistevenson.com> | 2026-08-25 00:32:16 -0700 |
| commit | a779011b9048cdda04887c77e20ee8936f332a7a (patch) | |
| tree | 6a771ff692d1717967755cc56e4534a582dd117a /src/renderer/mod.rs | |
| parent | 9cb48c504ddb3882f39e2b17048f0593316a95ce (diff) | |
vfx pipeline
Diffstat (limited to 'src/renderer/mod.rs')
| -rw-r--r-- | src/renderer/mod.rs | 222 |
1 files changed, 212 insertions, 10 deletions
diff --git a/src/renderer/mod.rs b/src/renderer/mod.rs index f21c794..c1b156e 100644 --- a/src/renderer/mod.rs +++ b/src/renderer/mod.rs @@ -9,21 +9,22 @@ use crate::{ Config, Diagnostics, InputManager, camera::Camera, config::{CELLS_IN_CHUNK, CHUNK_SIZE}, - content::materials::MaterialId, + content::{materials::MaterialId, vfx::VfxMaterialId}, renderer::ui::draw_egui, sim::{ - cell_manager::manager::CellManager, - entity::EntityId, - rb_manager::debug_render::DebugVertex, - sim_manager::{SimCtx, SimManager}, + cell_manager::manager::CellManager, entity::EntityId, + rb_manager::debug_render::DebugVertex, sim_manager::SimManager, }, + vfx::VfxManager, }; // TODO derive from shared chunk config const CHUNK_SLOTS: usize = 11 * 200; -const RB_ENTITY_SLOTS: usize = 64; -const CELL_SLOTS: usize = CHUNK_SLOTS + RB_ENTITY_SLOTS; +const ENTITY_SLOTS: usize = 64; +const CELL_SLOTS: usize = CHUNK_SLOTS + ENTITY_SLOTS; const PARTICLE_SLOTS: usize = 10_000; +// TODO make this dynamic +const VFX_LINE_SLOTS: usize = 10_000; const INITIAL_DEBUG_VERTEX_CAPACITY: usize = 4096; @@ -46,6 +47,25 @@ struct RendererParticle { life: f32, } +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct RendererVfxMaterial { + color: [f32; 4], + length: f32, + _padding: [u32; 3], +} + +#[repr(C)] +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +struct RendererVfxLine { + a: [f32; 2], + b: [f32; 2], + width: f32, + material: u32, + alive_for: f32, + lifetime: f32, +} + fn srgb_to_linear(channel: u8) -> f32 { let channel = channel as f32 / 255.0; if channel <= 0.04045 { @@ -84,7 +104,7 @@ pub struct RendererState { // common camera_uniform_buffer: wgpu::Buffer, - // world pixels + // instances instance_pipeline: wgpu::RenderPipeline, instance_buffer: wgpu::Buffer, cell_buffer: wgpu::Buffer, @@ -95,6 +115,11 @@ pub struct RendererState { particle_buffer: wgpu::Buffer, particle_bind_group: wgpu::BindGroup, + // vfx + vfx_line_pipeline: wgpu::RenderPipeline, + vfx_line_buffer: wgpu::Buffer, + vfx_line_bind_group: wgpu::BindGroup, + // physics debug lines debug_pipeline: wgpu::RenderPipeline, debug_bind_group: wgpu::BindGroup, @@ -193,6 +218,39 @@ impl RendererState { mapped_at_creation: false, }); + queue.write_buffer(&palette_buffer, 0, bytemuck::cast_slice(&palette)); + + // --- VFX materials --- + let mut vfx_material_palette = [RendererVfxMaterial { + color: [0.0f32; 4], + length: 0.0, + _padding: [0; 3], + }; VfxMaterialId::ALL.len()]; + for material in VfxMaterialId::ALL { + let def = material.def(); + let i = material as usize; + vfx_material_palette[i].color = [ + srgb_to_linear(def.color.0), + srgb_to_linear(def.color.1), + srgb_to_linear(def.color.2), + def.color.3 as f32 / 255.0, + ]; + vfx_material_palette[i].length = def.length; + } + + let vfx_material_palette_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("VFX Material Palette buffer"), + size: size_of_val(&vfx_material_palette) as u64, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + queue.write_buffer( + &vfx_material_palette_buffer, + 0, + bytemuck::cast_slice(&vfx_material_palette), + ); + // --- INSTANCES --- let instance_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -298,8 +356,6 @@ impl RendererState { mapped_at_creation: false, }); - queue.write_buffer(&palette_buffer, 0, bytemuck::cast_slice(&palette)); - let instance_buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("Instance buffer"), size: (CELL_SLOTS * size_of::<RendererInstance>()) as u64, @@ -450,6 +506,120 @@ impl RendererState { ], }); + // --- VFX_LINES --- + let vfx_line_bind_group_layout = + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[ + // camera uniform + wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: None, + }, + binding: 0, + count: None, + visibility: wgpu::ShaderStages::VERTEX, + }, + // material palette + wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + binding: 1, + count: None, + // don't need vertex here + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + }, + // instance buffer + wgpu::BindGroupLayoutEntry { + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: true }, + has_dynamic_offset: false, + min_binding_size: None, + }, + binding: 2, + count: None, + visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT, + }, + ], + }); + + let vfx_line_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("VFX Line shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("../shader/vfx_line.wgsl").into()), + }); + + let vfx_line_pipeline_layout = + device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + immediate_size: 0, + bind_group_layouts: &[Some(&vfx_line_bind_group_layout)], + }); + + let vfx_line_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: None, + layout: Some(&vfx_line_pipeline_layout), + vertex: wgpu::VertexState { + module: &vfx_line_shader, + entry_point: Some("vs_main"), + buffers: &[], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &vfx_line_shader, + entry_point: Some("fs_main"), + targets: &[Some(wgpu::ColorTargetState { + format: config.format, + blend: Some(wgpu::BlendState::ALPHA_BLENDING), + write_mask: wgpu::ColorWrites::ALL, + })], + compilation_options: wgpu::PipelineCompilationOptions::default(), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + strip_index_format: None, + front_face: wgpu::FrontFace::Ccw, + cull_mode: None, + polygon_mode: wgpu::PolygonMode::Fill, + unclipped_depth: false, + conservative: false, + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview_mask: None, + cache: None, + }); + + let vfx_line_buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("VFX line buffer"), + size: (VFX_LINE_SLOTS * size_of::<RendererVfxLine>()) as u64, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let vfx_line_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &vfx_line_bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: camera_uniform_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: vfx_material_palette_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: vfx_line_buffer.as_entire_binding(), + }, + ], + }); + // --- DEBUG --- let debug_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { @@ -554,6 +724,10 @@ impl RendererState { particle_buffer, particle_bind_group, + vfx_line_pipeline, + vfx_line_buffer, + vfx_line_bind_group, + debug_pipeline, debug_bind_group, debug_vertex_buffer, @@ -575,6 +749,7 @@ impl RendererState { pub fn render( &mut self, sim: &mut SimManager, + vfx_manager: &VfxManager, camera: &mut Camera, config: &mut Config, diagnostics: &Diagnostics, @@ -784,6 +959,28 @@ impl RendererState { .write_buffer(&self.particle_buffer, 0, bytemuck::cast_slice(&particles)); } + let mut vfx_lines: Vec<RendererVfxLine> = Vec::new(); + + { + puffin::profile_scope!("Build VFX lines"); + // TODO could cull this to visible, maybe it's slower? + vfx_lines = vfx_manager + .lines + .iter() + .map(|p| RendererVfxLine { + a: p.a.to_array(), + b: p.b.to_array(), + material: p.material as u32, + width: p.width, + alive_for: p.alive_for, + lifetime: p.lifetime, + }) + .collect(); + + self.queue + .write_buffer(&self.vfx_line_buffer, 0, bytemuck::cast_slice(&vfx_lines)); + } + let debug_vertex_count = if config.debug_render { puffin::profile_scope!("Build physics debug lines"); let vertices = sim.rb_manager.debug_render(config.debug_render_mode); @@ -854,6 +1051,11 @@ impl RendererState { render_pass.set_bind_group(0, &self.particle_bind_group, &[]); render_pass.draw(0..4, 0..particles.len() as u32); + // vfx + render_pass.set_pipeline(&self.vfx_line_pipeline); + render_pass.set_bind_group(0, &self.vfx_line_bind_group, &[]); + render_pass.draw(0..4, 0..vfx_lines.len() as u32); + // debug if debug_vertex_count > 0 { render_pass.set_pipeline(&self.debug_pipeline); |
