use crate::data_loader::Data; use crate::graphics::colormap::linear::LinearColormap; use crate::graphics::ppi::PPIConfig; use crate::graphics::transforms::position::Position; use crate::graphics::transforms::viewport::Viewport; use crate::graphics::transforms::ChainedTransform; use crate::graphics::{ppi::PPI, Graphics}; use crate::graphics::{AttaWithBuffer, AttaWithProgram, Config, MouseState}; use crate::{errors::*, ui::base}; use glow::{HasContext, NativeBuffer, NativeFramebuffer, NativeTexture, NativeVertexArray}; use imgui::{ImStr, ImString, Textures, Ui}; use log::info; use serde::de; use std::collections::HashSet; use std::{cell::RefCell, collections::HashMap, rc::Rc}; use winit::window; pub type Graphic = Rc>>; type RcGraphic = Rc>; pub struct App<'a> { pub ui_state: State, gl: &'a glow::Context, viewport: Viewport, windows: HashMap, programs: [Graphic; 1], pub ppi_module: RcGraphic, program_with_window: HashMap>, // Auto clean up all_vaos: HashMap, all_other_buffers: HashMap, all_framebuffers: HashMap, all_frametextures: HashMap, } impl<'a> App<'a> { pub fn new(gl: &'a glow::Context) -> Result { let viewport = Viewport::new()?; let mut cmap = LinearColormap::new().unwrap(); cmap.set_colors(vec![ [170, 170, 170, 255], [0, 34, 255, 255], [1, 160, 246, 255], [0, 236, 236, 255], [0, 216, 0, 255], [1, 144, 0, 255], [255, 255, 0, 255], [231, 192, 0, 255], [255, 144, 0, 255], [255, 0, 0, 255], [214, 0, 0, 255], [192, 0, 0, 255], [255, 0, 240, 255], [150, 0, 180, 255], ]); cmap.set_range(0.0, 70.0); let cmap = Box::new(cmap); let mut ppi = PPI::new()?; ppi.set_viewport(&viewport); ppi.set_colormap(cmap); let ppi = Rc::new(RefCell::new(ppi)); let programs = [ppi.clone() as Graphic]; Ok(Self { ui_state: State {}, viewport, programs, windows: HashMap::new(), ppi_module: ppi, gl, program_with_window: HashMap::new(), all_vaos: HashMap::with_capacity(30), all_other_buffers: HashMap::with_capacity(30), all_framebuffers: HashMap::with_capacity(30), all_frametextures: HashMap::with_capacity(30), }) } pub fn render(&mut self) { let mut need_clean = false; for (id, program) in self.programs.iter().enumerate() { let mut p = program.borrow_mut(); if self.program_with_window.len() == 0 { return; } p.mount(&self.gl).unwrap(); self.program_with_window.get(&id).map(|windows| { for window in windows.iter() { let window_info = self.windows.get_mut(window).unwrap(); if !window_info.need_redraw { continue; } let conf = if window_info.re_init { window_info.config.as_ref() } else { None }; { p.set_config(self.gl, conf).unwrap(); window_info.re_init = false; } unsafe { self.gl .bind_framebuffer(glow::FRAMEBUFFER, window_info.framebuffer); let attach = window_info.attach.as_ref(); if attach.is_some() { self.gl.bind_vertex_array(Some(attach.unwrap().vao)); } if attach.is_some() { let window_size = window_info.size; self.gl .viewport(0, 0, window_size[0] as i32, window_size[1] as i32); p.draw(&self.gl, attach.as_ref().unwrap().len).unwrap(); } if attach.is_some() { self.gl.bind_vertex_array(None); } self.gl.bind_framebuffer(glow::FRAMEBUFFER, None); window_info.need_redraw = false; need_clean = true; } } }); p.unmount(&self.gl).unwrap(); } if need_clean { self.clean(); } } pub fn render_ui(&mut self, ui: &Ui, window: &winit::window::Window, run: &mut bool) { base(ui, window, run, self); } pub fn create_framebuffer( &mut self, id: &str, size: (i32, i32), ) -> Result<(NativeFramebuffer, NativeTexture)> { let id = &ImString::new(id); let gl = self.gl; let tex = unsafe { let already = self.windows.contains_key(id) && self.windows.get(id).unwrap().framebuffer.is_some(); if already { return Ok(( self.windows[id].framebuffer.unwrap(), self.windows[id].frametexture.unwrap(), )); } let framebuffer = gl.create_framebuffer().unwrap(); gl.bind_framebuffer(glow::FRAMEBUFFER, Some(framebuffer)); let texture = gl.create_texture().unwrap(); gl.bind_texture(glow::TEXTURE_2D, Some(texture)); gl.tex_image_2d( glow::TEXTURE_2D, 0, glow::RGB8 as i32, size.0, size.1, 0, glow::RGB, glow::UNSIGNED_BYTE, None, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR as i32, ); gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32, ); gl.framebuffer_texture_2d( glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D, Some(texture), 0, ); assert_eq!( gl.check_framebuffer_status(glow::FRAMEBUFFER), glow::FRAMEBUFFER_COMPLETE ); gl.bind_framebuffer(glow::FRAMEBUFFER, None); gl.bind_texture(glow::TEXTURE_2D, None); self.all_framebuffers_add(&framebuffer); self.all_frametextures_add(&texture); (framebuffer, texture) }; Ok(tex) } pub fn prepare(&mut self) { for program in self.programs.iter() { let mut p = program.borrow_mut(); p.compile(&self.gl).unwrap(); } } pub fn destroy(&mut self) { for p in self.programs.iter() { let mut p = p.borrow_mut(); p.unmount(&self.gl).unwrap(); p.destroy(&self.gl).unwrap(); } info!("Cleaning up all resources"); unsafe { for vao in self.all_vaos.keys() { self.gl.delete_vertex_array(*vao); } for vbo in self.all_other_buffers.keys() { self.gl.delete_buffer(*vbo); } for framebuffer in self.all_framebuffers.keys() { self.gl.delete_framebuffer(*framebuffer); } for texture in self.all_frametextures.keys() { self.gl.delete_texture(*texture); } } } pub fn create_ppi_render(&mut self, id: &str, config: Option) { let id = &ImString::new(id); let (vao, vbo, ebo) = self.ppi_module.borrow().init(&self.gl); self.windows.get_mut(id).map(|w| { w.attach = Some(Attach { vao, vbo, ebo, len: 0, }); w.need_redraw = true; w.program = 0; w.config = Some(config.unwrap_or_default().into()); }); let v = self.program_with_window.entry(0).or_insert(vec![]); v.push(id.clone()); self.all_vaos_add(&vao); self.all_other_buffers_add(&vbo); ebo.map(|ebo| self.all_other_buffers_add(&ebo)); } pub fn bind_data(&mut self, id: &str, data: &Data) -> Result<()> { let id = &ImString::new(id); use bytemuck::cast_slice; let window = self.windows.get_mut(id).unwrap(); let program = window.program; let program = self.programs[program].borrow(); let data = program.bake(data)?; assert!(window.attach.is_some()); let attach = window.attach.as_mut().unwrap(); attach.len = data.2; unsafe { self.gl.bind_buffer(glow::VERTEX_ARRAY, Some(attach.vbo)); self.gl.buffer_data_u8_slice( glow::ARRAY_BUFFER, cast_slice(data.0.as_slice()), glow::STATIC_DRAW, ); if let Some(ebo) = attach.ebo { self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, attach.ebo); self.gl.buffer_data_u8_slice( glow::ELEMENT_ARRAY_BUFFER, cast_slice(&data.1.as_ref().unwrap()), glow::STATIC_DRAW, ); self.gl.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, None); } self.gl.bind_buffer(glow::VERTEX_ARRAY, None); } Ok(()) } fn all_framebuffers_add(&mut self, framebuffer: &NativeFramebuffer) { self.all_framebuffers .entry(*framebuffer) .and_modify(|v| *v += 1) .or_insert(1); info!( "Framebuffer: {:?} + 1, Framebuffer {:?}: {}", framebuffer, framebuffer, self.all_framebuffers[framebuffer] ); } fn all_framebuffers_minus(&mut self, framebuffer: &NativeFramebuffer) { self.all_framebuffers .entry(*framebuffer) .and_modify(|v| *v -= 1); info!( "Framebuffer: {:?} - 1, Framebuffer {:?}: {}", framebuffer, framebuffer, self.all_framebuffers[framebuffer] ); } fn all_frametextures_add(&mut self, texture: &NativeTexture) { self.all_frametextures .entry(*texture) .and_modify(|v| *v += 1) .or_insert(1); info!( "Texture: {:?} + 1, Frametexture {:?}: {}", texture, texture, self.all_frametextures[texture] ); } fn all_frametextures_minus(&mut self, texture: &NativeTexture) { self.all_frametextures .entry(*texture) .and_modify(|v| *v -= 1); info!( "Texture: {:?} - 1, Frametexture {:?}: {}", texture, texture, self.all_frametextures[texture] ); } fn all_vaos_add(&mut self, vao: &NativeVertexArray) { self.all_vaos .entry(*vao) .and_modify(|v| *v += 1) .or_insert(1); info!("Vao: {:?} + 1, Vao {:?}: {}", vao, vao, self.all_vaos[vao]); } fn all_vaos_minus(&mut self, vao: &NativeVertexArray) { self.all_vaos.entry(*vao).and_modify(|v| *v -= 1); info!("Vao: {:?} - 1, Vao {:?}: {}", vao, vao, self.all_vaos[vao]); } fn all_other_buffers_add(&mut self, buffer: &NativeBuffer) { self.all_other_buffers .entry(*buffer) .and_modify(|v| *v += 1) .or_insert(1); info!( "Buffer: {:?} + 1, Buffer {:?}: {}", buffer, buffer, self.all_other_buffers[buffer] ); } fn all_other_buffers_minus(&mut self, buffer: &NativeBuffer) { self.all_other_buffers .entry(*buffer) .and_modify(|v| *v -= 1); info!( "Buffer: {:?} - 1, Buffer {:?}: {}", buffer, buffer, self.all_other_buffers[buffer] ); } pub fn show_window(&mut self, ui: &Ui) { let mut need_resize = vec![]; for (id, window) in self.windows.iter_mut() { ui.window(&window.title) .size(window.size, imgui::Condition::FirstUseEver) .opened(&mut window.open) .flags(imgui::WindowFlags::NO_SCROLLBAR) .build(|| { if ui.is_mouse_clicked(imgui::MouseButton::Left) { let io = ui.io(); let pos = io.mouse_pos; window.last_mouse_position = pos; } if ui.is_mouse_dragging(imgui::MouseButton::Left) { let delta = ui.mouse_drag_delta(); window.last_mouse_delta = delta; window.accmulate_mouse_delta = [ window.accmulate_mouse_delta[0] + delta[0], window.accmulate_mouse_delta[1] + delta[1], ]; window.motion = Some(MouseState::Drag { from: window.last_mouse_position, delta: delta, }); } if ui.is_mouse_released(imgui::MouseButton::Left) { if window.size != ui.window_size() { window.size = ui.window_size(); println!("resized: {:?}", window.size); need_resize.push((window.title.clone(), ui.window_size())); } } if let Some(texture) = window.frametexture { let cursor = ui.cursor_pos(); imgui::Image::new( imgui::TextureId::new(texture.0.get() as usize), ui.window_size(), ) .build(ui); ui.set_cursor_pos(cursor); if ui.invisible_button(&window.title, ui.window_size()) { let io = ui.io(); let pos = io.mouse_pos; let window_pos = ui.window_pos(); let related_pos = [pos[0] - window_pos[0], pos[1] - window_pos[1]]; } } }); } for (id, size) in need_resize.iter() { self.reset_window_size(id, *size); } } // pub fn copy_window_resource( // &mut self, // src: &str, // dst: &str, // stick: bool, // ) -> Option { // let src = &ImString::new(src); // let window_info = self.windows.get(src).unwrap(); // let new_texture = if stick { // let new_framebuffer_tex = self.create_framebuffer(dst, (300, 300)).unwrap(); // unsafe { // self.gl // .bind_framebuffer(glow::READ_FRAMEBUFFER, window_info.framebuffer); // self.gl // .bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.framebuffers[dst].0)); // self.gl.blit_framebuffer( // 0, // 0, // 300, // 300, // 0, // 0, // 300, // 300, // glow::COLOR_BUFFER_BIT, // glow::NEAREST, // ); // self.gl.bind_framebuffer(glow::READ_FRAMEBUFFER, None); // self.gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, None); // } // self.framebuffers // .get_mut(dst) // .map(|(_, redraw, program, atta)| { // *atta = new_framebuffer.as_ref().map(|v| v.3.clone()).flatten(); // *redraw = true; // *program = new_framebuffer.as_ref().map(|v| v.2).unwrap(); // }); // Some(new_framebuffer_tex) // } else { // let texture = self.frametextures.get(src).map(|texture| *texture); // if let Some(ref framebuffer) = new_framebuffer { // if let Some(ref texture) = texture { // self.all_framebuffers_add(&framebuffer.0); // self.all_frametextures_add(&texture); // self.framebuffers // .insert(ImString::new(dst), framebuffer.clone()); // self.frametextures // .insert(ImString::new(dst), texture.clone()); // } // } // texture // }; // new_framebuffer.map(|framebuffer| { // new_texture.map(|_| { // self.all_vaos_add(&framebuffer.3.as_ref().unwrap().vao); // self.all_other_buffers_add(&framebuffer.3.as_ref().unwrap().vbo); // framebuffer.3.as_ref().unwrap().ebo.map(|ebo| { // self.all_other_buffers_add(&ebo); // }); // self.program_with_window.get_mut(&framebuffer.2).map(|v| { // v.push(ImString::new(dst)); // }); // }); // }); // new_texture // } pub fn create_render_window(&mut self, title: &str, size: [f32; 2]) -> Result<()> { // Insert the window data into the windows hashmap let id = ImString::new(title); let mut data = WindowData::new(id.clone(), size); let (fb, tex) = self.create_framebuffer(title, (size[0].floor() as i32, size[1].floor() as i32))?; data.framebuffer = Some(fb); data.frametexture = Some(tex); self.windows.insert(id, data); Ok(()) } pub fn set_config(&mut self, id: &str) -> Option<&mut Config> { let id = &ImString::new(id); self.windows .get_mut(id) .map(|v| { v.re_init = true; v.config.as_mut() }) .flatten() } fn reset_window_size(&mut self, id: &ImString, size: [f32; 2]) { let window_info = self.windows.get_mut(id).unwrap(); window_info.need_redraw = true; let tex = unsafe { self.gl .bind_framebuffer(glow::FRAMEBUFFER, window_info.framebuffer); let texture = self.gl.create_texture().unwrap(); self.gl.bind_texture(glow::TEXTURE_2D, Some(texture)); self.gl.tex_image_2d( glow::TEXTURE_2D, 0, glow::RGB8 as i32, size[0].floor() as i32, size[1].floor() as i32, 0, glow::RGB, glow::UNSIGNED_BYTE, None, ); self.gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MIN_FILTER, glow::LINEAR as i32, ); self.gl.tex_parameter_i32( glow::TEXTURE_2D, glow::TEXTURE_MAG_FILTER, glow::LINEAR as i32, ); self.gl.framebuffer_texture_2d( glow::FRAMEBUFFER, glow::COLOR_ATTACHMENT0, glow::TEXTURE_2D, Some(texture), 0, ); assert_eq!( self.gl.check_framebuffer_status(glow::FRAMEBUFFER), glow::FRAMEBUFFER_COMPLETE ); self.gl.bind_framebuffer(glow::FRAMEBUFFER, None); self.gl.bind_texture(glow::TEXTURE_2D, None); texture }; let raw_tex = window_info.frametexture.as_ref().unwrap().to_owned(); self.all_frametextures_minus(&raw_tex); self.all_frametextures_add(&tex); } pub fn destroy_window(&mut self) { let ids: Vec<_> = self .windows .iter() .filter(|v| v.1.open == false) .map(|v| v.0.clone()) .collect(); for id in ids.iter() { let window = self.windows.remove(id).unwrap(); window.framebuffer.map(|framebuffer| { self.all_framebuffers_minus(&framebuffer); }); window.frametexture.map(|texture| { self.all_frametextures_minus(&texture); }); if let Some(atta) = window.attach { self.all_vaos_minus(&atta.vao); self.all_other_buffers_minus(&atta.vbo); atta.ebo.map(|ebo| { self.all_other_buffers_minus(&ebo); }); } } } fn clean(&mut self) { info!("Cleaning up unused resources"); unsafe { self.all_framebuffers .iter() .filter(|v| *v.1 == 0) .for_each(|(bf, _)| { info!("Deleting framebuffer: {:?}", bf); self.gl.delete_framebuffer(*bf); }); self.all_frametextures .iter() .filter(|v| *v.1 == 0) .for_each(|(bf, _)| { info!("Deleting texture: {:?}", bf); self.gl.delete_texture(*bf); }); self.all_vaos .iter() .filter(|v| *v.1 == 0) .for_each(|(bf, _)| { info!("Deleting vao: {:?}", bf); self.gl.delete_vertex_array(*bf); }); self.all_other_buffers .iter() .filter(|v| *v.1 == 0) .for_each(|(bf, _)| { info!("Deleting buffer: {:?}", bf); self.gl.delete_buffer(*bf); }); } self.all_framebuffers.retain(|_, v| *v > 0); self.all_frametextures.retain(|_, v| *v > 0); self.all_vaos.retain(|_, v| *v > 0); self.all_other_buffers.retain(|_, v| *v > 0); } } #[derive(Debug, Clone)] pub struct Attach { pub vao: NativeVertexArray, pub vbo: NativeBuffer, pub ebo: Option, pub len: i32, } pub struct State {} pub struct WindowData { pub title: ImString, pub open: bool, pub copy_from: Option, pub size: [f32; 2], framebuffer: Option, frametexture: Option, need_redraw: bool, program: usize, attach: Option, re_init: bool, last_mouse_position: [f32; 2], last_mouse_delta: [f32; 2], accmulate_mouse_delta: [f32; 2], mouse_position: [f32; 2], motion: Option, config: Option, } impl WindowData { pub fn new(title: ImString, size: [f32; 2]) -> Self { Self { title, open: true, copy_from: None, size, last_mouse_position: [0.0, 0.0], last_mouse_delta: [0.0, 0.0], accmulate_mouse_delta: [0.0, 0.0], mouse_position: [0.0, 0.0], motion: None, framebuffer: None, frametexture: None, need_redraw: false, program: 0, attach: None, re_init: false, config: None, } } fn set_current_mouse_delta(&mut self, delta: [f32; 2]) { self.last_mouse_delta = delta; self.accmulate_mouse_delta = [ self.accmulate_mouse_delta[0] + delta[0], self.accmulate_mouse_delta[1] + delta[1], ]; } fn set_mouse_postion(&mut self, pos: [f32; 2]) { self.mouse_position = pos; } }