Browse Source

initial commit hacked out from seni/gui

Inderjit Gill 2 months ago
commit
b1e6a03903

+ 4
- 0
.gitignore View File

@@ -0,0 +1,4 @@
1
+/target
2
+**/*.rs.bk
3
+Cargo.lock
4
+Config.toml

+ 19
- 0
Cargo.toml View File

@@ -0,0 +1,19 @@
1
+[package]
2
+name = "depict"
3
+version = "0.1.0"
4
+authors = ["Inderjit Gill <email@indy.io>"]
5
+edition = "2018"
6
+license = "GPL-3.0+"
7
+
8
+[dependencies]
9
+clap = "2.32.0"
10
+config = "0.9"
11
+env_logger = "0.6"
12
+gl = { path = "lib/gl" }
13
+image = "0.21.1"
14
+imgui = "0.0.22"
15
+log = "0.4"
16
+sdl2 = "0.32.1"
17
+
18
+[features]
19
+gl_debug = ["gl/debug"]

+ 9
- 0
Config.toml.example View File

@@ -0,0 +1,9 @@
1
+# the default script if none is supplied on the command line
2
+script = "1918-einstein.seni"
3
+
4
+assets_path = "/home/indy/code/seni/gui/assets"
5
+scripts_path = "/home/indy/code/seni/server/static/seni"
6
+bitmaps_path = "/home/indy/code/seni/client/www/img"
7
+
8
+profiling = false
9
+debug = false

+ 8
- 0
README.md View File

@@ -0,0 +1,8 @@
1
+## Setup
2
+
3
+Copy the Config.toml.example file as Config.toml
4
+
5
+
6
+## Build
7
+
8
+On Windows when you're shipping your game make sure to copy SDL2.dll to the same directory that your compiled exe is in, otherwise the game won't launch.

BIN
SDL2.dll View File


+ 16
- 0
assets/shaders/blit.frag View File

@@ -0,0 +1,16 @@
1
+#version 330 core
2
+
3
+// in VS_OUTPUT {
4
+//     vec2 TextureCoord;
5
+// } IN;
6
+
7
+// Values that stay constant for the whole mesh.
8
+// uniform sampler2D myTextureSampler;
9
+
10
+out vec4 Colour;
11
+
12
+void main()
13
+{
14
+  // Colour = texture( myTextureSampler, IN.TextureCoord );
15
+  Colour = vec4(1.0, 0.0, 0.0, 1.0);
16
+}

+ 17
- 0
assets/shaders/blit.vert View File

@@ -0,0 +1,17 @@
1
+#version 330 core
2
+
3
+in vec2 Position;
4
+in vec2 UV;
5
+
6
+uniform mat4 uMVMatrix;
7
+uniform mat4 uPMatrix;
8
+
9
+// out VS_OUTPUT {
10
+//     vec2 TextureCoord;
11
+// } OUT;
12
+
13
+void main()
14
+{
15
+  gl_Position = uPMatrix * uMVMatrix * vec4(Position, 0.0, 1.0);
16
+  // OUT.TextureCoord = UV;
17
+}

+ 13
- 0
assets/shaders/imgui.frag View File

@@ -0,0 +1,13 @@
1
+#version 130
2
+
3
+uniform sampler2D Texture;
4
+
5
+in vec2 Frag_UV;
6
+in vec4 Frag_Color;
7
+
8
+out vec4 Out_Color;
9
+
10
+void main()
11
+{
12
+  Out_Color = Frag_Color * texture(Texture, Frag_UV.st);
13
+}

+ 17
- 0
assets/shaders/imgui.vert View File

@@ -0,0 +1,17 @@
1
+#version 130
2
+
3
+uniform mat4 ProjMtx;
4
+
5
+in vec2 Position;
6
+in vec2 UV;
7
+in vec4 Color;
8
+
9
+out vec2 Frag_UV;
10
+out vec4 Frag_Color;
11
+
12
+void main()
13
+{
14
+  Frag_UV = UV;
15
+  Frag_Color = Color;
16
+  gl_Position = ProjMtx * vec4(Position.xy,0,1);
17
+}

+ 21
- 0
assets/shaders/piece.frag View File

@@ -0,0 +1,21 @@
1
+#version 330 core
2
+
3
+in VS_OUTPUT {
4
+    vec4 Colour;
5
+    vec2 TextureCoord;
6
+} IN;
7
+
8
+// Values that stay constant for the whole mesh.
9
+uniform sampler2D myTextureSampler;
10
+
11
+layout(location = 0) out vec4 Colour;
12
+
13
+void main()
14
+{
15
+  vec4 tex = texture( myTextureSampler, IN.TextureCoord );
16
+
17
+  Colour.r = tex.r * IN.Colour.r;
18
+  Colour.g = tex.r * IN.Colour.g;
19
+  Colour.b = tex.r * IN.Colour.b;
20
+  Colour.a = tex.r * IN.Colour.a;
21
+}

+ 20
- 0
assets/shaders/piece.vert View File

@@ -0,0 +1,20 @@
1
+#version 330 core
2
+
3
+in vec2 Position;
4
+in vec4 Colour;
5
+in vec2 UV;
6
+
7
+uniform mat4 uMVMatrix;
8
+uniform mat4 uPMatrix;
9
+
10
+out VS_OUTPUT {
11
+    vec4 Colour;
12
+    vec2 TextureCoord;
13
+} OUT;
14
+
15
+void main()
16
+{
17
+  gl_Position = uPMatrix * uMVMatrix * vec4(Position, 0.0, 1.0);
18
+  OUT.Colour = Colour;
19
+  OUT.TextureCoord = UV;
20
+}

+ 3
- 0
lib/gl/.gitignore View File

@@ -0,0 +1,3 @@
1
+/target
2
+**/*.rs.bk
3
+Cargo.lock

+ 13
- 0
lib/gl/Cargo.toml View File

@@ -0,0 +1,13 @@
1
+[package]
2
+name = "gl"
3
+version = "4.1.0"
4
+authors = ["Inderjit Gill <email@indy.io>"]
5
+edition = "2018"
6
+license = "GPL-3.0+"
7
+
8
+[build-dependencies]
9
+gl_generator = "0.9"
10
+gl_generator_profiling_struct = "0.1"
11
+
12
+[features]
13
+debug = []

+ 22
- 0
lib/gl/build.rs View File

@@ -0,0 +1,22 @@
1
+use gl_generator::{Api, Fallbacks, Profile, Registry};
2
+use gl_generator_profiling_struct::ProfilingStructGenerator;
3
+use std::env;
4
+use std::fs::File;
5
+use std::path::Path;
6
+
7
+fn main() {
8
+    let out_dir = env::var("OUT_DIR").unwrap();
9
+    let mut file_gl = File::create(&Path::new(&out_dir).join("bindings.rs")).unwrap();
10
+
11
+    let registry = Registry::new(
12
+        Api::Gl,
13
+        (4, 5),
14
+        Profile::Core,
15
+        Fallbacks::All,
16
+        ["GL_NV_command_list"],
17
+    );
18
+
19
+    registry
20
+        .write_bindings(ProfilingStructGenerator, &mut file_gl)
21
+        .unwrap();
22
+}

+ 33
- 0
lib/gl/src/lib.rs View File

