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