diff --git a/Cargo.toml b/Cargo.toml index 2ce437c..c0c9f5c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ members = [ "imgui-glow-renderer", "imgui-sdl2-support", "imgui-winit-support", + "imgui-winit-glow-renderer-viewports", "imgui-examples", "xtask", ] diff --git a/imgui-winit-glow-renderer-viewports/Cargo.toml b/imgui-winit-glow-renderer-viewports/Cargo.toml new file mode 100644 index 0000000..9e711dc --- /dev/null +++ b/imgui-winit-glow-renderer-viewports/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "imgui-winit-glow-renderer-viewports" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +imgui = { version="0.9.0", path="../imgui", features=["docking"] } + +glow = "0.11.2" +glutin = "0.30.3" +raw-window-handle = "0.5.0" +winit = "0.27.5" +thiserror = "1.0.38" +glutin-winit = "0.2.1" diff --git a/imgui-winit-glow-renderer-viewports/examples/basic.rs b/imgui-winit-glow-renderer-viewports/examples/basic.rs new file mode 100644 index 0000000..37c9a43 --- /dev/null +++ b/imgui-winit-glow-renderer-viewports/examples/basic.rs @@ -0,0 +1,116 @@ +use std::{ffi::CString, num::NonZeroU32}; + +use glow::{Context, HasContext}; +use glutin::{ + config::ConfigTemplateBuilder, + context::ContextAttributesBuilder, + display::GetGlDisplay, + prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentContextGlSurfaceAccessor}, + surface::{SurfaceAttributesBuilder, WindowSurface, GlSurface}, +}; +use glutin_winit::DisplayBuilder; +use imgui::ConfigFlags; +use imgui_winit_glow_renderer_viewports::Renderer; +use raw_window_handle::HasRawWindowHandle; +use winit::{dpi::LogicalSize, event_loop::EventLoop, window::WindowBuilder}; + +fn main() { + let event_loop = EventLoop::new(); + + let window_builder = WindowBuilder::new() + .with_inner_size(LogicalSize::new(800.0, 600.0)) + .with_visible(true) + .with_resizable(true) + .with_title("Viewports example"); + + let template_builder = ConfigTemplateBuilder::new(); + let (window, gl_config) = DisplayBuilder::new() + .with_window_builder(Some(window_builder)) + .build(&event_loop, template_builder, |mut configs| { + configs.next().unwrap() + }) + .expect("Failed to create main window"); + + let window = window.unwrap(); + + let context_attribs = ContextAttributesBuilder::new().build(Some(window.raw_window_handle())); + let context = unsafe { + gl_config + .display() + .create_context(&gl_config, &context_attribs) + .expect("Failed to create main context") + }; + + let size = window.inner_size(); + let surface_attribs = SurfaceAttributesBuilder::::new().build( + window.raw_window_handle(), + NonZeroU32::new(size.width).unwrap(), + NonZeroU32::new(size.height).unwrap(), + ); + let surface = unsafe { + gl_config + .display() + .create_window_surface(&gl_config, &surface_attribs) + .expect("Failed to create main surface") + }; + + let context = context + .make_current(&surface) + .expect("Failed to make current"); + + let glow = unsafe { + Context::from_loader_function(|name| { + let name = CString::new(name).unwrap(); + context.display().get_proc_address(&name) + }) + }; + + let mut imgui = imgui::Context::create(); + imgui + .io_mut() + .config_flags + .insert(ConfigFlags::DOCKING_ENABLE); + imgui + .io_mut() + .config_flags + .insert(ConfigFlags::VIEWPORTS_ENABLE); + + let mut renderer = Renderer::new(&mut imgui, &window, &glow).expect("Failed to init Renderer"); + + event_loop.run(move |event, window_target, control_flow| { + control_flow.set_poll(); + + renderer.handle_event(&mut imgui, &window, &event); + + match event { + winit::event::Event::MainEventsCleared => { + window.request_redraw(); + }, + winit::event::Event::RedrawRequested(_) => { + let ui = imgui.frame(); + + ui.show_demo_window(&mut true); + + ui.end_frame_early(); + + imgui.update_platform_windows(); + renderer.update_viewports(&mut imgui, window_target, &glow).expect("Failed to update viewports"); + + let draw_data = imgui.render(); + + context.make_current(&surface).expect("Failed to make current"); + + unsafe { + glow.clear(glow::COLOR_BUFFER_BIT); + } + + renderer.render(&window, &glow, draw_data).expect("Failed to render main viewport"); + + surface.swap_buffers(&context).expect("Failed to swap buffers"); + + renderer.render_viewports(&glow, &mut imgui).expect("Failed to render viewports"); + }, + _ => {}, + } + }); +} diff --git a/imgui-winit-glow-renderer-viewports/src/fragment_shader.glsl b/imgui-winit-glow-renderer-viewports/src/fragment_shader.glsl new file mode 100644 index 0000000..b13bef2 --- /dev/null +++ b/imgui-winit-glow-renderer-viewports/src/fragment_shader.glsl @@ -0,0 +1,13 @@ +#version 450 core + +in vec2 v2f_UV; +in vec4 v2f_Color; + +layout(location = 0) uniform sampler2D u_FontTexture; + +layout(location = 0) out vec4 out_Color; + +void main() { + vec4 tex = texture(u_FontTexture, v2f_UV); + out_Color = v2f_Color * tex; +} diff --git a/imgui-winit-glow-renderer-viewports/src/lib.rs b/imgui-winit-glow-renderer-viewports/src/lib.rs new file mode 100644 index 0000000..59e97e1 --- /dev/null +++ b/imgui-winit-glow-renderer-viewports/src/lib.rs @@ -0,0 +1,767 @@ +use std::{ + cell::RefCell, + collections::{HashMap, VecDeque}, + num::NonZeroU32, + ptr::null_mut, + rc::Rc, slice, +}; + +use glow::HasContext; +use glutin::{ + config::ConfigTemplateBuilder, + context::{ContextAttributesBuilder, NotCurrentContext}, + display::GetGlDisplay, + prelude::{GlDisplay, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext}, + surface::{Surface, SurfaceAttributesBuilder, WindowSurface, GlSurface}, +}; +use glutin_winit::DisplayBuilder; +use imgui::{BackendFlags, Id, Key, ViewportFlags}; +use raw_window_handle::HasRawWindowHandle; +use thiserror::Error; +use winit::{ + dpi::{PhysicalPosition, PhysicalSize}, + event::{DeviceEvent, ElementState, KeyboardInput, TouchPhase, VirtualKeyCode}, + event_loop::EventLoopWindowTarget, + window::{Window, WindowBuilder}, +}; + +const VERTEX_SHADER: &str = include_str!("vertex_shader.glsl"); +const FRAGMENT_SHADER: &str = include_str!("fragment_shader.glsl"); + +#[derive(Debug, Error)] +pub enum RendererError { + #[error("OpenGL object creation failed: {0}")] + GlObjectCreationError(String), + #[error("Failed to create glutin Display")] + GlutinDisplay, +} + +#[derive(Debug)] +enum ViewportEvent { + Create(Id), + Destroy(Id), + SetPos(Id, [f32; 2]), + SetSize(Id, [f32; 2]), + SetVisible(Id), + SetFocus(Id), + SetTitle(Id, String), +} + +#[derive(Debug)] +pub struct Renderer { + gl_objects: GlObjects, + glutin_config: Option, + extra_windows: HashMap< + Id, + ( + Window, + Option, + Surface, + GlObjects, + ), + >, + event_queue: Rc>>, + font_width: u32, + font_height: u32, + font_pixels: Vec, +} + +#[derive(Debug)] +struct GlObjects { + program: glow::Program, + font_texture: glow::Texture, + vao: glow::VertexArray, + vbo: glow::Buffer, + ibo: glow::Buffer, +} + +impl GlObjects { + pub fn new( + font_width: u32, + font_height: u32, + font_pixels: &[u8], + glow: &glow::Context, + ) -> Result { + let program = unsafe { + let vertex_shader = glow + .create_shader(glow::VERTEX_SHADER) + .map_err(|e| RendererError::GlObjectCreationError(e))?; + glow.shader_source(vertex_shader, VERTEX_SHADER); + glow.compile_shader(vertex_shader); + assert!( + glow.get_shader_compile_status(vertex_shader), + "Vertex Shader contains error" + ); + + let fragment_shader = glow + .create_shader(glow::FRAGMENT_SHADER) + .map_err(|e| RendererError::GlObjectCreationError(e))?; + glow.shader_source(fragment_shader, FRAGMENT_SHADER); + glow.compile_shader(fragment_shader); + assert!( + glow.get_shader_compile_status(fragment_shader), + "Fragment Shader contains error" + ); + + let program = glow + .create_program() + .map_err(|e| RendererError::GlObjectCreationError(e))?; + glow.attach_shader(program, vertex_shader); + glow.attach_shader(program, fragment_shader); + glow.link_program(program); + assert!( + glow.get_program_link_status(program), + "Program contains error" + ); + + glow.delete_shader(vertex_shader); + glow.delete_shader(fragment_shader); + + program + }; + + let font_texture = unsafe { + let tex = glow + .create_texture() + .map_err(|e| RendererError::GlObjectCreationError(e))?; + glow.bind_texture(glow::TEXTURE_2D, Some(tex)); + glow.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MAG_FILTER, + glow::LINEAR as i32, + ); + glow.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_MIN_FILTER, + glow::LINEAR as i32, + ); + glow.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_WRAP_S, + glow::CLAMP_TO_EDGE as i32, + ); + glow.tex_parameter_i32( + glow::TEXTURE_2D, + glow::TEXTURE_WRAP_T, + glow::CLAMP_TO_EDGE as i32, + ); + glow.tex_image_2d( + glow::TEXTURE_2D, + 0, + glow::RGBA as i32, + font_width as i32, + font_height as i32, + 0, + glow::RGBA, + glow::UNSIGNED_BYTE, + Some(font_pixels), + ); + + tex + }; + + let vao = unsafe { + let vao = glow + .create_vertex_array() + .map_err(|e| RendererError::GlObjectCreationError(e))?; + + glow.bind_vertex_array(Some(vao)); + glow.enable_vertex_attrib_array(0); + glow.enable_vertex_attrib_array(1); + glow.enable_vertex_attrib_array(2); + glow.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 20, 0); + glow.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, 20, 8); + glow.vertex_attrib_pointer_f32(0, 4, glow::UNSIGNED_BYTE, true, 20, 16); + + vao + }; + + let vbo = unsafe { + glow.create_buffer() + .map_err(|e| RendererError::GlObjectCreationError(e))? + }; + let ibo = unsafe { + glow.create_buffer() + .map_err(|e| RendererError::GlObjectCreationError(e))? + }; + + Ok(Self { + program, + font_texture, + vao, + vbo, + ibo, + }) + } +} + +impl Renderer { + pub fn new( + imgui: &mut imgui::Context, + main_window: &Window, + gl_context: &glow::Context, + ) -> Result { + let io = imgui.io_mut(); + + io.backend_flags.insert(BackendFlags::HAS_MOUSE_CURSORS); + io.backend_flags.insert(BackendFlags::HAS_SET_MOUSE_POS); + io.backend_flags + .insert(BackendFlags::PLATFORM_HAS_VIEWPORTS); + io.backend_flags + .insert(BackendFlags::RENDERER_HAS_VIEWPORTS); + io.backend_flags + .insert(BackendFlags::RENDERER_HAS_VTX_OFFSET); + + io[Key::Tab] = VirtualKeyCode::Tab as _; + io[Key::LeftArrow] = VirtualKeyCode::Left as _; + io[Key::RightArrow] = VirtualKeyCode::Right as _; + io[Key::UpArrow] = VirtualKeyCode::Up as _; + io[Key::DownArrow] = VirtualKeyCode::Down as _; + io[Key::PageUp] = VirtualKeyCode::PageUp as _; + io[Key::PageDown] = VirtualKeyCode::PageDown as _; + io[Key::Home] = VirtualKeyCode::Home as _; + io[Key::End] = VirtualKeyCode::End as _; + io[Key::Insert] = VirtualKeyCode::Insert as _; + io[Key::Delete] = VirtualKeyCode::Delete as _; + io[Key::Backspace] = VirtualKeyCode::Back as _; + io[Key::Space] = VirtualKeyCode::Space as _; + io[Key::Enter] = VirtualKeyCode::Return as _; + io[Key::Escape] = VirtualKeyCode::Escape as _; + io[Key::KeyPadEnter] = VirtualKeyCode::NumpadEnter as _; + io[Key::A] = VirtualKeyCode::A as _; + io[Key::C] = VirtualKeyCode::C as _; + io[Key::V] = VirtualKeyCode::V as _; + io[Key::X] = VirtualKeyCode::X as _; + io[Key::Y] = VirtualKeyCode::Y as _; + io[Key::Z] = VirtualKeyCode::Z as _; + + let window_size = main_window.inner_size().cast::(); + io.display_size = [window_size.width, window_size.height]; + io.display_framebuffer_scale = [1.0, 1.0]; + + let viewport = imgui.main_viewport_mut(); + + let main_pos = main_window.inner_position().unwrap().cast::(); + + viewport.pos = [main_pos.x, main_pos.y]; + viewport.work_pos = viewport.pos; + viewport.size = [window_size.width, window_size.height]; + viewport.work_size = viewport.size; + viewport.dpi_scale = 1.0; + viewport.platform_user_data = Box::into_raw(Box::new(ViewportData { + pos: [main_pos.x, main_pos.y], + size: [window_size.width, window_size.height], + focus: true, + minimized: false, + })) + .cast(); + + let mut monitors = Vec::new(); + for monitor in main_window.available_monitors() { + monitors.push(imgui::PlatformMonitor { + main_pos: [monitor.position().x as f32, monitor.position().y as f32], + main_size: [monitor.size().width as f32, monitor.size().height as f32], + work_pos: [monitor.position().x as f32, monitor.position().y as f32], + work_size: [monitor.size().width as f32, monitor.size().height as f32], + dpi_scale: 1.0, + }); + } + imgui + .platform_io_mut() + .monitors + .replace_from_slice(&monitors); + + imgui.set_platform_name(Some(format!( + "imgui-winit-glow-renderer-viewports {}", + env!("CARGO_PKG_VERSION") + ))); + imgui.set_renderer_name(Some(format!( + "imgui-winit-glow-renderer-viewports {}", + env!("CARGO_PKG_VERSION") + ))); + + let event_queue = Rc::new(RefCell::new(VecDeque::new())); + + imgui.set_platform_backend(PlatformBackend { + event_queue: event_queue.clone(), + }); + imgui.set_renderer_backend(RendererBackend {}); + + let font_tex = imgui.fonts().build_rgba32_texture(); + let gl_objects = + GlObjects::new(font_tex.width, font_tex.height, font_tex.data, gl_context)?; + + Ok(Self { + gl_objects, + glutin_config: None, + extra_windows: HashMap::new(), + event_queue: Rc::new(RefCell::new(VecDeque::new())), + font_width: font_tex.width, + font_height: font_tex.height, + font_pixels: font_tex.data.to_vec(), + }) + } + + pub fn handle_event( + &mut self, + imgui: &mut imgui::Context, + main_window: &Window, + event: &winit::event::Event, + ) { + match *event { + winit::event::Event::WindowEvent { + window_id, + ref event, + } => { + let viewport = if window_id == main_window.id() { + imgui.main_viewport_mut() + } else { + if let Some(id) = self.extra_windows.iter().find_map(|(id, (wnd, _, _, _))| { + if wnd.id() == window_id { + Some(*id) + } else { + None + } + }) { + if let Some(viewport) = imgui.viewport_by_id_mut(id) { + viewport + } else { + return; + } + } else { + return; + } + }; + + match *event { + winit::event::WindowEvent::Resized(new_size) => { + viewport.size = [new_size.width as f32, new_size.height as f32]; + viewport.work_size = viewport.size; + + if window_id == main_window.id() { + imgui.io_mut().display_size = + [new_size.width as f32, new_size.height as f32]; + } + } + winit::event::WindowEvent::Moved(new_pos) => { + viewport.pos = [new_pos.x as f32, new_pos.y as f32]; + viewport.work_pos = viewport.pos; + } + winit::event::WindowEvent::CloseRequested if window_id != main_window.id() => { + viewport.platform_request_close = true; + } + winit::event::WindowEvent::ReceivedCharacter(c) => { + imgui.io_mut().add_input_character(c); + } + winit::event::WindowEvent::Focused(f) => unsafe { + (*(viewport.platform_user_data.cast::())).focus = f; + }, + winit::event::WindowEvent::KeyboardInput { + input: + KeyboardInput { + virtual_keycode: Some(key), + state: ElementState::Pressed, + .. + }, + .. + } => { + imgui.io_mut().keys_down[key as usize] = true; + } + winit::event::WindowEvent::CursorMoved { position, .. } => { + imgui.io_mut().mouse_pos = [position.x as f32, position.y as f32]; + } + winit::event::WindowEvent::MouseWheel { + delta, + phase: TouchPhase::Moved, + .. + } => match delta { + winit::event::MouseScrollDelta::LineDelta(h, v) => { + imgui.io_mut().mouse_wheel_h = h; + imgui.io_mut().mouse_wheel = v; + } + winit::event::MouseScrollDelta::PixelDelta(pos) => { + imgui.io_mut().mouse_wheel_h += if pos.x > 0.0 { + 1.0 + } else if pos.x < 0.0 { + -1.0 + } else { + 0.0 + }; + imgui.io_mut().mouse_wheel += if pos.y > 0.0 { + 1.0 + } else if pos.y < 0.0 { + -1.0 + } else { + 0.0 + }; + } + }, + winit::event::WindowEvent::MouseInput { state, button, .. } => { + let state = state == ElementState::Pressed; + + match button { + winit::event::MouseButton::Left => { + imgui.io_mut().mouse_down[0] = state; + } + winit::event::MouseButton::Right => { + imgui.io_mut().mouse_down[1] = state; + } + winit::event::MouseButton::Middle => { + imgui.io_mut().mouse_down[2] = state; + } + winit::event::MouseButton::Other(index @ 0..=4) => { + imgui.io_mut().mouse_down[index as usize] = state; + } + _ => {} + } + } + _ => {} + } + } + winit::event::Event::DeviceEvent { + event: + DeviceEvent::Key(KeyboardInput { + virtual_keycode: Some(key), + state: ElementState::Released, + .. + }), + .. + } => { + imgui.io_mut().keys_down[key as usize] = false; + } + _ => {} + } + } + + pub fn update_viewports( + &mut self, + imgui: &mut imgui::Context, + window_target: &EventLoopWindowTarget, + glow: &glow::Context, + ) -> Result<(), RendererError> { + loop { + let event = self.event_queue.borrow_mut().pop_front(); + let event = if let Some(event) = event { + event + } else { + break; + }; + + match event { + ViewportEvent::Create(id) => { + if let Some(viewport) = imgui.viewport_by_id_mut(id) { + let extra_window = + self.create_extra_window(viewport, window_target, glow)?; + self.extra_windows.insert(id, extra_window); + } + } + ViewportEvent::Destroy(id) => { + self.extra_windows.remove(&id); + } + ViewportEvent::SetPos(id, pos) => { + if let Some((wnd, _, _, _)) = self.extra_windows.get(&id) { + wnd.set_outer_position(PhysicalPosition::new(pos[0], pos[1])); + } + } + ViewportEvent::SetSize(id, size) => { + if let Some((wnd, _, _, _)) = self.extra_windows.get(&id) { + wnd.set_inner_size(PhysicalSize::new(size[0], size[1])); + } + } + ViewportEvent::SetVisible(id) => { + if let Some((wnd, _, _, _)) = self.extra_windows.get(&id) { + wnd.set_visible(true); + } + } + ViewportEvent::SetFocus(id) => { + if let Some((wnd, _, _, _)) = self.extra_windows.get(&id) { + wnd.focus_window(); + } + } + ViewportEvent::SetTitle(id, title) => { + if let Some((wnd, _, _, _)) = self.extra_windows.get(&id) { + wnd.set_title(&title); + } + } + } + } + + Ok(()) + } + + fn create_extra_window( + &mut self, + viewport: &mut imgui::Viewport, + window_target: &EventLoopWindowTarget, + glow: &glow::Context, + ) -> Result< + ( + Window, + Option, + Surface, + GlObjects, + ), + RendererError, + > { + let window_builder = WindowBuilder::new() + .with_position(PhysicalPosition::new(viewport.pos[0], viewport.pos[1])) + .with_inner_size(PhysicalSize::new(viewport.size[0], viewport.size[1])) + .with_visible(false) + .with_resizable(true) + .with_decorations(!viewport.flags.contains(ViewportFlags::NO_DECORATION)); + + let window = if let Some(glutin_config) = &self.glutin_config { + glutin_winit::finalize_window(window_target, window_builder, glutin_config) + .map_err(|_| RendererError::GlutinDisplay)? + } else { + let template_builder = ConfigTemplateBuilder::new(); + + let (window, cfg) = DisplayBuilder::new() + .with_window_builder(Some(window_builder)) + .build(window_target, template_builder, |mut configs| { + configs.next().unwrap() + }) + .map_err(|_| RendererError::GlutinDisplay)?; + + self.glutin_config = Some(cfg); + + window.unwrap() + }; + + let glutin_config = self.glutin_config.as_ref().unwrap(); + + let context_attribs = + ContextAttributesBuilder::new().build(Some(window.raw_window_handle())); + let context = unsafe { + glutin_config + .display() + .create_context(&glutin_config, &context_attribs) + .map_err(|_| RendererError::GlutinDisplay)? + }; + + let surface_attribs = SurfaceAttributesBuilder::::new().build( + window.raw_window_handle(), + NonZeroU32::new(viewport.size[0] as u32).unwrap(), + NonZeroU32::new(viewport.size[1] as u32).unwrap(), + ); + let surface = unsafe { + glutin_config + .display() + .create_window_surface(glutin_config, &surface_attribs) + .map_err(|_| RendererError::GlutinDisplay)? + }; + + let context = context + .make_current(&surface) + .map_err(|_| RendererError::GlutinDisplay)?; + + let gl_objects = + GlObjects::new(self.font_width, self.font_height, &self.font_pixels, glow)?; + + Ok(( + window, + Some(context.make_not_current().unwrap()), + surface, + gl_objects, + )) + } + + pub fn render( + &mut self, + main_window: &Window, + glow: &glow::Context, + draw_data: &imgui::DrawData, + ) -> Result<(), RendererError> { + Self::render_window(main_window, glow, draw_data, &self.gl_objects) + } + + pub fn render_viewports( + &mut self, + glow: &glow::Context, + imgui: &mut imgui::Context, + ) -> Result<(), RendererError> { + for (id, (wnd, context, surface, gl_objects)) in &mut self.extra_windows { + if let Some(viewport) = imgui.viewport_by_id(*id) { + let current_context = context.take().unwrap().make_current(surface).map_err(|_| RendererError::GlutinDisplay)?; + + unsafe { + glow.clear(glow::COLOR_BUFFER_BIT); + } + Self::render_window(wnd, glow, viewport.draw_data(), gl_objects)?; + surface.swap_buffers(¤t_context).map_err(|_| RendererError::GlutinDisplay)?; + + *context = Some(current_context.make_not_current().unwrap()); + } + } + + Ok(()) + } + + fn render_window( + window: &Window, + glow: &glow::Context, + draw_data: &imgui::DrawData, + gl_objects: &GlObjects, + ) -> Result<(), RendererError> { + unsafe { + let window_size = window.inner_size(); + + glow.viewport(0, 0, window_size.width as i32, window_size.height as i32); + + glow.bind_vertex_array(Some(gl_objects.vao)); + glow.bind_buffer(glow::ARRAY_BUFFER, Some(gl_objects.vbo)); + glow.bind_buffer(glow::ELEMENT_ARRAY_BUFFER, Some(gl_objects.ibo)); + glow.active_texture(glow::TEXTURE0); + glow.bind_texture(glow::TEXTURE_2D, Some(gl_objects.font_texture)); + glow.use_program(Some(gl_objects.program)); + + let left = draw_data.display_pos[0]; + let right = draw_data.display_pos[0] + draw_data.display_size[0]; + let top = draw_data.display_pos[1]; + let bottom = draw_data.display_pos[1] + draw_data.display_size[1]; + + + let matrix = [ + 2.0 / (right - left) , 0.0 , 0.0 , 0.0, + 0.0 , (2.0 / (top - bottom)) , 0.0 , 0.0, + 0.0 , 0.0 , -1.0, 0.0, + (right + left) / (left - right), (top + bottom) / (bottom - top), 0.0 , 1.0, + ]; + + let loc = glow.get_uniform_location(gl_objects.program, "u_Matrix").unwrap(); + glow.uniform_matrix_4_f32_slice(Some(&loc), false, &matrix); + + for list in draw_data.draw_lists() { + glow.buffer_data_u8_slice(glow::ARRAY_BUFFER, slice::from_raw_parts(list.vtx_buffer().as_ptr().cast(), list.vtx_buffer().len() * 20), glow::STREAM_DRAW); + glow.buffer_data_u8_slice(glow::ELEMENT_ARRAY_BUFFER, slice::from_raw_parts(list.idx_buffer().as_ptr().cast(), list.idx_buffer().len() * 2), glow::STREAM_DRAW); + + for cmd in list.commands() { + match cmd { + imgui::DrawCmd::Elements { count, cmd_params } => { + glow.draw_elements_base_vertex(glow::TRIANGLES, count as i32, glow::UNSIGNED_SHORT, (cmd_params.idx_offset * 2) as i32, cmd_params.vtx_offset as i32); + }, + _ => {}, + } + } + } + } + + Ok(()) + } +} + +struct ViewportData { + pos: [f32; 2], + size: [f32; 2], + focus: bool, + minimized: bool, +} + +struct PlatformBackend { + event_queue: Rc>>, +} + +impl imgui::PlatformViewportBackend for PlatformBackend { + fn create_window(&mut self, viewport: &mut imgui::Viewport) { + viewport.platform_user_data = Box::into_raw(Box::new(ViewportData { + pos: viewport.pos, + size: viewport.size, + focus: false, + minimized: false, + })) + .cast(); + self.event_queue + .borrow_mut() + .push_back(ViewportEvent::Create(viewport.id)); + } + + fn destroy_window(&mut self, viewport: &mut imgui::Viewport) { + unsafe { + drop(Box::from_raw( + viewport.platform_user_data.cast::(), + )); + } + viewport.platform_user_data = null_mut(); + + self.event_queue + .borrow_mut() + .push_back(ViewportEvent::Destroy(viewport.id)); + } + + fn show_window(&mut self, viewport: &mut imgui::Viewport) { + self.event_queue + .borrow_mut() + .push_back(ViewportEvent::SetVisible(viewport.id)); + } + + fn set_window_pos(&mut self, viewport: &mut imgui::Viewport, pos: [f32; 2]) { + self.event_queue + .borrow_mut() + .push_back(ViewportEvent::SetPos(viewport.id, pos)); + } + + fn get_window_pos(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { + unsafe { (*(viewport.platform_user_data.cast::())).pos } + } + + fn set_window_size(&mut self, viewport: &mut imgui::Viewport, size: [f32; 2]) { + self.event_queue + .borrow_mut() + .push_back(ViewportEvent::SetSize(viewport.id, size)); + } + + fn get_window_size(&mut self, viewport: &mut imgui::Viewport) -> [f32; 2] { + unsafe { (*(viewport.platform_user_data.cast::())).size } + } + + fn set_window_focus(&mut self, viewport: &mut imgui::Viewport) { + self.event_queue + .borrow_mut() + .push_back(ViewportEvent::SetFocus(viewport.id)); + } + + fn get_window_focus(&mut self, viewport: &mut imgui::Viewport) -> bool { + unsafe { (*(viewport.platform_user_data.cast::())).focus } + } + + fn get_window_minimized(&mut self, viewport: &mut imgui::Viewport) -> bool { + unsafe { (*(viewport.platform_user_data.cast::())).minimized } + } + + fn set_window_title(&mut self, viewport: &mut imgui::Viewport, title: &str) { + self.event_queue + .borrow_mut() + .push_back(ViewportEvent::SetTitle(viewport.id, title.to_owned())); + } + + fn set_window_alpha(&mut self, _viewport: &mut imgui::Viewport, _alpha: f32) {} + + fn update_window(&mut self, _viewport: &mut imgui::Viewport) {} + + fn render_window(&mut self, _viewport: &mut imgui::Viewport) {} + + fn swap_buffers(&mut self, _viewport: &mut imgui::Viewport) {} + + fn create_vk_surface( + &mut self, + _viewport: &mut imgui::Viewport, + _instance: u64, + _out_surface: &mut u64, + ) -> i32 { + 0 + } +} + +struct RendererBackend {} + +impl imgui::RendererViewportBackend for RendererBackend { + fn create_window(&mut self, _viewport: &mut imgui::Viewport) {} + + fn destroy_window(&mut self, _viewport: &mut imgui::Viewport) {} + + fn set_window_size(&mut self, _viewport: &mut imgui::Viewport, _size: [f32; 2]) {} + + fn render_window(&mut self, _viewport: &mut imgui::Viewport) {} + + fn swap_buffers(&mut self, _viewport: &mut imgui::Viewport) {} +} diff --git a/imgui-winit-glow-renderer-viewports/src/vertex_shader.glsl b/imgui-winit-glow-renderer-viewports/src/vertex_shader.glsl new file mode 100644 index 0000000..4d74b81 --- /dev/null +++ b/imgui-winit-glow-renderer-viewports/src/vertex_shader.glsl @@ -0,0 +1,16 @@ +#version 450 + +layout(location = 0) in vec2 in_Position; +layout(location = 1) in vec2 in_UV; +layout(location = 2) in vec4 in_Color; + +uniform mat4 u_Matrix; + +out vec2 v2f_UV; +out vec4 v2f_Color; + +void main() { + gl_Position = u_Matrix * vec4(in_Position, 0.0, 1.0); + v2f_UV = in_UV; + v2f_Color = in_Color; +}