@@ -0,0 +1,33 @@
1
+mod bindings {
2
+    include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
3
+}
4
+
5
+use std::ops::Deref;
6
+use std::rc::Rc;
7
+
8
+pub use crate::bindings::Gl as InnerGl;
9
+pub use crate::bindings::*;
10
+
11
+#[derive(Clone)]
12
+pub struct Gl {
13
+    inner: Rc<bindings::Gl>,
14
+}
15
+
16
+impl Gl {
17
+    pub fn load_with<F>(loadfn: F) -> Gl
18
+    where
19
+        F: FnMut(&'static str) -> *const types::GLvoid,
20
+    {
21
+        Gl {
22
+            inner: Rc::new(bindings::Gl::load_with(loadfn)),
23
+        }
24
+    }
25
+}
26
+
27
+impl Deref for Gl {
28
+    type Target = bindings::Gl;
29
+
30
+    fn deref(&self) -> &bindings::Gl {
31
+        &self.inner
32
+    }
33
+}

+ 96
- 0
src/error.rs View File

@@ -0,0 +1,96 @@
1
+// Copyright (C) 2019 Inderjit Gill
2
+
3
+// This program is free software: you can redistribute it and/or modify
4
+// it under the terms of the GNU General Public License as published by
5
+// the Free Software Foundation, either version 3 of the License, or
6
+// (at your option) any later version.
7
+
8
+// This program is distributed in the hope that it will be useful,
9
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+// GNU General Public License for more details.
12
+
13
+// You should have received a copy of the GNU General Public License
14
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
+
16
+use std::error;
17
+use std::fmt;
18
+
19
+pub type Result<T> = ::std::result::Result<T, Error>;
20
+
21
+#[derive(Debug)]
22
+pub enum Error {
23
+    Placeholder(String), // temp placeholder error
24
+
25
+    GLError(String),
26
+    ConfigError(config::ConfigError),
27
+    StringError(String),
28
+    SDL2WindowBuildError(sdl2::video::WindowBuildError),
29
+    FFINulError(std::ffi::NulError),
30
+    Io(std::io::Error),
31
+    ImageError(image::ImageError),
32
+    FileContainsNil,
33
+    AssetLoad { name: String },
34
+    CanNotDetermineShaderType { name: String },
35
+    CompileError { name: String, message: String },
36
+    LinkError { name: String, message: String },
37
+}
38
+
39
+impl From<config::ConfigError> for Error {
40
+    fn from(e: config::ConfigError) -> Error {
41
+        Error::ConfigError(e)
42
+    }
43
+}
44
+
45
+impl From<String> for Error {
46
+    fn from(e: String) -> Error {
47
+        Error::StringError(e)
48
+    }
49
+}
50
+
51
+impl From<sdl2::video::WindowBuildError> for Error {
52
+    fn from(e: sdl2::video::WindowBuildError) -> Error {
53
+        Error::SDL2WindowBuildError(e)
54
+    }
55
+}
56
+
57
+impl From<std::ffi::NulError> for Error {
58
+    fn from(e: std::ffi::NulError) -> Error {
59
+        Error::FFINulError(e)
60
+    }
61
+}
62
+
63
+impl From<std::io::Error> for Error {
64
+    fn from(other: std::io::Error) -> Self {
65
+        Error::Io(other)
66
+    }
67
+}
68
+
69
+impl From<image::ImageError> for Error {
70
+    fn from(other: image::ImageError) -> Self {
71
+        Error::ImageError(other)
72
+    }
73
+}
74
+
75
+// don't need to implement any of the trait's methods
76
+impl error::Error for Error {}
77
+
78
+impl fmt::Display for Error {
79
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80
+        match self {
81
+            Error::Placeholder(s) => write!(f, "seni gui: Placeholder: {:?}", s),
82
+            Error::GLError(s) => write!(f, "seni gui: OpenGL Error: {:?}", s),
83
+            Error::ConfigError(c) => write!(f, "seni gui: Config Error: {:?}", c),
84
+            Error::StringError(s) => write!(f, "seni gui: String Error: {}", s),
85
+            Error::SDL2WindowBuildError(e) => write!(f, "seni gui: SDL2WindowBuildError: {:?}", e),
86
+            Error::FFINulError(e) => write!(f, "seni gui: std::ffi:NulError: {:?}", e),
87
+            Error::Io(e) => write!(f, "seni gui: Io: {:?}", e),
88
+            Error::ImageError(e) => write!(f, "seni gui: ImageError: {:?}", e),
89
+            Error::FileContainsNil => write!(f, "seni gui: FileContainsNil"),
90
+            Error::AssetLoad { name } => write!(f, "seni gui: {}", name),
91
+            Error::CanNotDetermineShaderType { name } => write!(f, "seni gui: {}", name),
92
+            Error::CompileError { name, message } => write!(f, "seni gui: {} {}", name, message),
93
+            Error::LinkError { name, message } => write!(f, "seni gui: {} {}", name, message),
94
+        }
95
+    }
96
+}

+ 174
- 0
src/gl_util.rs View File

@@ -0,0 +1,174 @@
1
+// Copyright (C) 2019 Inderjit Gill
2
+
3
+// This program is free software: you can redistribute it and/or modify
4
+// it under the terms of the GNU General Public License as published by
5
+// the Free Software Foundation, either version 3 of the License, or
6
+// (at your option) any later version.
7
+
8
+// This program is distributed in the hope that it will be useful,
9
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+// GNU General Public License for more details.
12
+
13
+// You should have received a copy of the GNU General Public License
14
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
+
16
+use std::mem;
17
+
18
+use std::path::Path;
19
+
20
+use gl;
21
+use image::GenericImageView;
22
+use log::info;
23
+
24
+use crate::error::{Error, Result};
25
+
26
+
27
+#[derive(Default)]
28
+pub struct BitmapInfo {
29
+    pub width: usize,
30
+    pub height: usize,
31
+    pub data: Vec<u8>,
32
+}
33
+
34
+
35
+#[macro_export]
36
+macro_rules! c_str {
37
+    ($literal:expr) => {
38
+        std::ffi::CStr::from_bytes_with_nul_unchecked(concat!($literal, "\0").as_bytes())
39
+    };
40
+}
41
+
42
+pub fn return_param<T, F>(f: F) -> T
43
+where
44
+    F: FnOnce(&mut T),
45
+{
46
+    let mut val = unsafe { mem::uninitialized() };
47
+    f(&mut val);
48
+    val
49
+}
50
+
51
+pub fn load_texture(ppath: &Path, name: &str) -> Result<BitmapInfo> {
52
+    let path = ppath.join(name);
53
+
54
+    info!("load_bitmap: {:?}", path);
55
+    let image = image::open(&path)?;
56
+
57
+    let (w, h) = image.dimensions();
58
+    let width = w as usize;
59
+    let height = h as usize;
60
+    let mut data: Vec<u8> = Vec::with_capacity(width * height * 4);
61
+
62
+    info!("loading bitmap {} of size {} x {}", name, width, height);
63
+
64
+    for (_, _, rgba) in image.pixels() {
65
+        data.push(rgba.data[0]);
66
+        data.push(rgba.data[1]);
67
+        data.push(rgba.data[2]);
68
+        data.push(rgba.data[3]);
69
+    }
70
+
71
+    let mut data_flipped: Vec<u8> = Vec::with_capacity(width * height * 4);
72
+    for y in 0..height {
73
+        for x in 0..width {
74
+            let offset = ((height - y - 1) * (width * 4)) + (x * 4);
75
+            data_flipped.push(data[offset]);
76
+            data_flipped.push(data[offset + 1]);
77
+            data_flipped.push(data[offset + 2]);
78
+            data_flipped.push(data[offset + 3]);
79
+        }
80
+    }
81
+
82
+    let bitmap_info = BitmapInfo {
83
+        width,
84
+        height,
85
+        data: data_flipped,
86
+        ..Default::default()
87
+    };
88
+
89
+    Ok(bitmap_info)
90
+}
91
+
92
+pub fn create_framebuffer(gl: &gl::Gl) -> gl::types::GLuint {
93
+    let mut framebuffer_id: gl::types::GLuint = 0;
94
+
95
+    unsafe {
96
+        gl.GenFramebuffers(1, &mut framebuffer_id);
97
+    }
98
+
99
+    framebuffer_id
100
+}
101
+
102
+pub fn create_texture(gl: &gl::Gl, width: usize, height: usize) -> gl::types::GLuint {
103
+    let mut texture_id: gl::types::GLuint = 0;
104
+
105
+    unsafe {
106
+        gl.GenTextures(1, &mut texture_id);
107
+        // "Bind" the newly created texture : all future texture functions will modify this texture
108
+        gl.BindTexture(gl::TEXTURE_2D, texture_id);
109
+        // Give an empty image to OpenGL ( the last "0" )
110
+        gl.TexImage2D(
111
+            gl::TEXTURE_2D,
112
+            0,
113
+            gl::RGBA as _,
114
+            width as _,
115
+            height as _,
116
+            0,
117
+            gl::RGBA,
118
+            gl::UNSIGNED_BYTE,
119
+            0 as *const gl::types::GLvoid,
120
+        );
121
+
122
+        gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
123
+        gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _);
124
+        gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as _);
125
+        gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as _);
126
+    }
127
+
128
+    texture_id
129
+}
130
+
131
+pub fn attach_texture_to_framebuffer(
132
+    gl: &gl::Gl,
133
+    framebuffer_id: gl::types::GLuint,
134
+    texture_id: gl::types::GLuint,
135
+) {
136
+    unsafe {
137
+        gl.BindFramebuffer(gl::FRAMEBUFFER, framebuffer_id);
138
+        gl.FramebufferTexture2D(
139
+            gl::FRAMEBUFFER,
140
+            gl::COLOR_ATTACHMENT0,
141
+            gl::TEXTURE_2D,
142
+            texture_id,
143
+            0,
144
+        );
145
+    }
146
+}
147
+
148
+pub fn is_framebuffer_ok(gl: &gl::Gl) -> Result<()> {
149
+    unsafe {
150
+        if gl.CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
151
+            Err(Error::GLError("Framebuffer is not complete".to_string()))
152
+        } else {
153
+            Ok(())
154
+        }
155
+    }
156
+}
157
+
158
+pub fn bind_framebuffer(
159
+    gl: &gl::Gl,
160
+    framebuffer_id: gl::types::GLuint,
161
+    viewport_width: usize,
162
+    viewport_height: usize,
163
+) {
164
+    unsafe {
165
+        gl.BindFramebuffer(gl::FRAMEBUFFER, framebuffer_id);
166
+        gl.Viewport(0, 0, viewport_width as _, viewport_height as _);
167
+    }
168
+}
169
+
170
+pub fn update_viewport(gl: &gl::Gl, viewport_width: usize, viewport_height: usize) {
171
+    unsafe {
172
+        gl.Viewport(0, 0, viewport_width as _, viewport_height as _);
173
+    }
174
+}

