Initial commit

This commit is contained in:
Joonas Javanainen 2015-08-15 15:47:51 +03:00
commit ad10dff1cf
14 changed files with 1493 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*~
*.swp
/target
/Cargo.lock

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "third-party/cimgui"]
path = third-party/cimgui
url = https://github.com/Extrawurst/cimgui.git

24
Cargo.toml Normal file
View File

@ -0,0 +1,24 @@
[package]
name = "imgui-rs"
version = "0.0.1"
authors = ["Joonas Javanainen <joonas.javanainen@gmail.com>"]
build = "build.rs"
[lib]
name = "imgui"
[dependencies]
bitflags = "0.3"
libc = "0.1"
[dependencies.glium]
version = "0.8"
default-features = false
optional = true
[dependencies.sdl2]
version = "0.7"
optional = true
[build-dependencies]
gcc = "0.3"

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Joonas Javanainen <joonas.javanainen@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

9
README.markdown Normal file
View File

@ -0,0 +1,9 @@
# imgui-rs: Rust bindings for (ImGui)
**Ultra hyper turbo cyber mega extra über experimental!!!**
## License
imgui-rs is licensed under the MIT license.
Uses [ImGui](https://github.com/ocornut/imgui) and [cimgui](https://github.com/Extrawurst/cimgui).

13
build.rs Normal file
View File

@ -0,0 +1,13 @@
extern crate gcc;
fn main() {
gcc::Config::new()
.cpp(true)
.file("third-party/cimgui/cimgui/cimgui.cpp")
.file("third-party/cimgui/cimgui/fontAtlas.cpp")
.file("third-party/cimgui/cimgui/drawList.cpp")
.file("third-party/cimgui/imgui/imgui.cpp")
.file("third-party/cimgui/imgui/imgui_demo.cpp")
.file("third-party/cimgui/imgui/imgui_draw.cpp")
.compile("libcimgui.a");
}

1008
src/ffi.rs Normal file

File diff suppressed because it is too large Load Diff

209
src/glium_renderer.rs Normal file
View File

@ -0,0 +1,209 @@
use glium::{
index, program, texture, vertex,
DrawError, DrawParameters, IndexBuffer, Program, Rect, Surface, Texture2d, VertexBuffer
};
use glium::backend::{Context, Facade};
use glium::draw_parameters::{BlendingFunction, LinearBlendingFactor};
use glium::index::PrimitiveType;
use glium::texture::{ClientFormat, RawImage2d};
use glium::vertex::{Attribute, AttributeType, Vertex, VertexFormat};
use libc::c_float;
use std::borrow::Cow;
use std::mem;
use std::rc::Rc;
use super::{DrawList, Frame, ImDrawIdx, ImDrawVert, ImGui, ImVec2, ImVec4};
pub type RendererResult<T> = Result<T, RendererError>;
pub enum RendererError {
Vertex(vertex::BufferCreationError),
Index(index::BufferCreationError),
Program(program::ProgramCreationError),
Texture(texture::TextureCreationError),
Draw(DrawError)
}
impl From<vertex::BufferCreationError> for RendererError {
fn from(e: vertex::BufferCreationError) -> RendererError {
RendererError::Vertex(e)
}
}
impl From<index::BufferCreationError> for RendererError {
fn from(e: index::BufferCreationError) -> RendererError {
RendererError::Index(e)
}
}
impl From<program::ProgramCreationError> for RendererError {
fn from(e: program::ProgramCreationError) -> RendererError {
RendererError::Program(e)
}
}
impl From<texture::TextureCreationError> for RendererError {
fn from(e: texture::TextureCreationError) -> RendererError {
RendererError::Texture(e)
}
}
impl From<DrawError> for RendererError {
fn from(e: DrawError) -> RendererError {
RendererError::Draw(e)
}
}
unsafe impl Attribute for ImVec2 {
fn get_type() -> AttributeType { <(c_float, c_float) as Attribute>::get_type() }
}
unsafe impl Attribute for ImVec4 {
fn get_type() -> AttributeType {
<(c_float, c_float, c_float, c_float) as Attribute>::get_type()
}
}
impl Vertex for ImDrawVert {
fn build_bindings() -> VertexFormat {
unsafe {
let dummy: &ImDrawVert = mem::transmute(0usize);
Cow::Owned(vec![
("pos".into(), mem::transmute(&dummy.pos), <ImVec2 as Attribute>::get_type()),
("uv".into(), mem::transmute(&dummy.uv), <ImVec2 as Attribute>::get_type()),
("col".into(), mem::transmute(&dummy.col), AttributeType::U8U8U8U8)
])
}
}
}
pub struct Renderer {
ctx: Rc<Context>,
device_objects: DeviceObjects
}
impl Renderer {
pub fn init<F: Facade>(imgui: &mut ImGui, ctx: &F) -> RendererResult<Renderer> {
let device_objects = try!(DeviceObjects::init(imgui, ctx));
Ok(Renderer {
ctx: ctx.get_context().clone(),
device_objects: device_objects
})
}
pub fn render<'a, S: Surface>(&mut self, surface: &mut S,
frame: Frame<'a>) -> RendererResult<()> {
frame.render(|draw_list| self.render_draw_list(surface, draw_list))
}
fn render_draw_list<'a, S: Surface>(&mut self, surface: &mut S,
draw_list: DrawList<'a>) -> RendererResult<()> {
try!(self.device_objects.upload_vertex_buffer(&self.ctx, draw_list.vtx_buffer));
try!(self.device_objects.upload_index_buffer(&self.ctx, draw_list.idx_buffer));
let (width, height) = surface.get_dimensions();
let mut idx_start = 0;
for cmd in draw_list.cmd_buffer {
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 ],
[ -1.0, 1.0, 0.0, 1.0 ]
];
let uniforms = uniform! {
matrix: matrix,
texture: self.device_objects.texture.sampled()
};
let draw_params = DrawParameters {
blending_function: Some(BlendingFunction::Addition {
source: LinearBlendingFactor::SourceAlpha,
destination: LinearBlendingFactor::OneMinusSourceAlpha
}),
scissor: Some(Rect {
left: cmd.clip_rect.x as u32,
bottom: (height as f32 - cmd.clip_rect.w) as u32,
width: (cmd.clip_rect.z - cmd.clip_rect.x) as u32,
height: (cmd.clip_rect.w - cmd.clip_rect.y) as u32
}),
.. Default::default()
};
let idx_end = idx_start + cmd.elem_count as usize;
try!(surface.draw(&self.device_objects.vertex_buffer,
&self.device_objects.index_buffer.slice(idx_start ..idx_end)
.expect("Invalid index buffer range"),
&self.device_objects.program,
&uniforms,
&draw_params));
idx_start = idx_end;
}
Ok(())
}
}
pub struct DeviceObjects {
vertex_buffer: VertexBuffer<ImDrawVert>,
index_buffer: IndexBuffer<ImDrawIdx>,
program: Program,
texture: Texture2d
}
fn compile_default_program<F: Facade>(ctx: &F) -> Result<Program, program::ProgramCreationError> {
program!(
ctx,
140 => {
vertex: include_str!("shader/vert_140.glsl"),
fragment: include_str!("shader/frag_140.glsl"),
outputs_srgb: true
},
110 => {
vertex: include_str!("shader/vert_110.glsl"),
fragment: include_str!("shader/frag_110.glsl"),
outputs_srgb: true
}
)
}
impl DeviceObjects {
pub fn init<F: Facade>(im_gui: &mut ImGui, ctx: &F) -> RendererResult<DeviceObjects> {
let vertex_buffer = try!(VertexBuffer::empty_dynamic(ctx, 0));
let index_buffer = try!(IndexBuffer::empty_dynamic(ctx, PrimitiveType::TrianglesList, 0));
let program = try!(compile_default_program(ctx));
let texture = try!(im_gui.prepare_texture(|handle| {
let data = RawImage2d {
data: Cow::Borrowed(handle.pixels),
width: handle.width,
height: handle.height,
format: ClientFormat::U8U8U8U8
};
Texture2d::new(ctx, data)
}));
Ok(DeviceObjects {
vertex_buffer: vertex_buffer,
index_buffer: index_buffer,
program: program,
texture: texture
})
}
pub fn upload_vertex_buffer<F: Facade>(&mut self, ctx: &F,
vtx_buffer: &[ImDrawVert]) -> RendererResult<()> {
self.vertex_buffer.invalidate();
if let Some(slice) = self.vertex_buffer.slice_mut(0 .. vtx_buffer.len()) {
slice.write(vtx_buffer);
return Ok(());
}
self.vertex_buffer = try!(VertexBuffer::dynamic(ctx, vtx_buffer));
Ok(())
}
pub fn upload_index_buffer<F: Facade>(&mut self, ctx: &F,
idx_buffer: &[ImDrawIdx]) -> RendererResult<()> {
self.index_buffer.invalidate();
if let Some(slice) = self.index_buffer.slice_mut(0 .. idx_buffer.len()) {
slice.write(idx_buffer);
return Ok(());
}
self.index_buffer =
try!(IndexBuffer::dynamic(ctx, PrimitiveType::TrianglesList, idx_buffer));
Ok(())
}
}

