1use wgpu::util::DeviceExt;
2use engvis_core::{OrbitCamera, Scene, RenderState};
3use glam::Affine3A;
4
5use crate::depth::DepthTexture;
6use crate::grid_renderer::GridRenderer;
7use crate::lighting::LightingBuffer;
8use crate::material_pipeline::MaterialPipeline;
9use crate::mesh_renderer::MeshRenderer;
10use crate::overlay_renderer::OverlayRenderer;
11use crate::texture_cache::TextureCache;
12use crate::custom_material::CustomMaterial;
13
14
15#[derive(Debug, Clone, Copy)]
16enum OverlayDrawMode {
17 Vertices,
18 Edges,
19}
20
21#[repr(C)]
23#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
24pub struct SceneUniforms {
25 pub view_proj: [[f32; 4]; 4],
26 pub camera_pos: [f32; 4],
27 pub viewport: [f32; 4],
28 pub global_opacity: [f32; 4],
29}
30
31pub struct Renderer {
32 pub depth: DepthTexture,
33 pub msaa_texture: wgpu::Texture,
34 pub msaa_view: wgpu::TextureView,
35 pub surface_format: wgpu::TextureFormat,
36 pub scene_uniform_buffer: wgpu::Buffer,
37 pub scene_bind_group: wgpu::BindGroup,
38 pub scene_bind_group_layout: wgpu::BindGroupLayout,
39 pub lighting: LightingBuffer,
40 pub material_pipeline: MaterialPipeline,
41 pub mesh_renderer: MeshRenderer,
42 pub grid_renderer: GridRenderer,
43 pub overlay_renderer: OverlayRenderer,
44 pub texture_cache: TextureCache,
45
46 state: RenderState,
48 sample_count: u32,
50 custom_material: Option<Box<dyn CustomMaterial>>,
52}
53
54impl Renderer {
55 pub fn new(
56 device: &wgpu::Device,
57 queue: &wgpu::Queue,
58 surface_format: wgpu::TextureFormat,
59 scene: &Scene,
60 width: u32,
61 height: u32,
62 sample_count: u32,
63 ) -> Self {
64 let sc = sample_count.max(1);
65 let depth = DepthTexture::new(device, width.max(1), height.max(1), sc);
66
67 let msaa_texture = device.create_texture(&wgpu::TextureDescriptor {
68 label: Some("MSAA Color Texture"),
69 size: wgpu::Extent3d {
70 width: width.max(1),
71 height: height.max(1),
72 depth_or_array_layers: 1,
73 },
74 mip_level_count: 1,
75 sample_count: sc,
76 dimension: wgpu::TextureDimension::D2,
77 format: surface_format,
78 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
79 view_formats: &[],
80 });
81 let msaa_view = msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
82
83 let scene_bind_group_layout =
84 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
85 label: Some("Scene Bind Group Layout"),
86 entries: &[wgpu::BindGroupLayoutEntry {
87 binding: 0,
88 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
89 ty: wgpu::BindingType::Buffer {
90 ty: wgpu::BufferBindingType::Uniform,
91 has_dynamic_offset: false,
92 min_binding_size: None,
93 },
94 count: None,
95 }],
96 });
97
98 let initial_uniforms = SceneUniforms {
99 view_proj: glam::Mat4::IDENTITY.to_cols_array_2d(),
100 camera_pos: [0.0; 4],
101 viewport: [width as f32, height as f32, 0.0, 0.0],
102 global_opacity: [1.0, 0.0, 0.0, 0.0],
103 };
104
105 let scene_uniform_buffer =
106 device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
107 label: Some("Scene Uniform Buffer"),
108 contents: bytemuck::cast_slice(&[initial_uniforms]),
109 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
110 });
111
112 let scene_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
113 label: Some("Scene Bind Group"),
114 layout: &scene_bind_group_layout,
115 entries: &[wgpu::BindGroupEntry {
116 binding: 0,
117 resource: scene_uniform_buffer.as_entire_binding(),
118 }],
119 });
120
121 let lighting = LightingBuffer::new(device, &scene.lighting);
122
123 let scene_layout_for_grid = scene_bind_group_layout.clone();
124 let grid_renderer = GridRenderer::new(device, surface_format, &scene_layout_for_grid, sample_count);
125
126 let mesh_renderer = MeshRenderer::new(device);
127 let material_pipeline = MaterialPipeline::new(
128 device,
129 surface_format,
130 &scene_bind_group_layout,
131 &lighting.bind_group_layout,
132 &mesh_renderer.object_bind_group_layout,
133 sample_count,
134 );
135
136 let scene_layout_for_overlay = scene_bind_group_layout.clone();
137 let overlay_renderer = OverlayRenderer::new(
138 device,
139 surface_format,
140 crate::depth::DepthTexture::FORMAT,
141 &scene_layout_for_overlay,
142 &mesh_renderer.object_bind_group_layout,
143 sample_count,
144 );
145
146 let texture_cache = TextureCache::new(device, queue);
147
148 let mut renderer = Self {
149 depth,
150 msaa_texture,
151 msaa_view,
152 surface_format,
153 scene_uniform_buffer,
154 scene_bind_group,
155 scene_bind_group_layout,
156 lighting,
157 material_pipeline,
158 mesh_renderer,
159 grid_renderer,
160 overlay_renderer,
161 texture_cache,
162 state: RenderState::default(),
163 sample_count: sc,
164 custom_material: None,
165 };
166
167 renderer.upload_scene(device, queue, scene);
168 renderer
169 }
170
171 pub fn set_state(&mut self, state: &RenderState) {
175 self.state = *state;
176 }
177
178 pub fn set_state_from_camera(&mut self, _camera: &OrbitCamera) {}
181
182 pub fn state(&self) -> &RenderState {
184 &self.state
185 }
186
187 pub fn state_mut(&mut self) -> &mut RenderState {
189 &mut self.state
190 }
191
192 pub fn set_custom_material(&mut self, material: Option<Box<dyn CustomMaterial>>) {
194 self.custom_material = material;
195 }
196
197 pub fn has_custom_material(&self) -> bool {
199 self.custom_material.is_some()
200 }
201
202 pub fn upload_scene(
205 &mut self,
206 device: &wgpu::Device,
207 queue: &wgpu::Queue,
208 scene: &Scene,
209 ) {
210 self.mesh_renderer.mesh_buffers.clear();
212 for mesh in &scene.meshes {
213 self.mesh_renderer.upload_mesh(device, mesh);
214 }
215
216 self.mesh_renderer.material_bind_groups.clear();
218 self.mesh_renderer.material_uniform_buffers.clear();
219 for material in &scene.materials {
220 let (bg, buf) = self
221 .material_pipeline
222 .create_material_bind_group(device, material, &self.texture_cache);
223 self.mesh_renderer.material_bind_groups.push(bg);
224 self.mesh_renderer.material_uniform_buffers.push(buf);
225 }
226
227 self.lighting.update(queue, &scene.lighting);
229 }
230
231 pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) {
232 let w = width.max(1);
233 let h = height.max(1);
234 let sc = self.sample_count.max(1);
235 self.depth = DepthTexture::new(device, w, h, sc);
236 self.msaa_texture = device.create_texture(&wgpu::TextureDescriptor {
237 label: Some("MSAA Color Texture"),
238 size: wgpu::Extent3d {
239 width: w,
240 height: h,
241 depth_or_array_layers: 1,
242 },
243 mip_level_count: 1,
244 sample_count: sc,
245 dimension: wgpu::TextureDimension::D2,
246 format: self.surface_format,
247 usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
248 view_formats: &[],
249 });
250 self.msaa_view = self.msaa_texture.create_view(&wgpu::TextureViewDescriptor::default());
251 }
252
253 #[allow(clippy::too_many_arguments)]
256 pub fn render_scene_pass(
257 &self,
258 device: &wgpu::Device,
259 queue: &wgpu::Queue,
260 view: &wgpu::TextureView,
261 encoder: &mut wgpu::CommandEncoder,
262 scene: &Scene,
263 camera: &OrbitCamera,
264 width: u32,
265 height: u32,
266 ) {
267 let scene_uniforms = SceneUniforms {
269 view_proj: camera.view_projection().to_cols_array_2d(),
270 camera_pos: [camera.position().x, camera.position().y, camera.position().z, 1.0],
271 viewport: [width as f32, height as f32, 0.0, 0.0],
272 global_opacity: [self.state.opacity, 0.0, 0.0, 0.0],
273 };
274 queue.write_buffer(
275 &self.scene_uniform_buffer,
276 0,
277 bytemuck::cast_slice(&[scene_uniforms]),
278 );
279
280 self.lighting.update(queue, &scene.lighting);
282
283 let s = &self.state;
285 let depth_tex = &self.depth;
286
287 let resolve_target = if self.sample_count > 1 { Some(view) } else { None };
289 let color_view = if self.sample_count > 1 { &self.msaa_view } else { view };
290 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
291 label: Some("Main Render Pass"),
292 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
293 view: color_view,
294 resolve_target,
295 ops: wgpu::Operations {
296 load: wgpu::LoadOp::Clear(wgpu::Color {
297 r: 0.15,
298 g: 0.17,
299 b: 0.19,
300 a: 1.0,
301 }),
302 store: wgpu::StoreOp::Store,
303 },
304 depth_slice: None,
305 })],
306 depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
307 view: &depth_tex.view,
308 depth_ops: Some(wgpu::Operations {
309 load: wgpu::LoadOp::Clear(1.0),
310 store: wgpu::StoreOp::Store,
311 }),
312 stencil_ops: None,
313 }),
314 occlusion_query_set: None,
315 timestamp_writes: None,
316 });
317
318 render_pass.set_bind_group(0, &self.scene_bind_group, &[]);
319 render_pass.set_bind_group(1, &self.lighting.bind_group, &[]);
320
321 if s.show_grid {
323 self.grid_renderer.render(&mut render_pass);
324 render_pass.set_bind_group(0, &self.scene_bind_group, &[]);
326 render_pass.set_bind_group(1, &self.lighting.bind_group, &[]);
327 }
328
329 if s.show_surface {
331 render_pass.set_pipeline(&self.material_pipeline.solid_pipeline);
332 self.render_scene_nodes(&mut render_pass, scene, device, Affine3A::IDENTITY);
333 }
334
335 if s.edge_opts.enabled {
337 let (_buf, overlay_bg) = self
338 .overlay_renderer
339 .create_uniform_bind_group(device, s.edge_opts.color, 0.0, s.edge_opts.line_width);
340 render_pass.set_pipeline(&self.overlay_renderer.line_pipeline);
341 self.render_overlay_nodes(&mut render_pass, scene, device, Affine3A::IDENTITY, &overlay_bg, OverlayDrawMode::Edges);
342 }
343
344 if s.vertex_opts.enabled {
346 let (_buf, overlay_bg) = self
347 .overlay_renderer
348 .create_uniform_bind_group(device, s.vertex_opts.color, s.vertex_opts.point_size, 0.0);
349 render_pass.set_pipeline(&self.overlay_renderer.point_pipeline);
350 self.render_overlay_nodes(&mut render_pass, scene, device, Affine3A::IDENTITY, &overlay_bg, OverlayDrawMode::Vertices);
351 }
352 }
353
354 fn render_scene_nodes(
355 &self,
356 render_pass: &mut wgpu::RenderPass,
357 scene: &Scene,
358 device: &wgpu::Device,
359 parent_transform: Affine3A,
360 ) {
361 for node in &scene.nodes {
362 self.render_node(render_pass, scene, device, node, parent_transform);
363 }
364 }
365
366 fn render_node(
367 &self,
368 render_pass: &mut wgpu::RenderPass,
369 scene: &Scene,
370 device: &wgpu::Device,
371 node: &engvis_core::SceneNode,
372 parent_transform: Affine3A,
373 ) {
374 if !node.visible {
375 return;
376 }
377
378 let world_transform = parent_transform * node.local_transform;
379
380 if let Some(mesh_idx) = node.mesh_index {
381 if mesh_idx < self.mesh_renderer.mesh_buffers.len() {
382 let mesh_buf = &self.mesh_renderer.mesh_buffers[mesh_idx];
383 let mesh = &scene.meshes[mesh_idx];
384
385 render_pass.set_vertex_buffer(0, mesh_buf.vertex_buffer.slice(..));
386 render_pass.set_index_buffer(
387 mesh_buf.index_buffer.slice(..),
388 wgpu::IndexFormat::Uint32,
389 );
390
391 let (_obj_buf, obj_bg) = self
392 .mesh_renderer
393 .create_object_bind_group(device, world_transform);
394 render_pass.set_bind_group(3, &obj_bg, &[]);
395
396 for sub_mesh in &mesh.sub_meshes {
397 let mat_idx = sub_mesh.material_index;
398 if mat_idx < self.mesh_renderer.material_bind_groups.len() {
399 render_pass.set_bind_group(
400 2,
401 &self.mesh_renderer.material_bind_groups[mat_idx],
402 &[],
403 );
404 }
405 render_pass.draw_indexed(
406 sub_mesh.index_offset..sub_mesh.index_offset + sub_mesh.index_count,
407 0,
408 0..1,
409 );
410 }
411 }
412 }
413
414 for child in &node.children {
415 self.render_node(render_pass, scene, device, child, world_transform);
416 }
417 }
418
419 fn render_overlay_nodes(
420 &self,
421 render_pass: &mut wgpu::RenderPass,
422 scene: &Scene,
423 device: &wgpu::Device,
424 parent_transform: Affine3A,
425 overlay_bind_group: &wgpu::BindGroup,
426 mode: OverlayDrawMode,
427 ) {
428 for node in &scene.nodes {
429 self.render_overlay_node(render_pass, device, node, parent_transform, overlay_bind_group, mode);
430 }
431 }
432
433 fn render_overlay_node(
434 &self,
435 render_pass: &mut wgpu::RenderPass,
436 device: &wgpu::Device,
437 node: &engvis_core::SceneNode,
438 parent_transform: Affine3A,
439 overlay_bind_group: &wgpu::BindGroup,
440 mode: OverlayDrawMode,
441 ) {
442 if !node.visible {
443 return;
444 }
445
446 let world_transform = parent_transform * node.local_transform;
447
448 if let Some(mesh_idx) = node.mesh_index {
449 if mesh_idx < self.mesh_renderer.mesh_buffers.len() {
450 let mesh_buf = &self.mesh_renderer.mesh_buffers[mesh_idx];
451
452 render_pass.set_bind_group(0, &self.scene_bind_group, &[]);
453
454 let (_obj_buf, obj_bg) = self
455 .mesh_renderer
456 .create_object_bind_group(device, world_transform);
457 render_pass.set_bind_group(1, &obj_bg, &[]);
458 render_pass.set_bind_group(2, overlay_bind_group, &[]);
459
460 match mode {
461 OverlayDrawMode::Vertices => {
462 render_pass.set_vertex_buffer(0, mesh_buf.vertex_buffer.slice(..));
463 render_pass.set_vertex_buffer(1, self.overlay_renderer.point_quad_buffer.slice(..));
464 render_pass.draw(0..6, 0..mesh_buf.vertex_count);
465 }
466 OverlayDrawMode::Edges => {
467 render_pass.set_vertex_buffer(0, mesh_buf.edge_endpoint_buffer.slice(..));
468 render_pass.set_vertex_buffer(1, self.overlay_renderer.line_quad_buffer.slice(..));
469 render_pass.draw(0..6, 0..mesh_buf.edge_instance_count);
470 }
471 }
472 }
473 }
474
475 for child in &node.children {
476 self.render_overlay_node(render_pass, device, child, world_transform, overlay_bind_group, mode);
477 }
478 }
479}