+ 229
- 0
src/input_imgui.rs View File

@@ -0,0 +1,229 @@
1
+// code from:
2
+// https://github.com/michaelfairley/rust-imgui-sdl2
3
+// licensed under MIT/Apache 2.0
4
+//
5
+use imgui::sys as imgui_sys;
6
+use sdl2::sys as sdl2_sys;
7
+
8
+use imgui::{ImGui, ImGuiMouseCursor};
9
+use sdl2::mouse::{Cursor, MouseState, SystemCursor};
10
+use sdl2::video::Window;
11
+use std::os::raw::{c_char, c_void};
12
+use std::time::Instant;
13
+
14
+use sdl2::event::Event;
15
+
16
+pub struct ImguiSdl2 {
17
+    last_frame: Instant,
18
+    mouse_press: [bool; 5],
19
+    ignore_mouse: bool,
20
+    ignore_keyboard: bool,
21
+    cursor: (ImGuiMouseCursor, Option<Cursor>),
22
+}
23
+
24
+impl ImguiSdl2 {
25
+    pub fn new(imgui: &mut ImGui) -> Self {
26
+        // TODO: upstream to imgui-rs
27
+        {
28
+            let io = unsafe { &mut *imgui_sys::igGetIO() };
29
+
30
+            io.get_clipboard_text_fn = Some(get_clipboard_text);
31
+            io.set_clipboard_text_fn = Some(set_clipboard_text);
32
+            io.clipboard_user_data = std::ptr::null_mut();
33
+        }
34
+
35
+        {
36
+            use imgui::ImGuiKey;
37
+            use sdl2::keyboard::Scancode;
38
+
39
+            imgui.set_imgui_key(ImGuiKey::Tab, Scancode::Tab as u8);
40
+            imgui.set_imgui_key(ImGuiKey::LeftArrow, Scancode::Left as u8);
41
+            imgui.set_imgui_key(ImGuiKey::RightArrow, Scancode::Right as u8);
42
+            imgui.set_imgui_key(ImGuiKey::UpArrow, Scancode::Up as u8);
43
+            imgui.set_imgui_key(ImGuiKey::DownArrow, Scancode::Down as u8);
44
+            imgui.set_imgui_key(ImGuiKey::PageUp, Scancode::PageUp as u8);
45
+            imgui.set_imgui_key(ImGuiKey::PageDown, Scancode::PageDown as u8);
46
+            imgui.set_imgui_key(ImGuiKey::Home, Scancode::Home as u8);
47
+            imgui.set_imgui_key(ImGuiKey::End, Scancode::End as u8);
48
+            imgui.set_imgui_key(ImGuiKey::Delete, Scancode::Delete as u8);
49
+            imgui.set_imgui_key(ImGuiKey::Backspace, Scancode::Backspace as u8);
50
+            imgui.set_imgui_key(ImGuiKey::Enter, Scancode::Return as u8);
51
+            imgui.set_imgui_key(ImGuiKey::Escape, Scancode::Escape as u8);
52
+            imgui.set_imgui_key(ImGuiKey::A, Scancode::A as u8);
53
+            imgui.set_imgui_key(ImGuiKey::C, Scancode::C as u8);
54
+            imgui.set_imgui_key(ImGuiKey::V, Scancode::V as u8);
55
+            imgui.set_imgui_key(ImGuiKey::X, Scancode::X as u8);
56
+            imgui.set_imgui_key(ImGuiKey::Y, Scancode::Y as u8);
57
+            imgui.set_imgui_key(ImGuiKey::Z, Scancode::Z as u8);
58
+        }
59
+
60
+        Self {
61
+            last_frame: Instant::now(),
62
+            mouse_press: [false; 5],
63
+            ignore_keyboard: false,
64
+            ignore_mouse: false,
65
+            cursor: (ImGuiMouseCursor::None, None),
66
+        }
67
+    }
68
+
69
+    pub fn ignore_event(&self, event: &Event) -> bool {
70
+        match *event {
71
+            Event::KeyDown { .. }
72
+            | Event::KeyUp { .. }
73
+            | Event::TextEditing { .. }
74
+            | Event::TextInput { .. } => self.ignore_keyboard,
75
+            Event::MouseMotion { .. }
76
+            | Event::MouseButtonDown { .. }
77
+            | Event::MouseButtonUp { .. }
78
+            | Event::MouseWheel { .. }
79
+            | Event::FingerDown { .. }
80
+            | Event::FingerUp { .. }
81
+            | Event::FingerMotion { .. }
82
+            | Event::DollarGesture { .. }
83
+            | Event::DollarRecord { .. }
84
+            | Event::MultiGesture { .. } => self.ignore_mouse,
85
+            _ => false,
86
+        }
87
+    }
88
+
89
+    pub fn handle_event(&mut self, imgui: &mut ImGui, event: &Event) {
90
+        use sdl2::keyboard;
91
+        use sdl2::mouse::MouseButton;
92
+
93
+        fn set_mod(imgui: &mut ImGui, keymod: keyboard::Mod) {
94
+            let ctrl = keymod.intersects(keyboard::Mod::RCTRLMOD | keyboard::Mod::LCTRLMOD);
95
+            let alt = keymod.intersects(keyboard::Mod::RALTMOD | keyboard::Mod::LALTMOD);
96
+            let shift = keymod.intersects(keyboard::Mod::RSHIFTMOD | keyboard::Mod::LSHIFTMOD);
97
+            let super_ = keymod.intersects(keyboard::Mod::RGUIMOD | keyboard::Mod::LGUIMOD);
98
+
99
+            imgui.set_key_ctrl(ctrl);
100
+            imgui.set_key_alt(alt);
101
+            imgui.set_key_shift(shift);
102
+            imgui.set_key_super(super_);
103
+        }
104
+
105
+        match *event {
106
+            Event::MouseWheel { y, .. } => {
107
+                imgui.set_mouse_wheel(y as f32);
108
+            }
109
+            Event::MouseButtonDown { mouse_btn, .. } => {
110
+                if mouse_btn != MouseButton::Unknown {
111
+                    let index = match mouse_btn {
112
+                        MouseButton::Left => 0,
113
+                        MouseButton::Right => 1,
114
+                        MouseButton::Middle => 2,
115
+                        MouseButton::X1 => 3,
116
+                        MouseButton::X2 => 4,
117
+                        MouseButton::Unknown => unreachable!(),
118
+                    };
119
+                    self.mouse_press[index] = true;
120
+                }
121
+            }
122
+            Event::TextInput { ref text, .. } => {
123
+                for chr in text.chars() {
124
+                    imgui.add_input_character(chr);
125
+                }
126
+            }
127
+            Event::KeyDown {
128
+                scancode, keymod, ..
129
+            } => {
130
+                set_mod(imgui, keymod);
131
+                if let Some(scancode) = scancode {
132
+                    imgui.set_key(scancode as u8, true);
133
+                }
134
+            }
135
+            Event::KeyUp {
136
+                scancode, keymod, ..
137
+            } => {
138
+                set_mod(imgui, keymod);
139
+                if let Some(scancode) = scancode {
140
+                    imgui.set_key(scancode as u8, false);
141
+                }
142
+            }
143
+            _ => {}
144
+        }
145
+    }
146
+
147
+    pub fn frame<'ui>(
148
+        &mut self,
149
+        window: &Window,
150
+        imgui: &'ui mut ImGui,
151
+        mouse_state: &MouseState,
152
+    ) -> imgui::Ui<'ui> {
153
+        let mouse_util = window.subsystem().sdl().mouse();
154
+
155
+        // Merging the mousedown events we received into the current state prevents us from missing
156
+        // clicks that happen faster than a frame
157
+        let mouse_down = [
158
+            self.mouse_press[0] || mouse_state.left(),
159
+            self.mouse_press[1] || mouse_state.right(),
160
+            self.mouse_press[2] || mouse_state.middle(),
161
+            self.mouse_press[3] || mouse_state.x1(),
162
+            self.mouse_press[4] || mouse_state.x2(),
163
+        ];
164
+        imgui.set_mouse_down(mouse_down);
165
+        self.mouse_press = [false; 5];
166
+
167
+        let any_mouse_down = mouse_down.iter().any(|&b| b);
168
+        mouse_util.capture(any_mouse_down);
169
+
170
+        imgui.set_mouse_pos(mouse_state.x() as f32, mouse_state.y() as f32);
171
+
172
+        let mouse_cursor = imgui.mouse_cursor();
173
+        if imgui.mouse_draw_cursor() || mouse_cursor == ImGuiMouseCursor::None {
174
+            self.cursor = (ImGuiMouseCursor::None, None);
175
+            mouse_util.show_cursor(false);
176
+        } else {
177
+            mouse_util.show_cursor(true);
178
+
179
+            if mouse_cursor != self.cursor.0 {
180
+                let sdl_cursor = match mouse_cursor {
181
+                    ImGuiMouseCursor::None => unreachable!("mouse_cursor was None!"),
182
+                    ImGuiMouseCursor::Arrow => SystemCursor::Arrow,
183
+                    ImGuiMouseCursor::TextInput => SystemCursor::IBeam,
184
+                    ImGuiMouseCursor::ResizeAll => SystemCursor::SizeAll,
185
+                    ImGuiMouseCursor::ResizeNS => SystemCursor::SizeNS,
186
+                    ImGuiMouseCursor::ResizeEW => SystemCursor::SizeWE,
187
+                    ImGuiMouseCursor::ResizeNESW => SystemCursor::SizeNESW,
188
+                    ImGuiMouseCursor::ResizeNWSE => SystemCursor::SizeNWSE,
189
+                    ImGuiMouseCursor::Hand => SystemCursor::Hand,
190
+                };
191
+
192
+                let sdl_cursor = Cursor::from_system(sdl_cursor).unwrap();
193
+                sdl_cursor.set();
194
+
195
+                self.cursor = (mouse_cursor, Some(sdl_cursor));
196
+            }
197
+        }
198
+
199
+        let now = Instant::now();
200
+        let delta = now - self.last_frame;
201
+        let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
202
+        self.last_frame = now;
203
+
204
+        let window_size = window.size();
205
+        let display_size = window.drawable_size();
206
+
207
+        let frame_size = imgui::FrameSize {
208
+            logical_size: (window_size.0 as f64, window_size.1 as f64),
209
+            hidpi_factor: (display_size.0 as f64) / (window_size.0 as f64),
210
+        };
211
+        let ui = imgui.frame(frame_size, delta_s);
212
+
213
+        self.ignore_keyboard = ui.want_capture_keyboard();
214
+        self.ignore_mouse = ui.want_capture_mouse();
215
+
216
+        ui
217
+    }
218
+}
219
+
220
+#[doc(hidden)]
221
+pub extern "C" fn get_clipboard_text(_user_data: *mut c_void) -> *const c_char {
222
+    unsafe { sdl2_sys::SDL_GetClipboardText() }
223
+}
224
+
225
+#[doc(hidden)]
226
+#[cfg_attr(feature = "cargo-clippy", allow(not_unsafe_ptr_arg_deref))]
227
+pub extern "C" fn set_clipboard_text(_user_data: *mut c_void, text: *const c_char) {
228
+    unsafe { sdl2_sys::SDL_SetClipboardText(text) };
229
+}

