/* ENSnano, a 3d graphical application for DNA nanostructures. Copyright (C) 2021 Nicolas Levy and Nicolas Schabanel This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ use std::rc::Rc; use std::collections::HashMap; use iced_wgpu::wgpu; use wgpu::{Device, Queue, RenderPipeline}; use super::{DrawArea, CameraPtr}; use crate::PhySize; use crate::consts::*; use crate::utils::bindgroup_manager::{DynamicBindGroup, UniformBindGroup}; use crate::utils::texture::Texture; use crate::utils::{chars2d as chars, circles2d as circles}; use circles::CircleDrawer; pub use circles::CircleInstance; use chars::CharDrawer; pub use chars::CharInstance; pub struct View { device: Rc, queue: Rc, background_pipeline: RenderPipeline, area_size: PhySize, depth_texture: Texture, circle_drawer: CircleDrawer, char_drawers: HashMap, char_map: HashMap>, globals: UniformBindGroup, } impl View { pub fn new( device: Rc, queue: Rc, area: DrawArea, camera: CameraPtr, encoder: &mut wgpu::CommandEncoder, ) -> Self { let depth_texture = Texture::create_depth_texture(device.as_ref(), &area.size, SAMPLE_COUNT); let globals = UniformBindGroup::new(device.clone(), queue.clone(), camera.borrow().get_globals()); let depth_stencil_state = Some(wgpu::DepthStencilStateDescriptor { format: wgpu::TextureFormat::Depth32Float, depth_write_enabled: true, depth_compare: wgpu::CompareFunction::Less, stencil: wgpu::StencilStateDescriptor { front: wgpu::StencilStateFaceDescriptor::IGNORE, back: wgpu::StencilStateFaceDescriptor::IGNORE, read_mask: 0, write_mask: 0, }, }); let background_pipeline = background_pipeline(device.clone().as_ref(), depth_stencil_state); let circle_drawer = CircleDrawer::new(device.clone(), queue.clone(), encoder, globals.get_layout()); let chars = [ 'A', 'T', 'G', 'C', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', ]; let mut char_drawers = HashMap::new(); let mut char_map = HashMap::new(); for c in chars.iter() { char_drawers.insert( *c, CharDrawer::new(device.clone(), queue.clone(), globals.get_layout(), *c), ); char_map.insert(*c, Vec::new()); } Self { device, queue, background_pipeline, area_size: area.size, depth_texture, circle_drawer, char_map, char_drawers, globals, } } pub fn draw( &mut self, encoder: &mut wgpu::CommandEncoder, target: &wgpu::TextureView, area: DrawArea, ) { let clear_color = wgpu::Color { r: 1., g: 0., b: 0., a: 1., }; let msaa_texture = if SAMPLE_COUNT > 1 { Some(crate::utils::texture::Texture::create_msaa_texture( self.device.clone().as_ref(), &self.area_size, SAMPLE_COUNT, wgpu::TextureFormat::Bgra8UnormSrgb, )) } else { None }; let attachment = if msaa_texture.is_some() { msaa_texture.as_ref().unwrap() } else { target }; let resolve_target = if msaa_texture.is_some() { Some(target) } else { None }; let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor { attachment, resolve_target, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(clear_color), store: true, }, }], depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor { attachment: &self.depth_texture.view, depth_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(1.), store: true, }), stencil_ops: Some(wgpu::Operations { load: wgpu::LoadOp::Clear(0), store: true, }), }), }); // Draw the background render_pass.set_pipeline(&self.background_pipeline); render_pass.draw(0..4, 0..1); // Everything else requires the globals bind group to be set render_pass.set_bind_group(0, self.globals.get_bindgroup(), &[]); // Draw the circles representing the helices self.circle_drawer.draw(&mut render_pass); // Draw the helices numbers for drawer in self.char_drawers.values_mut() { drawer.draw(&mut render_pass); } } pub fn resize(&mut self, area: DrawArea) { self.depth_texture = Texture::create_depth_texture(self.device.as_ref(), &area.size, SAMPLE_COUNT); self.area_size = area.size; } pub fn update_circles(&mut self, circles: Vec) { self.circle_drawer.new_instances(Rc::new(circles)); for (c, v) in self.char_map.iter() { self.char_drawers .get_mut(c) .unwrap() .new_instances(Rc::new(v.clone())) } } pub fn get_char_map(&mut self) -> &mut HashMap> { &mut self.char_map } } fn background_pipeline(device: &Device, depth_stencil_state: Option) -> RenderPipeline { let vs_module = &device.create_shader_module(wgpu::include_spirv!("view/background.vert.spv")); let fs_module = &device.create_shader_module(wgpu::include_spirv!("view/background.frag.spv")); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { bind_group_layouts: &[], push_constant_ranges: &[], label: None, }); let desc = wgpu::RenderPipelineDescriptor { layout: Some(&pipeline_layout), vertex_stage: wgpu::ProgrammableStageDescriptor { module: &vs_module, entry_point: "main", }, fragment_stage: Some(wgpu::ProgrammableStageDescriptor { module: &fs_module, entry_point: "main", }), rasterization_state: Some(wgpu::RasterizationStateDescriptor { front_face: wgpu::FrontFace::Ccw, cull_mode: wgpu::CullMode::None, ..Default::default() }), primitive_topology: wgpu::PrimitiveTopology::TriangleStrip, color_states: &[wgpu::ColorStateDescriptor { format: wgpu::TextureFormat::Bgra8UnormSrgb, color_blend: wgpu::BlendDescriptor::REPLACE, alpha_blend: wgpu::BlendDescriptor::REPLACE, write_mask: wgpu::ColorWrite::ALL, }], depth_stencil_state, vertex_state: wgpu::VertexStateDescriptor { index_format: wgpu::IndexFormat::Uint16, vertex_buffers: &[], }, sample_count: SAMPLE_COUNT, sample_mask: !0, alpha_to_coverage_enabled: false, label: None, }; device.create_render_pipeline(&desc) }