Another project
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 10 kB View raw
1use wgpu::util::DeviceExt; 2 3use bone_types::ShadingModel; 4 5use crate::RenderTargets; 6use crate::ViewportRegion; 7use crate::gpu::{Gpu, PICK_FORMAT}; 8use crate::lower_f32; 9use crate::scene::SolidScene; 10use crate::snapshot::Style; 11 12pub(crate) const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; 13 14const LIGHT_DIR: [f32; 4] = [0.302, 0.503, 0.809, 0.0]; 15const FILL_DIR: [f32; 4] = [-0.302, -0.503, -0.809, 0.4]; 16const AMBIENT: f32 = 0.28; 17 18const SHADING_DEFAULT: u32 = 0; 19const SHADING_PHONG: u32 = 1; 20 21const FACE_SHADED: u32 = 0; 22const FACE_OCCLUDER: u32 = 1; 23 24const fn shading_code(model: ShadingModel) -> u32 { 25 match model { 26 ShadingModel::Phong => SHADING_PHONG, 27 ShadingModel::Flat | ShadingModel::Gouraud => SHADING_DEFAULT, 28 } 29} 30 31#[derive(Copy, Clone, Debug, PartialEq, Eq)] 32pub(crate) enum FaceFill { 33 Shaded, 34 Occluder, 35} 36 37const fn fill_code(fill: FaceFill) -> u32 { 38 match fill { 39 FaceFill::Shaded => FACE_SHADED, 40 FaceFill::Occluder => FACE_OCCLUDER, 41 } 42} 43 44#[derive(Copy, Clone)] 45pub(crate) struct SolidView { 46 pub clip_from_world: [f32; 16], 47 pub eye_world: [f32; 3], 48 pub shading: ShadingModel, 49 pub fill: FaceFill, 50 pub region: ViewportRegion, 51} 52 53#[repr(C)] 54#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] 55struct SolidVertex { 56 position: [f32; 3], 57 normal: [f32; 3], 58 color: [f32; 4], 59 pick: u32, 60} 61 62const VERTEX_STRIDE: u64 = core::mem::size_of::<SolidVertex>() as u64; 63 64#[repr(C, align(16))] 65#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] 66struct SolidUniform { 67 clip_from_world: [f32; 16], 68 light_dir: [f32; 4], 69 fill_dir: [f32; 4], 70 base_color: [f32; 4], 71 background: [f32; 4], 72 eye_world: [f32; 4], 73 ambient: f32, 74 shading_model: u32, 75 face_mode: u32, 76 pad: f32, 77} 78 79const UNIFORM_SIZE: u64 = core::mem::size_of::<SolidUniform>() as u64; 80 81const VERTEX_ATTRS: [wgpu::VertexAttribute; 4] = wgpu::vertex_attr_array![ 82 0 => Float32x3, 83 1 => Float32x3, 84 2 => Float32x4, 85 3 => Uint32, 86]; 87 88pub(crate) struct SolidPipeline { 89 device: wgpu::Device, 90 queue: wgpu::Queue, 91 pipeline: wgpu::RenderPipeline, 92 uniform_buffer: wgpu::Buffer, 93 bind_group: wgpu::BindGroup, 94} 95 96impl SolidPipeline { 97 #[must_use] 98 pub fn new(gpu: &Gpu, color_format: wgpu::TextureFormat) -> Self { 99 let device = gpu.device().clone(); 100 let queue = gpu.queue().clone(); 101 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { 102 label: Some("bone-render:solid-shader"), 103 source: wgpu::ShaderSource::Wgsl(include_str!("solid.wgsl").into()), 104 }); 105 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { 106 label: Some("bone-render:solid-bgl"), 107 entries: &[wgpu::BindGroupLayoutEntry { 108 binding: 0, 109 visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, 110 ty: wgpu::BindingType::Buffer { 111 ty: wgpu::BufferBindingType::Uniform, 112 has_dynamic_offset: false, 113 min_binding_size: wgpu::BufferSize::new(UNIFORM_SIZE), 114 }, 115 count: None, 116 }], 117 }); 118 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { 119 label: Some("bone-render:solid-layout"), 120 bind_group_layouts: &[Some(&bind_group_layout)], 121 immediate_size: 0, 122 }); 123 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { 124 label: Some("bone-render:solid-pipeline"), 125 layout: Some(&pipeline_layout), 126 vertex: wgpu::VertexState { 127 module: &shader, 128 entry_point: Some("vs"), 129 compilation_options: wgpu::PipelineCompilationOptions::default(), 130 buffers: &[wgpu::VertexBufferLayout { 131 array_stride: VERTEX_STRIDE, 132 step_mode: wgpu::VertexStepMode::Vertex, 133 attributes: &VERTEX_ATTRS, 134 }], 135 }, 136 fragment: Some(wgpu::FragmentState { 137 module: &shader, 138 entry_point: Some("fs"), 139 compilation_options: wgpu::PipelineCompilationOptions::default(), 140 targets: &[ 141 Some(wgpu::ColorTargetState { 142 format: color_format, 143 blend: None, 144 write_mask: wgpu::ColorWrites::ALL, 145 }), 146 Some(wgpu::ColorTargetState { 147 format: PICK_FORMAT, 148 blend: None, 149 write_mask: wgpu::ColorWrites::ALL, 150 }), 151 ], 152 }), 153 primitive: wgpu::PrimitiveState { 154 topology: wgpu::PrimitiveTopology::TriangleList, 155 strip_index_format: None, 156 front_face: wgpu::FrontFace::Ccw, 157 cull_mode: Some(wgpu::Face::Back), 158 polygon_mode: wgpu::PolygonMode::Fill, 159 conservative: false, 160 unclipped_depth: false, 161 }, 162 depth_stencil: Some(wgpu::DepthStencilState { 163 format: DEPTH_FORMAT, 164 depth_write_enabled: Some(true), 165 depth_compare: Some(wgpu::CompareFunction::Less), 166 stencil: wgpu::StencilState::default(), 167 bias: wgpu::DepthBiasState::default(), 168 }), 169 multisample: wgpu::MultisampleState::default(), 170 multiview_mask: None, 171 cache: None, 172 }); 173 let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor { 174 label: Some("bone-render:solid-uniform"), 175 size: UNIFORM_SIZE, 176 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, 177 mapped_at_creation: false, 178 }); 179 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { 180 label: Some("bone-render:solid-bg"), 181 layout: &bind_group_layout, 182 entries: &[wgpu::BindGroupEntry { 183 binding: 0, 184 resource: uniform_buffer.as_entire_binding(), 185 }], 186 }); 187 Self { 188 device, 189 queue, 190 pipeline, 191 uniform_buffer, 192 bind_group, 193 } 194 } 195 196 pub fn draw( 197 &self, 198 encoder: &mut wgpu::CommandEncoder, 199 targets: RenderTargets<'_>, 200 depth_view: &wgpu::TextureView, 201 scene: &SolidScene, 202 view: SolidView, 203 style: &Style, 204 ) { 205 let uniform = SolidUniform { 206 clip_from_world: view.clip_from_world, 207 light_dir: LIGHT_DIR, 208 fill_dir: FILL_DIR, 209 base_color: style.solid_base_color().to_array(), 210 background: style.background().to_rgba_array(), 211 eye_world: [view.eye_world[0], view.eye_world[1], view.eye_world[2], 1.0], 212 ambient: AMBIENT, 213 shading_model: shading_code(view.shading), 214 face_mode: fill_code(view.fill), 215 pad: 0.0, 216 }; 217 self.queue 218 .write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&uniform)); 219 let vertices = build_vertices(scene); 220 let indices = build_indices(scene); 221 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { 222 label: Some("bone-render:solid-pass"), 223 color_attachments: &crate::solid_clear_color_attachments(targets, style), 224 depth_stencil_attachment: Some(crate::solid_clear_depth_attachment(depth_view)), 225 timestamp_writes: None, 226 occlusion_query_set: None, 227 multiview_mask: None, 228 }); 229 crate::apply_viewport_region(&mut pass, view.region); 230 let Ok(count) = u32::try_from(indices.len()) else { 231 panic!("solid index count {} exceeds u32::MAX", indices.len()); 232 }; 233 if count == 0 { 234 return; 235 } 236 let vertex_buffer = self 237 .device 238 .create_buffer_init(&wgpu::util::BufferInitDescriptor { 239 label: Some("bone-render:solid-vertices"), 240 contents: bytemuck::cast_slice(&vertices), 241 usage: wgpu::BufferUsages::VERTEX, 242 }); 243 let index_buffer = self 244 .device 245 .create_buffer_init(&wgpu::util::BufferInitDescriptor { 246 label: Some("bone-render:solid-indices"), 247 contents: bytemuck::cast_slice(&indices), 248 usage: wgpu::BufferUsages::INDEX, 249 }); 250 pass.set_pipeline(&self.pipeline); 251 pass.set_bind_group(0, &self.bind_group, &[]); 252 pass.set_vertex_buffer(0, vertex_buffer.slice(..)); 253 pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint32); 254 pass.draw_indexed(0..count, 0, 0..1); 255 } 256} 257 258impl core::fmt::Debug for SolidPipeline { 259 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 260 f.debug_struct("SolidPipeline").finish_non_exhaustive() 261 } 262} 263 264fn build_vertices(scene: &SolidScene) -> Vec<SolidVertex> { 265 scene 266 .positions() 267 .iter() 268 .zip(scene.normals()) 269 .zip(scene.colors()) 270 .zip(scene.pick_ids()) 271 .map(|(((point, normal), color), pick)| { 272 let (px, py, pz) = point.coords_mm(); 273 let (nx, ny, nz) = normal.components(); 274 SolidVertex { 275 position: [lower_f32(px), lower_f32(py), lower_f32(pz)], 276 normal: [lower_f32(nx), lower_f32(ny), lower_f32(nz)], 277 color: color.to_array(), 278 pick: pick.raw(), 279 } 280 }) 281 .collect() 282} 283 284fn build_indices(scene: &SolidScene) -> Vec<u32> { 285 scene 286 .triangles() 287 .iter() 288 .flat_map(|tri| tri.iter().copied()) 289 .collect() 290}