mirror of
https://github.com/eliasstepanik/imgui-rs.git
synced 2026-01-14 15:08:36 +00:00
Merge pull request #154 from SpaceManiac/patch/textures
Add custom texture support (both renderers)
This commit is contained in:
commit
6ccfbf0305
@ -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<T> = Result<T, RendererError>;
|
||||
|
||||
@ -17,6 +18,7 @@ pub enum RendererError {
|
||||
Buffer(gfx::buffer::CreationError),
|
||||
Pipeline(gfx::PipelineStateError<String>),
|
||||
Combined(gfx::CombinedError),
|
||||
BadTexture(ImTexture),
|
||||
}
|
||||
|
||||
impl From<gfx::UpdateError<usize>> for RendererError {
|
||||
@ -43,7 +45,42 @@ impl From<gfx::CombinedError> 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<R>>::Data,)*
|
||||
}
|
||||
|
||||
impl<'a, R: Resources> gfx::pso::PipelineData<R> for BorrowedData<'a, R> {
|
||||
type Meta = pipe::Meta;
|
||||
|
||||
fn bake_to(&self, out: &mut RawDataSet<R>, meta: &Self::Meta, man: &mut gfx::handle::Manager<R>, access: &mut AccessInfo<R>) {
|
||||
$(meta.$field.bind_to(out, &self.$field, man, access);)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extended_defines! {
|
||||
pipeline pipe {
|
||||
vertex_buffer: gfx::VertexBuffer<ImDrawVert> = (),
|
||||
matrix: gfx::Global<[[f32; 4]; 4]> = "matrix",
|
||||
@ -94,9 +131,12 @@ impl Shaders {
|
||||
}
|
||||
}
|
||||
|
||||
pub type Texture<R> = (gfx::handle::ShaderResourceView<R, [f32; 4]>, gfx::handle::Sampler<R>);
|
||||
|
||||
pub struct Renderer<R: Resources> {
|
||||
bundle: Bundle<R, pipe::Data<R>>,
|
||||
index_buffer: Buffer<R, u16>,
|
||||
textures: Textures<Texture<R>>,
|
||||
}
|
||||
|
||||
impl<R: Resources> Renderer<R> {
|
||||
@ -131,26 +171,12 @@ impl<R: Resources> Renderer<R> {
|
||||
&[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<R: Resources> Renderer<R> {
|
||||
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<R, gfx::format::Rgba8>) {
|
||||
self.bundle.data.out = out;
|
||||
self.bundle.out = out;
|
||||
}
|
||||
|
||||
pub fn textures(&mut self) -> &mut Textures<Texture<R>> {
|
||||
&mut self.textures
|
||||
}
|
||||
|
||||
pub fn render<'a, F: Factory<R>, C: CommandBuffer<R>>(
|
||||
&mut self,
|
||||
ui: Ui<'a>,
|
||||
@ -185,7 +223,7 @@ impl<R: Resources> Renderer<R> {
|
||||
(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<R: Resources> Renderer<R> {
|
||||
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<R: Resources> Renderer<R> {
|
||||
encoder: &mut Encoder<R, C>,
|
||||
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<R: Resources> Renderer<R> {
|
||||
|
||||
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<R: Resources> Renderer<R> {
|
||||
.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<R: Resources> Renderer<R> {
|
||||
encoder: &mut Encoder<R, C>,
|
||||
vtx_buffer: &[ImDrawVert],
|
||||
) -> RendererResult<()> {
|
||||
if self.bundle.data.vertex_buffer.len() < vtx_buffer.len() {
|
||||
self.bundle.data.vertex_buffer = factory.create_buffer::<ImDrawVert>(
|
||||
if self.bundle.vertex_buffer.len() < vtx_buffer.len() {
|
||||
self.bundle.vertex_buffer = factory.create_buffer::<ImDrawVert>(
|
||||
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<F: Factory<R>, C: CommandBuffer<R>>(
|
||||
&mut self,
|
||||
@ -268,3 +316,10 @@ impl<R: Resources> Renderer<R> {
|
||||
Ok(encoder.update_buffer(&self.index_buffer, idx_buffer, 0)?)
|
||||
}
|
||||
}
|
||||
|
||||
struct Bundle<R: Resources, Data: PipelineData<R>> {
|
||||
slice: Slice<R>,
|
||||
pso: PipelineState<R, Data::Meta>,
|
||||
vertex_buffer: Buffer<R, ImDrawVert>,
|
||||
out: RenderTargetView<R, gfx::format::Rgba8>,
|
||||
}
|
||||
|
||||
@ -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<Texture2d> {
|
||||
&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<ImDrawVert>,
|
||||
index_buffer: IndexBuffer<ImDrawIdx>,
|
||||
program: Program,
|
||||
texture: Texture2d,
|
||||
textures: Textures<Texture2d>,
|
||||
}
|
||||
|
||||
fn compile_default_program<F: Facade>(
|
||||
@ -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<F: Facade>(
|
||||
|
||||
144
src/image.rs
Normal file
144
src/image.rs
Normal file
@ -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<usize> 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<S>(_: &Ui<'ui>, texture_id: ImTexture, size: S) -> Self
|
||||
where
|
||||
S: Into<ImVec2>,
|
||||
{
|
||||
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<T: Into<ImVec2>>(mut self, size: T) -> Self {
|
||||
self.size = size.into();
|
||||
self
|
||||
}
|
||||
/// Set uv0 (default `[0.0, 0.0]`)
|
||||
pub fn uv0<T: Into<ImVec2>>(mut self, uv0: T) -> Self {
|
||||
self.uv0 = uv0.into();
|
||||
self
|
||||
}
|
||||
/// Set uv1 (default `[1.0, 1.0]`)
|
||||
pub fn uv1<T: Into<ImVec2>>(mut self, uv1: T) -> Self {
|
||||
self.uv1 = uv1.into();
|
||||
self
|
||||
}
|
||||
/// Set tint color (default: no tint color)
|
||||
pub fn tint_col<T: Into<ImVec4>>(mut self, tint_col: T) -> Self {
|
||||
self.tint_col = tint_col.into();
|
||||
self
|
||||
}
|
||||
/// Set border color (default: no border)
|
||||
pub fn border_col<T: Into<ImVec4>>(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<T> {
|
||||
textures: HashMap<usize, T>,
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl<T> Textures<T> {
|
||||
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<T> {
|
||||
self.textures.insert(id.0, texture)
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, id: ImTexture) -> Option<T> {
|
||||
self.textures.remove(&id.0)
|
||||
}
|
||||
|
||||
pub fn get(&self, id: ImTexture) -> Option<&T> {
|
||||
self.textures.get(&id.0)
|
||||
}
|
||||
}
|
||||
16
src/lib.rs
16
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<ImString>) {
|
||||
{
|
||||
@ -1337,6 +1339,16 @@ impl<'ui> Ui<'ui> {
|
||||
}
|
||||
}
|
||||
|
||||
// Image
|
||||
impl<'ui> Ui<'ui> {
|
||||
pub fn image<S>(&self, texture: ImTexture, size: S) -> Image
|
||||
where
|
||||
S: Into<ImVec2>,
|
||||
{
|
||||
Image::new(self, texture, size)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'ui> Ui<'ui> {
|
||||
/// Calculate the size required for a given text string.
|
||||
///
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user