+ 211
- 0
src/main.rs View File

@@ -0,0 +1,211 @@
1
+// Copyright (C) 2019 Inderjit Gill
2
+
3
+// This program is free software: you can redistribute it and/or modify
4
+// it under the terms of the GNU General Public License as published by
5
+// the Free Software Foundation, either version 3 of the License, or
6
+// (at your option) any later version.
7
+
8
+// This program is distributed in the hope that it will be useful,
9
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+// GNU General Public License for more details.
12
+
13
+// You should have received a copy of the GNU General Public License
14
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
+
16
+// Order of mod matters!. Declare gl_util before using it in other modules
17
+#[macro_use]
18
+mod gl_util;
19
+mod matrix_util;
20
+
21
+mod error;
22
+mod input_imgui;
23
+mod render_gl;
24
+mod render_imgui;
25
+mod render_square;
26
+
27
+use std::path::Path;
28
+
29
+use clap::{value_t, App, Arg};
30
+use config;
31
+use env_logger;
32
+use gl;
33
+use imgui;
34
+use imgui::im_str;
35
+use log::info;
36
+use sdl2;
37
+
38
+use crate::error::Result;
39
+
40
+fn main() -> Result<()> {
41
+    // Add in `./Config.toml`
42
+    // Add in config from the environment (with a prefix of DEPICT)
43
+    // Eg.. `DEPICT_DEBUG=1 ./target/app` would set the `debug` key
44
+    //
45
+    let mut config = config::Config::default();
46
+    config
47
+        .merge(config::File::with_name("Config"))?
48
+        .merge(config::Environment::with_prefix("DEPICT"))?;
49
+
50
+    // update config with command line options
51
+    //
52
+    let matches = App::new("seni-gui")
53
+        .version("4.1.0")
54
+        .author("Inderjit Gill <email@indy.io>")
55
+        .about("A tool to play with GLSL Shaders")
56
+        .arg(
57
+            Arg::with_name("SCRIPT")
58
+                .help("Sets the input seni script to use")
59
+                .index(1),
60
+        )
61
+        .arg(
62
+            Arg::with_name("seed")
63
+                .short("s")
64
+                .long("seed")
65
+                .help("The seed to use")
66
+                .takes_value(true),
67
+        )
68
+        .get_matches();
69
+
70
+    env_logger::init();
71
+
72
+    if let Some(script) = matches.value_of("SCRIPT") {
73
+        // this should always pass as SCRIPT is required
74
+        info!("Using script file: {}", script);
75
+
76
+        config.set("script", script)?;
77
+    }
78
+
79
+    if let Ok(seed) = value_t!(matches.value_of("seed"), i64) {
80
+        config.set("seed", seed)?;
81
+    }
82
+
83
+    run(&config)
84
+}
85
+
86
+fn run(config: &config::Config) -> Result<()> {
87
+    let assets_location = config.get_str("assets_path")?;
88
+    let assets_path = Path::new(&assets_location);
89
+
90
+    let bitmaps_location = config.get_str("bitmaps_path")?;
91
+    let bitmaps_path = Path::new(&bitmaps_location);
92
+
93
+    let script_filename = config.get_str("script")?;
94
+
95
+    let sdl_context = sdl2::init()?;
96
+    let video = sdl_context.video()?;
97
+
98
+    {
99
+        let gl_attr = video.gl_attr();
100
+        gl_attr.set_context_profile(sdl2::video::GLProfile::Core);
101
+        gl_attr.set_context_version(4, 5);
102
+    }
103
+
104
+    let window = video
105
+        .window("depict", 1000, 1000)
106
+        .position_centered()
107
+        .resizable()
108
+        .opengl()
109
+        .allow_highdpi()
110
+        .build()?;
111
+
112
+    let _gl_context = window
113
+        .gl_create_context()
114
+        .expect("Couldn't create GL context");
115
+    // provide a function to load function pointer by string
116
+    let gl = gl::Gl::load_with(|s| video.gl_get_proc_address(s) as _);
117
+
118
+    let mut imgui = imgui::ImGui::init();
119
+    imgui.set_ini_filename(None);
120
+
121
+    let mut input_imgui = input_imgui::ImguiSdl2::new(&mut imgui);
122
+
123
+    let mut viewport_width: usize = 900;
124
+    let mut viewport_height: usize = 700;
125
+
126
+    unsafe {
127
+        // set up shared state for window
128
+        //
129
+        gl.ClearColor(1.0, 1.0, 1.0, 1.0);
130
+
131
+        // assuming that we'll be using pre-multiplied alpha
132
+        // see http://www.realtimerendering.com/blog/gpus-prefer-premultiplication/
133
+        gl.Enable(gl::BLEND);
134
+        gl.BlendEquation(gl::FUNC_ADD);
135
+        gl.BlendFunc(gl::ONE, gl::ONE_MINUS_SRC_ALPHA);
136
+    }
137
+
138
+    let imgui_renderer = render_imgui::Renderer::new(&gl, &assets_path, &mut imgui)?;
139
+    let square_renderer = render_square::Renderer::new(&gl, &assets_path)?;
140
+
141
+    gl_util::update_viewport(&gl, viewport_width, viewport_height);
142
+
143
+    let mut event_pump = sdl_context.event_pump()?;
144
+
145
+    'running: loop {
146
+        use sdl2::event::{Event, WindowEvent};
147
+        use sdl2::keyboard::Keycode;
148
+
149
+        for event in event_pump.poll_iter() {
150
+            input_imgui.handle_event(&mut imgui, &event);
151
+            if input_imgui.ignore_event(&event) {
152
+                continue;
153
+            }
154
+
155
+            match event {
156
+                Event::Quit { .. }
157
+                | Event::KeyDown {
158
+                    keycode: Some(Keycode::Escape),
159
+                    ..
160
+                } => break 'running,
161
+                Event::Window {
162
+                    win_event: WindowEvent::Resized(w, h),
163
+                    ..
164
+                } => {
165
+                    viewport_width = w as _;
166
+                    viewport_height = h as _;
167
+                    gl_util::update_viewport(&gl, viewport_width, viewport_height);
168
+                }
169
+                _ => {}
170
+            }
171
+        }
172
+
173
+        let ui = input_imgui.frame(&window, &mut imgui, &event_pump.mouse_state());
174
+        ui.show_demo_window(&mut true);
175
+
176
+        // ~/repos/rust/imgui-rs/imgui-glium-examples/examples/test_window_impl.rs
177
+        // ui.window(im_str!("Seni"))
178
+        //     .position((0.0, 0.0), imgui::ImGuiCond::FirstUseEver)
179
+        //     .size((800.0, 600.0), imgui::ImGuiCond::FirstUseEver)
180
+        //     .build(|| {
181
+        //         if ui.button(im_str!("Load.."), (0.0, 0.0)) {
182
+        //             ui.open_popup(im_str!("modal"));
183
+        //         }
184
+
185
+        //         ui.popup_modal(im_str!("modal")).build(|| {
186
+        //             ui.text("Content of my modal");
187
+        //             if ui.button(im_str!("OK"), (0.0, 0.0)) {
188
+        //                 ui.close_current_popup();
189
+        //             }
190
+        //         });
191
+
192
+        //         ui.separator();
193
+        //         if ui.collapsing_header(im_str!("script")).build() {
194
+        //             ui.text(im_str!("{}", seni_source));
195
+        //         }
196
+        //     });
197
+
198
+        unsafe {
199
+            gl.Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
200
+        }
201
+
202
+        square_renderer.render(viewport_width, viewport_height);
203
+        imgui_renderer.render(ui);
204
+
205
+        window.gl_swap_window();
206
+
207
+        ::std::thread::sleep(::std::time::Duration::new(0, 1_000_000_000u32 / 60));
208
+    }
209
+
210
+    Ok(())
211
+}

