diff --git a/imgui-gfx-renderer/src/lib.rs b/imgui-gfx-renderer/src/lib.rs index 0d7e74e..97437cc 100644 --- a/imgui-gfx-renderer/src/lib.rs +++ b/imgui-gfx-renderer/src/lib.rs @@ -6,8 +6,9 @@ use gfx::handle::{Buffer, RenderTargetView}; use gfx::memory::Bind; use gfx::texture::{FilterMethod, SamplerInfo, WrapMode}; use gfx::traits::FactoryExt; -use gfx::{Bundle, CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; -use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui}; +use gfx::{CommandBuffer, Encoder, Factory, IntoIndexBuffer, Rect, Resources, Slice}; +use gfx::pso::{PipelineData, PipelineState}; +use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures, ImTexture}; pub type RendererResult = Result; @@ -17,6 +18,7 @@ pub enum RendererError { Buffer(gfx::buffer::CreationError), Pipeline(gfx::PipelineStateError), Combined(gfx::CombinedError), + BadTexture(ImTexture), } impl From> for RendererError { @@ -43,7 +45,42 @@ impl From for RendererError { } } -gfx_defines!{ +// Based on gfx_defines! / gfx_pipeline!, to allow for not having to clone Arcs +// every draw call when selecting which texture is going to be shown. +macro_rules! extended_defines { + (pipeline $module:ident { $( $field:ident: $ty:ty = $value:expr, )* }) => { + #[allow(missing_docs)] + mod $module { + #[allow(unused_imports)] + use super::*; + #[allow(unused_imports)] + use super::gfx; + gfx_pipeline_inner!{ $( + $field: $ty, + )*} + + pub fn new() -> Init<'static> { + Init { + $( $field: $value, )* + } + } + + pub struct BorrowedData<'a, R: Resources> { + $(pub $field: &'a <$ty as DataBind>::Data,)* + } + + impl<'a, R: Resources> gfx::pso::PipelineData for BorrowedData<'a, R> { + type Meta = pipe::Meta; + + fn bake_to(&self, out: &mut RawDataSet, meta: &Self::Meta, man: &mut gfx::handle::Manager, access: &mut AccessInfo) { + $(meta.$field.bind_to(out, &self.$field, man, access);)* + } + } + } + } +} + +extended_defines! { pipeline pipe { vertex_buffer: gfx::VertexBuffer = (), matrix: gfx::Global<[[f32; 4]; 4]> = "matrix", @@ -94,9 +131,12 @@ impl Shaders { } } +pub type Texture = (gfx::handle::ShaderResourceView, gfx::handle::Sampler); + pub struct Renderer { bundle: Bundle>, index_buffer: Buffer, + textures: Textures>, } impl Renderer { @@ -131,26 +171,12 @@ impl Renderer { &[handle.pixels], ) })?; - // TODO: set texture id in imgui let sampler = factory.create_sampler(SamplerInfo::new(FilterMethod::Trilinear, WrapMode::Clamp)); - let data = pipe::Data { - vertex_buffer: vertex_buffer, - matrix: [ - [0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, 0.0, 0.0], - [0.0, 0.0, -1.0, 0.0], - [-1.0, 1.0, 0.0, 1.0], - ], - tex: (texture, sampler), - out: out, - scissor: Rect { - x: 0, - y: 0, - w: 0, - h: 0, - }, - }; + let pair = (texture, sampler); + let mut textures = Textures::new(); + imgui.set_font_texture_id(textures.insert(pair)); + let slice = Slice { start: 0, end: 0, @@ -159,13 +185,25 @@ impl Renderer { buffer: index_buffer.clone().into_index_buffer(factory), }; Ok(Renderer { - bundle: Bundle::new(slice, pso, data), + bundle: Bundle { + slice: slice, + pso: pso, + vertex_buffer: vertex_buffer, + out: out, + }, index_buffer: index_buffer, + textures: textures, }) } + pub fn update_render_target(&mut self, out: RenderTargetView) { - self.bundle.data.out = out; + self.bundle.out = out; } + + pub fn textures(&mut self) -> &mut Textures> { + &mut self.textures + } + pub fn render<'a, F: Factory, C: CommandBuffer>( &mut self, ui: Ui<'a>, @@ -185,7 +223,7 @@ impl Renderer { (height * hidpi_factor) as f32, ); - self.bundle.data.matrix = [ + let matrix = [ [(2.0 / width) as f32, 0.0, 0.0, 0.0], [0.0, (2.0 / -height) as f32, 0.0, 0.0], [0.0, 0.0, -1.0, 0.0], @@ -195,7 +233,7 @@ impl Renderer { ui.render(|ui, mut draw_data| { draw_data.scale_clip_rects(ui.imgui().display_framebuffer_scale()); for draw_list in &draw_data { - self.render_draw_list(factory, encoder, &draw_list, fb_size)?; + self.render_draw_list(factory, encoder, &draw_list, fb_size, &matrix)?; } Ok(()) }) @@ -206,6 +244,7 @@ impl Renderer { encoder: &mut Encoder, draw_list: &DrawList<'a>, fb_size: (f32, f32), + matrix: &[[f32; 4]; 4], ) -> RendererResult<()> { let (fb_width, fb_height) = fb_size; @@ -214,10 +253,12 @@ impl Renderer { self.bundle.slice.start = 0; for cmd in draw_list.cmd_buffer { - // TODO: check cmd.texture_id + let texture_id = cmd.texture_id.into(); + let tex = self.textures.get(texture_id) + .ok_or_else(|| RendererError::BadTexture(texture_id))?; self.bundle.slice.end = self.bundle.slice.start + cmd.elem_count; - self.bundle.data.scissor = Rect { + let scissor = Rect { x: cmd.clip_rect.x.max(0.0).min(fb_width).round() as u16, y: cmd.clip_rect.y.max(0.0).min(fb_height).round() as u16, w: (cmd.clip_rect.z - cmd.clip_rect.x) @@ -229,7 +270,14 @@ impl Renderer { .min(fb_height) .round() as u16, }; - self.bundle.encode(encoder); + let data = pipe::BorrowedData { + vertex_buffer: &self.bundle.vertex_buffer, + matrix: matrix, + tex: tex, + out: &self.bundle.out, + scissor: &scissor, + }; + encoder.draw(&self.bundle.slice, &self.bundle.pso, &data); self.bundle.slice.start = self.bundle.slice.end; } Ok(()) @@ -240,15 +288,15 @@ impl Renderer { encoder: &mut Encoder, vtx_buffer: &[ImDrawVert], ) -> RendererResult<()> { - if self.bundle.data.vertex_buffer.len() < vtx_buffer.len() { - self.bundle.data.vertex_buffer = factory.create_buffer::( + if self.bundle.vertex_buffer.len() < vtx_buffer.len() { + self.bundle.vertex_buffer = factory.create_buffer::( vtx_buffer.len(), gfx::buffer::Role::Vertex, gfx::memory::Usage::Dynamic, Bind::empty(), )?; } - Ok(encoder.update_buffer(&self.bundle.data.vertex_buffer, vtx_buffer, 0)?) + Ok(encoder.update_buffer(&self.bundle.vertex_buffer, vtx_buffer, 0)?) } fn upload_index_buffer, C: CommandBuffer>( &mut self, @@ -268,3 +316,10 @@ impl Renderer { Ok(encoder.update_buffer(&self.index_buffer, idx_buffer, 0)?) } } + +struct Bundle> { + slice: Slice, + pso: PipelineState, + vertex_buffer: Buffer, + out: RenderTargetView, +} diff --git a/imgui-glium-renderer/src/lib.rs b/imgui-glium-renderer/src/lib.rs index cd560c6..9091103 100644 --- a/imgui-glium-renderer/src/lib.rs +++ b/imgui-glium-renderer/src/lib.rs @@ -7,8 +7,8 @@ use glium::index::{self, PrimitiveType}; use glium::program; use glium::texture; use glium::vertex; -use glium::{DrawError, GlObject, IndexBuffer, Program, Surface, Texture2d, VertexBuffer}; -use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui}; +use glium::{DrawError, IndexBuffer, Program, Surface, Texture2d, VertexBuffer}; +use imgui::{DrawList, FrameSize, ImDrawIdx, ImDrawVert, ImGui, Ui, Textures, ImTexture}; use std::borrow::Cow; use std::fmt; use std::rc::Rc; @@ -22,6 +22,7 @@ pub enum RendererError { Program(program::ProgramChooserCreationError), Texture(texture::TextureCreationError), Draw(DrawError), + BadTexture(ImTexture), } impl fmt::Display for RendererError { @@ -33,6 +34,7 @@ impl fmt::Display for RendererError { Program(ref e) => write!(f, "Program creation failed: {}", e), Texture(_) => write!(f, "Texture creation failed"), Draw(ref e) => write!(f, "Drawing failed: {}", e), + BadTexture(ref t) => write!(f, "Bad texture ID: {}", t.id()), } } } @@ -81,6 +83,10 @@ impl Renderer { }) } + pub fn textures(&mut self) -> &mut Textures { + &mut self.device_objects.textures + } + pub fn render<'a, S: Surface>(&mut self, surface: &mut S, ui: Ui<'a>) -> RendererResult<()> { let _ = self.ctx.insert_debug_marker("imgui-rs: starting rendering"); let FrameSize { @@ -128,12 +134,11 @@ impl Renderer { self.device_objects .upload_index_buffer(&self.ctx, draw_list.idx_buffer)?; - let font_texture_id = self.device_objects.texture.get_id() as usize; - let mut idx_start = 0; for cmd in draw_list.cmd_buffer { - // We don't support custom textures...yet! - assert!(cmd.texture_id as usize == font_texture_id); + let texture_id = cmd.texture_id.into(); + let texture = self.device_objects.textures.get(texture_id) + .ok_or_else(|| RendererError::BadTexture(texture_id))?; let idx_end = idx_start + cmd.elem_count as usize; @@ -147,7 +152,7 @@ impl Renderer { &self.device_objects.program, &uniform! { matrix: matrix, - tex: self.device_objects.texture.sampled() + tex: texture.sampled() }, &DrawParameters { blend: Blend::alpha_blending(), @@ -178,7 +183,7 @@ pub struct DeviceObjects { vertex_buffer: VertexBuffer, index_buffer: IndexBuffer, program: Program, - texture: Texture2d, + textures: Textures, } fn compile_default_program( @@ -231,13 +236,14 @@ impl DeviceObjects { }; Texture2d::new(ctx, data) })?; - im_gui.set_texture_id(texture.get_id() as usize); + let mut textures = Textures::new(); + im_gui.set_font_texture_id(textures.insert(texture)); Ok(DeviceObjects { vertex_buffer: vertex_buffer, index_buffer: index_buffer, program: program, - texture: texture, + textures: textures, }) } pub fn upload_vertex_buffer( diff --git a/src/image.rs b/src/image.rs new file mode 100644 index 0000000..18bb692 --- /dev/null +++ b/src/image.rs @@ -0,0 +1,144 @@ +use super::{ImVec2, ImVec4, Ui}; +use std::marker::PhantomData; +use std::collections::HashMap; +use std::os::raw::c_void; +use sys; + +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct ImTexture(usize); + +impl ImTexture { + pub fn id(&self) -> usize { + self.0 + } +} + +impl From for ImTexture { + fn from(id: usize) -> Self { + ImTexture(id) + } +} + +impl From<*mut c_void> for ImTexture { + fn from(ptr: *mut c_void) -> Self { + ImTexture(ptr as usize) + } +} + +/// Represent an image about to be drawn. +/// See [`Ui::image`]. +/// +/// Create your image using the builder pattern then [`Image::build`] it. +#[must_use] +pub struct Image<'ui> { + texture_id: ImTexture, + size: ImVec2, + uv0: ImVec2, + uv1: ImVec2, + tint_col: ImVec4, + border_col: ImVec4, + _phantom: PhantomData<&'ui Ui<'ui>>, +} + +impl<'ui> Image<'ui> { + pub fn new(_: &Ui<'ui>, texture_id: ImTexture, size: S) -> Self + where + S: Into, + { + const DEFAULT_UV0: ImVec2 = ImVec2 { x: 0.0, y: 0.0 }; + const DEFAULT_UV1: ImVec2 = ImVec2 { x: 1.0, y: 1.0 }; + const DEFAULT_TINT_COL: ImVec4 = ImVec4 { + x: 1.0, + y: 1.0, + z: 1.0, + w: 1.0, + }; + const DEFAULT_BORDER_COL: ImVec4 = ImVec4 { + x: 0.0, + y: 0.0, + z: 0.0, + w: 0.0, + }; + Image { + texture_id: texture_id, + size: size.into(), + uv0: DEFAULT_UV0, + uv1: DEFAULT_UV1, + tint_col: DEFAULT_TINT_COL, + border_col: DEFAULT_BORDER_COL, + _phantom: PhantomData, + } + } + /// Set size (default based on texture) + pub fn size>(mut self, size: T) -> Self { + self.size = size.into(); + self + } + /// Set uv0 (default `[0.0, 0.0]`) + pub fn uv0>(mut self, uv0: T) -> Self { + self.uv0 = uv0.into(); + self + } + /// Set uv1 (default `[1.0, 1.0]`) + pub fn uv1>(mut self, uv1: T) -> Self { + self.uv1 = uv1.into(); + self + } + /// Set tint color (default: no tint color) + pub fn tint_col>(mut self, tint_col: T) -> Self { + self.tint_col = tint_col.into(); + self + } + /// Set border color (default: no border) + pub fn border_col>(mut self, border_col: T) -> Self { + self.border_col = border_col.into(); + self + } + /// Draw image where the cursor currently is + pub fn build(self) { + unsafe { + sys::igImage( + self.texture_id.0 as *mut c_void, + self.size, + self.uv0, + self.uv1, + self.tint_col, + self.border_col, + ); + } + } +} + +/// Generic texture mapping for use by renderers. +pub struct Textures { + textures: HashMap, + next: usize, +} + +impl Textures { + pub fn new() -> Self { + Textures { + textures: HashMap::new(), + next: 0, + } + } + + pub fn insert(&mut self, texture: T) -> ImTexture { + let id = self.next; + self.textures.insert(id, texture); + self.next += 1; + ImTexture(id) + } + + pub fn replace(&mut self, id: ImTexture, texture: T) -> Option { + self.textures.insert(id.0, texture) + } + + pub fn remove(&mut self, id: ImTexture) -> Option { + self.textures.remove(&id.0) + } + + pub fn get(&self, id: ImTexture) -> Option<&T> { + self.textures.get(&id.0) + } +} diff --git a/src/lib.rs b/src/lib.rs index 3bccdb8..c16ea67 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -18,6 +18,7 @@ pub use drag::{ DragInt4, DragIntRange2, }; pub use fonts::{FontGlyphRange, ImFont, ImFontAtlas, ImFontConfig}; +pub use image::{ImTexture, Image, Textures}; pub use input::{ InputFloat, InputFloat2, InputFloat3, InputFloat4, InputInt, InputInt2, InputInt3, InputInt4, InputText, InputTextMultiline, @@ -46,6 +47,7 @@ mod child_frame; mod color_editors; mod drag; mod fonts; +mod image; mod input; mod menus; mod plothistogram; @@ -172,8 +174,8 @@ impl ImGui { }) } } - pub fn set_texture_id(&mut self, value: usize) { - self.fonts().set_texture_id(value); + pub fn set_font_texture_id(&mut self, value: ImTexture) { + self.fonts().set_texture_id(value.id()); } pub fn set_ini_filename(&mut self, value: Option) { { @@ -1337,6 +1339,16 @@ impl<'ui> Ui<'ui> { } } +// Image +impl<'ui> Ui<'ui> { + pub fn image(&self, texture: ImTexture, size: S) -> Image + where + S: Into, + { + Image::new(self, texture, size) + } +} + impl<'ui> Ui<'ui> { /// Calculate the size required for a given text string. ///