Raw File
/*
ENSnano, a 3d graphical application for DNA nanostructures.
    Copyright (C) 2021  Nicolas Levy <nicolaspierrelevy@gmail.com> and Nicolas Schabanel <nicolas.schabanel@ens-lyon.fr>

    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 <https://www.gnu.org/licenses/>.
*/

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<Device>,
    queue: Rc<Queue>,
    background_pipeline: RenderPipeline,
    area_size: PhySize,
    depth_texture: Texture,
    circle_drawer: CircleDrawer,
    char_drawers: HashMap<char, CharDrawer>,
    char_map: HashMap<char, Vec<CharInstance>>,
    globals: UniformBindGroup,
}

impl View {
    pub fn new(
        device: Rc<Device>,
        queue: Rc<Queue>,
        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<CircleInstance>) {
        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<char, Vec<CharInstance>> {
        &mut self.char_map
    }
}

fn background_pipeline(device: &Device, depth_stencil_state: Option<wgpu::DepthStencilStateDescriptor>) -> 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)
}

back to top