147
src/lib.rs Normal file
View File

@ -0,0 +1,147 @@
#[macro_use]
extern crate bitflags;
#[cfg(feature = "glium")]
#[macro_use]
extern crate glium;
extern crate libc;
#[cfg(feature = "sdl2")]
extern crate sdl2;
use libc::{c_float, c_int, c_uchar};
use std::marker::PhantomData;
use std::mem;
use std::ptr;
use std::slice;
pub use ffi::{ImDrawIdx, ImDrawVert, ImVec2, ImVec4};
pub mod ffi;
#[cfg(feature = "glium")]
pub mod glium_renderer;
pub struct ImGui;
pub struct TextureHandle<'a> {
pub width: u32,
pub height: u32,
pub pixels: &'a [c_uchar]
}
impl ImGui {
pub fn init() -> ImGui {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
io.render_draw_lists_fn = Some(render_draw_lists);
ImGui
}
pub fn prepare_texture<'a, F, T>(&mut self, f: F) -> T where F: FnOnce(TextureHandle<'a>) -> T {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
let mut pixels: *mut c_uchar = ptr::null_mut();
let mut width: c_int = 0;
let mut height: c_int = 0;
let mut bytes_per_pixel: c_int = 0;
unsafe {
ffi::ImFontAtlas_GetTexDataAsRGBA32(io.fonts, &mut pixels, &mut width, &mut height, &mut bytes_per_pixel);
f(TextureHandle {
width: width as u32,
height: height as u32,
pixels: slice::from_raw_parts(pixels, (width * height * bytes_per_pixel) as usize)
})
}
}
pub fn draw_mouse_cursor(&mut self, value: bool) {
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
io.mouse_draw_cursor = value;
}
#[cfg(feature = "sdl2")]
pub fn update_mouse(&mut self, mouse: &::sdl2::mouse::MouseUtil) {
let (mouse_state, mouse_x, mouse_y) = mouse.get_mouse_state();
let io: &mut ffi::ImGuiIO = unsafe { mem::transmute(ffi::igGetIO()) };
io.mouse_pos.x = mouse_x as f32;
io.mouse_pos.y = mouse_y as f32;
io.mouse_down = [
mouse_state.left(),
mouse_state.right(),
mouse_state.middle(),
mouse_state.x1(),
mouse_state.x2()
];
}
pub fn frame<'a>(&'a mut self, width: u32, height: u32, delta_time: f32) -> Frame<'a> {
unsafe {
let io: &mut ffi::ImGuiIO = mem::transmute(ffi::igGetIO());
io.display_size.x = width as c_float;
io.display_size.y = height as c_float;
io.delta_time = delta_time;
ffi::igNewFrame();
}
Frame {
_phantom: PhantomData
}
}
}
impl Drop for ImGui {
fn drop(&mut self) {
unsafe {
ffi::igShutdown();
}
}
}
pub struct DrawList<'a> {
pub cmd_buffer: &'a [ffi::ImDrawCmd],
pub idx_buffer: &'a [ffi::ImDrawIdx],
pub vtx_buffer: &'a [ffi::ImDrawVert]
}
pub struct Frame<'a> {
_phantom: PhantomData<&'a ImGui>
}
impl<'a> Frame<'a> {
pub fn show_test_window(&mut self) -> bool {
let mut opened = true;
unsafe {
ffi::igShowTestWindow(&mut opened);
}
opened
}
pub fn render<F, E>(self, mut f: F) -> Result<(), E>
where F: FnMut(DrawList<'a>) -> Result<(), E> {
unsafe {
let mut im_draw_data = mem::zeroed();
RENDER_DRAW_LISTS_STATE.0 = &mut im_draw_data;
ffi::igRender();
RENDER_DRAW_LISTS_STATE.0 = 0 as *mut ffi::ImDrawData;
for &cmd_list in im_draw_data.cmd_lists() {
let draw_list =
DrawList {
cmd_buffer: (*cmd_list).cmd_buffer.as_slice(),
idx_buffer: (*cmd_list).idx_buffer.as_slice(),
vtx_buffer: (*cmd_list).vtx_buffer.as_slice()
};
try!(f(draw_list));
}
}
Ok(())
}
}
struct RenderDrawListsState(*mut ffi::ImDrawData);
unsafe impl Sync for RenderDrawListsState {}
static mut RENDER_DRAW_LISTS_STATE: RenderDrawListsState =
RenderDrawListsState(0 as *mut ffi::ImDrawData);
extern "C" fn render_draw_lists(data: *mut ffi::ImDrawData) {
unsafe {
ptr::copy_nonoverlapping(data, RENDER_DRAW_LISTS_STATE.0, 1);
}
}

