├── .gitignore ├── .travis.yml ├── Cargo.toml ├── README.md └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /target 3 | **/*.rs.bk 4 | Cargo.lock 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | 7 | cache: 8 | directories: 9 | - SDL2-2.0.8 10 | 11 | before_install: 12 | - wget https://www.libsdl.org/release/SDL2-2.0.8.tar.gz -O SDL2-2.0.8.tar.gz 13 | - tar -xzvf SDL2-2.0.8.tar.gz 14 | install: 15 | - (cd SDL2-2.0.8 && ./configure && make && sudo make install) 16 | 17 | script: 18 | - cargo build --all-targets --verbose 19 | - cargo test --verbose 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "imgui-opengl-renderer" 3 | description = "An OpenGL renderer for imgui-rs" 4 | version = "0.12.1" 5 | authors = ["Michael Fairley "] 6 | build = "build.rs" 7 | homepage = "https://github.com/michaelfairley/rust-imgui-opengl-renderer" 8 | repository = "https://github.com/michaelfairley/rust-imgui-opengl-renderer" 9 | readme = "README.md" 10 | categories = ["gui", "rendering::graphics-api"] 11 | license = "MIT/Apache-2.0" 12 | 13 | [dependencies] 14 | imgui = ">=0.9.0, <0.12.0" 15 | 16 | [build-dependencies] 17 | gl_generator = "0.14.0" 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # imgui-opengl-renderer 2 | 3 | [![Build Status](https://travis-ci.org/michaelfairley/rust-imgui-opengl-renderer.svg?branch=master)](https://travis-ci.org/michaelfairley/rust-imgui-opengl-renderer) 4 | [![Documentation](https://docs.rs/imgui-opengl-renderer/badge.svg)](https://docs.rs/imgui-opengl-renderer) 5 | [![Version](https://img.shields.io/crates/v/imgui-opengl-renderer.svg)](https://crates.io/crates/imgui-opengl-renderer) 6 | 7 | OpenGL (3+) rendering for [imgui-rs](https://github.com/Gekkio/imgui-rs) 8 | 9 | ## Integration guide 10 | 11 | 1. Construct it (passing in an OpenGL function loader from [SDL2](https://github.com/Rust-SDL2/rust-sdl2) or [glutin](https://github.com/tomaka/glutin) or somesuch). 12 | ```rust 13 | let renderer = imgui_opengl_renderer::Renderer::new(&mut imgui, |s| video.gl_get_proc_address(s) as _); 14 | ``` 15 | 2. Call `render` to draw the UI. 16 | ```rust 17 | renderer.render(ui); 18 | ``` 19 | 20 | Take a look at the [example app](https://github.com/michaelfairley/rust-imgui-sdl2/blob/master/examples/demo.rs) to see it all in context. 21 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate imgui; 2 | 3 | use imgui::Context; 4 | use std::mem; 5 | 6 | mod gl { 7 | #![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal, clippy::too_many_arguments, clippy::unused_unit))] 8 | 9 | include!(concat!(env!("OUT_DIR"), "/bindings.rs")); 10 | } 11 | 12 | use gl::types::*; 13 | 14 | pub struct Renderer { 15 | gl: gl::Gl, 16 | program: GLuint, 17 | locs: Locs, 18 | vbo: GLuint, 19 | ebo: GLuint, 20 | font_texture: GLuint, 21 | } 22 | 23 | struct Locs { 24 | texture: GLint, 25 | proj_mtx: GLint, 26 | position: GLuint, 27 | uv: GLuint, 28 | color: GLuint, 29 | } 30 | 31 | impl Renderer { 32 | pub fn new( 33 | imgui: &mut Context, 34 | load_fn: F, 35 | ) -> Self 36 | where 37 | F: FnMut(&'static str) -> *const ::std::os::raw::c_void 38 | { 39 | let gl = gl::Gl::load_with(load_fn); 40 | 41 | unsafe { 42 | #[cfg(target_os = "macos")] 43 | let glsl_version = b"#version 150\n\0"; 44 | #[cfg(not(target_os = "macos"))] 45 | let glsl_version = b"#version 130\n\0"; 46 | 47 | let vert_source = b" 48 | uniform mat4 ProjMtx; 49 | in vec2 Position; 50 | in vec2 UV; 51 | in vec4 Color; 52 | out vec2 Frag_UV; 53 | out vec4 Frag_Color; 54 | void main() 55 | { 56 | Frag_UV = UV; 57 | Frag_Color = Color; 58 | gl_Position = ProjMtx * vec4(Position.xy,0,1); 59 | } 60 | \0"; 61 | 62 | let frag_source = b" 63 | uniform sampler2D Texture; 64 | in vec2 Frag_UV; 65 | in vec4 Frag_Color; 66 | out vec4 Out_Color; 67 | void main() 68 | { 69 | Out_Color = Frag_Color * texture(Texture, Frag_UV.st); 70 | } 71 | \0"; 72 | 73 | let vert_sources = [glsl_version.as_ptr() as *const GLchar, 74 | vert_source.as_ptr() as *const GLchar]; 75 | let vert_sources_len = [glsl_version.len() as GLint - 1, 76 | vert_source.len() as GLint - 1]; 77 | let frag_sources = [glsl_version.as_ptr() as *const GLchar, 78 | frag_source.as_ptr() as *const GLchar]; 79 | let frag_sources_len = [glsl_version.len() as GLint - 1, 80 | frag_source.len() as GLint - 1]; 81 | 82 | let program = gl.CreateProgram(); 83 | let vert_shader = gl.CreateShader(gl::VERTEX_SHADER); 84 | let frag_shader = gl.CreateShader(gl::FRAGMENT_SHADER); 85 | gl.ShaderSource(vert_shader, 2, vert_sources.as_ptr(), vert_sources_len.as_ptr()); 86 | gl.ShaderSource(frag_shader, 2, frag_sources.as_ptr(), frag_sources_len.as_ptr()); 87 | gl.CompileShader(vert_shader); 88 | gl.CompileShader(frag_shader); 89 | gl.AttachShader(program, vert_shader); 90 | gl.AttachShader(program, frag_shader); 91 | gl.LinkProgram(program); 92 | gl.DeleteShader(vert_shader); 93 | gl.DeleteShader(frag_shader); 94 | 95 | let locs = Locs{ 96 | texture: gl.GetUniformLocation(program, b"Texture\0".as_ptr() as _), 97 | proj_mtx: gl.GetUniformLocation(program, b"ProjMtx\0".as_ptr() as _), 98 | position: gl.GetAttribLocation(program, b"Position\0".as_ptr() as _) as _, 99 | uv: gl.GetAttribLocation(program, b"UV\0".as_ptr() as _) as _, 100 | color: gl.GetAttribLocation(program, b"Color\0".as_ptr() as _) as _, 101 | }; 102 | 103 | let vbo = return_param(|x| gl.GenBuffers(1, x) ); 104 | let ebo = return_param(|x| gl.GenBuffers(1, x) ); 105 | 106 | let mut current_texture = 0; 107 | gl.GetIntegerv(gl::TEXTURE_BINDING_2D, &mut current_texture); 108 | 109 | 110 | let font_texture = return_param(|x| gl.GenTextures(1, x)); 111 | gl.BindTexture(gl::TEXTURE_2D, font_texture); 112 | gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _); 113 | gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _); 114 | gl.PixelStorei(gl::UNPACK_ROW_LENGTH, 0); 115 | 116 | { 117 | let mut atlas = imgui.fonts(); 118 | 119 | let texture = atlas.build_rgba32_texture(); 120 | gl.TexImage2D(gl::TEXTURE_2D, 0, gl::RGBA as _, texture.width as _, texture.height as _, 0, gl::RGBA, gl::UNSIGNED_BYTE, texture.data.as_ptr() as _); 121 | 122 | atlas.tex_id = (font_texture as usize).into(); 123 | } 124 | 125 | gl.BindTexture(gl::TEXTURE_2D, current_texture as _); 126 | 127 | Self{ 128 | gl, 129 | program, 130 | locs, 131 | vbo, 132 | ebo, 133 | font_texture, 134 | } 135 | } 136 | } 137 | 138 | pub fn render( 139 | &self, 140 | ctx: &mut Context, 141 | ) { 142 | use imgui::{DrawVert,DrawIdx,DrawCmd,DrawCmdParams}; 143 | 144 | let gl = &self.gl; 145 | 146 | unsafe { 147 | let last_active_texture = return_param(|x| gl.GetIntegerv(gl::ACTIVE_TEXTURE, x)); 148 | gl.ActiveTexture(gl::TEXTURE0); 149 | let last_program = return_param(|x| gl.GetIntegerv(gl::CURRENT_PROGRAM, x)); 150 | let last_texture = return_param(|x| gl.GetIntegerv(gl::TEXTURE_BINDING_2D, x)); 151 | let last_sampler = if gl.BindSampler.is_loaded() { return_param(|x| gl.GetIntegerv(gl::SAMPLER_BINDING, x)) } else { 0 }; 152 | let last_array_buffer = return_param(|x| gl.GetIntegerv(gl::ARRAY_BUFFER_BINDING, x)); 153 | let last_element_array_buffer = return_param(|x| gl.GetIntegerv(gl::ELEMENT_ARRAY_BUFFER_BINDING, x)); 154 | let last_vertex_array = return_param(|x| gl.GetIntegerv(gl::VERTEX_ARRAY_BINDING, x)); 155 | let last_polygon_mode = return_param(|x: &mut [GLint; 2]| gl.GetIntegerv(gl::POLYGON_MODE, x.as_mut_ptr())); 156 | let last_viewport = return_param(|x: &mut [GLint; 4]| gl.GetIntegerv(gl::VIEWPORT, x.as_mut_ptr())); 157 | let last_scissor_box = return_param(|x: &mut [GLint; 4]| gl.GetIntegerv(gl::SCISSOR_BOX, x.as_mut_ptr())); 158 | let last_blend_src_rgb = return_param(|x| gl.GetIntegerv(gl::BLEND_SRC_RGB, x)); 159 | let last_blend_dst_rgb = return_param(|x| gl.GetIntegerv(gl::BLEND_DST_RGB, x)); 160 | let last_blend_src_alpha = return_param(|x| gl.GetIntegerv(gl::BLEND_SRC_ALPHA, x)); 161 | let last_blend_dst_alpha = return_param(|x| gl.GetIntegerv(gl::BLEND_DST_ALPHA, x)); 162 | let last_blend_equation_rgb = return_param(|x| gl.GetIntegerv(gl::BLEND_EQUATION_RGB, x)); 163 | let last_blend_equation_alpha = return_param(|x| gl.GetIntegerv(gl::BLEND_EQUATION_ALPHA, x)); 164 | let last_enable_blend = gl.IsEnabled(gl::BLEND) == gl::TRUE; 165 | let last_enable_cull_face = gl.IsEnabled(gl::CULL_FACE) == gl::TRUE; 166 | let last_enable_depth_test = gl.IsEnabled(gl::DEPTH_TEST) == gl::TRUE; 167 | let last_enable_scissor_test = gl.IsEnabled(gl::SCISSOR_TEST) == gl::TRUE; 168 | 169 | 170 | gl.Enable(gl::BLEND); 171 | gl.BlendEquation(gl::FUNC_ADD); 172 | gl.BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); 173 | gl.Disable(gl::CULL_FACE); 174 | gl.Disable(gl::DEPTH_TEST); 175 | gl.Enable(gl::SCISSOR_TEST); 176 | gl.PolygonMode(gl::FRONT_AND_BACK, gl::FILL); 177 | 178 | 179 | let [width, height] = ctx.io().display_size; 180 | let [scale_w, scale_h] = ctx.io().display_framebuffer_scale; 181 | 182 | let fb_width = width * scale_w; 183 | let fb_height = height * scale_h; 184 | 185 | gl.Viewport(0, 0, fb_width as _, fb_height as _); 186 | let matrix = [ 187 | [ 2.0 / width as f32, 0.0, 0.0, 0.0], 188 | [ 0.0, 2.0 / -(height as f32), 0.0, 0.0], 189 | [ 0.0, 0.0, -1.0, 0.0], 190 | [-1.0, 1.0, 0.0, 1.0], 191 | ]; 192 | gl.UseProgram(self.program); 193 | gl.Uniform1i(self.locs.texture, 0); 194 | gl.UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _); 195 | if gl.BindSampler.is_loaded() { gl.BindSampler(0, 0); } 196 | 197 | 198 | let vao = return_param(|x| gl.GenVertexArrays(1, x)); 199 | gl.BindVertexArray(vao); 200 | gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo); 201 | gl.EnableVertexAttribArray(self.locs.position); 202 | gl.EnableVertexAttribArray(self.locs.uv); 203 | gl.EnableVertexAttribArray(self.locs.color); 204 | gl.VertexAttribPointer(self.locs.position, 2, gl::FLOAT, gl::FALSE, mem::size_of::() as _, field_offset::(|v| &v.pos) as _); 205 | gl.VertexAttribPointer(self.locs.uv, 2, gl::FLOAT, gl::FALSE, mem::size_of::() as _, field_offset::(|v| &v.uv) as _); 206 | gl.VertexAttribPointer(self.locs.color, 4, gl::UNSIGNED_BYTE, gl::TRUE, mem::size_of::() as _, field_offset::(|v| &v.col) as _); 207 | 208 | 209 | let draw_data = ctx.render(); 210 | 211 | for draw_list in draw_data.draw_lists() { 212 | let vtx_buffer = draw_list.vtx_buffer(); 213 | let idx_buffer = draw_list.idx_buffer(); 214 | 215 | gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo); 216 | gl.BufferData(gl::ARRAY_BUFFER, (vtx_buffer.len() * mem::size_of::()) as _, vtx_buffer.as_ptr() as _, gl::STREAM_DRAW); 217 | 218 | gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo); 219 | gl.BufferData(gl::ELEMENT_ARRAY_BUFFER, (idx_buffer.len() * mem::size_of::()) as _, idx_buffer.as_ptr() as _, gl::STREAM_DRAW); 220 | 221 | for cmd in draw_list.commands() { 222 | match cmd { 223 | DrawCmd::Elements { 224 | count, 225 | cmd_params: DrawCmdParams { 226 | clip_rect: [x, y, z, w], 227 | texture_id, 228 | idx_offset, 229 | .. 230 | }, 231 | } => { 232 | gl.BindTexture(gl::TEXTURE_2D, texture_id.id() as _); 233 | 234 | gl.Scissor((x * scale_w) as GLint, 235 | (fb_height - w * scale_h) as GLint, 236 | ((z - x) * scale_w) as GLint, 237 | ((w - y) * scale_h) as GLint); 238 | 239 | let idx_size = if mem::size_of::() == 2 { gl::UNSIGNED_SHORT } else { gl::UNSIGNED_INT }; 240 | 241 | gl.DrawElements(gl::TRIANGLES, count as _, idx_size, (idx_offset * mem::size_of::()) as _); 242 | }, 243 | DrawCmd::ResetRenderState => { 244 | unimplemented!("Haven't implemented DrawCmd::ResetRenderState yet"); 245 | }, 246 | DrawCmd::RawCallback { .. } => { 247 | unimplemented!("Haven't implemented user callbacks yet"); 248 | } 249 | } 250 | } 251 | } 252 | 253 | gl.DeleteVertexArrays(1, &vao); 254 | 255 | gl.UseProgram(last_program as _); 256 | gl.BindTexture(gl::TEXTURE_2D, last_texture as _); 257 | if gl.BindSampler.is_loaded() { gl.BindSampler(0, last_sampler as _); } 258 | gl.ActiveTexture(last_active_texture as _); 259 | gl.BindVertexArray(last_vertex_array as _); 260 | gl.BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _); 261 | gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_array_buffer as _); 262 | gl.BlendEquationSeparate(last_blend_equation_rgb as _, last_blend_equation_alpha as _); 263 | gl.BlendFuncSeparate(last_blend_src_rgb as _, last_blend_dst_rgb as _, last_blend_src_alpha as _, last_blend_dst_alpha as _); 264 | if last_enable_blend { gl.Enable(gl::BLEND) } else { gl.Disable(gl::BLEND) }; 265 | if last_enable_cull_face { gl.Enable(gl::CULL_FACE) } else { gl.Disable(gl::CULL_FACE) }; 266 | if last_enable_depth_test { gl.Enable(gl::DEPTH_TEST) } else { gl.Disable(gl::DEPTH_TEST) }; 267 | if last_enable_scissor_test { gl.Enable(gl::SCISSOR_TEST) } else { gl.Disable(gl::SCISSOR_TEST) }; 268 | gl.PolygonMode(gl::FRONT_AND_BACK, last_polygon_mode[0] as _); 269 | gl.Viewport(last_viewport[0] as _, last_viewport[1] as _, last_viewport[2] as _, last_viewport[3] as _); 270 | gl.Scissor(last_scissor_box[0] as _, last_scissor_box[1] as _, last_scissor_box[2] as _, last_scissor_box[3] as _); 271 | 272 | } 273 | } 274 | } 275 | 276 | impl Drop for Renderer { 277 | fn drop(&mut self) { 278 | let gl = &self.gl; 279 | 280 | unsafe { 281 | gl.DeleteBuffers(1, &self.vbo); 282 | gl.DeleteBuffers(1, &self.ebo); 283 | 284 | gl.DeleteProgram(self.program); 285 | 286 | gl.DeleteTextures(1, &self.font_texture); 287 | } 288 | } 289 | } 290 | 291 | fn field_offset FnOnce(&'a T) -> &'a U>(f: F) -> usize { 292 | unsafe { 293 | let instance = mem::zeroed::(); 294 | 295 | let offset = { 296 | let field: &U = f(&instance); 297 | field as *const U as usize - &instance as *const T as usize 298 | }; 299 | 300 | mem::forget(instance); 301 | 302 | offset 303 | } 304 | } 305 | 306 | fn return_param(f: F) -> T where F: FnOnce(&mut T) { 307 | let mut val = unsafe{ mem::zeroed() }; 308 | f(&mut val); 309 | val 310 | } 311 | --------------------------------------------------------------------------------