Skip to main content

ruckus/
graphics.rs

1use crate::opengl::*;
2use crate::buffers::*;
3use crate::sys::*;
4use std::ops::*;
5use num::Num;
6use nalgebra_glm as glm;
7
8use std::collections::HashMap;
9
10#[allow(unused_imports)]
11use image::{open, DynamicImage};
12
13use image::imageops::{flip_vertical, flip_vertical_in};
14
15pub trait NumDefault: Num + Default + Copy{}
16impl <T: Num + Default + Copy> NumDefault for T {}
17
18#[derive(Debug, Copy, Clone)]
19pub enum TextureFormat {
20    Alpha = gl::ALPHA as isize,
21    Rgb = gl::RGB as isize,
22    Rgba = gl::RGBA as isize,
23}
24
25pub struct Texture {
26    id: u32,
27    unit: u32,
28    pub size: glm::TVec2<u32>
29}
30
31impl Texture {
32
33    pub fn from_file(filename: &str) -> Result<Texture, String> {
34
35        let im = match image::open(filename) {
36            Ok(d) => d,
37            Err(e) => return Err(format!("Error loading file: {} :: ImageError: {}", filename, e))
38        };
39        let im = flip_vertical(&im);
40        let layout = im.sample_layout();
41
42        let format = match layout.channels {
43            3 => TextureFormat::Rgb,
44            4 => TextureFormat::Rgba,
45            _ => TextureFormat::Alpha
46        };
47        let (w, h) = (im.width(), im.height());
48
49        let t = Texture::from_memory(im.into_raw(), w, h, format);
50        Ok(t)
51    }
52
53    pub fn from_memory(data: Vec<u8>, w: u32, h: u32, format: TextureFormat) -> Texture {
54        let gl = opengl();
55    
56        let tid = gl_gen_texture();
57        unsafe {
58            opengl().BindTexture(gl::TEXTURE_2D, tid);
59            gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
60            gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
61            gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
62            gl.TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
63        }
64        
65        
66        unsafe {
67            gl.TexImage2D(gl::TEXTURE_2D, 0, 
68                gl::RGBA as i32, w as i32, h as i32,
69                0, gl::RGBA as u32, gl::UNSIGNED_BYTE, data.as_ptr() as *const _
70            );
71            gl.GenerateMipmap(gl::TEXTURE_2D);
72        };
73
74        Texture { 
75            id: tid, 
76            unit: gl::TEXTURE0, size: glm::vec2(w,h)
77        }
78    }
79
80    pub fn new_blank() -> Self {
81        let texture_data = vec![255,255,255,255];
82        Texture::from_memory(texture_data.into(), 1, 1, TextureFormat::Rgba)
83    }
84
85    pub fn set_alignment(alignment: i32) {
86        unsafe { opengl().PixelStorei(gl::UNPACK_ALIGNMENT, alignment) }
87    } 
88
89    pub fn unit(&self) -> u32 { self.unit }
90    pub fn set_unit(&mut self, unit_num: u32) {
91        self.unit = gl::TEXTURE0 + unit_num;
92    }
93
94    pub fn write(&self, offset: glm::TVec2<i32>, w: i32, h: i32, format: TextureFormat, dtype: DataType, data: Vec<u8>) {
95        self.apply();
96        unsafe {
97            opengl().TexSubImage2D(gl::TEXTURE_2D, 0, offset.x, offset.y, w, h, format as u32, dtype as u32, data.as_ptr() as *const _);
98        }
99    }
100
101    pub fn id(&self) -> u32 { self.id }
102
103    pub fn apply(&self) {
104        unsafe { 
105            opengl().ActiveTexture(self.unit());
106            opengl().BindTexture(gl::TEXTURE_2D, self.id);
107        }
108    }
109}
110
111
112// TODO :: Consolidate these with #ifdefs
113const FRAG_TEMPLATE_VARS: &'static [u8] = b"
114#version 330
115out vec4 FragColor;
116in vec2 TexCoord;
117in vec4 Color;
118in vec3 FragPos;
119
120uniform sampler2D u_texture;
121\0";
122
123const FRAG_TEMPLATE_MAIN: &'static [u8] = b"
124void main()
125{
126    FragColor = effect(Color, u_texture, TexCoord, FragPos);
127}
128\0";
129
130const VERT_TEMPLATE_DECLS: &'static [u8] = b"#version 330
131    layout(location = 0) in vec3 l_pos;
132    layout(location = 1) in vec2 l_texCoords;
133    layout(location = 2) in vec4 l_color;
134
135    out vec2 TexCoord;
136    out vec4 Color;
137    out vec3 FragPos;
138
139    uniform mat4 u_model;
140    uniform mat4 u_view;
141    uniform mat4 u_projection;
142\0";
143
144
145const VERT_TEMPLATE_MAIN: &'static [u8] = b"
146void main()
147{
148    TexCoord = l_texCoords;
149    Color = l_color;
150
151    FragPos = vec3(u_model * vec4(l_pos, 1.0));
152
153    gl_Position = position(u_projection * u_view * u_model, vec4(l_pos.x, l_pos.y, l_pos.z, 1.0));
154}
155\0";
156
157const VERT_TEMPLATE_DECLS_INSTANCED: &'static [u8] = b"#version 330
158    layout(location = 0) in vec3 l_pos;
159    layout(location = 1) in vec2 l_texCoords;
160    layout(location = 2) in vec4 l_color;
161    layout(location = 3) in mat4 l_matrixMVP;
162    layout(location = 7) in mat4 u_modelMatrix;
163
164    out vec2 TexCoord;
165    out vec4 Color;
166    out vec3 FragPos;
167    mat4 matrixMVP = l_matrixMVP;
168\0";
169
170const DEFAULT_VERT: &'static [u8] = b"#version 330
171    layout(location = 0) in vec3 l_pos;
172    layout(location = 1) in vec2 l_texCoords;
173    layout(location = 2) in vec4 l_color;
174
175    out vec2 TexCoord;
176    out vec4 Color;
177    out vec3 FragPos;
178
179    uniform mat4 u_model;
180    uniform mat4 u_view;
181    uniform mat4 u_projection;
182
183    void main()
184    {
185        TexCoord = l_texCoords;
186        Color = l_color;
187
188        mat4 mvp = u_projection * u_view * u_model;
189        gl_Position = mvp * vec4(l_pos, 1.0);
190    }
191\0";
192
193const DEFAULT_INSTANCED_VERT: &'static [u8] = b"#version 330
194layout(location = 0) in vec3 l_pos;
195layout(location = 1) in vec2 l_texCoords;
196layout(location = 2) in vec4 l_color;
197layout(location = 3) in mat4 l_matrixMVP;
198layout(location = 7) in mat4 u_modelMatrix;
199
200out vec2 TexCoord;
201out vec4 Color;
202out vec3 FragPos;
203
204void main()
205{
206    TexCoord = l_texCoords;
207    Color = l_color;
208
209    FragPos = vec3(u_modelMatrix * vec4(l_pos, 1.0));
210
211    gl_Position = l_matrixMVP * vec4(l_pos, 1.0);
212}
213\0";
214
215const DEFAULT_FRAG: &'static [u8] = b"#version 330
216out vec4 FragColor;
217
218in vec4 Color;
219in vec2 TexCoord;
220uniform sampler2D u_texture;
221
222void main()
223{
224	FragColor = texture(u_texture, TexCoord) * Color;
225}
226\0";
227
228#[derive(Debug, Copy, Clone)]
229pub enum ShaderType {
230    Vertex = gl::VERTEX_SHADER as isize,
231    Fragment = gl::FRAGMENT_SHADER as isize
232}
233
234pub struct Shader {
235    id: u32,
236    uniform_locations: std::collections::HashMap<String, i32>
237}
238
239impl Shader {
240
241    pub fn from_file(vert_filename: &str, frag_filename: &str) -> Result<Self, String> {
242        let vshader = gl_compile_shader_from_file(vert_filename, ShaderType::Vertex)?;
243        let fshader = gl_compile_shader_from_file(frag_filename, ShaderType::Fragment)?;
244        
245        let shaderid = gl_create_shader_program(vshader, fshader)?;
246        let uniforms = gl_get_active_uniforms(shaderid);
247        Ok(Shader::new(shaderid, uniforms))
248    }
249    
250    pub fn from_memory<T>(vert: T, frag: T) -> Result<Self, String> where T: Into<Vec<u8>> {
251        let vshader = gl_compile_shader(vert.into().as_slice(), ShaderType::Vertex)?;
252        let fshader = gl_compile_shader(frag.into().as_slice(), ShaderType::Fragment)?;
253
254        let id = gl_create_shader_program(vshader, fshader)?;
255        let uniforms = gl_get_active_uniforms(id);
256        Ok(Shader::new(id, uniforms))
257    }
258
259    pub fn from_template(position: &[u8], effect: &[u8]) -> Result<Self, String> {
260        let vert_full = Self::concat_shader_sources(VERT_TEMPLATE_DECLS, position, VERT_TEMPLATE_MAIN);
261        let frag_full = Self::concat_shader_sources(FRAG_TEMPLATE_VARS, effect, FRAG_TEMPLATE_MAIN);
262        let s = Self::from_memory(vert_full.as_slice(), frag_full.as_slice())?;
263
264        Ok(s)
265    }
266
267    pub fn from_vert_template(position: &[u8]) -> Result<Self, String> {
268        let vert_full = Shader::concat_shader_sources(VERT_TEMPLATE_DECLS, position, VERT_TEMPLATE_MAIN);
269        let frag_full = DEFAULT_FRAG;
270        let s = Shader::from_memory(vert_full.as_slice(), frag_full)?;
271        Ok(s)
272    }
273    pub fn from_frag_template(effect: &[u8]) -> Result<Self, String> {
274        let vert_full = DEFAULT_VERT;
275        let frag_full = Shader::concat_shader_sources(FRAG_TEMPLATE_VARS, effect, FRAG_TEMPLATE_MAIN);
276        let s = Shader::from_memory(vert_full, frag_full.as_slice())?;
277        Ok(s)
278    }
279    
280    pub fn from_template_instanced<'a, T>(position: T, effect:T) -> Result<Self, String> where T: Into<Option<Vec<u8>>> {
281        let (position, effect) = (position.into(), effect.into());
282        assert!(position.is_some() || effect.is_some(), " Both of the arguments for function from_template_instanced() are None. Please pass at least 1 value with Some");
283        let vert_full = match position {
284            Some(s) => Shader::concat_shader_sources(VERT_TEMPLATE_DECLS_INSTANCED, s.as_slice(), VERT_TEMPLATE_MAIN),
285            None => DEFAULT_INSTANCED_VERT.into()
286        };
287        let frag_full = match effect {
288            Some(s) => Shader::concat_shader_sources(FRAG_TEMPLATE_VARS, s.as_slice(), FRAG_TEMPLATE_MAIN),
289            None => DEFAULT_FRAG.into()
290        };
291        let s = Shader::from_memory(vert_full, frag_full)?;
292        Ok(s)
293    }
294
295    pub fn set_uniform_4f(&self, name: &str, floats: (f32, f32, f32, f32)) {
296        self.apply();
297        gl_set_uniform_4f(self.uniform_locations[name], floats);
298    }
299
300    pub fn set_uniform_3f(&self, name: &str, floats: (f32, f32, f32)) {
301        self.apply();
302        gl_set_uniform_3f(self.uniform_locations[name], floats);
303    }
304
305    pub fn set_uniform_2f(&self, name: &str, floats: (f32, f32)) {
306        self.apply();
307        gl_set_uniform_2f(self.uniform_locations[name], floats);
308    }
309
310    pub fn set_uniform_f(&self, name: &str, n: f32) {
311        self.apply();
312        gl_set_uniform_f(self.uniform_locations[name], n);
313    }
314
315    pub fn set_uniform_i(&self, name: &str, n: i32) {
316        self.apply();
317        gl_set_uniform_i(self.uniform_locations[name], n);
318    }
319
320    pub fn set_uniform_matrix(&self, name: &str, mat: &glm::Mat4) {
321        self.set_uniform_matrix_xpose(name, mat, false)
322        
323    }
324
325    pub fn set_uniform_matrix_xpose(&self, name: &str, mat: &glm::Mat4, transpose: bool) {
326        self.apply();
327        gl_set_uniform_matrix_xpose(self.uniform_locations[name], glm::value_ptr(mat), transpose)
328    }
329    
330    pub fn apply(&self) {
331        unsafe { opengl().UseProgram(self.id) }
332    }
333    
334    pub fn default_instanced() -> Self {
335        Self::from_memory(DEFAULT_INSTANCED_VERT, DEFAULT_FRAG).unwrap()
336    }
337
338    fn new(id: u32, uniform_locations: HashMap<String, i32>) -> Self {
339        Shader { id, uniform_locations }
340    }
341
342    fn concat_shader_sources<'a, T>(a: T, b: T, c: T) -> Vec<u8> where T: Into<Vec<u8>>{
343        let su = String::from_utf8;
344        let (a, b, c) = (a.into(), b.into(), c.into());
345        let (a, b, c) = (su(a).unwrap(), su(b).unwrap(), su(c).unwrap());
346        let mut result = String::from(a);
347        result.push_str(&b);
348        result.push_str(&c); 
349        result.as_bytes().to_vec()
350    }
351}
352
353impl Default for Shader {
354    fn default() -> Self {
355        Self::from_memory(DEFAULT_VERT, DEFAULT_FRAG).unwrap() 
356    }
357}
358
359pub struct Mesh {
360    pub transform: Transform,
361    pub buffer: VertexBuffer,
362    pub indices: Option<ElementBuffer>,
363    pub texture: Option<Texture>,
364    pub shader: Option<Shader>,
365}
366
367impl Mesh {
368    pub fn new(buffer: VertexBuffer) -> Self {
369        Self {
370            transform: Transform::default(),
371            buffer: buffer,
372            indices: None,
373            texture: None,
374            shader: None,
375        }
376    }
377
378    pub fn new_quad(usage: DrawUsage) -> Self {
379        Self {
380            transform: Transform::default(),
381            buffer: VertexBuffer::new(&Quad::default_verts(), usage),
382            indices: Some(ElementBuffer::new_quad(6)),
383            texture: None,
384            shader: None,
385
386        }
387    }
388
389    pub fn indices(&self) -> &ElementBuffer {
390        self.indices.as_ref().expect("No Elementbuffer for Mesh")
391    }
392
393    pub fn texture(&self) -> &Texture {
394        self.texture.as_ref().expect("No Texture for Mesh")
395    }
396
397    pub fn shader(&self) -> &Shader {
398        self.shader.as_ref().expect("No Shader for Mesh")
399    }
400}
401
402pub struct RenderTexture {
403    pub frame_buffer: FrameBuffer,
404    pub render_buffer: RenderBuffer,
405    pub texture: Texture
406}
407
408impl RenderTexture {
409    pub fn new(width: u32, height: u32) -> Result<Self, String> {
410        let rt = RenderTexture {
411            frame_buffer: FrameBuffer::new(),
412            render_buffer: RenderBuffer::new(width as i32, height as i32),
413            texture: Texture::from_memory(vec![], width, height, TextureFormat::Rgb)
414        };
415        rt.frame_buffer.attach_texture(&rt.texture);
416        rt.frame_buffer.attach_render_buffer(&rt.render_buffer);
417
418        unsafe {
419            if opengl().CheckFramebufferStatus(gl::FRAMEBUFFER) != gl::FRAMEBUFFER_COMPLETE {
420                return Err("OpenGL :: Could not create RenderTexture. Framebuffer is not complete".into());
421            }
422        }
423        FrameBuffer::unbind();
424        Ok(rt)
425    }
426}
427
428impl Drop for RenderTexture {
429    fn drop(&mut self) { }
430}
431
432impl Drop for Mesh {
433    fn drop(&mut self) { }
434}
435
436impl Drop for Shader {
437    
438    fn drop(&mut self) {
439        unsafe { opengl().DeleteProgram(self.id) }
440    }
441}
442
443impl Drop for Texture {
444    
445    fn drop(&mut self) { 
446        unsafe { opengl().DeleteTextures(1, &self.id) }
447    }
448}
449