10
src/shader/frag_110.glsl Normal file
View File

@ -0,0 +1,10 @@
#version 110
uniform sampler2D tex;
varying vec2 f_uv;
varying vec4 f_color;
void main() {
gl_FragColor = f_color * texture2D(tex, f_uv.st);
}

12
src/shader/frag_140.glsl Normal file
View File

@ -0,0 +1,12 @@
#version 140
uniform sampler2D tex;
in vec2 f_uv;
in vec4 f_color;
out vec4 out_color;
void main() {
out_color = f_color * texture(tex, f_uv.st);
}

16
src/shader/vert_110.glsl Normal file
View File

@ -0,0 +1,16 @@
#version 110
uniform mat4 matrix;
attribute vec2 pos;
attribute vec2 uv;
attribute vec4 col;
varying vec2 f_uv;
varying vec4 f_color;
void main() {
f_uv = uv;
f_color = col / 255.0;
gl_Position = matrix * vec4(pos.xy, 0, 1);
}

16
src/shader/vert_140.glsl Normal file
View File

@ -0,0 +1,16 @@
#version 140
uniform mat4 matrix;
in vec2 pos;
in vec2 uv;
in vec4 col;
out vec2 f_uv;
out vec4 f_color;
void main() {
f_uv = uv;
f_color = col / 255.0;
gl_Position = matrix * vec4(pos.xy, 0, 1);
}

1
third-party/cimgui vendored Submodule

@ -0,0 +1 @@
Subproject commit 5d061e7db70f85caa8fa2d6c96263ebe724eff88