+ 67
- 0
src/matrix_util.rs View File

@@ -0,0 +1,67 @@
1
+// Copyright (C) 2019 Inderjit Gill
2
+
3
+// This program is free software: you can redistribute it and/or modify
4
+// it under the terms of the GNU General Public License as published by
5
+// the Free Software Foundation, either version 3 of the License, or
6
+// (at your option) any later version.
7
+
8
+// This program is distributed in the hope that it will be useful,
9
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+// GNU General Public License for more details.
12
+
13
+// You should have received a copy of the GNU General Public License
14
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
+
16
+pub fn create_identity_matrix() -> [f32; 16] {
17
+    let out: [f32; 16] = [
18
+        1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
19
+    ];
20
+    out
21
+}
22
+
23
+pub fn create_ortho_matrix(
24
+    left: f32,
25
+    right: f32,
26
+    bottom: f32,
27
+    top: f32,
28
+    near: f32,
29
+    far: f32,
30
+) -> [f32; 16] {
31
+    let lr = 1.0 / (left - right);
32
+    let bt = 1.0 / (bottom - top);
33
+    let nf = 1.0 / (near - far);
34
+
35
+    let out: [f32; 16] = [
36
+        -2.0 * lr,
37
+        0.0,
38
+        0.0,
39
+        0.0,
40
+        0.0,
41
+        -2.0 * bt,
42
+        0.0,
43
+        0.0,
44
+        0.0,
45
+        0.0,
46
+        2.0 * nf,
47
+        0.0,
48
+        (left + right) * lr,
49
+        (top + bottom) * bt,
50
+        (far + near) * nf,
51
+        1.0,
52
+    ];
53
+
54
+    out
55
+}
56
+
57
+pub fn matrix_scale(mat: &mut [f32; 16], x: f32, y: f32, z: f32) {
58
+    mat[0] *= x;
59
+    mat[5] *= y;
60
+    mat[10] *= z;
61
+}
62
+
63
+pub fn matrix_translate(mat: &mut [f32; 16], x: f32, y: f32, z: f32) {
64
+    mat[12] += x;
65
+    mat[13] += y;
66
+    mat[14] += z;
67
+}

+ 214
- 0
src/render_gl.rs View File

@@ -0,0 +1,214 @@
1
+use std::ffi;
2
+use std::fs;
3
+use std::io::Read;
4
+use std::path::Path;
5
+
6
+use gl;
7
+use std;
8
+use std::ffi::{CStr, CString};
9
+
10
+use crate::error::{Error, Result};
11
+
12
+pub struct Program {
13
+    gl: gl::Gl,
14
+    id: gl::types::GLuint,
15
+}
16
+
17
+impl Program {
18
+    pub fn from_path(gl: &gl::Gl, path: &Path, name: &str) -> Result<Program> {
19
+        const POSSIBLE_EXT: [&str; 2] = [".vert", ".frag"];
20
+
21
+        let resource_names = POSSIBLE_EXT
22
+            .iter()
23
+            .map(|file_extension| format!("{}{}", name, file_extension))
24
+            .collect::<Vec<String>>();
25
+
26
+        let shaders = resource_names
27
+            .iter()
28
+            .map(|resource_name| Shader::from_path(gl, path, resource_name))
29
+            .collect::<Result<Vec<Shader>>>()?;
30
+
31
+        Program::from_shaders(gl, &shaders[..]).map_err(|message| Error::LinkError {
32
+            name: name.into(),
33
+            message: message.to_string(),
34
+        })
35
+    }
36
+
37
+    pub fn from_shaders(gl: &gl::Gl, shaders: &[Shader]) -> Result<Program> {
38
+        let program_id = unsafe { gl.CreateProgram() };
39
+
40
+        for shader in shaders {
41
+            unsafe {
42
+                gl.AttachShader(program_id, shader.id());
43
+            }
44
+        }
45
+
46
+        unsafe {
47
+            gl.LinkProgram(program_id);
48
+        }
49
+
50
+        let mut success: gl::types::GLint = 1;
51
+        unsafe {
52
+            gl.GetProgramiv(program_id, gl::LINK_STATUS, &mut success);
53
+        }
54
+
55
+        if success == 0 {
56
+            let mut len: gl::types::GLint = 0;
57
+            unsafe {
58
+                gl.GetProgramiv(program_id, gl::INFO_LOG_LENGTH, &mut len);
59
+            }
60
+
61
+            let error = create_whitespace_cstring_with_len(len as usize);
62
+
63
+            unsafe {
64
+                gl.GetProgramInfoLog(
65
+                    program_id,
66
+                    len,
67
+                    std::ptr::null_mut(),
68
+                    error.as_ptr() as *mut gl::types::GLchar,
69
+                );
70
+            }
71
+
72
+            return Err(Error::Placeholder(error.to_string_lossy().into_owned()));
73
+        }
74
+
75
+        for shader in shaders {
76
+            unsafe {
77
+                gl.DetachShader(program_id, shader.id());
78
+            }
79
+        }
80
+
81
+        Ok(Program {
82
+            gl: gl.clone(),
83
+            id: program_id,
84
+        })
85
+    }
86
+
87
+    pub fn id(&self) -> gl::types::GLuint {
88
+        self.id
89
+    }
90
+
91
+    pub fn set_used(&self) {
92
+        unsafe {
93
+            self.gl.UseProgram(self.id);
94
+        }
95
+    }
96
+}
97
+
98
+impl Drop for Program {
99
+    fn drop(&mut self) {
100
+        unsafe {
101
+            self.gl.DeleteProgram(self.id);
102
+        }
103
+    }
104
+}
105
+
106
+pub struct Shader {
107
+    gl: gl::Gl,
108
+    id: gl::types::GLuint,
109
+}
110
+
111
+impl Shader {
112
+    pub fn from_path(gl: &gl::Gl, path: &Path, name: &str) -> Result<Shader> {
113
+        const POSSIBLE_EXT: [(&str, gl::types::GLenum); 2] =
114
+            [(".vert", gl::VERTEX_SHADER), (".frag", gl::FRAGMENT_SHADER)];
115
+
116
+        let shader_kind = POSSIBLE_EXT
117
+            .iter()
118
+            .find(|&&(file_extension, _)| name.ends_with(file_extension))
119
+            .map(|&(_, kind)| kind)
120
+            .ok_or_else(|| Error::CanNotDetermineShaderType { name: name.into() })?;
121
+
122
+        let source = load_cstring(path, name).map_err(|_e| Error::AssetLoad {
123
+            name: name.into(),
124
+            //            inner: e,
125
+        })?;
126
+
127
+        Shader::from_source(gl, &source, shader_kind).map_err(|message| Error::CompileError {
128
+            name: name.into(),
129
+            message: message.to_string(),
130
+        })
131
+    }
132
+
133
+    pub fn from_source(gl: &gl::Gl, source: &CStr, kind: gl::types::GLenum) -> Result<Shader> {
134
+        let id = shader_from_source(gl, source, kind)?;
135
+        Ok(Shader { gl: gl.clone(), id })
136
+    }
137
+
138
+    pub fn id(&self) -> gl::types::GLuint {
139
+        self.id
140
+    }
141
+}
142
+
143
+impl Drop for Shader {
144
+    fn drop(&mut self) {
145
+        unsafe {
146
+            self.gl.DeleteShader(self.id);
147
+        }
148
+    }
149
+}
150
+
151
+fn shader_from_source(
152
+    gl: &gl::Gl,
153
+    source: &CStr,
154
+    kind: gl::types::GLenum,
155
+) -> Result<gl::types::GLuint> {
156
+    let id = unsafe { gl.CreateShader(kind) };
157
+    unsafe {
158
+        gl.ShaderSource(id, 1, &source.as_ptr(), std::ptr::null());
159
+        gl.CompileShader(id);
160
+    }
161
+
162
+    let mut success: gl::types::GLint = 1;
163
+    unsafe {
164
+        gl.GetShaderiv(id, gl::COMPILE_STATUS, &mut success);
165
+    }
166
+
167
+    if success == 0 {
168
+        let mut len: gl::types::GLint = 0;
169
+        unsafe {
170
+            gl.GetShaderiv(id, gl::INFO_LOG_LENGTH, &mut len);
171
+        }
172
+
173
+        let error = create_whitespace_cstring_with_len(len as usize);
174
+
175
+        unsafe {
176
+            gl.GetShaderInfoLog(
177
+                id,
178
+                len,
179
+                std::ptr::null_mut(),
180
+                error.as_ptr() as *mut gl::types::GLchar,
181
+            );
182
+        }
183
+
184
+        return Err(Error::Placeholder(error.to_string_lossy().into_owned()));
185
+    }
186
+
187
+    Ok(id)
188
+}
189
+
190
+// todo: move these into gl_util or replace them
191
+
192
+fn create_whitespace_cstring_with_len(len: usize) -> CString {
193
+    // allocate buffer of correct size
194
+    let mut buffer: Vec<u8> = Vec::with_capacity(len + 1);
195
+    // fill it with len spaces
196
+    buffer.extend([b' '].iter().cycle().take(len));
197
+    // convert buffer to CString
198
+    unsafe { CString::from_vec_unchecked(buffer) }
199
+}
200
+
201
+fn load_cstring(path: &Path, resource_name: &str) -> Result<ffi::CString> {
202
+    let mut file = fs::File::open(path.join(resource_name))?;
203
+
204
+    // allocate buffer of the same size as file
205
+    let mut buffer: Vec<u8> = Vec::with_capacity(file.metadata()?.len() as usize + 1);
206
+    file.read_to_end(&mut buffer)?;
207
+
208
+    // check for nul byte
209
+    if buffer.iter().find(|i| **i == 0).is_some() {
210
+        return Err(Error::FileContainsNil);
211
+    }
212
+
213
+    Ok(unsafe { ffi::CString::from_vec_unchecked(buffer) })
214
+}

