use imgui::{BackendFlags, DrawCmd, DrawCmdParams, DrawVert, MouseCursor};
use macroquad::input::MouseButton;
use macroquad::miniquad::{
Bindings, BlendFactor, BlendState, BlendValue, BufferLayout, BufferSource, BufferType, BufferUsage, CursorIcon, Equation, EventHandler, KeyMods,
Pipeline, PipelineParams, ShaderSource, TextureId, UniformsSource, VertexAttribute, VertexFormat,
};
use macroquad::{miniquad, window};
const STARTING_VERTEX_BUFFER_SIZE: usize = 1024;
const STARTING_INDEX_BUFFER_SIZE: usize = 1024;
pub struct ImguiMacroquadRenderer {
pub imgui: imgui::Context,
last_frame: std::time::Instant,
last_window_size: (f32, f32),
input_subscriber_id: usize,
pipeline: Pipeline,
font_texture: TextureId,
draw_calls: Vec<Bindings>,
}
struct MacroquadClipboardBackend;
impl imgui::ClipboardBackend for MacroquadClipboardBackend {
fn get(&mut self) -> Option<String> {
miniquad::window::clipboard_get()
}
fn set(&mut self, value: &str) {
miniquad::window::clipboard_set(value);
}
}
impl ImguiMacroquadRenderer {
pub fn new() -> ImguiMacroquadRenderer {
let mut imgui = imgui::Context::create();
imgui.io_mut().backend_flags.insert(BackendFlags::HAS_MOUSE_CURSORS);
imgui.set_clipboard_backend(MacroquadClipboardBackend);
let input_subscriber_id = macroquad::input::utils::register_input_subscriber();
let ctx = unsafe { window::get_internal_gl().quad_context };
let shader_src = ShaderSource::Glsl {
vertex: shader::VERTEX,
fragment: shader::FRAGMENT,
};
let shader = ctx.new_shader(shader_src, shader::meta()).unwrap();
let pipeline_params = PipelineParams {
color_blend: Some(BlendState::new(
Equation::Add,
BlendFactor::Value(BlendValue::SourceAlpha),
BlendFactor::OneMinusValue(BlendValue::SourceAlpha),
)),
..Default::default()
};
let pipeline = ctx.new_pipeline(
&[BufferLayout::default()],
&[
VertexAttribute::new("position", VertexFormat::Float2),
VertexAttribute::new("texcoord", VertexFormat::Float2),
VertexAttribute::new("color0", VertexFormat::Byte4),
],
shader,
pipeline_params,
);
let font_texture = {
imgui.fonts().add_font(&[imgui::FontSource::DefaultFontData {
config: Some(imgui::FontConfig {
rasterizer_multiply: 1.75,
..imgui::FontConfig::default()
}),
}]);
let fonts = imgui.fonts();
let texture = fonts.build_rgba32_texture();
ctx.new_texture_from_rgba8(texture.width as u16, texture.height as u16, texture.data)
};
let (w, h) = miniquad::window::screen_size();
let io = imgui.io_mut();
io.font_global_scale = 1.0;
io.display_size = [w, h];
ImguiMacroquadRenderer {
imgui,
last_frame: std::time::Instant::now(),
last_window_size: miniquad::window::screen_size(),
input_subscriber_id,
pipeline,
font_texture,
draw_calls: Vec::with_capacity(200),
}
}
}
impl ImguiMacroquadRenderer {
pub fn draw<F: FnMut(&mut imgui::Ui)>(&mut self, f: F) {
unsafe {
window::get_internal_gl().flush();
}
self.draw_no_flush(f);
}
pub fn draw_no_flush<F: FnMut(&mut imgui::Ui)>(&mut self, mut f: F) {
macroquad::input::utils::repeat_all_miniquad_input(self, self.input_subscriber_id);
let ctx = unsafe { window::get_internal_gl().quad_context };
let (width, height) = miniquad::window::screen_size();
if self.last_window_size != (width, height) {
self.last_window_size = (width, height);
self.resize_event(width, height);
}
let draw_data = {
let io = self.imgui.io_mut();
let now = std::time::Instant::now();
io.update_delta_time(now.duration_since(self.last_frame));
self.last_frame = now;
let mut ui = self.imgui.new_frame();
f(&mut ui);
if let Some(mouse_cursor) = ui.mouse_cursor() {
miniquad::window::show_mouse(true);
match mouse_cursor {
MouseCursor::Arrow => miniquad::window::set_mouse_cursor(CursorIcon::Default),
MouseCursor::TextInput => miniquad::window::set_mouse_cursor(CursorIcon::Text),
MouseCursor::ResizeAll => miniquad::window::set_mouse_cursor(CursorIcon::NESWResize),
MouseCursor::ResizeNS => miniquad::window::set_mouse_cursor(CursorIcon::NSResize),
MouseCursor::ResizeEW => miniquad::window::set_mouse_cursor(CursorIcon::EWResize),
MouseCursor::ResizeNESW => miniquad::window::set_mouse_cursor(CursorIcon::NESWResize),
MouseCursor::ResizeNWSE => miniquad::window::set_mouse_cursor(CursorIcon::NWSEResize),
MouseCursor::Hand => miniquad::window::set_mouse_cursor(CursorIcon::Move),
MouseCursor::NotAllowed => miniquad::window::set_mouse_cursor(CursorIcon::NotAllowed),
}
} else {
miniquad::window::show_mouse(false);
}
self.imgui.render()
};
let projection = glam::Mat4::orthographic_rh_gl(0., width, height, 0., -1., 1.);
let clip_off = draw_data.display_pos;
let clip_scale = draw_data.framebuffer_scale;
if draw_data.draw_lists_count() > 0 {
for (n, draw_list) in draw_data.draw_lists().enumerate() {
let vertices = draw_list.vtx_buffer();
let indices = draw_list.idx_buffer();
if n >= self.draw_calls.len() {
let vertex_buffer = ctx.new_buffer(
BufferType::VertexBuffer,
BufferUsage::Stream,
BufferSource::empty::<DrawVert>(STARTING_VERTEX_BUFFER_SIZE),
);
let index_buffer = ctx.new_buffer(
BufferType::IndexBuffer,
BufferUsage::Stream,
BufferSource::empty::<u16>(STARTING_INDEX_BUFFER_SIZE),
);
let bindings = Bindings {
vertex_buffers: vec![vertex_buffer],
index_buffer,
images: vec![],
};
self.draw_calls.push(bindings);
}
let dc = &mut self.draw_calls[n];
let vertex_buffer_0_size = ctx.buffer_size(dc.vertex_buffers[0]);
let index_buffer_size = ctx.buffer_size(dc.index_buffer);
if size_of_val(vertices) > vertex_buffer_0_size {
println!("imgui: Vertex buffer too small, reallocating");
dc.vertex_buffers[0] = ctx.new_buffer(
BufferType::VertexBuffer,
BufferUsage::Stream,
BufferSource::empty::<DrawVert>(size_of_val(vertices)),
);
}
if size_of_val(indices) > index_buffer_size {
println!("imgui: Index buffer too small, reallocating");
dc.index_buffer = ctx.new_buffer(
BufferType::IndexBuffer,
BufferUsage::Stream,
BufferSource::empty::<u16>(size_of_val(indices)),
);
}
ctx.buffer_update(dc.vertex_buffers[0], BufferSource::slice(vertices));
ctx.buffer_update(dc.index_buffer, BufferSource::slice(indices));
dc.images = vec![self.font_texture];
let mut slice_start = 0;
for cmd in draw_list.commands() {
match cmd {
DrawCmd::Elements {
count,
cmd_params: DrawCmdParams { clip_rect, .. },
} => {
let clip_rect = [
(clip_rect[0] - clip_off[0]) * clip_scale[0],
(clip_rect[1] - clip_off[1]) * clip_scale[1],
(clip_rect[2] - clip_off[0]) * clip_scale[0],
(clip_rect[3] - clip_off[1]) * clip_scale[1],
];
ctx.apply_pipeline(&self.pipeline);
let h = clip_rect[3] - clip_rect[1];
ctx.apply_scissor_rect(
clip_rect[0] as i32,
height as i32 - (clip_rect[1] + h) as i32,
(clip_rect[2] - clip_rect[0]) as i32,
h as i32,
);
ctx.apply_bindings(&dc);
ctx.apply_uniforms(UniformsSource::table(&shader::Uniforms { projection }));
ctx.draw(slice_start, count as i32, 1);
slice_start += count as i32;
}
_ => {}
}
}
}
}
}
}
impl EventHandler for ImguiMacroquadRenderer {
fn update(&mut self) {
unimplemented!("MacroquadImgui::update should no be called")
}
fn draw(&mut self) {
unimplemented!("MacroquadImgui::draw should no be called")
}
fn resize_event(&mut self, width: f32, height: f32) {
let io = self.imgui.io_mut();
io.display_size = [width, height];
}
fn mouse_motion_event(&mut self, x: f32, y: f32) {
let io = self.imgui.io_mut();
io.add_mouse_pos_event([x, y]);
}
fn mouse_wheel_event(&mut self, x: f32, y: f32) {
let io = self.imgui.io_mut();
io.add_mouse_wheel_event([x, y]);
}
fn mouse_button_down_event(&mut self, button: MouseButton, _x: f32, _y: f32) {
let io = self.imgui.io_mut();
match button {
MouseButton::Left => {
io.mouse_down[0] = true;
}
MouseButton::Middle => {
io.mouse_down[2] = true;
}
MouseButton::Right => {
io.mouse_down[1] = true;
}
MouseButton::Unknown => {
}
}
}
fn mouse_button_up_event(&mut self, button: MouseButton, _x: f32, _y: f32) {
let io = self.imgui.io_mut();
match button {
MouseButton::Left => {
io.mouse_down[0] = false;
}
MouseButton::Middle => {
io.mouse_down[2] = false;
}
MouseButton::Right => {
io.mouse_down[1] = false;
}
MouseButton::Unknown => {
}
}
}
fn char_event(&mut self, character: char, _keymods: KeyMods, _repeat: bool) {
let io = self.imgui.io_mut();
io.add_input_character(character);
}
fn key_down_event(&mut self, keycode: miniquad::KeyCode, _keymods: KeyMods, _: bool) {
let io = self.imgui.io_mut();
let imgui_keycode = map_miniquad_keycode_to_imgui_keycode(keycode);
if let Ok(imgui_keycode) = imgui_keycode {
if imgui_keycode == imgui::Key::LeftShift || imgui_keycode == imgui::Key::RightShift {
io.add_key_event(imgui::Key::ModShift, true);
}
if imgui_keycode == imgui::Key::LeftCtrl || imgui_keycode == imgui::Key::RightCtrl {
io.add_key_event(imgui::Key::ModCtrl, true);
}
if imgui_keycode == imgui::Key::LeftAlt || imgui_keycode == imgui::Key::RightAlt {
io.add_key_event(imgui::Key::ModAlt, true);
}
if imgui_keycode == imgui::Key::LeftSuper || imgui_keycode == imgui::Key::RightSuper {
io.add_key_event(imgui::Key::ModSuper, true);
}
io.add_key_event(imgui_keycode, true);
}
}
fn key_up_event(&mut self, keycode: miniquad::KeyCode, _mods: KeyMods) {
let io = self.imgui.io_mut();
let imgui_keycode = map_miniquad_keycode_to_imgui_keycode(keycode);
if let Ok(imgui_keycode) = imgui_keycode {
let mut remove_mod_if_other_not_pressed = |left: imgui::Key, right: imgui::Key, mod_key: imgui::Key| {
if (
imgui_keycode == left && !io.keys_down[right as usize]) || (imgui_keycode == right && !io.keys_down[left as usize] ) {
io.add_key_event(mod_key, false);
}
};
remove_mod_if_other_not_pressed(imgui::Key::LeftShift, imgui::Key::RightShift, imgui::Key::ModShift);
remove_mod_if_other_not_pressed(imgui::Key::LeftCtrl, imgui::Key::RightCtrl, imgui::Key::ModCtrl);
remove_mod_if_other_not_pressed(imgui::Key::LeftAlt, imgui::Key::RightAlt, imgui::Key::ModAlt);
remove_mod_if_other_not_pressed(imgui::Key::LeftSuper, imgui::Key::RightSuper, imgui::Key::ModSuper);
io.add_key_event(imgui_keycode, false);
}
}
}
pub enum KeyMapError {
NoMapping { miniquad_keycode: miniquad::KeyCode },
}
pub const fn map_miniquad_keycode_to_imgui_keycode(miniquad_keycode: miniquad::KeyCode) -> Result<imgui::Key, KeyMapError> {
use miniquad::KeyCode;
use imgui::Key;
match miniquad_keycode {
KeyCode::Space => Ok(Key::Space),
KeyCode::Apostrophe => Ok(Key::Apostrophe),
KeyCode::Comma => Ok(Key::Comma),
KeyCode::Minus => Ok(Key::Minus),
KeyCode::Period => Ok(Key::Period),
KeyCode::Slash => Ok(Key::Slash),
KeyCode::Key0 => Ok(Key::Alpha0),
KeyCode::Key1 => Ok(Key::Alpha1),
KeyCode::Key2 => Ok(Key::Alpha2),
KeyCode::Key3 => Ok(Key::Alpha3),
KeyCode::Key4 => Ok(Key::Alpha4),
KeyCode::Key5 => Ok(Key::Alpha5),
KeyCode::Key6 => Ok(Key::Alpha6),
KeyCode::Key7 => Ok(Key::Alpha7),
KeyCode::Key8 => Ok(Key::Alpha8),
KeyCode::Key9 => Ok(Key::Alpha9),
KeyCode::Semicolon => Ok(Key::Semicolon),
KeyCode::Equal => Ok(Key::Equal),
KeyCode::A => Ok(Key::A),
KeyCode::B => Ok(Key::B),
KeyCode::C => Ok(Key::C),
KeyCode::D => Ok(Key::D),
KeyCode::E => Ok(Key::E),
KeyCode::F => Ok(Key::F),
KeyCode::G => Ok(Key::G),
KeyCode::H => Ok(Key::H),
KeyCode::I => Ok(Key::I),
KeyCode::J => Ok(Key::J),
KeyCode::K => Ok(Key::K),
KeyCode::L => Ok(Key::L),
KeyCode::M => Ok(Key::M),
KeyCode::N => Ok(Key::N),
KeyCode::O => Ok(Key::O),
KeyCode::P => Ok(Key::P),
KeyCode::Q => Ok(Key::Q),
KeyCode::R => Ok(Key::R),
KeyCode::S => Ok(Key::S),
KeyCode::T => Ok(Key::T),
KeyCode::U => Ok(Key::U),
KeyCode::V => Ok(Key::V),
KeyCode::W => Ok(Key::W),
KeyCode::X => Ok(Key::X),
KeyCode::Y => Ok(Key::Y),
KeyCode::Z => Ok(Key::Z),
KeyCode::LeftBracket => Ok(Key::LeftBracket),
KeyCode::Backslash => Ok(Key::Backslash),
KeyCode::RightBracket => Ok(Key::RightBracket),
KeyCode::GraveAccent => Ok(Key::GraveAccent),
KeyCode::Escape => Ok(Key::Escape),
KeyCode::Enter => Ok(Key::Enter),
KeyCode::Tab => Ok(Key::Tab),
KeyCode::Backspace => Ok(Key::Backspace),
KeyCode::Insert => Ok(Key::Insert),
KeyCode::Delete => Ok(Key::Delete),
KeyCode::Right => Ok(Key::RightArrow),
KeyCode::Left => Ok(Key::LeftArrow),
KeyCode::Down => Ok(Key::DownArrow),
KeyCode::Up => Ok(Key::UpArrow),
KeyCode::PageUp => Ok(Key::PageUp),
KeyCode::PageDown => Ok(Key::PageDown),
KeyCode::Home => Ok(Key::Home),
KeyCode::End => Ok(Key::End),
KeyCode::CapsLock => Ok(Key::CapsLock),
KeyCode::ScrollLock => Ok(Key::ScrollLock),
KeyCode::NumLock => Ok(Key::NumLock),
KeyCode::PrintScreen => Ok(Key::PrintScreen),
KeyCode::Pause => Ok(Key::Pause),
KeyCode::F1 => Ok(Key::F1),
KeyCode::F2 => Ok(Key::F2),
KeyCode::F3 => Ok(Key::F3),
KeyCode::F4 => Ok(Key::F4),
KeyCode::F5 => Ok(Key::F5),
KeyCode::F6 => Ok(Key::F6),
KeyCode::F7 => Ok(Key::F7),
KeyCode::F8 => Ok(Key::F8),
KeyCode::F9 => Ok(Key::F9),
KeyCode::F10 => Ok(Key::F10),
KeyCode::F11 => Ok(Key::F11),
KeyCode::F12 => Ok(Key::F12),
KeyCode::Kp0 => Ok(Key::Keypad0),
KeyCode::Kp1 => Ok(Key::Keypad1),
KeyCode::Kp2 => Ok(Key::Keypad2),
KeyCode::Kp3 => Ok(Key::Keypad3),
KeyCode::Kp4 => Ok(Key::Keypad4),
KeyCode::Kp5 => Ok(Key::Keypad5),
KeyCode::Kp6 => Ok(Key::Keypad6),
KeyCode::Kp7 => Ok(Key::Keypad7),
KeyCode::Kp8 => Ok(Key::Keypad8),
KeyCode::Kp9 => Ok(Key::Keypad9),
KeyCode::KpDecimal => Ok(Key::KeypadDecimal),
KeyCode::KpDivide => Ok(Key::KeypadDivide),
KeyCode::KpMultiply => Ok(Key::KeypadMultiply),
KeyCode::KpSubtract => Ok(Key::KeypadSubtract),
KeyCode::KpAdd => Ok(Key::KeypadAdd),
KeyCode::KpEnter => Ok(Key::KeypadEnter),
KeyCode::KpEqual => Ok(Key::KeypadEqual),
KeyCode::LeftShift => Ok(Key::LeftShift),
KeyCode::LeftControl => Ok(Key::LeftCtrl),
KeyCode::LeftAlt => Ok(Key::LeftAlt),
KeyCode::LeftSuper => Ok(Key::LeftSuper),
KeyCode::RightShift => Ok(Key::RightShift),
KeyCode::RightControl => Ok(Key::RightCtrl),
KeyCode::RightAlt => Ok(Key::RightAlt),
KeyCode::RightSuper => Ok(Key::RightSuper),
KeyCode::Menu => Ok(Key::Menu),
_ => Err(KeyMapError::NoMapping { miniquad_keycode }),
}
}
mod shader {
use macroquad::miniquad::{ShaderMeta, UniformBlockLayout, UniformDesc, UniformType};
pub const VERTEX: &str = r#"#version 100
attribute vec2 position;
attribute vec2 texcoord;
attribute vec4 color0;
varying lowp vec2 uv;
varying lowp vec4 color;
uniform mat4 Projection;
void main() {
gl_Position = Projection * vec4(position, 0, 1);
gl_Position.z = 0.;
color = color0 / 255.0;
uv = texcoord;
}"#;
pub const FRAGMENT: &str = r#"#version 100
varying lowp vec4 color;
varying lowp vec2 uv;
uniform sampler2D Texture;
void main() {
gl_FragColor = color * texture2D(Texture, uv);
}"#;
pub fn meta() -> ShaderMeta {
ShaderMeta {
images: vec!["Texture".to_string()],
uniforms: UniformBlockLayout {
uniforms: vec![UniformDesc::new("Projection", UniformType::Mat4)],
},
}
}
#[repr(C)]
#[derive(Debug)]
pub struct Uniforms {
pub projection: glam::Mat4,
}
}