use std::{collections::HashSet, sync::Arc, time::Duration};
use egui::Context;
use lin_alg::f32::{Mat4, Vec3};
use wgpu::{
self, BindGroup, BindGroupLayout, BindingType, BlendState, Buffer, BufferBindingType,
BufferUsages, CommandEncoder, CommandEncoderDescriptor, DepthStencilState, Device, Face,
FragmentState, Queue, RenderPass, RenderPassDepthStencilAttachment, RenderPassDescriptor,
RenderPipeline, ShaderStages, StoreOp, SurfaceConfiguration, SurfaceTexture, TextureDescriptor,
TextureView, VertexBufferLayout, VertexState,
util::{BufferInitDescriptor, DeviceExt},
};
use winit::{
event::{DeviceEvent, WindowEvent},
window::Window,
};
use crate::{
camera::CAMERA_SIZE,
gauss::{
CAM_BASIS_SIZE, CameraBasis, GAUSS_INST_LAYOUT, GaussianInstance, QUAD_VERTEX_LAYOUT,
QUAD_VERTICES,
},
gui::GuiState,
input::{self, InputsCommanded},
system::{COLOR_FORMAT, DEPTH_FORMAT, process_engine_updates},
text_overlay::draw_text_overlay,
texture::Texture,
types::{
AmbientOcclusion, ControlScheme, EngineUpdates, GraphicsSettings, INSTANCE_LAYOUT,
INSTANCE_SIZE, InputSettings, Instance, Scene, UiSettings, VERTEX_LAYOUT,
},
viewport_rect,
};
pub const UP_VEC: Vec3 = Vec3 {
x: 0.,
y: 1.,
z: 0.,
};
pub const RIGHT_VEC: Vec3 = Vec3 {
x: 1.,
y: 0.,
z: 0.,
};
pub const FWD_VEC: Vec3 = Vec3 {
x: 0.,
y: 0.,
z: 1.,
};
#[derive(Clone, Debug, PartialEq, Default)]
pub enum EntityUpdate {
#[default]
None,
All,
Classes(Vec<u32>),
Ids(Vec<u32>),
Indexes((usize, usize)),
Append(usize),
}
impl EntityUpdate {
pub fn push_class(&mut self, class: u32) {
match self {
EntityUpdate::All => (),
EntityUpdate::Classes(v) => v.push(class),
_ => *self = EntityUpdate::Classes(vec![class]),
}
}
pub fn push_id(&mut self, id: u32) {
match self {
EntityUpdate::All => (),
EntityUpdate::Ids(v) => v.push(id),
_ => *self = EntityUpdate::Ids(vec![id]),
}
}
}
pub(crate) struct GraphicsState {
pub vertex_buf: Buffer,
pub vertex_buf_quad: Buffer, pub index_buf: Buffer,
instance_buf: Buffer,
instance_buf_transparent: Buffer,
instance_buf_gauss: Buffer,
pub bind_groups: BindGroupData,
pub camera_buf: Buffer,
camera_buf_halo: Buffer,
bind_group_cam_halo: wgpu::BindGroup,
pub cam_basis_buf: Buffer, lighting_buf: Buffer,
pub pipeline_mesh: RenderPipeline, pub pipeline_mesh_transparent: RenderPipeline, pub pipeline_mesh_transparent_back: RenderPipeline, pub pipeline_gauss: RenderPipeline, pipeline_halo: RenderPipeline,
pub depth_texture: Texture,
pub msaa_texture: Option<TextureView>, pub inputs_commanded: InputsCommanded,
pub scene: Scene,
mesh_mappings: Vec<(i32, u32, u32)>,
mesh_mappings_transparent: Vec<(i32, u32, u32)>,
pub window: Arc<Window>,
pub halo_expansion: f32,
pub depth_texture_contour: Texture,
pipeline_contour_depth: RenderPipeline,
pipeline_contour_overlay: RenderPipeline,
pub bind_group_contour: wgpu::BindGroup,
pub layout_contour: wgpu::BindGroupLayout,
pub contour_uniform_buf: Buffer,
pub depth_revealing: f32,
pub intersection_revealing: f32,
pub ssao_strength: f32,
pub msaa_samples: u32,
pub pending_msaa: Option<u32>,
pub surface_cfg: SurfaceConfiguration,
shader_mesh: wgpu::ShaderModule,
shader_gauss: wgpu::ShaderModule,
pipeline_ssao: RenderPipeline,
pub layout_ssao: wgpu::BindGroupLayout,
pub bind_group_ssao: wgpu::BindGroup,
pub ssao_uniform_buf: Buffer,
}
impl GraphicsState {
pub(crate) fn new(
device: &Device,
surface_cfg: &SurfaceConfiguration,
mut scene: Scene,
window: Arc<Window>,
msaa_samples: u32,
) -> Self {
let vertex_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Vertex buffer"),
contents: &[], usage: BufferUsages::VERTEX,
});
let mut quad_bytes = Vec::with_capacity(QUAD_VERTICES.len() * 8);
for q in QUAD_VERTICES {
quad_bytes.extend_from_slice(q.to_bytes().as_slice());
}
let vertex_buf_quad = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Gauss quadVertex buffer"),
contents: &quad_bytes,
usage: BufferUsages::VERTEX,
});
let index_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Index buffer"),
contents: &[], usage: BufferUsages::INDEX,
});
scene.camera.update_proj_mat();
let cam_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Camera buffer"),
contents: &scene.camera.to_bytes(),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});
let cam_basis_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("camera basis"),
size: size_of::<CameraBasis>() as wgpu::BufferAddress,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let lighting_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Lighting buffer"),
contents: &scene.lighting.to_bytes(),
usage: BufferUsages::STORAGE | BufferUsages::COPY_DST,
});
let bind_groups = create_bindgroups(device, &cam_buf, &cam_basis_buf, &lighting_buf);
let cam_halo_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Camera halo buffer"),
contents: &scene.camera.to_bytes(),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});
let bind_group_cam_halo = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &bind_groups.layout_cam,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: cam_halo_buf.as_entire_binding(),
}],
label: Some("Camera halo bind group"),
});
let depth_texture =
Texture::create_depth_texture(device, surface_cfg, "Depth texture", msaa_samples);
let shader_mesh = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Graphics shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
});
let pipeline_layout_mesh = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render pipeline layout"),
bind_group_layouts: &[&bind_groups.layout_cam, &bind_groups.layout_lighting],
push_constant_ranges: &[],
});
let depth_stencil_mesh = DepthStencilState {
format: DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
};
let pipeline_mesh = create_render_pipeline(
device,
&pipeline_layout_mesh,
shader_mesh.clone(),
surface_cfg,
msaa_samples,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
Some(depth_stencil_mesh.clone()),
None,
Some(Face::Back),
"Render pipeline mesh opaque",
);
let pipeline_mesh_transparent = create_render_pipeline(
device,
&pipeline_layout_mesh,
shader_mesh.clone(),
surface_cfg,
msaa_samples,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
Some(depth_stencil_mesh.clone()),
Some(BlendState::ALPHA_BLENDING),
Some(Face::Back),
"Render pipeline mesh transparent",
);
let pipeline_mesh_transparent_back = create_render_pipeline(
device,
&pipeline_layout_mesh,
shader_mesh.clone(),
surface_cfg,
msaa_samples,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
Some(depth_stencil_mesh.clone()),
Some(BlendState::ALPHA_BLENDING),
Some(Face::Front),
"Render pipeline mesh transparent – backfaces",
);
let pipeline_layout_halo = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Halo pipeline layout"),
bind_group_layouts: &[&bind_groups.layout_cam, &bind_groups.layout_lighting],
push_constant_ranges: &[],
});
let pipeline_halo = create_render_pipeline_depth_only(
device,
&pipeline_layout_halo,
shader_mesh.clone(),
msaa_samples,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
depth_stencil_mesh.clone(),
);
let depth_texture_contour =
Texture::create_depth_texture(device, surface_cfg, "Depth texture contour", 1);
let pipeline_contour_depth = {
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Contour depth prepass layout"),
bind_group_layouts: &[&bind_groups.layout_cam],
push_constant_ranges: &[],
});
create_contour_depth_pipeline(
device,
&layout,
shader_mesh.clone(),
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
)
};
let shader_contour = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Contour overlay shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shader_contour.wgsl").into()),
});
let layout_contour = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Contour bind group layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(32),
},
count: None,
},
],
});
let contour_uniform_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Contour uniform buffer"),
contents: &contour_uniform_bytes(0.1, 0., 0., scene.camera.near, scene.camera.far),
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
});
let bind_group_contour = create_contour_bind_group(
device,
&layout_contour,
&depth_texture_contour.view,
&contour_uniform_buf,
);
let pipeline_contour_overlay = {
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Contour overlay pipeline layout"),
bind_group_layouts: &[&layout_contour],
push_constant_ranges: &[],
});
create_contour_overlay_pipeline(device, &layout, shader_contour, surface_cfg)
};
let shader_ssao = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("SSAO shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shader_ssao.wgsl").into()),
});
let layout_ssao = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("SSAO bind group layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Depth,
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(SSAO_UNIFORM_SIZE as u64),
},
count: None,
},
],
});
let ssao_uniform_buf = device.create_buffer(&wgpu::BufferDescriptor {
label: Some("SSAO uniform buffer"),
size: SSAO_UNIFORM_SIZE as wgpu::BufferAddress,
usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group_ssao = create_ssao_bind_group(
device,
&layout_ssao,
&depth_texture_contour.view,
&ssao_uniform_buf,
);
let pipeline_ssao = {
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("SSAO pipeline layout"),
bind_group_layouts: &[&layout_ssao],
push_constant_ranges: &[],
});
create_ssao_pipeline(device, &layout, shader_ssao, surface_cfg)
};
let instance_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Instance buffer"),
contents: &[], usage: BufferUsages::VERTEX,
});
let instance_buf_transparent = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Instance buffer transparent"),
contents: &[], usage: BufferUsages::VERTEX,
});
let shader_gauss = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Graphics shader"),
source: wgpu::ShaderSource::Wgsl(include_str!("shader_gauss.wgsl").into()),
});
let pipeline_layout_gauss =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Gaussian pipeline layout"),
bind_group_layouts: &[&bind_groups.layout_cam_gauss],
push_constant_ranges: &[],
});
let depth_stencil_gauss = Some(DepthStencilState {
format: DEPTH_FORMAT,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
});
let pipeline_gauss = create_render_pipeline(
device,
&pipeline_layout_gauss,
shader_gauss.clone(),
surface_cfg,
msaa_samples,
&[QUAD_VERTEX_LAYOUT, GAUSS_INST_LAYOUT],
depth_stencil_gauss,
Some(BlendState::ALPHA_BLENDING),
None,
"Render pipeline gaussian",
);
let instance_gauss_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Gaussian Instance buffer"),
contents: &[], usage: BufferUsages::VERTEX,
});
let mesh_mappings = Vec::new();
let mesh_mappings_transparent = Vec::new();
window.set_title(&scene.window_title);
let msaa_texture = if msaa_samples > 1 {
Some(Self::create_msaa_texture(device, surface_cfg, msaa_samples))
} else {
None
};
let mut result = Self {
vertex_buf,
vertex_buf_quad,
index_buf,
instance_buf,
instance_buf_transparent,
instance_buf_gauss: instance_gauss_buf,
bind_groups,
camera_buf: cam_buf,
camera_buf_halo: cam_halo_buf,
bind_group_cam_halo,
cam_basis_buf,
lighting_buf,
pipeline_mesh,
pipeline_mesh_transparent,
pipeline_mesh_transparent_back,
pipeline_gauss,
pipeline_halo,
depth_texture_contour,
pipeline_contour_depth,
pipeline_contour_overlay,
bind_group_contour,
layout_contour,
contour_uniform_buf,
depth_revealing: 0.,
intersection_revealing: 0.,
ssao_strength: 0.,
msaa_samples,
pending_msaa: None,
surface_cfg: surface_cfg.clone(),
shader_mesh,
shader_gauss,
pipeline_ssao,
layout_ssao,
bind_group_ssao,
ssao_uniform_buf,
depth_texture,
scene,
inputs_commanded: Default::default(),
mesh_mappings,
mesh_mappings_transparent,
window,
msaa_texture,
halo_expansion: 0.,
};
result.setup_vertices_indices(device);
result.setup_entities(device);
result
}
pub(crate) fn create_msaa_texture(
device: &Device,
surface_cfg: &SurfaceConfiguration,
sample_count: u32,
) -> TextureView {
let msaa_texture = device.create_texture(&TextureDescriptor {
label: Some("Multisampled Texture"),
size: wgpu::Extent3d {
width: surface_cfg.width,
height: surface_cfg.height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count,
dimension: wgpu::TextureDimension::D2,
format: surface_cfg.format,
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
view_formats: &[],
});
msaa_texture.create_view(&wgpu::TextureViewDescriptor::default())
}
pub(crate) fn handle_input_device(
&mut self,
event: &DeviceEvent,
input_settings: &InputSettings,
) {
match input_settings.control_scheme {
ControlScheme::FreeCamera | ControlScheme::Arc { center: _ } => {
input::add_input_cmd_device(
event,
&mut self.inputs_commanded,
input_settings.device_events_for_cam_controls,
)
}
_ => unimplemented!(),
}
}
pub(crate) fn handle_input_window(
&mut self,
event: &WindowEvent,
input_settings: &InputSettings,
) {
match input_settings.control_scheme {
ControlScheme::FreeCamera | ControlScheme::Arc { center: _ } => {
input::add_input_cmd_window(
event,
&mut self.inputs_commanded,
input_settings.device_events_for_cam_controls,
)
}
_ => unimplemented!(),
}
}
pub(crate) fn setup_vertices_indices(&mut self, device: &Device) {
let mut vertices = Vec::new();
let mut indices = Vec::new();
for mesh in &self.scene.meshes {
for vertex in &mesh.vertices {
vertices.push(vertex)
}
for index in &mesh.indices {
indices.push(index);
}
}
let mut vertex_data = Vec::new();
for vertex in vertices {
for byte in vertex.to_bytes() {
vertex_data.push(byte);
}
}
let mut index_data = Vec::new();
for index in indices {
let bytes = index.to_ne_bytes();
index_data.push(bytes[0]);
index_data.push(bytes[1]);
index_data.push(bytes[2]);
index_data.push(bytes[3]);
}
let vertex_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Vertex buffer"),
contents: &vertex_data,
usage: BufferUsages::VERTEX,
});
let index_buf = device.create_buffer_init(&BufferInitDescriptor {
label: Some("Index buffer"),
contents: &index_data,
usage: BufferUsages::INDEX,
});
self.vertex_buf = vertex_buf;
self.index_buf = index_buf;
}
pub(crate) fn replace_instance_entries(
&mut self,
queue: &Queue,
device: &Device,
update_type: &EntityUpdate,
) {
let classes_or_ids: HashSet<_> = match update_type {
EntityUpdate::Classes(v) | EntityUpdate::Ids(v) => v.iter().copied().collect(),
_ => HashSet::new(), };
let mut needs_full_rebuild = false;
let ents_to_update = match update_type {
EntityUpdate::Indexes((start, end)) => &self.scene.entities[*start..*end],
_ => &self.scene.entities,
};
for ent in ents_to_update {
match update_type {
EntityUpdate::Classes(_) => {
if !classes_or_ids.contains(&ent.class) {
continue;
}
}
EntityUpdate::Ids(_) => {
if !classes_or_ids.contains(&ent.id) {
continue;
}
}
_ => (),
};
let Some(slot) = ent.buf_i else {
needs_full_rebuild = true;
break;
};
let now_is_transparent = ent.opacity < 0.99;
if now_is_transparent != ent.buf_is_transparent {
needs_full_rebuild = true;
break;
}
let instance: Instance = ent.into();
let bytes = instance.to_bytes();
let byte_offset = (slot * INSTANCE_SIZE) as u64;
if ent.buf_is_transparent {
queue.write_buffer(&self.instance_buf_transparent, byte_offset, &bytes);
} else {
queue.write_buffer(&self.instance_buf, byte_offset, &bytes);
}
}
if needs_full_rebuild {
self.setup_entities(device);
}
}
pub(crate) fn setup_entities(&mut self, device: &Device) {
let mut instances = Vec::new();
let mut instances_transparent: Vec<Instance> = Vec::new();
let mut instances_gauss = Vec::with_capacity(self.scene.gaussians.len());
let mut mesh_mappings = Vec::new();
let mut mesh_mappings_transparent = Vec::new();
let mut vertex_start_this_mesh = 0;
let mut instance_start_this_mesh = 0;
let mut instance_start_this_mesh_transparent = 0;
let mut i_opaque = 0;
let mut i_transparent = 0;
for (i, mesh) in self.scene.meshes.iter().enumerate() {
let mut instance_count_this_mesh = 0;
let mut instance_count_this_mesh_transparent = 0;
for entity in self.scene.entities.iter_mut().filter(|e| e.mesh == i) {
let instance: Instance = (&*entity).into();
if entity.opacity < 0.99 {
instances_transparent.push(instance);
instance_count_this_mesh_transparent += 1;
entity.buf_i = Some(i_transparent);
entity.buf_is_transparent = true;
i_transparent += 1;
} else {
instances.push(instance);
instance_count_this_mesh += 1;
entity.buf_i = Some(i_opaque);
entity.buf_is_transparent = false;
i_opaque += 1;
}
}
mesh_mappings.push((
vertex_start_this_mesh,
instance_start_this_mesh,
instance_count_this_mesh,
));
mesh_mappings_transparent.push((
vertex_start_this_mesh,
instance_start_this_mesh_transparent,
instance_count_this_mesh_transparent,
));
vertex_start_this_mesh += mesh.vertices.len() as i32;
instance_start_this_mesh += instance_count_this_mesh;
instance_start_this_mesh_transparent += instance_count_this_mesh_transparent;
}
self.mesh_mappings = mesh_mappings;
self.mesh_mappings_transparent = mesh_mappings_transparent;
for gauss in &self.scene.gaussians {
instances_gauss.push(gauss.to_instance());
}
self.instance_buf = setup_instance_buf(device, &instances, "Instance buffer");
self.instance_buf_transparent = setup_instance_buf(
device,
&instances_transparent,
"Instance buffer transparent",
);
self.instance_buf_gauss =
setup_instance_buf_gauss(device, &instances_gauss, "Instance buffer Gaussian");
}
pub(crate) fn update_camera(&mut self, queue: &Queue) {
queue.write_buffer(&self.camera_buf, 0, &self.scene.camera.to_bytes());
if self.halo_expansion > 0.0 {
let mut halo_cam = self.scene.camera.clone();
halo_cam.halo_expansion = self.halo_expansion;
queue.write_buffer(&self.camera_buf_halo, 0, &halo_cam.to_bytes());
}
if !self.scene.gaussians.is_empty() {
queue.write_buffer(
&self.cam_basis_buf,
0,
&CameraBasis::new(self.scene.camera.view_mat()).to_bytes(),
);
}
}
pub(crate) fn update_lighting(&mut self, queue: &Queue) {
queue.write_buffer(&self.lighting_buf, 0, &self.scene.lighting.to_bytes());
}
pub(crate) fn update_ssao_uniforms(&self, queue: &Queue) {
let proj_view = self.scene.camera.proj_mat.clone() * self.scene.camera.view_mat();
let proj_view_inv = proj_view.inverse().unwrap_or_else(Mat4::new_identity);
let bytes = ssao_uniform_bytes(
&proj_view,
&proj_view_inv,
self.scene.camera.position,
self.scene.camera.near,
self.scene.camera.far,
0.5, 0.001, self.ssao_strength,
);
queue.write_buffer(&self.ssao_uniform_buf, 0, &bytes);
}
pub(crate) fn apply_graphics_settings(&mut self, settings: &GraphicsSettings, queue: &Queue) {
let new_edge = settings.edge_cueing.unwrap_or(0.0);
if self.scene.camera.edge_cueing != new_edge {
self.scene.camera.edge_cueing = new_edge;
self.update_camera(queue);
}
let new_halo = settings.depth_aware_halos.unwrap_or(0.0);
if self.halo_expansion != new_halo {
self.halo_expansion = new_halo;
self.update_camera(queue);
}
let new_depth_rev = settings.depth_revealing_contour_lines.unwrap_or(0.0);
let new_isect_rev = settings.intersection_revealing_contour_lines.unwrap_or(0.0);
if self.depth_revealing != new_depth_rev || self.intersection_revealing != new_isect_rev {
self.depth_revealing = new_depth_rev;
self.intersection_revealing = new_isect_rev;
queue.write_buffer(
&self.contour_uniform_buf,
0,
&contour_uniform_bytes(
0.1,
new_depth_rev,
new_isect_rev,
self.scene.camera.near,
self.scene.camera.far,
),
);
}
self.ssao_strength = match settings.ambient_occlusion {
AmbientOcclusion::Ssao => 1.5,
_ => 0.0,
};
}
pub(crate) fn apply_msaa_change(&mut self, device: &Device) {
let new_msaa = self.msaa_samples;
self.depth_texture =
Texture::create_depth_texture(device, &self.surface_cfg, "Depth texture", new_msaa);
self.msaa_texture = if new_msaa > 1 {
Some(Self::create_msaa_texture(
device,
&self.surface_cfg,
new_msaa,
))
} else {
None
};
let depth_stencil_mesh = DepthStencilState {
format: DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
};
let pipeline_layout_mesh = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Render pipeline layout"),
bind_group_layouts: &[
&self.bind_groups.layout_cam,
&self.bind_groups.layout_lighting,
],
push_constant_ranges: &[],
});
self.pipeline_mesh = create_render_pipeline(
device,
&pipeline_layout_mesh,
self.shader_mesh.clone(),
&self.surface_cfg,
new_msaa,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
Some(depth_stencil_mesh.clone()),
None,
Some(Face::Back),
"Render pipeline mesh opaque",
);
self.pipeline_mesh_transparent = create_render_pipeline(
device,
&pipeline_layout_mesh,
self.shader_mesh.clone(),
&self.surface_cfg,
new_msaa,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
Some(depth_stencil_mesh.clone()),
Some(BlendState::ALPHA_BLENDING),
Some(Face::Back),
"Render pipeline mesh transparent",
);
self.pipeline_mesh_transparent_back = create_render_pipeline(
device,
&pipeline_layout_mesh,
self.shader_mesh.clone(),
&self.surface_cfg,
new_msaa,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
Some(depth_stencil_mesh.clone()),
Some(BlendState::ALPHA_BLENDING),
Some(Face::Front),
"Render pipeline mesh transparent – backfaces",
);
let pipeline_layout_halo = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Halo pipeline layout"),
bind_group_layouts: &[
&self.bind_groups.layout_cam,
&self.bind_groups.layout_lighting,
],
push_constant_ranges: &[],
});
self.pipeline_halo = create_render_pipeline_depth_only(
device,
&pipeline_layout_halo,
self.shader_mesh.clone(),
new_msaa,
&[VERTEX_LAYOUT, INSTANCE_LAYOUT],
depth_stencil_mesh,
);
let depth_stencil_gauss = Some(DepthStencilState {
format: DEPTH_FORMAT,
depth_write_enabled: false,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
});
let pipeline_layout_gauss =
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Gaussian pipeline layout"),
bind_group_layouts: &[&self.bind_groups.layout_cam_gauss],
push_constant_ranges: &[],
});
self.pipeline_gauss = create_render_pipeline(
device,
&pipeline_layout_gauss,
self.shader_gauss.clone(),
&self.surface_cfg,
new_msaa,
&[QUAD_VERTEX_LAYOUT, GAUSS_INST_LAYOUT],
depth_stencil_gauss,
Some(BlendState::ALPHA_BLENDING),
None,
"Render pipeline gaussian",
);
}
fn setup_render_pass<'a>(
&mut self,
encoder: &'a mut CommandEncoder,
output_view: &TextureView,
win_width: u32,
win_height: u32,
ui_settings: &UiSettings,
ui_size: (f32, f32),
pixels_per_pt: f32, ) -> RenderPass<'a> {
let (x, y, eff_width, eff_height) =
viewport_rect(ui_size, win_width, win_height, ui_settings, pixels_per_pt);
let color_attachment = if let Some(msaa_texture) = &self.msaa_texture {
wgpu::RenderPassColorAttachment {
view: msaa_texture,
depth_slice: None, resolve_target: Some(output_view), ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
store: StoreOp::Discard,
},
}
} else {
wgpu::RenderPassColorAttachment {
view: output_view,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: self.scene.background_color.0 as f64,
g: self.scene.background_color.1 as f64,
b: self.scene.background_color.2 as f64,
a: 1.0,
}),
store: StoreOp::Store,
},
}
};
let mut rpass = encoder.begin_render_pass(&RenderPassDescriptor {
label: Some("Render pass"),
color_attachments: &[Some(color_attachment)],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &self.depth_texture.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
rpass.set_viewport(x, y, eff_width, eff_height, 0., 1.);
if self.halo_expansion > 0.0 && self.instance_buf.size() > 0 {
rpass.set_pipeline(&self.pipeline_halo);
rpass.set_bind_group(0, &self.bind_group_cam_halo, &[]);
rpass.set_bind_group(1, &self.bind_groups.lighting, &[]);
rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));
rpass.set_vertex_buffer(1, self.instance_buf.slice(..));
rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint32);
let mut start_ind = 0u32;
for (i, mesh) in self.scene.meshes.iter().enumerate() {
let (vertex_start, instance_start, instance_count) = self.mesh_mappings[i];
if instance_count == 0 {
start_ind += mesh.indices.len() as u32;
continue;
}
rpass.draw_indexed(
start_ind..start_ind + mesh.indices.len() as u32,
vertex_start,
instance_start..instance_start + instance_count,
);
start_ind += mesh.indices.len() as u32;
}
}
for (inst_buf, pipeline, mappings) in [
(&self.instance_buf, &self.pipeline_mesh, &self.mesh_mappings),
(
&self.instance_buf_transparent,
&self.pipeline_mesh_transparent_back,
&self.mesh_mappings_transparent,
),
(
&self.instance_buf_transparent,
&self.pipeline_mesh_transparent,
&self.mesh_mappings_transparent,
),
]
.into_iter()
{
if inst_buf.size() == 0 {
continue;
}
rpass.set_pipeline(pipeline);
rpass.set_bind_group(0, &self.bind_groups.cam, &[]);
rpass.set_bind_group(1, &self.bind_groups.lighting, &[]);
rpass.set_vertex_buffer(0, self.vertex_buf.slice(..));
rpass.set_vertex_buffer(1, inst_buf.slice(..));
rpass.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint32);
let mut start_ind = 0;
for (i, mesh) in self.scene.meshes.iter().enumerate() {
let (vertex_start_this_mesh, instance_start_this_mesh, instance_count_this_mesh) =
mappings[i];
if instance_count_this_mesh == 0 {
start_ind += mesh.indices.len() as u32;
continue;
}
rpass.draw_indexed(
start_ind..start_ind + mesh.indices.len() as u32,
vertex_start_this_mesh,
instance_start_this_mesh..instance_start_this_mesh + instance_count_this_mesh,
);
start_ind += mesh.indices.len() as u32;
}
}
if !self.scene.gaussians.is_empty() {
rpass.set_pipeline(&self.pipeline_gauss);
rpass.set_bind_group(0, &self.bind_groups.cam_gauss, &[]);
rpass.set_vertex_buffer(0, self.vertex_buf_quad.slice(..));
rpass.set_vertex_buffer(1, self.instance_buf_gauss.slice(..));
rpass.draw(0..6, 0..self.scene.gaussians.len() as _); }
rpass.set_viewport(x, y, eff_width, eff_height, 0., 1.);
rpass
}
pub(crate) fn render<T>(
&mut self,
gui: &mut GuiState,
surface_texture: SurfaceTexture,
output_texture: &TextureView,
device: &Device,
queue: &Queue,
dt: Duration,
width: u32,
height: u32,
ui_settings: &mut UiSettings,
gui_handler: impl FnMut(&mut T, &Context, &mut Scene) -> EngineUpdates,
user_state: &mut T,
) -> bool {
if self.inputs_commanded.inputs_present() {
let dt_secs = dt.as_secs() as f32 + dt.subsec_micros() as f32 / 1_000_000.;
let cam_changed = match self.scene.input_settings.control_scheme {
ControlScheme::FreeCamera => input::adjust_camera_free(
&mut self.scene.camera,
&mut self.inputs_commanded,
&self.scene.input_settings,
dt_secs,
),
ControlScheme::Arc { center } => input::adjust_camera_arc(
&mut self.scene.camera,
&mut self.inputs_commanded,
&self.scene.input_settings,
center,
dt_secs,
),
ControlScheme::None => false,
ControlScheme::Fps => unimplemented!(),
};
if cam_changed {
self.update_camera(queue);
}
self.inputs_commanded.mouse_delta_x = 0.;
self.inputs_commanded.mouse_delta_y = 0.;
}
let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor {
label: Some("Render encoder"),
});
let mut updates_gui = Default::default();
let (gui_full_output, tris, screen_descriptor, resize_required) = gui.render_gui_pre_rpass(
self,
user_state,
device,
gui_handler,
&mut encoder,
queue,
width,
height,
&mut updates_gui,
);
draw_text_overlay(self, gui, ui_settings, width, height);
process_engine_updates(&updates_gui, self, device, queue);
let contours_active = self.depth_revealing > 0. || self.intersection_revealing > 0.;
let ssao_active = self.ssao_strength > 0.;
let prepass_active = contours_active || ssao_active;
if prepass_active && self.instance_buf.size() > 0 {
let mut pre = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Contour depth prepass"),
color_attachments: &[],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &self.depth_texture_contour.view,
depth_ops: Some(wgpu::Operations {
load: wgpu::LoadOp::Clear(1.0),
store: StoreOp::Store,
}),
stencil_ops: None,
}),
timestamp_writes: None,
occlusion_query_set: None,
});
pre.set_pipeline(&self.pipeline_contour_depth);
pre.set_bind_group(0, &self.bind_groups.cam, &[]);
pre.set_vertex_buffer(0, self.vertex_buf.slice(..));
pre.set_vertex_buffer(1, self.instance_buf.slice(..));
pre.set_index_buffer(self.index_buf.slice(..), wgpu::IndexFormat::Uint32);
let mut start_ind = 0u32;
for (i, mesh) in self.scene.meshes.iter().enumerate() {
let (vertex_start, instance_start, instance_count) = self.mesh_mappings[i];
if instance_count == 0 {
start_ind += mesh.indices.len() as u32;
continue;
}
pre.draw_indexed(
start_ind..start_ind + mesh.indices.len() as u32,
vertex_start,
instance_start..instance_start + instance_count,
);
start_ind += mesh.indices.len() as u32;
}
drop(pre);
}
let rpass = self.setup_render_pass(
&mut encoder,
output_texture,
width,
height,
ui_settings, gui.size, 0., );
let mut viewport_w = width as f32;
let mut viewport_h = height as f32;
viewport_w -= gui.size.0;
viewport_h -= gui.size.1;
if viewport_w > 0.0 && viewport_h > 0.0 {
self.scene.camera.aspect = viewport_w / viewport_h;
self.scene.camera.update_proj_mat();
self.update_camera(queue);
}
drop(rpass);
if contours_active {
let mut overlay = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Contour overlay"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: output_texture,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load, store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
overlay.set_pipeline(&self.pipeline_contour_overlay);
overlay.set_bind_group(0, &self.bind_group_contour, &[]);
overlay.draw(0..3, 0..1); drop(overlay);
}
if ssao_active {
self.update_ssao_uniforms(queue);
let mut overlay = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("SSAO overlay"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: output_texture,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load, store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
overlay.set_pipeline(&self.pipeline_ssao);
overlay.set_bind_group(0, &self.bind_group_ssao, &[]);
overlay.draw(0..3, 0..1); drop(overlay);
}
{
let mut egui_pass = encoder
.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("egui render pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: output_texture,
depth_slice: None,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Load,
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
})
.forget_lifetime();
gui.egui_renderer
.render(&mut egui_pass, &tris, &screen_descriptor);
}
for x in &gui_full_output.textures_delta.free {
gui.egui_renderer.free_texture(x)
}
queue.submit(Some(encoder.finish()));
surface_texture.present();
resize_required
}
}
fn create_render_pipeline(
device: &Device,
layout: &wgpu::PipelineLayout,
shader: wgpu::ShaderModule,
config: &SurfaceConfiguration,
sample_count: u32,
vertex_buffers: &'static [VertexBufferLayout<'static>],
depth_stencil: Option<DepthStencilState>,
blend: Option<BlendState>,
cull_mode: Option<Face>,
label: &str,
) -> RenderPipeline {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some(label),
layout: Some(layout),
vertex: VertexState {
module: &shader,
entry_point: Some("vs_main"),
compilation_options: Default::default(),
buffers: vertex_buffers,
},
fragment: Some(FragmentState {
module: &shader,
entry_point: Some("fs_main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: config.format, blend,
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode,
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil,
multisample: wgpu::MultisampleState {
count: sample_count, mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
})
}
fn create_render_pipeline_depth_only(
device: &Device,
layout: &wgpu::PipelineLayout,
shader: wgpu::ShaderModule,
sample_count: u32,
vertex_buffers: &'static [VertexBufferLayout<'static>],
depth_stencil: DepthStencilState,
) -> RenderPipeline {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Halo depth-only pipeline"),
layout: Some(layout),
vertex: VertexState {
module: &shader,
entry_point: Some("vs_main"),
compilation_options: Default::default(),
buffers: vertex_buffers,
},
fragment: Some(FragmentState {
module: &shader,
entry_point: Some("fs_main"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: COLOR_FORMAT,
blend: None,
write_mask: wgpu::ColorWrites::empty(),
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(Face::Front),
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(depth_stencil),
multisample: wgpu::MultisampleState {
count: sample_count,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
})
}
pub(crate) fn contour_uniform_bytes(
depth_threshold: f32,
depth_revealing: f32,
intersection_revealing: f32,
near: f32,
far: f32,
) -> [u8; 32] {
let mut b = [0u8; 32];
b[0..4].copy_from_slice(&depth_threshold.to_ne_bytes());
b[4..8].copy_from_slice(&depth_revealing.to_ne_bytes());
b[8..12].copy_from_slice(&intersection_revealing.to_ne_bytes());
b[12..16].copy_from_slice(&near.to_ne_bytes());
b[16..20].copy_from_slice(&far.to_ne_bytes());
b
}
fn create_contour_depth_pipeline(
device: &Device,
layout: &wgpu::PipelineLayout,
shader: wgpu::ShaderModule,
vertex_buffers: &'static [VertexBufferLayout<'static>],
) -> RenderPipeline {
let depth_stencil = DepthStencilState {
format: DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil: wgpu::StencilState::default(),
bias: wgpu::DepthBiasState::default(),
};
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Contour depth prepass pipeline"),
layout: Some(layout),
vertex: VertexState {
module: &shader,
entry_point: Some("vs_main"),
compilation_options: Default::default(),
buffers: vertex_buffers,
},
fragment: None, primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: Some(Face::Back),
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: Some(depth_stencil),
multisample: wgpu::MultisampleState {
count: 1, mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
})
}
fn create_contour_overlay_pipeline(
device: &Device,
layout: &wgpu::PipelineLayout,
shader: wgpu::ShaderModule,
config: &SurfaceConfiguration,
) -> RenderPipeline {
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Contour overlay pipeline"),
layout: Some(layout),
vertex: VertexState {
module: &shader,
entry_point: Some("vs_contour"),
compilation_options: Default::default(),
buffers: &[], },
fragment: Some(FragmentState {
module: &shader,
entry_point: Some("fs_contour"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(BlendState::ALPHA_BLENDING),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None, multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
})
}
pub(crate) fn create_contour_bind_group(
device: &Device,
layout: &wgpu::BindGroupLayout,
depth_view: &TextureView,
uniform_buf: &Buffer,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Contour bind group"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(depth_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: uniform_buf.as_entire_binding(),
},
],
})
}
pub(crate) const SSAO_UNIFORM_SIZE: usize = 176;
pub(crate) fn ssao_uniform_bytes(
proj_view: &Mat4,
proj_view_inv: &Mat4,
cam_pos: Vec3,
near: f32,
far: f32,
radius: f32,
bias: f32,
strength: f32,
) -> [u8; SSAO_UNIFORM_SIZE] {
let mut b = [0u8; SSAO_UNIFORM_SIZE];
b[0..64].copy_from_slice(&proj_view.to_bytes());
b[64..128].copy_from_slice(&proj_view_inv.to_bytes());
b[128..132].copy_from_slice(&cam_pos.x.to_ne_bytes());
b[132..136].copy_from_slice(&cam_pos.y.to_ne_bytes());
b[136..140].copy_from_slice(&cam_pos.z.to_ne_bytes());
b[140..144].copy_from_slice(&0f32.to_ne_bytes());
b[144..148].copy_from_slice(&near.to_ne_bytes());
b[148..152].copy_from_slice(&far.to_ne_bytes());
b[152..156].copy_from_slice(&radius.to_ne_bytes());
b[156..160].copy_from_slice(&bias.to_ne_bytes());
b[160..164].copy_from_slice(&strength.to_ne_bytes());
b
}
pub(crate) fn create_ssao_bind_group(
device: &Device,
layout: &wgpu::BindGroupLayout,
depth_view: &TextureView,
uniform_buf: &Buffer,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("SSAO bind group"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(depth_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: uniform_buf.as_entire_binding(),
},
],
})
}
fn create_ssao_pipeline(
device: &Device,
layout: &wgpu::PipelineLayout,
shader: wgpu::ShaderModule,
config: &SurfaceConfiguration,
) -> RenderPipeline {
use wgpu::{BlendComponent, BlendFactor, BlendOperation};
let multiply_blend = BlendState {
color: BlendComponent {
src_factor: BlendFactor::Zero,
dst_factor: BlendFactor::Src,
operation: BlendOperation::Add,
},
alpha: BlendComponent {
src_factor: BlendFactor::Zero,
dst_factor: BlendFactor::One,
operation: BlendOperation::Add,
},
};
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("SSAO overlay pipeline"),
layout: Some(layout),
vertex: VertexState {
module: &shader,
entry_point: Some("vs_ssao"),
compilation_options: Default::default(),
buffers: &[],
},
fragment: Some(FragmentState {
module: &shader,
entry_point: Some("fs_ssao"),
compilation_options: Default::default(),
targets: &[Some(wgpu::ColorTargetState {
format: config.format,
blend: Some(multiply_blend),
write_mask: wgpu::ColorWrites::ALL,
})],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
strip_index_format: None,
front_face: wgpu::FrontFace::Ccw,
cull_mode: None,
unclipped_depth: false,
polygon_mode: wgpu::PolygonMode::Fill,
conservative: false,
},
depth_stencil: None,
multisample: wgpu::MultisampleState {
count: 1,
mask: !0,
alpha_to_coverage_enabled: false,
},
multiview: None,
cache: None,
})
}
pub(crate) struct BindGroupData {
pub layout_cam: BindGroupLayout,
pub cam: BindGroup,
pub layout_cam_gauss: BindGroupLayout,
pub cam_gauss: BindGroup,
pub layout_lighting: BindGroupLayout,
pub lighting: BindGroup,
pub _layout_texture: BindGroupLayout,
}
fn create_bindgroups(
device: &Device,
cam_buf: &Buffer,
cam_basis_buf: &Buffer,
lighting_buf: &Buffer,
) -> BindGroupData {
let cam_entry = wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(CAMERA_SIZE as _),
},
count: None,
};
let layout_cam = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: std::slice::from_ref(&cam_entry),
label: Some("Camera bind group layout"),
});
let cam = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout_cam,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: cam_buf.as_entire_binding(),
}],
label: Some("Camera bind group"),
});
let layout_cam_gauss = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[
cam_entry,
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::VERTEX | ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: wgpu::BufferSize::new(CAM_BASIS_SIZE as _),
},
count: None,
},
],
label: Some("Camera gaussian bind group layout"),
});
let cam_gauss = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Gaussian camera bind group"),
layout: &layout_cam_gauss,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: cam_buf.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: cam_basis_buf.as_entire_binding(),
},
],
});
let layout_lighting = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
entries: &[wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Buffer {
ty: BufferBindingType::Storage { read_only: true }, has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
}],
label: Some("Lighting bind group layout"),
});
let lighting = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &layout_lighting,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: lighting_buf.as_entire_binding(),
}],
label: Some("Lighting bind group"),
});
let layout_texture = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("egui_texture_bind_group_layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Texture {
multisampled: false,
view_dimension: wgpu::TextureViewDimension::D2,
sample_type: wgpu::TextureSampleType::Float { filterable: true },
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: ShaderStages::FRAGMENT,
ty: BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
});
BindGroupData {
layout_cam,
cam,
layout_cam_gauss,
cam_gauss,
layout_lighting,
lighting,
_layout_texture: layout_texture,
}
}
fn setup_instance_buf(device: &Device, instances: &[Instance], name: &str) -> Buffer {
let mut instance_data = Vec::with_capacity(instances.len() * INSTANCE_SIZE);
for instance in instances {
for byte in instance.to_bytes() {
instance_data.push(byte);
}
}
device.create_buffer_init(&BufferInitDescriptor {
label: Some(name),
contents: &instance_data,
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
})
}
fn setup_instance_buf_gauss(device: &Device, instances: &[GaussianInstance], name: &str) -> Buffer {
let mut instance_data = Vec::with_capacity(instances.len() * INSTANCE_SIZE);
for instance in instances {
for byte in instance.to_bytes() {
instance_data.push(byte);
}
}
device.create_buffer_init(&BufferInitDescriptor {
label: Some(name),
contents: &instance_data,
usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
})
}