+ 314
- 0
src/render_imgui.rs View File

@@ -0,0 +1,314 @@
1
+// code from:
2
+// https://github.com/michaelfairley/rust-imgui-opengl-renderer.git
3
+// licensed under MIT/Apache 2.0
4
+//
5
+use std::mem;
6
+use std::path::Path;
7
+
8
+use gl::types::*;
9
+use imgui::{ImGui, Ui};
10
+
11
+use crate::error::Result;
12
+use crate::gl_util::return_param;
13
+use crate::render_gl;
14
+
15
+pub struct Renderer {
16
+    gl: gl::Gl,
17
+    program: render_gl::Program,
18
+    locs: Locs,
19
+    vbo: GLuint,
20
+    ebo: GLuint,
21
+    font_texture: GLuint,
22
+}
23
+
24
+struct Locs {
25
+    texture: GLint,
26
+    proj_mtx: GLint,
27
+    position: GLuint,
28
+    uv: GLuint,
29
+    color: GLuint,
30
+}
31
+
32
+impl Renderer {
33
+    pub fn new(gl: &gl::Gl, assets_path: &Path, imgui: &mut ImGui) -> Result<Renderer> {
34
+        let gl = gl.clone();
35
+
36
+        let program = render_gl::Program::from_path(&gl, assets_path, "shaders/imgui")?;
37
+
38
+        unsafe {
39
+            let locs = Locs {
40
+                texture: gl.GetUniformLocation(program.id(), c_str!("Texture").as_ptr() as _),
41
+                proj_mtx: gl.GetUniformLocation(program.id(), c_str!("ProjMtx").as_ptr() as _),
42
+                position: gl.GetAttribLocation(program.id(), c_str!("Position").as_ptr() as _) as _,
43
+                uv: gl.GetAttribLocation(program.id(), c_str!("UV").as_ptr() as _) as _,
44
+                color: gl.GetAttribLocation(program.id(), c_str!("Color").as_ptr() as _) as _,
45
+            };
46
+
47
+            let vbo = return_param(|x| gl.GenBuffers(1, x));
48
+            let ebo = return_param(|x| gl.GenBuffers(1, x));
49
+
50
+            let mut current_texture = 0;
51
+            gl.GetIntegerv(gl::TEXTURE_BINDING_2D, &mut current_texture);
52
+
53
+            let font_texture = return_param(|x| gl.GenTextures(1, x));
54
+            gl.BindTexture(gl::TEXTURE_2D, font_texture);
55
+            gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as _);
56
+            gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as _);
57
+            gl.PixelStorei(gl::UNPACK_ROW_LENGTH, 0);
58
+
59
+            imgui.prepare_texture(|handle| {
60
+                gl.TexImage2D(
61
+                    gl::TEXTURE_2D,
62
+                    0,
63
+                    gl::RGBA as _,
64
+                    handle.width as _,
65
+                    handle.height as _,
66
+                    0,
67
+                    gl::RGBA,
68
+                    gl::UNSIGNED_BYTE,
69
+                    handle.pixels.as_ptr() as _,
70
+                );
71
+            });
72
+
73
+            gl.BindTexture(gl::TEXTURE_2D, current_texture as _);
74
+
75
+            imgui.set_font_texture_id((font_texture as usize).into());
76
+
77
+            Ok(Self {
78
+                gl,
79
+                program,
80
+                locs,
81
+                vbo,
82
+                ebo,
83
+                font_texture,
84
+            })
85
+        }
86
+    }
87
+
88
+    pub fn render<'ui>(&self, ui: Ui<'ui>) {
89
+        use imgui::{ImDrawIdx, ImDrawVert};
90
+
91
+        let gl = &self.gl;
92
+
93
+        unsafe {
94
+            let last_active_texture = return_param(|x| gl.GetIntegerv(gl::ACTIVE_TEXTURE, x));
95
+            gl.ActiveTexture(gl::TEXTURE0);
96
+            let last_program = return_param(|x| gl.GetIntegerv(gl::CURRENT_PROGRAM, x));
97
+            let last_texture = return_param(|x| gl.GetIntegerv(gl::TEXTURE_BINDING_2D, x));
98
+            let last_sampler = if gl.BindSampler.is_loaded() {
99
+                return_param(|x| gl.GetIntegerv(gl::SAMPLER_BINDING, x))
100
+            } else {
101
+                0
102
+            };
103
+            let last_array_buffer = return_param(|x| gl.GetIntegerv(gl::ARRAY_BUFFER_BINDING, x));
104
+            let last_element_array_buffer =
105
+                return_param(|x| gl.GetIntegerv(gl::ELEMENT_ARRAY_BUFFER_BINDING, x));
106
+            let last_vertex_array = return_param(|x| gl.GetIntegerv(gl::VERTEX_ARRAY_BINDING, x));
107
+            let last_polygon_mode =
108
+                return_param(|x: &mut [GLint; 2]| gl.GetIntegerv(gl::POLYGON_MODE, x.as_mut_ptr()));
109
+            let last_viewport =
110
+                return_param(|x: &mut [GLint; 4]| gl.GetIntegerv(gl::VIEWPORT, x.as_mut_ptr()));
111
+            let last_scissor_box =
112
+                return_param(|x: &mut [GLint; 4]| gl.GetIntegerv(gl::SCISSOR_BOX, x.as_mut_ptr()));
113
+            let last_blend_src_rgb = return_param(|x| gl.GetIntegerv(gl::BLEND_SRC_RGB, x));
114
+            let last_blend_dst_rgb = return_param(|x| gl.GetIntegerv(gl::BLEND_DST_RGB, x));
115
+            let last_blend_src_alpha = return_param(|x| gl.GetIntegerv(gl::BLEND_SRC_ALPHA, x));
116
+            let last_blend_dst_alpha = return_param(|x| gl.GetIntegerv(gl::BLEND_DST_ALPHA, x));
117
+            let last_blend_equation_rgb =
118
+                return_param(|x| gl.GetIntegerv(gl::BLEND_EQUATION_RGB, x));
119
+            let last_blend_equation_alpha =
120
+                return_param(|x| gl.GetIntegerv(gl::BLEND_EQUATION_ALPHA, x));
121
+            let last_enable_blend = gl.IsEnabled(gl::BLEND) == gl::TRUE;
122
+            let last_enable_cull_face = gl.IsEnabled(gl::CULL_FACE) == gl::TRUE;
123
+            let last_enable_depth_test = gl.IsEnabled(gl::DEPTH_TEST) == gl::TRUE;
124
+            let last_enable_scissor_test = gl.IsEnabled(gl::SCISSOR_TEST) == gl::TRUE;
125
+
126
+            gl.Enable(gl::BLEND);
127
+            gl.BlendEquation(gl::FUNC_ADD);
128
+            gl.BlendFunc(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA);
129
+            gl.Disable(gl::CULL_FACE);
130
+            gl.Disable(gl::DEPTH_TEST);
131
+            gl.Enable(gl::SCISSOR_TEST);
132
+            gl.PolygonMode(gl::FRONT_AND_BACK, gl::FILL);
133
+
134
+            let (width, height) = ui.imgui().display_size();
135
+
136
+            let fb_width = width * ui.imgui().display_framebuffer_scale().0;
137
+            let fb_height = height * ui.imgui().display_framebuffer_scale().1;
138
+
139
+            gl.Viewport(0, 0, fb_width as _, fb_height as _);
140
+            let matrix = [
141
+                [2.0 / width as f32, 0.0, 0.0, 0.0],
142
+                [0.0, 2.0 / -(height as f32), 0.0, 0.0],
143
+                [0.0, 0.0, -1.0, 0.0],
144
+                [-1.0, 1.0, 0.0, 1.0],
145
+            ];
146
+            gl.UseProgram(self.program.id());
147
+            gl.Uniform1i(self.locs.texture, 0);
148
+            gl.UniformMatrix4fv(self.locs.proj_mtx, 1, gl::FALSE, matrix.as_ptr() as _);
149
+            if gl.BindSampler.is_loaded() {
150
+                gl.BindSampler(0, 0);
151
+            }
152
+
153
+            let vao = return_param(|x| gl.GenVertexArrays(1, x));
154
+            gl.BindVertexArray(vao);
155
+            gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo);
156
+            gl.EnableVertexAttribArray(self.locs.position);
157
+            gl.EnableVertexAttribArray(self.locs.uv);
158
+            gl.EnableVertexAttribArray(self.locs.color);
159
+            gl.VertexAttribPointer(
160
+                self.locs.position,
161
+                2,
162
+                gl::FLOAT,
163
+                gl::FALSE,
164
+                mem::size_of::<ImDrawVert>() as _,
165
+                field_offset::<ImDrawVert, _, _>(|v| &v.pos) as _,
166
+            );
167
+            gl.VertexAttribPointer(
168
+                self.locs.uv,
169
+                2,
170
+                gl::FLOAT,
171
+                gl::FALSE,
172
+                mem::size_of::<ImDrawVert>() as _,
173
+                field_offset::<ImDrawVert, _, _>(|v| &v.uv) as _,
174
+            );
175
+            gl.VertexAttribPointer(
176
+                self.locs.color,
177
+                4,
178
+                gl::UNSIGNED_BYTE,
179
+                gl::TRUE,
180
+                mem::size_of::<ImDrawVert>() as _,
181
+                field_offset::<ImDrawVert, _, _>(|v| &v.col) as _,
182
+            );
183
+
184
+            ui.render::<_, ()>(|ui, mut draw_data| {
185
+                draw_data.scale_clip_rects(ui.imgui().display_framebuffer_scale());
186
+
187
+                for draw_list in &draw_data {
188
+                    gl.BindBuffer(gl::ARRAY_BUFFER, self.vbo);
189
+                    gl.BufferData(
190
+                        gl::ARRAY_BUFFER,
191
+                        (draw_list.vtx_buffer.len() * mem::size_of::<ImDrawVert>()) as _,
192
+                        draw_list.vtx_buffer.as_ptr() as _,
193
+                        gl::STREAM_DRAW,
194
+                    );
195
+
196
+                    gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, self.ebo);
197
+                    gl.BufferData(
198
+                        gl::ELEMENT_ARRAY_BUFFER,
199
+                        (draw_list.idx_buffer.len() * mem::size_of::<ImDrawIdx>()) as _,
200
+                        draw_list.idx_buffer.as_ptr() as _,
201
+                        gl::STREAM_DRAW,
202
+                    );
203
+
204
+                    let mut idx_start = 0;
205
+                    for cmd in draw_list.cmd_buffer {
206
+                        if let Some(_callback) = cmd.user_callback {
207
+                            unimplemented!("Haven't implemented user callbacks yet");
208
+                        } else {
209
+                            gl.BindTexture(gl::TEXTURE_2D, cmd.texture_id as _);
210
+                            gl.Scissor(
211
+                                cmd.clip_rect.x as GLint,
212
+                                (fb_height - cmd.clip_rect.w) as GLint,
213
+                                (cmd.clip_rect.z - cmd.clip_rect.x) as GLint,
214
+                                (cmd.clip_rect.w - cmd.clip_rect.y) as GLint,
215
+                            );
216
+                            gl.DrawElements(
217
+                                gl::TRIANGLES,
218
+                                cmd.elem_count as _,
219
+                                if mem::size_of::<ImDrawIdx>() == 2 {
220
+                                    gl::UNSIGNED_SHORT
221
+                                } else {
222
+                                    gl::UNSIGNED_INT
223
+                                },
224
+                                idx_start as _,
225
+                            );
226
+                        }
227
+                        idx_start += cmd.elem_count * mem::size_of::<ImDrawIdx>() as u32;
228
+                    }
229
+                }
230
+
231
+                Ok(())
232
+            })
233
+            .unwrap();
234
+            gl.DeleteVertexArrays(1, &vao);
235
+
236
+            gl.UseProgram(last_program as _);
237
+            gl.BindTexture(gl::TEXTURE_2D, last_texture as _);
238
+            if gl.BindSampler.is_loaded() {
239
+                gl.BindSampler(0, last_sampler as _);
240
+            }
241
+            gl.ActiveTexture(last_active_texture as _);
242
+            gl.BindVertexArray(last_vertex_array as _);
243
+            gl.BindBuffer(gl::ARRAY_BUFFER, last_array_buffer as _);
244
+            gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, last_element_array_buffer as _);
245
+            gl.BlendEquationSeparate(last_blend_equation_rgb as _, last_blend_equation_alpha as _);
246
+            gl.BlendFuncSeparate(
247
+                last_blend_src_rgb as _,
248
+                last_blend_dst_rgb as _,
249
+                last_blend_src_alpha as _,
250
+                last_blend_dst_alpha as _,
251
+            );
252
+            if last_enable_blend {
253
+                gl.Enable(gl::BLEND)
254
+            } else {
255
+                gl.Disable(gl::BLEND)
256
+            };
257
+            if last_enable_cull_face {
258
+                gl.Enable(gl::CULL_FACE)
259
+            } else {
260
+                gl.Disable(gl::CULL_FACE)
261
+            };
262
+            if last_enable_depth_test {
263
+                gl.Enable(gl::DEPTH_TEST)
264
+            } else {
265
+                gl.Disable(gl::DEPTH_TEST)
266
+            };
267
+            if last_enable_scissor_test {
268
+                gl.Enable(gl::SCISSOR_TEST)
269
+            } else {
270
+                gl.Disable(gl::SCISSOR_TEST)
271
+            };
272
+            gl.PolygonMode(gl::FRONT_AND_BACK, last_polygon_mode[0] as _);
273
+            gl.Viewport(
274
+                last_viewport[0] as _,
275
+                last_viewport[1] as _,
276
+                last_viewport[2] as _,
277
+                last_viewport[3] as _,
278
+            );
279
+            gl.Scissor(
280
+                last_scissor_box[0] as _,
281
+                last_scissor_box[1] as _,
282
+                last_scissor_box[2] as _,
283
+                last_scissor_box[3] as _,
284
+            );
285
+        }
286
+    }
287
+}
288
+
289
+impl Drop for Renderer {
290
+    fn drop(&mut self) {
291
+        let gl = &self.gl;
292
+
293
+        unsafe {
294
+            gl.DeleteBuffers(1, &self.vbo);
295
+            gl.DeleteBuffers(1, &self.ebo);
296
+            gl.DeleteTextures(1, &self.font_texture);
297
+        }
298
+    }
299
+}
300
+
301
+fn field_offset<T, U, F: for<'a> FnOnce(&'a T) -> &'a U>(f: F) -> usize {
302
+    unsafe {
303
+        let instance = mem::uninitialized::<T>();
304
+
305
+        let offset = {
306
+            let field: &U = f(&instance);
307
+            field as *const U as usize - &instance as *const T as usize
308
+        };
309
+
310
+        mem::forget(instance);
311
+
312
+        offset
313
+    }
314
+}

+ 234
- 0
src/render_square.rs View File

@@ -0,0 +1,234 @@
1
+// Copyright (C) 2019 Inderjit Gill
2
+
3
+// This program is free software: you can redistribute it and/or modify
4
+// it under the terms of the GNU General Public License as published by
5
+// the Free Software Foundation, either version 3 of the License, or
6
+// (at your option) any later version.
7
+
8
+// This program is distributed in the hope that it will be useful,
9
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
+// GNU General Public License for more details.
12
+
13
+// You should have received a copy of the GNU General Public License
14
+// along with this program.  If not, see <https://www.gnu.org/licenses/>.
15
+
16
+use gl::types::*;
17
+
18
+use std::path::Path;
19
+
20
+use crate::error::Result;
21
+use crate::matrix_util;
22
+use crate::render_gl;
23
+
24
+struct GeometryLayout {
25
+    stride: usize,
26
+    position_num_elements: usize,
27
+    texture_num_elements: usize,
28
+}
29
+
30
+pub struct Renderer {
31
+    gl: gl::Gl,
32
+    program: render_gl::Program,
33
+
34
+    vao: GLuint,
35
+    vbo: GLuint,
36
+
37
+    texture: GLuint,
38
+
39
+    geometry: Vec<f32>,
40
+
41
+    locations: RendererLocations,
42
+}
43
+
44
+struct RendererLocations {
45
+    texture: GLint,
46
+    projection_mtx: GLint,
47
+    modelview_mtx: GLint,
48
+
49
+    position: GLuint,
50
+    uv: GLuint,
51
+}
52
+
53
+impl Drop for Renderer {
54
+    fn drop(&mut self) {
55
+        let gl = &self.gl;
56
+
57
+        // todo: should program be explicitly dropped or does that happen implicitly?
58
+        unsafe {
59
+            gl.DeleteBuffers(1, &self.vbo);
60
+            gl.DeleteTextures(1, &self.texture);
61
+            gl.DeleteVertexArrays(1, &self.vao);
62
+        }
63
+    }
64
+}
65
+
66
+impl Renderer {
67
+    pub fn new(gl: &gl::Gl, assets_path: &Path) -> Result<Renderer> {
68
+        let program = render_gl::Program::from_path(gl, assets_path, "shaders/blit")?;
69
+
70
+        let mut vao: gl::types::GLuint = 0;
71
+        let mut vbo: gl::types::GLuint = 0;
72
+
73
+        let location_texture: gl::types::GLint;
74
+        let location_projection_mtx: gl::types::GLint;
75
+        let location_modelview_mtx: gl::types::GLint;
76
+        let location_position: gl::types::GLuint;
77
+        let location_uv: gl::types::GLuint;
78
+
79
+        program.set_used();
80
+
81
+        unsafe {
82
+            // set up vertex array object
83
+            //
84
+            gl.GenVertexArrays(1, &mut vao);
85
+            gl.BindVertexArray(vao);
86
+
87
+            // set up vertex buffer object
88
+            //
89
+            gl.GenBuffers(1, &mut vbo);
90
+            gl.BindBuffer(gl::ARRAY_BUFFER, vbo);
91
+
92
+            location_texture = 0;
93
+            // location_texture =
94
+            //     gl.GetUniformLocation(program.id(), c_str!("myTextureSampler").as_ptr());
95
+            location_projection_mtx =
96
+                gl.GetUniformLocation(program.id(), c_str!("uPMatrix").as_ptr());
97
+            location_modelview_mtx =
98
+                gl.GetUniformLocation(program.id(), c_str!("uMVMatrix").as_ptr());
99
+
100
+            location_position =
101
+                gl.GetAttribLocation(program.id(), c_str!("Position").as_ptr()) as _;
102
+            location_uv = gl.GetAttribLocation(program.id(), c_str!("UV").as_ptr()) as _;
103
+
104
+            // gl.BindTexture(gl::TEXTURE_2D, texture);
105
+
106
+            // gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
107
+            // gl.TexParameteri(
108
+            //     gl::TEXTURE_2D,
109
+            //     gl::TEXTURE_MIN_FILTER,
110
+            //     gl::LINEAR_MIPMAP_LINEAR as i32,
111
+            // );
112
+            // gl.GenerateMipmap(gl::TEXTURE_2D);
113
+        }
114
+
115
+        let locations = RendererLocations {
116
+            texture: location_texture,
117
+            projection_mtx: location_projection_mtx,
118
+            modelview_mtx: location_modelview_mtx,
119
+            position: location_position,
120
+            uv: location_uv,
121
+        };
122
+
123
+        let projection_matrix =
124
+            matrix_util::create_ortho_matrix(0.0, 1000.0, 0.0, 1000.0, 10.0, -10.0);
125
+        let mut model_view_matrix = matrix_util::create_identity_matrix();
126
+        matrix_util::matrix_scale(&mut model_view_matrix, 800.0, 800.0, 1.0);
127
+        matrix_util::matrix_translate(&mut model_view_matrix, 100.0, 100.0, 0.0);
128
+
129
+        let layout = GeometryLayout {
130
+            stride: 4, // x, y, u, v
131
+            position_num_elements: 2,
132
+            texture_num_elements: 2,
133
+        };
134
+
135
+        unsafe {
136
+            gl.Uniform1i(locations.texture, 0);
137
+            gl.UniformMatrix4fv(
138
+                locations.projection_mtx,
139
+                1,
140
+                gl::FALSE,
141
+                projection_matrix.as_ptr(),
142
+            );
143
+            gl.UniformMatrix4fv(
144
+                locations.modelview_mtx,
145
+                1,
146
+                gl::FALSE,
147
+                model_view_matrix.as_ptr(),
148
+            );
149
+
150
+            gl.EnableVertexAttribArray(locations.position);
151
+            gl.EnableVertexAttribArray(locations.uv);
152
+
153
+            gl.VertexAttribPointer(
154
+                locations.position,
155
+                layout.position_num_elements as i32, // the number of components per generic vertex attribute
156
+                gl::FLOAT,                           // data type
157
+                gl::FALSE,                           // normalized (int-to-float conversion)
158
+                (layout.stride * std::mem::size_of::<f32>()) as gl::types::GLint, // stride
159
+                std::ptr::null(),                    // offset of the first component
160
+            );
161
+
162
+            let texture_offset = layout.position_num_elements;
163
+            gl.VertexAttribPointer(
164
+                locations.uv,
165
+                layout.texture_num_elements as i32, // the number of components per generic vertex attribute
166
+                gl::FLOAT,                          // data type
167
+                gl::FALSE,                          // normalized (int-to-float conversion)
168
+                (layout.stride * std::mem::size_of::<f32>()) as gl::types::GLint, // stride
169
+                (texture_offset * std::mem::size_of::<f32>()) as *const gl::types::GLvoid, // offset of the first component
170
+            );
171
+        }
172
+
173
+        // generate geometry
174
+        let geometry: Vec<f32> = vec![
175
+            0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 1.0, 1.0,
176
+        ];
177
+
178
+        unsafe {
179
+            gl.BufferData(
180
+                gl::ARRAY_BUFFER,                                                       // target
181
+                (geometry.len() * std::mem::size_of::<f32>()) as gl::types::GLsizeiptr, // size of data in bytes
182
+                geometry.as_ptr() as *const gl::types::GLvoid, // pointer to data
183
+                gl::STATIC_DRAW,                               // usage
184
+            );
185
+        }
186
+
187
+        let texture: GLuint = 0;
188
+
189
+        Ok(Renderer {
190
+            gl: gl.clone(),
191
+            program,
192
+            vao,
193
+            vbo,
194
+            texture,
195
+            geometry,
196
+            locations,
197
+        })
198
+    }
199
+
200
+    pub fn render(&self, viewport_width: usize, viewport_height: usize) {
201
+        let gl = &self.gl;
202
+
203
+        let projection_matrix = matrix_util::create_ortho_matrix(
204
+            0.0,
205
+            viewport_width as f32,
206
+            0.0,
207
+            viewport_height as f32,
208
+            10.0,
209
+            -10.0,
210
+        );
211
+
212
+        unsafe {
213
+            gl.ActiveTexture(gl::TEXTURE0);
214
+            gl.BindTexture(gl::TEXTURE_2D, self.texture);
215
+
216
+            gl.UseProgram(self.program.id());
217
+
218
+            gl.BindVertexArray(self.vao);
219
+
220
+            gl.UniformMatrix4fv(
221
+                self.locations.projection_mtx,
222
+                1,
223
+                gl::FALSE,
224
+                projection_matrix.as_ptr(),
225
+            );
226
+
227
+            gl.DrawArrays(
228
+                gl::TRIANGLE_STRIP,         // mode
229
+                0,                          // starting index in the enabled arrays
230
+                self.geometry.len() as i32, // number of indices to be rendered
231
+            );
232
+        }
233
+    }
234
+}

Loading…
Cancel
Save