├── glow_wrap ├── .gitignore ├── Cargo.toml └── src │ ├── key.rs │ └── lib.rs ├── Cargo.png ├── assets ├── rust.png ├── rust-white.png ├── FiraSans-Regular.ttf ├── glow.html └── LICENSE ├── .travis.yml ├── .gitignore ├── src ├── error.rs ├── lib.rs ├── shader_uniforms.rs ├── draw_state.rs ├── glyph_cache.rs ├── shader_utils.rs ├── texture.rs └── back_end.rs ├── README.md ├── LICENSE ├── examples ├── hello_world.rs ├── text_test.rs ├── image_test.rs ├── texture_swap.rs ├── texture_src_rect.rs ├── colored_image_test.rs ├── async_test.rs ├── draw_state.rs ├── texture_wrap.rs ├── nested_clipping.rs ├── glow-hello_world.rs └── glow.rs └── Cargo.toml /glow_wrap/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/opengl_graphics/HEAD/Cargo.png -------------------------------------------------------------------------------- /assets/rust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/opengl_graphics/HEAD/assets/rust.png -------------------------------------------------------------------------------- /assets/rust-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/opengl_graphics/HEAD/assets/rust-white.png -------------------------------------------------------------------------------- /assets/FiraSans-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/opengl_graphics/HEAD/assets/FiraSans-Regular.ttf -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | before_install: 3 | - wget https://www.libsdl.org/release/SDL2-2.0.5.tar.gz -O SDL2-2.0.5.tar.gz 4 | - tar -xzvf SDL2-2.0.5.tar.gz 5 | install: 6 | - (cd SDL2-2.0.5 && ./configure && make && sudo make install) 7 | script: 8 | - cargo test -v 9 | - cargo doc -v 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | *# 4 | *.o 5 | *.so 6 | *.swp 7 | *.dylib 8 | *.dSYM 9 | *.dll 10 | *.rlib 11 | *.dummy 12 | *.exe 13 | *-test 14 | /bin/main 15 | /bin/test-internal 16 | /bin/test-external 17 | /doc/ 18 | /target/ 19 | /build/ 20 | /.rust/ 21 | rusti.sh 22 | watch.sh 23 | /examples/** 24 | !/examples/*.rs 25 | !/examples/assets/ 26 | 27 | Cargo.lock 28 | -------------------------------------------------------------------------------- /assets/glow.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | //! Errors 2 | 3 | use std::fmt; 4 | 5 | /// An enum to represent various possible run-time errors that may occur. 6 | #[derive(Debug)] 7 | pub enum Error { 8 | /// An error happened with I/O. 9 | IoError(::std::io::Error), 10 | } 11 | 12 | impl fmt::Display for Error { 13 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 14 | fmt::Debug::fmt(self, f) 15 | } 16 | } 17 | 18 | impl From<::std::io::Error> for Error { 19 | fn from(err: ::std::io::Error) -> Error { 20 | Error::IoError(err) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /glow_wrap/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "piston2d-glow_wrap" 3 | version = "0.2.0" 4 | edition = "2021" 5 | description = "Glow wrapping code for Piston OpenGL backend" 6 | license = "MIT" 7 | keywords = ["glow", "graphics", "2d", "piston"] 8 | repository = "https://github.com/PistonDevelopers/opengl_graphics.git" 9 | homepage = "https://github.com/PistonDevelopers/opengl_graphics" 10 | documentation = "https://docs.rs/piston2d-glow_wrap" 11 | 12 | [lib] 13 | name = "glow_wrap" 14 | 15 | [dependencies] 16 | glow = "0.16.0" 17 | 18 | [target.'cfg(target_arch = "wasm32")'.dependencies] 19 | slotmap = "1.0.7" 20 | web_sys = { version = "~0.3.60", package = "web-sys", features = [ 21 | "WebGlUniformLocation", 22 | ] } 23 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![deny(missing_docs)] 2 | #![deny(missing_copy_implementations)] 3 | 4 | //! An OpenGL back-end for Rust-Graphics 5 | 6 | #[cfg(not(feature = "glow"))] 7 | extern crate gl; 8 | #[cfg(feature = "glow")] 9 | extern crate glow_wrap as gl; 10 | extern crate graphics; 11 | extern crate image; 12 | extern crate shader_version; 13 | extern crate shaders_graphics2d as shaders; 14 | extern crate texture as texture_lib; 15 | extern crate viewport; 16 | 17 | pub use crate::back_end::{Colored, GlGraphics, Textured, TexturedColor}; 18 | pub use crate::texture::Texture; 19 | pub use shader_version::glsl::GLSL; 20 | pub use shader_version::{OpenGL, Shaders}; 21 | pub use texture_lib::*; 22 | 23 | pub mod error; 24 | pub mod shader_uniforms; 25 | pub mod shader_utils; 26 | 27 | /// Glyph cache implementation for OpenGL backend. 28 | pub type GlyphCache<'a> = graphics::glyph_cache::rusttype::GlyphCache<'a, (), Texture>; 29 | 30 | mod back_end; 31 | mod draw_state; 32 | mod texture; 33 | 34 | #[cfg(feature = "glow")] 35 | pub use gl::set_context; 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # opengl_graphics [![Build Status](https://travis-ci.org/PistonDevelopers/opengl_graphics.svg)](https://travis-ci.org/PistonDevelopers/opengl_graphics) [![Crates.io](https://img.shields.io/crates/v/piston2d-opengl_graphics.svg)](https://crates.io/crates/piston2d-opengl_graphics) [![Crates.io](https://img.shields.io/crates/l/piston2d-opengl_graphics.svg)](https://github.com/PistonDevelopers/opengl_graphics/blob/master/LICENSE) 2 | 3 | An OpenGL 2D back-end for the Piston game engine 4 | 5 | Maintainers: @Potpourri, @bvssvni, @mitchmindtree 6 | 7 | ### Important! 8 | 9 | OpenGL needs to load function pointers before use. 10 | If you are experiencing strange error messages like "X not loaded" this is likely the case. 11 | This is done automatically for you in the SDL2 and GLFW window back-ends for Piston. 12 | To do this manually, see the README in [gl-rs](https://github.com/bjz/gl-rs) 13 | 14 | ## Dependencies 15 | 16 | ![dependencies](./Cargo.png) 17 | 18 | [How to contribute](https://github.com/PistonDevelopers/piston/blob/master/CONTRIBUTING.md) 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 PistonDevelopers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /examples/hello_world.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate opengl_graphics; 3 | extern crate piston; 4 | extern crate sdl2_window; 5 | 6 | use opengl_graphics::GlyphCache; 7 | use opengl_graphics::*; 8 | use piston::event_loop::*; 9 | use piston::input::*; 10 | use piston::window::WindowSettings; 11 | use sdl2_window::Sdl2Window; 12 | 13 | fn main() { 14 | let opengl = OpenGL::V3_2; 15 | let mut window: Sdl2Window = WindowSettings::new("opengl_graphics: hello_world", [200, 200]) 16 | .exit_on_esc(true) 17 | .graphics_api(opengl) 18 | .build() 19 | .unwrap(); 20 | 21 | let mut glyphs = 22 | GlyphCache::new("assets/FiraSans-Regular.ttf", (), TextureSettings::new()).unwrap(); 23 | let mut gl = GlGraphics::new(opengl); 24 | let mut events = Events::new(EventSettings::new()); 25 | while let Some(e) = events.next(&mut window) { 26 | use graphics::*; 27 | 28 | if let Some(args) = e.render_args() { 29 | gl.draw(args.viewport(), |c, g| { 30 | let transform = c.transform.trans(10.0, 100.0); 31 | 32 | clear([0.0, 0.0, 0.0, 1.0], g); 33 | text::Text::new_color([0.0, 1.0, 0.0, 1.0], 32) 34 | .draw("Hello world!", &mut glyphs, &c.draw_state, transform, g) 35 | .unwrap(); 36 | }); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/text_test.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate opengl_graphics; 3 | extern crate piston; 4 | extern crate sdl2_window; 5 | 6 | use opengl_graphics::GlyphCache; 7 | use opengl_graphics::*; 8 | use piston::event_loop::*; 9 | use piston::input::*; 10 | use piston::window::WindowSettings; 11 | use sdl2_window::Sdl2Window; 12 | 13 | fn main() { 14 | let opengl = OpenGL::V3_2; 15 | let size = [500, 300]; 16 | let window: &mut Sdl2Window = &mut WindowSettings::new("opengl_graphics: text_test", size) 17 | .exit_on_esc(true) 18 | .graphics_api(opengl) 19 | .build() 20 | .unwrap(); 21 | 22 | let mut glyph_cache = 23 | GlyphCache::new("assets/FiraSans-Regular.ttf", (), TextureSettings::new()).unwrap(); 24 | 25 | let mut gl = GlGraphics::new(opengl); 26 | let mut events = Events::new(EventSettings::new().lazy(true)); 27 | while let Some(e) = events.next(window) { 28 | if let Some(args) = e.render_args() { 29 | gl.draw(args.viewport(), |c, g| { 30 | use graphics::*; 31 | 32 | clear([1.0; 4], g); 33 | text::Text::new_color([0.0, 0.5, 0.0, 1.0], 32) 34 | .draw( 35 | "Hello opengl_graphics!", 36 | &mut glyph_cache, 37 | &DrawState::default(), 38 | c.transform.trans(10.0, 100.0), 39 | g, 40 | ) 41 | .unwrap(); 42 | }); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /examples/image_test.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate opengl_graphics; 3 | extern crate piston; 4 | extern crate sdl2_window; 5 | 6 | use opengl_graphics::*; 7 | use piston::event_loop::*; 8 | use piston::input::*; 9 | use piston::window::WindowSettings; 10 | use sdl2_window::Sdl2Window; 11 | use std::path::Path; 12 | 13 | fn main() { 14 | let opengl = OpenGL::V3_2; 15 | let mut window: Sdl2Window = WindowSettings::new("opengl_graphics: image_test", [300, 300]) 16 | .exit_on_esc(true) 17 | .graphics_api(opengl) 18 | .build() 19 | .unwrap(); 20 | 21 | let rust_logo = 22 | Texture::from_path(Path::new("./assets/rust.png"), &TextureSettings::new()).unwrap(); 23 | let mut gl = GlGraphics::new(opengl); 24 | let mut events = Events::new(EventSettings::new()); 25 | while let Some(e) = events.next(&mut window) { 26 | use graphics::*; 27 | 28 | if let Some(args) = e.render_args() { 29 | gl.draw(args.viewport(), |c, g| { 30 | let transform = c.transform.trans(100.0, 100.0); 31 | 32 | clear([1.0; 4], g); 33 | Rectangle::new([1.0, 0.0, 0.0, 1.0]).draw( 34 | [0.0, 0.0, 100.0, 100.0], 35 | &c.draw_state, 36 | c.transform, 37 | g, 38 | ); 39 | Rectangle::new([0.0, 1.0, 0.0, 0.3]).draw( 40 | [50.0, 50.0, 100.0, 100.0], 41 | &c.draw_state, 42 | c.transform, 43 | g, 44 | ); 45 | image(&rust_logo, transform, g); 46 | }); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /glow_wrap/src/key.rs: -------------------------------------------------------------------------------- 1 | use crate::types::GLuint; 2 | 3 | pub trait ConvertKey { 4 | fn from_key(key: GLuint) -> Self; 5 | fn to_key(&self) -> GLuint; 6 | } 7 | 8 | #[cfg(not(target_arch = "wasm32"))] 9 | mod native { 10 | use crate::types::GLuint; 11 | use std::num::NonZeroU32; 12 | macro_rules! convert_key { 13 | ($type:path) => { 14 | impl super::ConvertKey for $type { 15 | fn from_key(key: GLuint) -> Self { 16 | unsafe { Self(NonZeroU32::new_unchecked(key)) } 17 | } 18 | fn to_key(&self) -> GLuint { 19 | self.0.get() 20 | } 21 | } 22 | }; 23 | } 24 | convert_key!(glow::NativeBuffer); 25 | convert_key!(glow::NativeProgram); 26 | convert_key!(glow::NativeShader); 27 | convert_key!(glow::NativeTexture); 28 | convert_key!(glow::NativeVertexArray); 29 | 30 | impl super::ConvertKey for glow::NativeUniformLocation { 31 | fn from_key(key: crate::types::GLuint) -> Self { 32 | Self(key) 33 | } 34 | fn to_key(&self) -> crate::types::GLuint { 35 | self.0 36 | } 37 | } 38 | } 39 | 40 | #[cfg(target_arch = "wasm32")] 41 | mod web { 42 | use crate::types::GLuint; 43 | use slotmap::Key; 44 | macro_rules! convert_key { 45 | ($type:path) => { 46 | impl super::ConvertKey for $type { 47 | fn from_key(key: GLuint) -> Self { 48 | Self::from(slotmap::KeyData::from_ffi((1 as u64) << 32 | key as u64)) 49 | } 50 | fn to_key(&self) -> GLuint { 51 | self.data().as_ffi() as GLuint 52 | } 53 | } 54 | }; 55 | } 56 | 57 | convert_key!(glow::WebBufferKey); 58 | convert_key!(glow::WebProgramKey); 59 | convert_key!(glow::WebShaderKey); 60 | convert_key!(glow::WebTextureKey); 61 | convert_key!(glow::WebVertexArrayKey); 62 | convert_key!(crate::WebGlUniformLocationKey); 63 | } 64 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | 3 | name = "piston2d-opengl_graphics" 4 | version = "0.89.0" 5 | edition = "2018" 6 | authors = [ 7 | "bvssvni ", 8 | "Coeuvre ", 9 | "gmorenz", 10 | "leonkunert ", 11 | "mitchmindtree ", 12 | "Christiandh", 13 | "Apointos", 14 | "ccgn", 15 | "reem", 16 | "TyOverby " 17 | ] 18 | keywords = ["opengl", "graphics", "2d", "piston"] 19 | description = "An OpenGL 2D back-end for the Piston game engine" 20 | license = "MIT" 21 | readme = "README.md" 22 | repository = "https://github.com/PistonDevelopers/opengl_graphics.git" 23 | homepage = "https://github.com/PistonDevelopers/opengl_graphics" 24 | documentation = "https://docs.rs/piston2d-opengl_graphics" 25 | exclude = ["assets/*", "Cargo.png"] 26 | 27 | [lib] 28 | name = "opengl_graphics" 29 | path = "src/lib.rs" 30 | 31 | [features] 32 | default = ["gl"] 33 | webgl = ["gl"] 34 | glow = ["piston2d-glow_wrap", "dep:glow"] 35 | 36 | [dependencies] 37 | image = {version = "0.25.2", features = ["png"]} 38 | 39 | piston-shaders_graphics2d = "0.4.0" 40 | piston-texture = "0.9.0" 41 | piston-viewport = "1.0.2" 42 | shader_version = "0.7.0" 43 | fnv = "1.0.7" 44 | 45 | piston2d-glow_wrap = { path = "glow_wrap", version = "0.2.0", optional = true} 46 | gl = {version = "0.14.0", optional = true} 47 | 48 | 49 | [dependencies.piston2d-graphics] 50 | version = "0.45.0" 51 | features = ["glyph_cache_rusttype"] 52 | 53 | [target.'cfg(not(any(target_arch = "wasm32")))'.dev-dependencies] 54 | piston = {version = "1.0.0", features = ["async"]} 55 | pistoncore-sdl2_window = "0.70.0" 56 | rand = "0.8.5" 57 | tokio = {version = "1.40.0", features = ["full"]} 58 | futures = "0.3.31" 59 | glutin = "0.32.2" 60 | winit = "0.30.9" 61 | glutin-winit = "0.5.0" 62 | glow = "0.16.0" 63 | 64 | [target.'cfg(target_arch = "wasm32")'.dependencies] 65 | glow = {version = "0.16.0", optional = true} 66 | web-sys = { version = "0.3.72", features = [ 67 | "HtmlCanvasElement", 68 | "WebGl2RenderingContext", 69 | "Window", 70 | ] } 71 | wasm-bindgen = { version = "0.2.95" } 72 | 73 | [build-dependencies] 74 | khronos_api = "3.1.0" 75 | -------------------------------------------------------------------------------- /examples/texture_swap.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate image as im; 3 | extern crate opengl_graphics; 4 | extern crate piston; 5 | extern crate rand; 6 | extern crate sdl2_window; 7 | 8 | use graphics::*; 9 | use opengl_graphics::*; 10 | use piston::event_loop::*; 11 | use piston::input::*; 12 | use piston::window::*; 13 | use sdl2_window::Sdl2Window; 14 | 15 | fn main() { 16 | let opengl = OpenGL::V3_2; 17 | let texture_count = 1024; 18 | let frames = 200; 19 | let size = 32.0; 20 | 21 | let mut window: Sdl2Window = WindowSettings::new("texture_swap", [1024; 2]) 22 | .graphics_api(opengl) 23 | .build() 24 | .unwrap(); 25 | 26 | let textures = { 27 | (0..texture_count) 28 | .map(|_| { 29 | let mut img = im::ImageBuffer::new(2, 2); 30 | for y in 0..2 { 31 | for x in 0..2 { 32 | img.put_pixel( 33 | x, 34 | y, 35 | im::Rgba([rand::random(), rand::random(), rand::random(), 255]), 36 | ); 37 | } 38 | } 39 | Texture::from_image(&img, &TextureSettings::new()) 40 | }) 41 | .collect::>() 42 | }; 43 | 44 | let mut positions = (0..texture_count) 45 | .map(|_| (rand::random(), rand::random())) 46 | .collect::>(); 47 | 48 | let mut counter = 0; 49 | let mut gl = GlGraphics::new(opengl); 50 | let mut events = Events::new(EventSettings::new().bench_mode(true)); 51 | while let Some(e) = events.next(&mut window) { 52 | if e.render_args().is_some() { 53 | counter += 1; 54 | if counter > frames { 55 | break; 56 | } 57 | } 58 | if let Some(args) = e.render_args() { 59 | gl.draw(args.viewport(), |c, g| { 60 | clear([0.0, 0.0, 0.0, 1.0], g); 61 | for p in &mut positions { 62 | let (x, y) = *p; 63 | *p = ( 64 | x + (rand::random::() - 0.5) * 0.01, 65 | y + (rand::random::() - 0.5) * 0.01, 66 | ); 67 | } 68 | for i in 0..texture_count { 69 | let p = positions[i]; 70 | image( 71 | &textures[i], 72 | c.transform.trans(p.0 * 1024.0, p.1 * 1024.0).zoom(size), 73 | g, 74 | ); 75 | } 76 | }); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /examples/texture_src_rect.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate image as im; 3 | extern crate opengl_graphics; 4 | extern crate piston; 5 | extern crate rand; 6 | extern crate sdl2_window; 7 | 8 | use graphics::*; 9 | use opengl_graphics::*; 10 | use piston::event_loop::*; 11 | use piston::input::*; 12 | use piston::window::*; 13 | use sdl2_window::Sdl2Window; 14 | 15 | fn main() { 16 | let opengl = OpenGL::V3_2; 17 | let texture_count = 1024; 18 | let frames = 200; 19 | let size = 32.0; 20 | let texture_width = 512; 21 | let texture_height = 512; 22 | 23 | let mut window: Sdl2Window = WindowSettings::new("texture_swap_draw_many", [1024; 2]) 24 | .graphics_api(opengl) 25 | .build() 26 | .unwrap(); 27 | 28 | let texture = { 29 | let mut img = im::ImageBuffer::new(texture_width, texture_height); 30 | for y in 0..texture_height { 31 | for x in 0..texture_width { 32 | img.put_pixel( 33 | x, 34 | y, 35 | im::Rgba([rand::random(), rand::random(), rand::random(), 255]), 36 | ); 37 | } 38 | } 39 | Texture::from_image(&img, &TextureSettings::new()) 40 | }; 41 | 42 | let src_rects = (0..texture_count) 43 | .map(|_| { 44 | [ 45 | (rand::random::() * (texture_width as f64 - 2.0)).floor(), 46 | (rand::random::() * (texture_height as f64 - 2.0)).floor(), 47 | 2.0, 48 | 2.0, 49 | ] 50 | }) 51 | .collect::>(); 52 | let mut positions = (0..texture_count) 53 | .map(|_| (rand::random(), rand::random())) 54 | .collect::>(); 55 | 56 | let mut counter = 0; 57 | let mut gl = GlGraphics::new(opengl); 58 | let mut events = Events::new(EventSettings::new().bench_mode(true)); 59 | while let Some(e) = events.next(&mut window) { 60 | if e.render_args().is_some() { 61 | counter += 1; 62 | if counter > frames { 63 | break; 64 | } 65 | } 66 | if let Some(args) = e.render_args() { 67 | gl.draw(args.viewport(), |c, g| { 68 | clear([0.0, 0.0, 0.0, 1.0], g); 69 | for p in &mut positions { 70 | let (x, y) = *p; 71 | *p = ( 72 | x + (rand::random::() - 0.5) * 0.01, 73 | y + (rand::random::() - 0.5) * 0.01, 74 | ); 75 | } 76 | for i in 0..texture_count { 77 | let p = positions[i]; 78 | image::Image::new().src_rect(src_rects[i]).draw( 79 | &texture, 80 | &c.draw_state, 81 | c.transform.trans(p.0 * 1024.0, p.1 * 1024.0).zoom(size), 82 | g, 83 | ); 84 | } 85 | }); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /examples/colored_image_test.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate opengl_graphics; 3 | extern crate piston; 4 | extern crate sdl2_window; 5 | 6 | use opengl_graphics::*; 7 | use piston::event_loop::*; 8 | use piston::input::*; 9 | use piston::window::WindowSettings; 10 | use sdl2_window::Sdl2Window; 11 | use std::path::Path; 12 | 13 | fn main() { 14 | let opengl = OpenGL::V3_2; 15 | let mut window: Sdl2Window = 16 | WindowSettings::new("opengl_graphics: colored_image_test", [300, 300]) 17 | .exit_on_esc(true) 18 | .graphics_api(opengl) 19 | .build() 20 | .unwrap(); 21 | 22 | let rust_logo = Texture::from_path( 23 | Path::new("./assets/rust-white.png"), 24 | &TextureSettings::new(), 25 | ) 26 | .unwrap(); 27 | let mut gl = GlGraphics::new(opengl); 28 | let mut events = Events::new(EventSettings::new()); 29 | while let Some(e) = events.next(&mut window) { 30 | use graphics::*; 31 | 32 | if let Some(args) = e.render_args() { 33 | gl.draw(args.viewport(), |c, g| { 34 | use graphics::triangulation::{tx, ty}; 35 | 36 | let transform = c.transform.trans(0.0, 0.0); 37 | 38 | let tr = |p: [f64; 2]| [tx(transform, p[0], p[1]), ty(transform, p[0], p[1])]; 39 | 40 | clear([1.0; 4], g); 41 | Rectangle::new([1.0, 0.0, 0.0, 1.0]).draw( 42 | [0.0, 0.0, 100.0, 100.0], 43 | &c.draw_state, 44 | c.transform, 45 | g, 46 | ); 47 | Rectangle::new([0.0, 1.0, 0.0, 0.3]).draw( 48 | [50.0, 50.0, 100.0, 100.0], 49 | &c.draw_state, 50 | c.transform, 51 | g, 52 | ); 53 | g.tri_list_uv_c(&c.draw_state, &rust_logo, |f| { 54 | (f)( 55 | &[ 56 | tr([0.0, 0.0]), 57 | tr([300.0, 0.0]), 58 | tr([0.0, 300.0]), 59 | tr([300.0, 0.0]), 60 | tr([0.0, 300.0]), 61 | tr([300.0, 300.0]), 62 | ], 63 | &[ 64 | [0.0, 0.0], 65 | [1.0, 0.0], 66 | [0.0, 1.0], 67 | [1.0, 0.0], 68 | [0.0, 1.0], 69 | [1.0, 1.0], 70 | ], 71 | &[ 72 | [1.0, 0.0, 0.0, 1.0], 73 | [0.0, 1.0, 0.0, 1.0], 74 | [0.0, 0.0, 1.0, 1.0], 75 | [0.0, 00.0, 0.0, 1.0], 76 | [0.0, 00.0, 0.0, 1.0], 77 | [0.0, 00.0, 0.0, 1.0], 78 | ], 79 | ) 80 | }); 81 | }); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /examples/async_test.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate opengl_graphics; 3 | extern crate piston; 4 | extern crate sdl2_window; 5 | 6 | use opengl_graphics::*; 7 | use piston::event_loop::*; 8 | use piston::input::*; 9 | use piston::window::Window; 10 | use piston::window::WindowSettings; 11 | use piston::Size; 12 | use sdl2_window::Sdl2Window; 13 | 14 | use futures::stream::FuturesUnordered; 15 | use futures::StreamExt; 16 | 17 | #[tokio::main] 18 | async fn main() { 19 | let opengl = OpenGL::V3_2; 20 | let mut window: Sdl2Window = WindowSettings::new("opengl_graphics: async test", [300, 300]) 21 | .exit_on_esc(true) 22 | .graphics_api(opengl) 23 | .build() 24 | .unwrap(); 25 | 26 | let mut gl = GlGraphics::new(opengl); 27 | let mut events = Events::new(EventSettings::new()); 28 | events.set_ups(10); 29 | 30 | let n = 100_000; 31 | let size = window.size(); 32 | let mut ps: Vec<[f64; 2]> = (0..n) 33 | .map(|_| { 34 | [ 35 | rand::random::() * size.width, 36 | rand::random::() * size.height, 37 | ] 38 | }) 39 | .collect(); 40 | let mut task: FuturesUnordered<_> = FuturesUnordered::new(); 41 | let mut cursor = [0.0, 0.0]; 42 | let mut time = 0.0; 43 | while let Some(e) = events.async_next(&mut window).await { 44 | use graphics::*; 45 | 46 | while let Some(pos) = task.next().await { 47 | ps.push(pos); 48 | } 49 | 50 | if let Some(args) = e.render_args() { 51 | gl.draw(args.viewport(), |c, g| { 52 | clear([1.0; 4], g); 53 | for pos in &ps { 54 | Rectangle::new([1.0, 0.0, 0.0, 0.05]).draw( 55 | [pos[0], pos[1], 2.0, 2.0], 56 | &c.draw_state, 57 | c.transform, 58 | g, 59 | ); 60 | } 61 | }); 62 | } 63 | 64 | if let Some(args) = e.update_args() { 65 | time += args.dt; 66 | let size = window.size(); 67 | task.extend( 68 | ps.into_iter() 69 | .map(|pos| update(time, args.dt, cursor, pos, size)), 70 | ); 71 | ps = vec![]; 72 | } 73 | 74 | if let Some(pos) = e.mouse_cursor_args() { 75 | cursor = pos; 76 | } 77 | } 78 | } 79 | 80 | async fn update(time: f64, dt: f64, cursor: [f64; 2], pos: [f64; 2], size: Size) -> [f64; 2] { 81 | let old_pos = pos; 82 | let mut pos = pos; 83 | 84 | let dir = [pos[0] - cursor[0], pos[1] - cursor[1]]; 85 | let dir_len = (dir[0] * dir[0] + dir[1] * dir[1]).sqrt(); 86 | let dir_len = if dir_len <= 0.001 { 1.0 } else { dir_len }; 87 | let trigger = if (0.1 * (dir_len + time)).sin() < 0.1 { 88 | 0.1 89 | } else { 90 | -0.1 91 | }; 92 | let speed = 10.0; 93 | 94 | pos[0] += speed 95 | * dt 96 | * (2.0 * rand::random::() - 1.0 + trigger * rand::random::() * dir[0] / dir_len); 97 | pos[1] += speed 98 | * dt 99 | * (2.0 * rand::random::() - 1.0 + trigger * rand::random::() * dir[1] / dir_len); 100 | if pos[0] <= 0.0 || pos[0] >= size.width { 101 | pos[0] = old_pos[0]; 102 | } 103 | if pos[1] <= 0.0 || pos[1] >= size.height { 104 | pos[1] = old_pos[1]; 105 | } 106 | pos 107 | } 108 | -------------------------------------------------------------------------------- /examples/draw_state.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate opengl_graphics; 3 | extern crate piston; 4 | extern crate sdl2_window; 5 | 6 | use graphics::draw_state::Blend; 7 | use opengl_graphics::{GlGraphics, Texture, TextureSettings}; 8 | use piston::event_loop::*; 9 | use piston::input::*; 10 | use piston::window::WindowSettings; 11 | use sdl2_window::{OpenGL, Sdl2Window}; 12 | use std::path::Path; 13 | 14 | fn main() { 15 | println!("Press A to change blending"); 16 | println!("Press S to change clip inside/out"); 17 | 18 | let opengl = OpenGL::V3_2; 19 | let (w, h) = (640, 480); 20 | let mut window: Sdl2Window = WindowSettings::new("opengl_graphics: draw_state", [w, h]) 21 | .exit_on_esc(true) 22 | .graphics_api(opengl) 23 | .build() 24 | .unwrap(); 25 | 26 | let mut clip_inside = true; 27 | let blends = [ 28 | Blend::Alpha, 29 | Blend::Add, 30 | Blend::Invert, 31 | Blend::Multiply, 32 | Blend::Lighter, 33 | ]; 34 | let mut blend = 0; 35 | let rust_logo = 36 | Texture::from_path(Path::new("./assets/rust.png"), &TextureSettings::new()).unwrap(); 37 | let mut gl = GlGraphics::new(opengl); 38 | let mut events = Events::new(EventSettings::new().lazy(true)); 39 | while let Some(e) = events.next(&mut window) { 40 | if let Some(args) = e.render_args() { 41 | use graphics::*; 42 | 43 | gl.draw(args.viewport(), |c, g| { 44 | clear([0.8, 0.8, 0.8, 1.0], g); 45 | Rectangle::new([1.0, 0.0, 0.0, 1.0]).draw( 46 | [0.0, 0.0, 100.0, 100.0], 47 | &c.draw_state, 48 | c.transform, 49 | g, 50 | ); 51 | 52 | let draw_state = c.draw_state.blend(blends[blend]); 53 | Rectangle::new([0.5, 1.0, 0.0, 0.3]).draw( 54 | [50.0, 50.0, 100.0, 100.0], 55 | &draw_state, 56 | c.transform, 57 | g, 58 | ); 59 | 60 | let transform = c.transform.trans(100.0, 100.0); 61 | // Clip rectangle from upper left corner. 62 | let clipped = c.draw_state.scissor([100, 100, 100, 100]); 63 | Image::new().draw(&rust_logo, &clipped, transform, g); 64 | 65 | let transform = c.transform.trans(200.0, 200.0); 66 | Ellipse::new([1.0, 0.0, 0.0, 1.0]).draw( 67 | [0.0, 0.0, 50.0, 50.0], 68 | &DrawState::new_clip(), 69 | transform, 70 | g, 71 | ); 72 | Image::new().draw( 73 | &rust_logo, 74 | &if clip_inside { 75 | DrawState::new_inside() 76 | } else { 77 | DrawState::new_outside() 78 | }, 79 | transform, 80 | g, 81 | ); 82 | }); 83 | } 84 | 85 | if let Some(Button::Keyboard(Key::A)) = e.press_args() { 86 | blend = (blend + 1) % blends.len(); 87 | println!("Changed blending to {:?}", blends[blend]); 88 | } 89 | 90 | if let Some(Button::Keyboard(Key::S)) = e.press_args() { 91 | clip_inside = !clip_inside; 92 | if clip_inside { 93 | println!("Changed to clip inside"); 94 | } else { 95 | println!("Changed to clip outside"); 96 | } 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /examples/texture_wrap.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate image as im; 3 | extern crate opengl_graphics; 4 | extern crate piston; 5 | extern crate sdl2_window; 6 | 7 | use opengl_graphics::{GlGraphics, Texture, TextureSettings, Wrap}; 8 | use piston::event_loop::*; 9 | use piston::input::*; 10 | use piston::window::WindowSettings; 11 | use sdl2_window::{OpenGL, Sdl2Window}; 12 | use std::path::Path; 13 | 14 | fn main() { 15 | println!("Press U to change the texture wrap mode for the u coordinate"); 16 | println!("Press V to change the texture wrap mode for the v coordinate"); 17 | 18 | let opengl = OpenGL::V3_2; 19 | let (w, h) = (640, 480); 20 | let mut window: Sdl2Window = WindowSettings::new("opengl_graphics: texture_wrap", [w, h]) 21 | .exit_on_esc(true) 22 | .graphics_api(opengl) 23 | .build() 24 | .unwrap(); 25 | 26 | // Set up wrap modes 27 | let wrap_modes = [ 28 | Wrap::ClampToEdge, 29 | Wrap::ClampToBorder, 30 | Wrap::Repeat, 31 | Wrap::MirroredRepeat, 32 | ]; 33 | let mut ix_u = 0; 34 | let mut ix_v = 0; 35 | let mut texture_settings = TextureSettings::new(); 36 | texture_settings.set_border_color([0.0, 0.0, 0.0, 1.0]); 37 | 38 | // Set up texture 39 | let path = Path::new("./assets/rust.png"); 40 | let img = match im::open(path) { 41 | Ok(img) => img, 42 | Err(e) => { 43 | panic!("Could not load '{:?}': {:?}", path.file_name().unwrap(), e); 44 | } 45 | }; 46 | let img = match img { 47 | im::DynamicImage::ImageRgba8(img) => img, 48 | x => x.to_rgba8(), 49 | }; 50 | let mut rust_logo = Texture::from_image(&img, &texture_settings); 51 | 52 | let mut gl = GlGraphics::new(opengl); 53 | let mut events = Events::new(EventSettings::new().lazy(true)); 54 | while let Some(e) = events.next(&mut window) { 55 | if let Some(args) = e.render_args() { 56 | use graphics::*; 57 | gl.draw(args.viewport(), |_c, g| { 58 | clear([1.0; 4], g); 59 | let points = [[0.5, 0.5], [-0.5, 0.5], [-0.5, -0.5], [0.5, -0.5]]; 60 | // (0, 1, 2) and (0, 2, 3) 61 | let uvs = [ 62 | [4.0, 0.0], 63 | [0.0, 0.0], 64 | [0.0, 4.0], 65 | [4.0, 0.0], 66 | [0.0, 4.0], 67 | [4.0, 4.0], 68 | ]; 69 | let mut verts = [[0.0, 0.0]; 6]; 70 | let indices_points: [usize; 6] = [0, 1, 2, 0, 2, 3]; 71 | for (ixv, &ixp) in (0..6).zip((&indices_points).iter()) { 72 | verts[ixv] = points[ixp]; 73 | } 74 | g.tri_list_uv(&DrawState::new_alpha(), &[1.0; 4], &rust_logo, |f| { 75 | f(&verts, &uvs) 76 | }); 77 | }); 78 | } 79 | 80 | if let Some(Button::Keyboard(Key::U)) = e.press_args() { 81 | ix_u = (ix_u + 1) % wrap_modes.len(); 82 | texture_settings.set_wrap_u(wrap_modes[ix_u]); 83 | rust_logo = Texture::from_image(&img, &texture_settings); 84 | println!( 85 | "Changed texture wrap mode for u coordinate to: {:?}", 86 | wrap_modes[ix_u] 87 | ); 88 | } 89 | 90 | if let Some(Button::Keyboard(Key::V)) = e.press_args() { 91 | ix_v = (ix_v + 1) % wrap_modes.len(); 92 | texture_settings.set_wrap_v(wrap_modes[ix_v]); 93 | rust_logo = Texture::from_image(&img, &texture_settings); 94 | println!( 95 | "Changed texture wrap mode for v coordinate to: {:?}", 96 | wrap_modes[ix_v] 97 | ); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /examples/nested_clipping.rs: -------------------------------------------------------------------------------- 1 | extern crate graphics; 2 | extern crate opengl_graphics; 3 | extern crate piston; 4 | extern crate sdl2_window; 5 | 6 | use graphics::draw_state::{Blend, Stencil}; 7 | use graphics::DrawState; 8 | use opengl_graphics::GlGraphics; 9 | use piston::event_loop::*; 10 | use piston::input::*; 11 | use piston::window::WindowSettings; 12 | use sdl2_window::{OpenGL, Sdl2Window}; 13 | 14 | fn main() { 15 | let opengl = OpenGL::V3_2; 16 | let (w, h) = (640, 480); 17 | let mut window: Sdl2Window = WindowSettings::new("opengl_graphics: nested_clipping", [w, h]) 18 | .exit_on_esc(true) 19 | .graphics_api(opengl) 20 | .build() 21 | .unwrap(); 22 | 23 | let mut gl = GlGraphics::new(opengl); 24 | let mut events = Events::new(EventSettings::new().lazy(true)); 25 | 26 | let increment = DrawState::new_increment(); 27 | let inside_level1 = DrawState { 28 | blend: Some(Blend::Alpha), 29 | stencil: Some(Stencil::Inside(1)), 30 | scissor: None, 31 | }; 32 | let inside_level2 = DrawState { 33 | blend: Some(Blend::Alpha), 34 | stencil: Some(Stencil::Inside(2)), 35 | scissor: None, 36 | }; 37 | let inside_level3 = DrawState { 38 | blend: Some(Blend::Alpha), 39 | stencil: Some(Stencil::Inside(3)), 40 | scissor: None, 41 | }; 42 | let mut clip = true; 43 | while let Some(e) = events.next(&mut window) { 44 | if let Some(args) = e.render_args() { 45 | use graphics::*; 46 | 47 | gl.draw(args.viewport(), |c, g| { 48 | clear([0.8, 0.8, 0.8, 1.0], g); 49 | 50 | if clip { 51 | Rectangle::new([1.0; 4]).draw( 52 | [10.0, 10.0, 200.0, 200.0], 53 | &increment, 54 | c.transform, 55 | g, 56 | ); 57 | Rectangle::new([1.0, 0.0, 0.0, 1.0]).draw( 58 | [10.0, 10.0, 200.0, 200.0], 59 | &inside_level1, 60 | c.transform, 61 | g, 62 | ); 63 | 64 | Rectangle::new([1.0; 4]).draw( 65 | [100.0, 100.0, 200.0, 200.0], 66 | &increment, 67 | c.transform, 68 | g, 69 | ); 70 | Rectangle::new([0.0, 0.0, 1.0, 1.0]).draw( 71 | [100.0, 100.0, 200.0, 200.0], 72 | &inside_level2, 73 | c.transform, 74 | g, 75 | ); 76 | 77 | Rectangle::new([1.0; 4]).draw( 78 | [100.0, 100.0, 200.0, 200.0], 79 | &increment, 80 | c.transform, 81 | g, 82 | ); 83 | Rectangle::new([0.0, 1.0, 0.0, 1.0]).draw( 84 | [50.0, 50.0, 200.0, 100.0], 85 | &inside_level3, 86 | c.transform, 87 | g, 88 | ); 89 | } else { 90 | Rectangle::new([1.0, 0.0, 0.0, 1.0]).draw( 91 | [10.0, 10.0, 200.0, 200.0], 92 | &c.draw_state, 93 | c.transform, 94 | g, 95 | ); 96 | 97 | Rectangle::new([0.0, 0.0, 1.0, 1.0]).draw( 98 | [100.0, 100.0, 200.0, 200.0], 99 | &c.draw_state, 100 | c.transform, 101 | g, 102 | ); 103 | 104 | Rectangle::new([0.0, 1.0, 0.0, 1.0]).draw( 105 | [50.0, 50.0, 200.0, 100.0], 106 | &c.draw_state, 107 | c.transform, 108 | g, 109 | ); 110 | } 111 | }); 112 | } 113 | if e.press_args().is_some() { 114 | clip = !clip; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /assets/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 2 | with Reserved Font Name Fira Sans. 3 | 4 | Copyright (c) 2014, Mozilla Foundation https://mozilla.org/ 5 | with Reserved Font Name Fira Mono. 6 | 7 | Copyright (c) 2014, Telefonica S.A. 8 | 9 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 10 | This license is copied below, and is also available with a FAQ at: 11 | http://scripts.sil.org/OFL 12 | 13 | 14 | ----------------------------------------------------------- 15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 16 | ----------------------------------------------------------- 17 | 18 | PREAMBLE 19 | The goals of the Open Font License (OFL) are to stimulate worldwide 20 | development of collaborative font projects, to support the font creation 21 | efforts of academic and linguistic communities, and to provide a free and 22 | open framework in which fonts may be shared and improved in partnership 23 | with others. 24 | 25 | The OFL allows the licensed fonts to be used, studied, modified and 26 | redistributed freely as long as they are not sold by themselves. The 27 | fonts, including any derivative works, can be bundled, embedded, 28 | redistributed and/or sold with any software provided that any reserved 29 | names are not used by derivative works. The fonts and derivatives, 30 | however, cannot be released under any other type of license. The 31 | requirement for fonts to remain under this license does not apply 32 | to any document created using the fonts or their derivatives. 33 | 34 | DEFINITIONS 35 | "Font Software" refers to the set of files released by the Copyright 36 | Holder(s) under this license and clearly marked as such. This may 37 | include source files, build scripts and documentation. 38 | 39 | "Reserved Font Name" refers to any names specified as such after the 40 | copyright statement(s). 41 | 42 | "Original Version" refers to the collection of Font Software components as 43 | distributed by the Copyright Holder(s). 44 | 45 | "Modified Version" refers to any derivative made by adding to, deleting, 46 | or substituting -- in part or in whole -- any of the components of the 47 | Original Version, by changing formats or by porting the Font Software to a 48 | new environment. 49 | 50 | "Author" refers to any designer, engineer, programmer, technical 51 | writer or other person who contributed to the Font Software. 52 | 53 | PERMISSION & CONDITIONS 54 | Permission is hereby granted, free of charge, to any person obtaining 55 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 56 | redistribute, and sell modified and unmodified copies of the Font 57 | Software, subject to the following conditions: 58 | 59 | 1) Neither the Font Software nor any of its individual components, 60 | in Original or Modified Versions, may be sold by itself. 61 | 62 | 2) Original or Modified Versions of the Font Software may be bundled, 63 | redistributed and/or sold with any software, provided that each copy 64 | contains the above copyright notice and this license. These can be 65 | included either as stand-alone text files, human-readable headers or 66 | in the appropriate machine-readable metadata fields within text or 67 | binary files as long as those fields can be easily viewed by the user. 68 | 69 | 3) No Modified Version of the Font Software may use the Reserved Font 70 | Name(s) unless explicit written permission is granted by the corresponding 71 | Copyright Holder. This restriction only applies to the primary font name as 72 | presented to the users. 73 | 74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 75 | Software shall not be used to promote, endorse or advertise any 76 | Modified Version, except to acknowledge the contribution(s) of the 77 | Copyright Holder(s) and the Author(s) or with their explicit written 78 | permission. 79 | 80 | 5) The Font Software, modified or unmodified, in part or in whole, 81 | must be distributed entirely under this license, and must not be 82 | distributed under any other license. The requirement for fonts to 83 | remain under this license does not apply to any document created 84 | using the Font Software. 85 | 86 | TERMINATION 87 | This license becomes null and void if any of the above conditions are 88 | not met. 89 | 90 | DISCLAIMER 91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 99 | OTHER DEALINGS IN THE FONT SOFTWARE. 100 | -------------------------------------------------------------------------------- /src/shader_uniforms.rs: -------------------------------------------------------------------------------- 1 | //! Types and methods for setting shader uniforms 2 | 3 | // External crates. 4 | use gl; 5 | use gl::types::{GLboolean, GLint}; 6 | use std::ffi::CString; 7 | use std::marker::PhantomData; 8 | 9 | // Local crate. 10 | use crate::back_end::GlGraphics; 11 | 12 | /// Describes a shader uniform of a given type. 13 | #[derive(Clone, Copy)] 14 | pub struct ShaderUniform { 15 | location: GLint, 16 | phantom: PhantomData, 17 | } 18 | 19 | /// Shader uniform type 20 | /// 21 | /// For now a small subset 22 | pub trait UniformType {} 23 | 24 | /// Shader uniform float 25 | #[derive(Clone, Copy)] 26 | pub struct SUFloat {} 27 | impl UniformType for SUFloat {} 28 | 29 | /// Shader uniform integer 30 | #[derive(Clone, Copy)] 31 | pub struct SUInt {} 32 | impl UniformType for SUInt {} 33 | 34 | /// Shader uniform vector of size 2 35 | /// Vector elements are floats 36 | #[derive(Clone, Copy)] 37 | pub struct SUVec2 {} 38 | impl UniformType for SUVec2 {} 39 | 40 | /// Shader uniform vector of size 3 41 | /// Vector elements are floats 42 | #[derive(Clone, Copy)] 43 | pub struct SUVec3 {} 44 | impl UniformType for SUVec3 {} 45 | 46 | /// Shader uniform vector of size 4 47 | /// Vector elements are floats 48 | #[derive(Clone, Copy)] 49 | pub struct SUVec4 {} 50 | impl UniformType for SUVec4 {} 51 | 52 | /// Shader uniform 2x2 matrix 53 | /// Matrix elements are floats 54 | #[derive(Clone, Copy)] 55 | pub struct SUMat2x2 {} 56 | impl UniformType for SUMat2x2 {} 57 | 58 | /// Shader uniform 3x3 matrix 59 | /// Matrix elements are floats 60 | #[derive(Clone, Copy)] 61 | pub struct SUMat3x3 {} 62 | impl UniformType for SUMat3x3 {} 63 | 64 | /// Shader uniform 4x4 matrix 65 | /// Matrix elements are floats 66 | #[derive(Clone, Copy)] 67 | pub struct SUMat4x4 {} 68 | impl UniformType for SUMat4x4 {} 69 | 70 | impl GlGraphics { 71 | /// Try to get uniform from the current shader of a given name. 72 | pub fn get_uniform(&self, name: &str) -> Option> { 73 | self.get_current_program().and_then(|p| unsafe { 74 | let c_source = CString::new(name).ok(); 75 | c_source.and_then(|name| { 76 | let uniform = match gl::GetUniformLocation(p, name.as_ptr()) { 77 | -1 => None, 78 | location => Some(ShaderUniform { 79 | location, 80 | phantom: PhantomData, 81 | }), 82 | }; 83 | drop(name); 84 | uniform 85 | }) 86 | }) 87 | } 88 | } 89 | 90 | impl ShaderUniform { 91 | /// Set the value of the float uniform. 92 | pub fn set(&self, gl: &GlGraphics, value: f32) { 93 | if let Some(p) = gl.get_current_program() { 94 | unsafe { gl::ProgramUniform1f(p, self.location, value) }; 95 | } 96 | } 97 | } 98 | 99 | impl ShaderUniform { 100 | /// Set the value of the integer uniform. 101 | pub fn set(&self, gl: &GlGraphics, value: i32) { 102 | if let Some(p) = gl.get_current_program() { 103 | unsafe { gl::ProgramUniform1i(p, self.location, value) }; 104 | } 105 | } 106 | } 107 | 108 | impl ShaderUniform { 109 | /// Set the value of the vector 2 uniform. 110 | pub fn set(&self, gl: &GlGraphics, value: &[f32; 2]) { 111 | if let Some(p) = gl.get_current_program() { 112 | unsafe { gl::ProgramUniform2f(p, self.location, value[0], value[1]) }; 113 | } 114 | } 115 | } 116 | 117 | impl ShaderUniform { 118 | /// Set the value of the vector 3 uniform. 119 | pub fn set(&self, gl: &GlGraphics, value: &[f32; 3]) { 120 | if let Some(p) = gl.get_current_program() { 121 | unsafe { gl::ProgramUniform3f(p, self.location, value[0], value[1], value[2]) } 122 | } 123 | } 124 | } 125 | 126 | impl ShaderUniform { 127 | /// Set the value of the vector 4 uniform. 128 | pub fn set(&self, gl: &GlGraphics, value: &[f32; 4]) { 129 | if let Some(p) = gl.get_current_program() { 130 | unsafe { 131 | gl::ProgramUniform4f(p, self.location, value[0], value[1], value[2], value[3]) 132 | } 133 | } 134 | } 135 | } 136 | 137 | impl ShaderUniform { 138 | /// Set the value of the 2x2 matrix uniform. 139 | pub fn set(&self, gl: &GlGraphics, values: &[f32; 4]) { 140 | if let Some(p) = gl.get_current_program() { 141 | unsafe { 142 | gl::ProgramUniformMatrix2fv( 143 | p, 144 | self.location, 145 | 1 as GLint, 146 | false as GLboolean, 147 | values.as_ptr(), 148 | ) 149 | } 150 | } 151 | } 152 | } 153 | 154 | impl ShaderUniform { 155 | /// Set the value of the 3x3 matrix uniform. 156 | pub fn set(&self, gl: &GlGraphics, values: &[f32; 9]) { 157 | if let Some(p) = gl.get_current_program() { 158 | unsafe { 159 | gl::ProgramUniformMatrix3fv( 160 | p, 161 | self.location, 162 | 1 as GLint, 163 | false as GLboolean, 164 | values.as_ptr(), 165 | ) 166 | } 167 | } 168 | } 169 | } 170 | 171 | impl ShaderUniform { 172 | /// Set the value of the 4x4 matrix uniform. 173 | pub fn set(&self, gl: &GlGraphics, values: &[f32; 16]) { 174 | if let Some(p) = gl.get_current_program() { 175 | unsafe { 176 | gl::ProgramUniformMatrix4fv( 177 | p, 178 | self.location, 179 | 1 as GLint, 180 | false as GLboolean, 181 | values.as_ptr(), 182 | ) 183 | } 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /src/draw_state.rs: -------------------------------------------------------------------------------- 1 | use graphics::draw_state::*; 2 | use viewport::Viewport; 3 | 4 | pub fn bind_state(old_state: &DrawState, new_state: &DrawState, viewport: &Option) { 5 | if old_state.scissor != new_state.scissor { 6 | bind_scissor(new_state.scissor, viewport); 7 | } 8 | if old_state.stencil != new_state.stencil { 9 | bind_stencil(new_state.stencil); 10 | } 11 | if old_state.blend != new_state.blend { 12 | bind_blend(new_state.blend); 13 | } 14 | } 15 | 16 | pub fn bind_scissor(rect: Option<[u32; 4]>, viewport: &Option) { 17 | match rect { 18 | Some(r) => { 19 | // https://www.khronos.org/opengl/wiki/Scissor_Test indicates that 20 | // gl::Scissor takes x,y defined as lower left, 21 | // but piston passes rect with x,y defined as upper left. 22 | // To fix this we need to know height of the viewport 23 | // so that we can transform y as top measured from top (yt) 24 | // into y as bottom measured from bottom (yb) 25 | // using yb = viewport_height - (yt + rect_height) 26 | let yb = if let Some(vp) = viewport { 27 | vp.rect[3] - (r[1] + r[3]) as i32 28 | } else { 29 | r[1] as i32 30 | }; 31 | unsafe { 32 | gl::Enable(gl::SCISSOR_TEST); 33 | gl::Scissor( 34 | r[0] as gl::types::GLint, 35 | yb as gl::types::GLint, 36 | r[2] as gl::types::GLint, 37 | r[3] as gl::types::GLint, 38 | ); 39 | } 40 | } 41 | None => unsafe { gl::Disable(gl::SCISSOR_TEST) }, 42 | } 43 | } 44 | 45 | pub fn bind_stencil(stencil: Option) { 46 | unsafe { 47 | match stencil { 48 | Some(s) => { 49 | gl::Enable(gl::STENCIL_TEST); 50 | match s { 51 | Stencil::Increment => { 52 | gl::StencilFunc(gl::NEVER, 0 as gl::types::GLint, 255); 53 | gl::StencilMask(255); 54 | gl::StencilOp(gl::INCR, gl::KEEP, gl::KEEP); 55 | } 56 | Stencil::Clip(val) => { 57 | gl::StencilFunc(gl::NEVER, val as gl::types::GLint, 255); 58 | gl::StencilMask(255); 59 | gl::StencilOp(gl::REPLACE, gl::KEEP, gl::KEEP); 60 | } 61 | Stencil::Inside(val) => { 62 | gl::StencilFunc(gl::EQUAL, val as gl::types::GLint, 255); 63 | gl::StencilMask(255); 64 | gl::StencilOp(gl::KEEP, gl::KEEP, gl::KEEP); 65 | } 66 | Stencil::Outside(val) => { 67 | gl::StencilFunc(gl::NOTEQUAL, val as gl::types::GLint, 255); 68 | gl::StencilMask(255); 69 | gl::StencilOp(gl::KEEP, gl::KEEP, gl::KEEP); 70 | } 71 | } 72 | } 73 | None => gl::Disable(gl::STENCIL_TEST), 74 | } 75 | } 76 | } 77 | 78 | /* 79 | fn map_equation(eq: Equation) -> gl::types::GLenum { 80 | match eq { 81 | Equation::Add => gl::FUNC_ADD, 82 | Equation::Sub => gl::FUNC_SUBTRACT, 83 | Equation::RevSub => gl::FUNC_REVERSE_SUBTRACT, 84 | Equation::Min => gl::MIN, 85 | Equation::Max => gl::MAX, 86 | } 87 | } 88 | 89 | fn map_factor(factor: Factor) -> gl::types::GLenum { 90 | match factor { 91 | Factor::Zero => gl::ZERO, 92 | Factor::One => gl::ONE, 93 | Factor::ZeroPlus(BlendValue::SourceColor) => gl::SRC_COLOR, 94 | Factor::OneMinus(BlendValue::SourceColor) => gl::ONE_MINUS_SRC_COLOR, 95 | Factor::ZeroPlus(BlendValue::SourceAlpha) => gl::SRC_ALPHA, 96 | Factor::OneMinus(BlendValue::SourceAlpha) => gl::ONE_MINUS_SRC_ALPHA, 97 | Factor::ZeroPlus(BlendValue::DestColor) => gl::DST_COLOR, 98 | Factor::OneMinus(BlendValue::DestColor) => gl::ONE_MINUS_DST_COLOR, 99 | Factor::ZeroPlus(BlendValue::DestAlpha) => gl::DST_ALPHA, 100 | Factor::OneMinus(BlendValue::DestAlpha) => gl::ONE_MINUS_DST_ALPHA, 101 | Factor::ZeroPlus(BlendValue::ConstColor) => gl::CONSTANT_COLOR, 102 | Factor::OneMinus(BlendValue::ConstColor) => gl::ONE_MINUS_CONSTANT_COLOR, 103 | Factor::ZeroPlus(BlendValue::ConstAlpha) => gl::CONSTANT_ALPHA, 104 | Factor::OneMinus(BlendValue::ConstAlpha) => gl::ONE_MINUS_CONSTANT_ALPHA, 105 | Factor::SourceAlphaSaturated => gl::SRC_ALPHA_SATURATE, 106 | } 107 | } 108 | */ 109 | 110 | pub fn bind_blend(blend: Option) { 111 | unsafe { 112 | match blend { 113 | Some(b) => { 114 | gl::Enable(gl::BLEND); 115 | gl::BlendColor(1.0, 1.0, 1.0, 1.0); 116 | match b { 117 | Blend::Alpha => { 118 | gl::BlendEquationSeparate(gl::FUNC_ADD, gl::FUNC_ADD); 119 | gl::BlendFuncSeparate( 120 | gl::SRC_ALPHA, 121 | gl::ONE_MINUS_SRC_ALPHA, 122 | gl::ONE, 123 | gl::ONE, 124 | ); 125 | } 126 | Blend::Add => { 127 | gl::BlendEquationSeparate(gl::FUNC_ADD, gl::FUNC_ADD); 128 | gl::BlendFuncSeparate(gl::ONE, gl::ONE, gl::ONE, gl::ONE); 129 | } 130 | Blend::Lighter => { 131 | gl::BlendEquationSeparate(gl::FUNC_ADD, gl::FUNC_ADD); 132 | gl::BlendFuncSeparate(gl::SRC_ALPHA, gl::ONE, gl::ZERO, gl::ONE); 133 | } 134 | Blend::Multiply => { 135 | gl::BlendEquationSeparate(gl::FUNC_ADD, gl::FUNC_ADD); 136 | gl::BlendFuncSeparate(gl::DST_COLOR, gl::ZERO, gl::DST_ALPHA, gl::ZERO); 137 | } 138 | Blend::Invert => { 139 | gl::BlendEquationSeparate(gl::FUNC_SUBTRACT, gl::FUNC_ADD); 140 | gl::BlendFuncSeparate(gl::CONSTANT_COLOR, gl::SRC_COLOR, gl::ZERO, gl::ONE); 141 | } 142 | } 143 | } 144 | None => gl::Disable(gl::BLEND), 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/glyph_cache.rs: -------------------------------------------------------------------------------- 1 | //! Glyph caching 2 | 3 | use {rusttype, graphics, Texture, TextureSettings}; 4 | use std::collections::HashMap; 5 | use graphics::types::Scalar; 6 | 7 | extern crate fnv; 8 | use self::fnv::FnvHasher; 9 | use std::hash::BuildHasherDefault; 10 | 11 | use std::path::Path; 12 | use std::io::Read; 13 | use std::fs::File; 14 | use error::Error; 15 | 16 | pub use graphics::types::FontSize; 17 | use graphics::character::CharacterCache; 18 | 19 | /// The type alias for font characters. 20 | pub type Character<'a> = graphics::character::Character<'a, Texture>; 21 | 22 | /// A struct used for caching rendered font. 23 | pub struct GlyphCache<'a> { 24 | /// The font. 25 | pub font: rusttype::Font<'a>, 26 | /// The settings to render the font with. 27 | settings: TextureSettings, 28 | // Maps from fontsize and character to offset, size and texture. 29 | data: HashMap<(FontSize, char), 30 | ([Scalar; 2], [Scalar; 2], Texture), 31 | BuildHasherDefault>, 32 | } 33 | 34 | impl<'a> GlyphCache<'a> { 35 | /// Constructs a GlyphCache from a Font. 36 | pub fn from_font(font: rusttype::Font<'a>, settings: TextureSettings) -> Self { 37 | let fnv = BuildHasherDefault::::default(); 38 | GlyphCache { 39 | font: font, 40 | settings: settings, 41 | data: HashMap::with_hasher(fnv), 42 | } 43 | } 44 | 45 | /// Constructor for a GlyphCache. 46 | pub fn new

(font: P, settings: TextureSettings) -> Result, Error> 47 | where P: AsRef 48 | { 49 | let fnv = BuildHasherDefault::::default(); 50 | let mut file = try!(File::open(font)); 51 | let mut file_buffer = Vec::new(); 52 | try!(file.read_to_end(&mut file_buffer)); 53 | 54 | let collection = rusttype::FontCollection::from_bytes(file_buffer); 55 | let font = collection.into_font().unwrap(); 56 | Ok(GlyphCache { 57 | font: font, 58 | settings: settings, 59 | data: HashMap::with_hasher(fnv), 60 | }) 61 | } 62 | 63 | /// Creates a GlyphCache for a font stored in memory. 64 | pub fn from_bytes(font: &'a [u8], settings: TextureSettings) -> Result, Error> { 65 | let collection = rusttype::FontCollection::from_bytes(font); 66 | let font = collection.into_font().unwrap(); 67 | Ok(Self::from_font(font, settings)) 68 | } 69 | 70 | /// Load all characters in the `chars` iterator for `size` 71 | pub fn preload_chars(&mut self, size: FontSize, chars: I) 72 | where I: Iterator 73 | { 74 | for ch in chars { 75 | self.character(size, ch); 76 | } 77 | } 78 | 79 | /// Load all the printable ASCII characters for `size`. Includes space. 80 | pub fn preload_printable_ascii(&mut self, size: FontSize) { 81 | // [0x20, 0x7F) contains all printable ASCII characters ([' ', '~']) 82 | self.preload_chars(size, (0x20u8..0x7F).map(|ch| ch as char)); 83 | } 84 | 85 | /// Return `ch` for `size` if it's already cached. Don't load. 86 | /// See the `preload_*` functions. 87 | pub fn opt_character(&self, size: FontSize, ch: char) -> Option { 88 | self.data.get(&(size, ch)).map(|&(offset, size, ref texture)| { 89 | Character { 90 | offset: offset, 91 | size: size, 92 | texture: texture, 93 | } 94 | }) 95 | } 96 | } 97 | 98 | impl<'b> CharacterCache for GlyphCache<'b> { 99 | type Texture = Texture; 100 | 101 | fn character<'a>(&'a mut self, size: FontSize, ch: char) -> Character<'a> { 102 | use std::collections::hash_map::Entry; 103 | use rusttype as rt; 104 | 105 | let size = ((size as f32) * 1.333).round() as u32; // convert points to pixels 106 | 107 | match self.data.entry((size, ch)) { 108 | //returning `into_mut()' to get reference with 'a lifetime 109 | Entry::Occupied(v) => { 110 | let &mut (offset, size, ref texture) = v.into_mut(); 111 | Character { 112 | offset: offset, 113 | size: size, 114 | texture: texture, 115 | } 116 | } 117 | Entry::Vacant(v) => { 118 | // this is only None for invalid GlyphIds, 119 | // but char is converted to a Codepoint which must result in a glyph. 120 | let glyph = self.font.glyph(ch).unwrap(); 121 | let scale = rt::Scale::uniform(size as f32); 122 | let mut glyph = glyph.scaled(scale); 123 | 124 | // some fonts do not contain glyph zero as fallback, instead try U+FFFD. 125 | if glyph.id() == rt::GlyphId(0) && glyph.shape().is_none() { 126 | glyph = self.font.glyph('\u{FFFD}').unwrap().scaled(scale); 127 | } 128 | 129 | let h_metrics = glyph.h_metrics(); 130 | let bounding_box = glyph.exact_bounding_box().unwrap_or(rt::Rect { 131 | min: rt::Point { x: 0.0, y: 0.0 }, 132 | max: rt::Point { x: 0.0, y: 0.0 }, 133 | }); 134 | let glyph = glyph.positioned(rt::point(0.0, 0.0)); 135 | let pixel_bounding_box = glyph.pixel_bounding_box().unwrap_or(rt::Rect { 136 | min: rt::Point { x: 0, y: 0 }, 137 | max: rt::Point { x: 0, y: 0 }, 138 | }); 139 | let pixel_bb_width = pixel_bounding_box.width() + 2; 140 | let pixel_bb_height = pixel_bounding_box.height() + 2; 141 | 142 | let mut image_buffer = Vec::::new(); 143 | image_buffer.resize((pixel_bb_width * pixel_bb_height) as usize, 0); 144 | glyph.draw(|x, y, v| { 145 | let pos = ((x + 1) + (y + 1) * (pixel_bb_width as u32)) as usize; 146 | image_buffer[pos] = (255.0 * v) as u8; 147 | }); 148 | 149 | let &mut (offset, size, ref texture) = 150 | v.insert(([bounding_box.min.x as Scalar - 1.0, 151 | -pixel_bounding_box.min.y as Scalar + 1.0], 152 | [h_metrics.advance_width as Scalar, 0 as Scalar], 153 | { 154 | if pixel_bb_width == 0 || pixel_bb_height == 0 { 155 | Texture::empty(&self.settings).unwrap() 156 | } else { 157 | Texture::from_memory_alpha(&image_buffer, 158 | pixel_bb_width as u32, 159 | pixel_bb_height as u32, 160 | &self.settings) 161 | .unwrap() 162 | } 163 | })); 164 | Character { 165 | offset: offset, 166 | size: size, 167 | texture: texture, 168 | } 169 | } 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/shader_utils.rs: -------------------------------------------------------------------------------- 1 | //! Helper functions for dealing with shaders. 2 | 3 | // External crates. 4 | use gl; 5 | use gl::types::{GLboolean, GLenum, GLint, GLsizeiptr, GLuint}; 6 | use std::ffi::CString; 7 | use std::{mem, ptr}; 8 | 9 | #[cfg(not(feature = "glow"))] 10 | use gl::types::GLchar; 11 | 12 | /// Describes a shader attribute. 13 | pub struct DynamicAttribute { 14 | /// The vertex buffer object. 15 | vbo: GLuint, 16 | /// The number of components. 17 | size: i32, 18 | /// The location of the attribute in shader. 19 | location: GLuint, 20 | /// Whether to normalize when sending to GPU. 21 | normalize: GLboolean, 22 | /// The type, for example gl::FLOAT. 23 | ty: GLenum, 24 | } 25 | 26 | impl Drop for DynamicAttribute { 27 | fn drop(&mut self) { 28 | unsafe { 29 | gl::DeleteBuffers(1, &self.vbo); 30 | } 31 | } 32 | } 33 | 34 | impl DynamicAttribute { 35 | /// Binds to a vertex array object. 36 | /// 37 | /// The vertex array object remembers the format for later. 38 | fn bind_vao(&self, vao: GLuint) { 39 | let stride = 0; 40 | unsafe { 41 | gl::BindVertexArray(vao); 42 | gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); 43 | gl::VertexAttribPointer( 44 | self.location, 45 | self.size, 46 | self.ty, 47 | self.normalize, 48 | stride, 49 | ptr::null(), 50 | ); 51 | } 52 | } 53 | 54 | fn new( 55 | program: GLuint, 56 | name: &str, 57 | size: i32, 58 | normalize: GLboolean, 59 | ty: GLenum, 60 | vao: GLuint, 61 | ) -> Result { 62 | let location = attribute_location(program, name)?; 63 | let mut vbo = 0; 64 | unsafe { 65 | gl::GenBuffers(1, &mut vbo); 66 | } 67 | let res = DynamicAttribute { 68 | vbo, 69 | size, 70 | location, 71 | normalize, 72 | ty, 73 | }; 74 | res.bind_vao(vao); 75 | Ok(res) 76 | } 77 | 78 | /// Create XYZ vertex attribute. 79 | pub fn xyz(program: GLuint, name: &str, vao: GLuint) -> Result { 80 | DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao) 81 | } 82 | 83 | /// Create XY vertex attribute. 84 | pub fn xy(program: GLuint, name: &str, vao: GLuint) -> Result { 85 | DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao) 86 | } 87 | 88 | /// Create RGB color attribute. 89 | pub fn rgb(program: GLuint, name: &str, vao: GLuint) -> Result { 90 | DynamicAttribute::new(program, name, 3, gl::FALSE, gl::FLOAT, vao) 91 | } 92 | 93 | /// Create RGBA color attribute. 94 | pub fn rgba(program: GLuint, name: &str, vao: GLuint) -> Result { 95 | DynamicAttribute::new(program, name, 4, gl::FALSE, gl::FLOAT, vao) 96 | } 97 | 98 | /// Create texture coordinate attribute. 99 | pub fn uv(program: GLuint, name: &str, vao: GLuint) -> Result { 100 | DynamicAttribute::new(program, name, 2, gl::FALSE, gl::FLOAT, vao) 101 | } 102 | 103 | /// Sets attribute data. 104 | pub unsafe fn set(&self, data: &[T]) { 105 | gl::EnableVertexAttribArray(self.location); 106 | gl::BindBuffer(gl::ARRAY_BUFFER, self.vbo); 107 | gl::BufferData( 108 | gl::ARRAY_BUFFER, 109 | data.len() as GLsizeiptr * mem::size_of::() as GLsizeiptr, 110 | mem::transmute(data.as_ptr()), 111 | gl::DYNAMIC_DRAW, 112 | ); 113 | } 114 | } 115 | 116 | /// Compiles a shader. 117 | /// 118 | /// Returns a shader or a message with the error. 119 | pub fn compile_shader(shader_type: GLenum, source: &str) -> Result { 120 | unsafe { 121 | let shader = gl::CreateShader(shader_type); 122 | let c_source = match CString::new(source) { 123 | Ok(x) => x, 124 | Err(err) => return Err(format!("compile_shader: {}", err)), 125 | }; 126 | gl::ShaderSource(shader, 1, &c_source.as_ptr(), ptr::null()); 127 | drop(c_source); 128 | gl::CompileShader(shader); 129 | let mut status = gl::FALSE as GLint; 130 | 131 | #[cfg(feature = "glow")] 132 | { 133 | gl::GetCompleStatus(shader, &mut status); 134 | } 135 | #[cfg(not(feature = "glow"))] 136 | { 137 | gl::GetShaderiv(shader, gl::COMPILE_STATUS, &mut status); 138 | } 139 | 140 | if status == (gl::TRUE as GLint) { 141 | Ok(shader) 142 | } else { 143 | #[cfg(feature = "glow")] 144 | { 145 | Err(gl::GetShaderInfoLog(shader)) 146 | } 147 | #[cfg(not(feature = "glow"))] 148 | { 149 | let mut len = 0; 150 | gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); 151 | 152 | if len == 0 { 153 | Err("Compilation failed with no log. \ 154 | The OpenGL context might have been created on another thread, \ 155 | or not have been created." 156 | .to_string()) 157 | } else { 158 | // Subtract 1 to skip the trailing null character. 159 | let mut buf = vec![0; len as usize - 1]; 160 | gl::GetShaderInfoLog( 161 | shader, 162 | len, 163 | ptr::null_mut(), 164 | buf.as_mut_ptr() as *mut GLchar, 165 | ); 166 | 167 | gl::DeleteShader(shader); 168 | 169 | Err(String::from_utf8(buf).expect("ShaderInfoLog not valid utf8")) 170 | } 171 | } 172 | } 173 | } 174 | } 175 | 176 | /// Finds attribute location from a program. 177 | /// 178 | /// Returns `Err` if there is no attribute with such name. 179 | pub fn attribute_location(program: GLuint, name: &str) -> Result { 180 | unsafe { 181 | let c_name = match CString::new(name) { 182 | Ok(x) => x, 183 | Err(err) => return Err(format!("attribute_location: {}", err)), 184 | }; 185 | let id = gl::GetAttribLocation(program, c_name.as_ptr()); 186 | drop(c_name); 187 | if id < 0 { 188 | Err(format!("Attribute '{}' does not exists in shader", name)) 189 | } else { 190 | Ok(id as GLuint) 191 | } 192 | } 193 | } 194 | 195 | /// Finds uniform location from a program. 196 | /// 197 | /// Returns `Err` if there is no uniform with such name. 198 | pub fn uniform_location(program: GLuint, name: &str) -> Result { 199 | unsafe { 200 | let c_name = match CString::new(name) { 201 | Ok(x) => x, 202 | Err(err) => return Err(format!("uniform_location: {}", err)), 203 | }; 204 | let id = gl::GetUniformLocation(program, c_name.as_ptr()); 205 | drop(c_name); 206 | if id < 0 { 207 | Err(format!("Uniform '{}' does not exists in shader", name)) 208 | } else { 209 | Ok(id as GLuint) 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /examples/glow-hello_world.rs: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Code copied from: 4 | https://github.com/grovesNL/glow/blob/main/examples/hello/src/main.rs 5 | 6 | This example is used as reference for other Glow examples. 7 | 8 | */ 9 | 10 | use glow::*; 11 | 12 | fn main() { 13 | unsafe { 14 | // Create a context from a WebGL2 context on wasm32 targets 15 | #[cfg(target_arch = "wasm32")] 16 | let (gl, shader_version) = { 17 | use wasm_bindgen::JsCast; 18 | let canvas = web_sys::window() 19 | .unwrap() 20 | .document() 21 | .unwrap() 22 | .get_element_by_id("canvas") 23 | .unwrap() 24 | .dyn_into::() 25 | .unwrap(); 26 | let webgl2_context = canvas 27 | .get_context("webgl2") 28 | .unwrap() 29 | .unwrap() 30 | .dyn_into::() 31 | .unwrap(); 32 | let gl = glow::Context::from_webgl2_context(webgl2_context); 33 | (gl, "#version 300 es") 34 | }; 35 | 36 | // Create a context from a glutin window on non-wasm32 targets 37 | #[cfg(not(target_arch = "wasm32"))] 38 | // #[cfg(feature = "glutin_winit")] 39 | let (gl, gl_surface, gl_context, shader_version, _window, event_loop) = { 40 | use glutin::{ 41 | config::{ConfigTemplateBuilder, GlConfig}, 42 | context::{ContextApi, ContextAttributesBuilder, NotCurrentGlContext}, 43 | display::{GetGlDisplay, GlDisplay}, 44 | surface::{GlSurface, SwapInterval}, 45 | }; 46 | use glutin_winit::{DisplayBuilder, GlWindow}; 47 | use winit::raw_window_handle::HasRawWindowHandle; 48 | use std::num::NonZeroU32; 49 | 50 | let event_loop = winit::event_loop::EventLoopBuilder::new().build().unwrap(); 51 | let window_builder = winit::window::WindowAttributes::new() 52 | .with_title("Hello triangle!") 53 | .with_inner_size(winit::dpi::LogicalSize::new(1024.0, 768.0)); 54 | 55 | let template = ConfigTemplateBuilder::new(); 56 | 57 | let display_builder = DisplayBuilder::new().with_window_attributes(Some(window_builder)); 58 | 59 | let (window, gl_config) = display_builder 60 | .build(&event_loop, template, |configs| { 61 | configs 62 | .reduce(|accum, config| { 63 | use glutin::config::GlConfig; 64 | 65 | if config.num_samples() > accum.num_samples() { 66 | config 67 | } else { 68 | accum 69 | } 70 | }) 71 | .unwrap() 72 | }) 73 | .unwrap(); 74 | 75 | let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle().unwrap()); 76 | 77 | let gl_display = gl_config.display(); 78 | let context_attributes = ContextAttributesBuilder::new() 79 | .with_context_api(ContextApi::OpenGl(Some(glutin::context::Version { 80 | major: 4, 81 | minor: 1, 82 | }))) 83 | .build(raw_window_handle); 84 | 85 | let not_current_gl_context = gl_display 86 | .create_context(&gl_config, &context_attributes) 87 | .unwrap(); 88 | 89 | let window = window.unwrap(); 90 | 91 | let attrs = window.build_surface_attributes(Default::default()).unwrap(); 92 | let gl_surface = gl_display 93 | .create_window_surface(&gl_config, &attrs) 94 | .unwrap(); 95 | 96 | let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap(); 97 | 98 | let gl = glow::Context::from_loader_function_cstr(|s| gl_display.get_proc_address(s)); 99 | 100 | gl_surface 101 | .set_swap_interval(&gl_context, SwapInterval::Wait(NonZeroU32::new(1).unwrap())) 102 | .unwrap(); 103 | 104 | ( 105 | gl, 106 | gl_surface, 107 | gl_context, 108 | "#version 410", 109 | window, 110 | event_loop, 111 | ) 112 | }; 113 | 114 | let vertex_array = gl 115 | .create_vertex_array() 116 | .expect("Cannot create vertex array"); 117 | gl.bind_vertex_array(Some(vertex_array)); 118 | 119 | let program = gl.create_program().expect("Cannot create program"); 120 | 121 | let (vertex_shader_source, fragment_shader_source) = ( 122 | r#"const vec2 verts[3] = vec2[3]( 123 | vec2(0.5f, 1.0f), 124 | vec2(0.0f, 0.0f), 125 | vec2(1.0f, 0.0f) 126 | ); 127 | out vec2 vert; 128 | void main() { 129 | vert = verts[gl_VertexID]; 130 | gl_Position = vec4(vert - 0.5, 0.0, 1.0); 131 | }"#, 132 | r#"precision mediump float; 133 | in vec2 vert; 134 | out vec4 color; 135 | void main() { 136 | color = vec4(vert, 0.5, 1.0); 137 | }"#, 138 | ); 139 | 140 | let shader_sources = [ 141 | (glow::VERTEX_SHADER, vertex_shader_source), 142 | (glow::FRAGMENT_SHADER, fragment_shader_source), 143 | ]; 144 | 145 | let mut shaders = Vec::with_capacity(shader_sources.len()); 146 | 147 | for (shader_type, shader_source) in shader_sources.iter() { 148 | let shader = gl 149 | .create_shader(*shader_type) 150 | .expect("Cannot create shader"); 151 | gl.shader_source(shader, &format!("{}\n{}", shader_version, shader_source)); 152 | gl.compile_shader(shader); 153 | if !gl.get_shader_compile_status(shader) { 154 | panic!("{}", gl.get_shader_info_log(shader)); 155 | } 156 | gl.attach_shader(program, shader); 157 | shaders.push(shader); 158 | } 159 | 160 | gl.link_program(program); 161 | if !gl.get_program_link_status(program) { 162 | panic!("{}", gl.get_program_info_log(program)); 163 | } 164 | 165 | for shader in shaders { 166 | gl.detach_shader(program, shader); 167 | gl.delete_shader(shader); 168 | } 169 | 170 | gl.use_program(Some(program)); 171 | gl.clear_color(0.1, 0.2, 0.3, 1.0); 172 | 173 | // We handle events differently between targets 174 | 175 | #[cfg(not(target_arch = "wasm32"))] 176 | // #[cfg(feature = "glutin_winit")] 177 | { 178 | use glutin::prelude::GlSurface; 179 | use winit::event::{Event, WindowEvent}; 180 | let _ = event_loop.run(move |event, elwt| { 181 | if let Event::WindowEvent { event, .. } = event { 182 | match event { 183 | WindowEvent::CloseRequested => { 184 | elwt.exit(); 185 | } 186 | WindowEvent::RedrawRequested => { 187 | gl.clear(glow::COLOR_BUFFER_BIT); 188 | gl.draw_arrays(glow::TRIANGLES, 0, 3); 189 | gl_surface.swap_buffers(&gl_context).unwrap(); 190 | } 191 | _ => (), 192 | } 193 | } 194 | }); 195 | } 196 | 197 | #[cfg(target_arch = "wasm32")] 198 | { 199 | // This could be called from `requestAnimationFrame`, a winit event 200 | // loop, etc. 201 | gl.clear(glow::COLOR_BUFFER_BIT); 202 | gl.draw_arrays(glow::TRIANGLES, 0, 3); 203 | gl.delete_program(program); 204 | gl.delete_vertex_array(vertex_array); 205 | } 206 | } 207 | } 208 | 209 | -------------------------------------------------------------------------------- /examples/glow.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "glow")] 2 | use graphics::{clear, Graphics, Rectangle, Transformed as _}; 3 | #[cfg(feature = "glow")] 4 | use graphics::{image, text}; 5 | 6 | #[cfg(feature = "glow")] 7 | use opengl_graphics::*; 8 | 9 | #[cfg(feature = "glow")] 10 | use std::sync::Arc; 11 | #[cfg(feature = "glow")] 12 | use viewport::Viewport; 13 | 14 | #[cfg(not(feature = "glow"))] 15 | fn main() {} 16 | 17 | /// 18 | /// Run with glutin 19 | /// ``` 20 | /// cargo run --features glow --example glow 21 | /// ``` 22 | /// 23 | /// Run with web_sys 24 | /// ``` 25 | /// cargo build --features glow --target wasm32-unknown-unknown --example glow 26 | /// mkdir -p target/glow 27 | /// wasm-bindgen target/wasm32-unknown-unknown/debug/examples/glow.wasm --out-dir target/glow --target web 28 | /// cp assets/glow.html target/glow/index.html 29 | /// cd target/glow 30 | /// cargo install cargo-server 31 | /// cargo server --open 32 | /// ``` 33 | #[cfg(feature = "glow")] 34 | fn main() { 35 | let w = 600; 36 | let h = 600; 37 | 38 | #[cfg(not(target_arch = "wasm32"))] 39 | let (gl, window, event_loop, gl_surface, gl_context) = { 40 | unsafe { 41 | let event_loop = winit::event_loop::EventLoopBuilder::new().build().unwrap(); 42 | let window_builder = winit::window::WindowAttributes::new() 43 | .with_title("Hello Rust!") 44 | .with_inner_size(winit::dpi::LogicalSize::new(w as f64, w as f64)); 45 | /* 46 | let window = glutin::ContextBuilder::new() 47 | .with_vsync(true) 48 | .build_windowed(window_builder, &event_loop) 49 | .unwrap() 50 | .make_current() 51 | .unwrap(); 52 | */ 53 | 54 | let template = glutin::config::ConfigTemplateBuilder::new(); 55 | 56 | let display_builder = glutin_winit::DisplayBuilder::new().with_window_attributes(Some(window_builder)); 57 | 58 | use glutin::context::NotCurrentGlContext; 59 | let (window, gl_config) = display_builder 60 | .build(&event_loop, template, |configs| { 61 | configs 62 | .reduce(|accum, config| { 63 | use glutin::config::GlConfig; 64 | 65 | if config.num_samples() > accum.num_samples() { 66 | config 67 | } else { 68 | accum 69 | } 70 | }) 71 | .unwrap() 72 | }) 73 | .unwrap(); 74 | 75 | use winit::raw_window_handle::HasRawWindowHandle; 76 | let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle().unwrap()); 77 | 78 | use glutin::display::{GetGlDisplay, GlDisplay}; 79 | let gl_display = gl_config.display(); 80 | use glutin::context::ContextAttributesBuilder; 81 | use glutin::context::ContextApi; 82 | let context_attributes = ContextAttributesBuilder::new() 83 | .with_context_api(ContextApi::OpenGl(Some(glutin::context::Version { 84 | major: 4, 85 | minor: 1, 86 | }))) 87 | .build(raw_window_handle); 88 | 89 | let not_current_gl_context = gl_display 90 | .create_context(&gl_config, &context_attributes) 91 | .unwrap(); 92 | 93 | let window = window.unwrap(); 94 | 95 | use glutin_winit::GlWindow; 96 | let attrs = window.build_surface_attributes(Default::default()).unwrap(); 97 | let gl_surface = gl_display 98 | .create_window_surface(&gl_config, &attrs) 99 | .unwrap(); 100 | let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap(); 101 | 102 | /* 103 | let gl = 104 | glow::Context::from_loader_function(|s| window.get_proc_address(s) as *const _); 105 | */ 106 | let gl = glow::Context::from_loader_function_cstr(|s| gl_display.get_proc_address(s)); 107 | (gl, window, event_loop, gl_surface, gl_context) 108 | } 109 | }; 110 | 111 | #[cfg(target_arch = "wasm32")] 112 | let gl = { 113 | use wasm_bindgen::JsCast; 114 | let canvas = web_sys::window() 115 | .unwrap() 116 | .document() 117 | .unwrap() 118 | .get_element_by_id("canvas") 119 | .unwrap() 120 | .dyn_into::() 121 | .unwrap(); 122 | let webgl2_context = canvas 123 | .get_context("webgl2") 124 | .unwrap() 125 | .unwrap() 126 | .dyn_into::() 127 | .unwrap(); 128 | let gl = glow::Context::from_webgl2_context(webgl2_context); 129 | gl 130 | }; 131 | 132 | opengl_graphics::set_context(Arc::new(gl)); 133 | let mut gl = GlGraphics::new(OpenGL::V4_2); 134 | 135 | #[cfg(not(target_arch = "wasm32"))] 136 | let (rust_logo, mut glyph_cache) = { 137 | let rust_logo = Texture::from_path( 138 | &std::path::Path::new("assets/rust.png"), 139 | &TextureSettings::new(), 140 | ) 141 | .unwrap(); 142 | let glyph_cache = 143 | GlyphCache::new("assets/FiraSans-Regular.ttf", (), TextureSettings::new()).unwrap(); 144 | (rust_logo, glyph_cache) 145 | }; 146 | 147 | #[cfg(target_arch = "wasm32")] 148 | let (rust_logo, mut glyph_cache) = { 149 | let mut setting = TextureSettings::new(); 150 | setting.set_convert_gamma(true); 151 | let rust_logo = 152 | Texture::from_bytes(include_bytes!("../assets/rust.png"), &setting).unwrap(); 153 | let glyph_cache = GlyphCache::from_bytes( 154 | include_bytes!("../assets/FiraSans-Regular.ttf"), 155 | (), 156 | setting, 157 | ) 158 | .unwrap(); 159 | (rust_logo, glyph_cache) 160 | }; 161 | 162 | let scale = window.scale_factor(); 163 | let viewport = Viewport { 164 | rect: [0, 0, (scale * w as f64) as i32, (scale * h as f64) as i32], 165 | draw_size: [w as u32, h as u32], 166 | window_size: [w.into(), h.into()], 167 | }; 168 | 169 | #[cfg(not(target_arch = "wasm32"))] 170 | { 171 | use winit::event::{Event, WindowEvent}; 172 | use winit::event_loop::ControlFlow; 173 | 174 | let _ = event_loop.run(move |event, elwt| { 175 | if let Event::WindowEvent { event, .. } = event { 176 | match event { 177 | WindowEvent::CloseRequested => { 178 | elwt.exit(); 179 | } 180 | WindowEvent::RedrawRequested => { 181 | render(&mut gl, viewport, &rust_logo, &mut glyph_cache); 182 | // window.swap_buffers().unwrap(); 183 | use glutin::prelude::GlSurface; 184 | gl_surface.swap_buffers(&gl_context).unwrap(); 185 | } 186 | _ => (), 187 | } 188 | } 189 | }); 190 | } 191 | 192 | #[cfg(target_arch = "wasm32")] 193 | { 194 | render(&mut gl, viewport, &rust_logo, &mut glyph_cache); 195 | } 196 | } 197 | 198 | #[cfg(feature = "glow")] 199 | fn render( 200 | gl: &mut GlGraphics, 201 | viewport: Viewport, 202 | logo: &opengl_graphics::Texture, 203 | glyph_cache: &mut GlyphCache, 204 | ) { 205 | gl.clear_color([0.5, 0.5, 0.5, 1.0]); 206 | gl.draw(viewport, |c, g| { 207 | let c = c.zoom(2.0); 208 | let transform = c.transform.trans(100.0, 100.0); 209 | 210 | clear([1.0; 4], g); 211 | Rectangle::new([1.0, 0.0, 0.0, 1.0]).draw( 212 | [0.0, 0.0, 100.0, 100.0], 213 | &c.draw_state, 214 | c.transform, 215 | g, 216 | ); 217 | Rectangle::new([0.0, 1.0, 0.0, 0.3]).draw( 218 | [50.0, 50.0, 100.0, 100.0], 219 | &c.draw_state, 220 | c.transform, 221 | g, 222 | ); 223 | image(logo, transform.rot_deg(45.0), g); 224 | 225 | text::Text::new_color([0.0, 0.5, 0.0, 1.0], 32) 226 | .draw( 227 | "Hello opengl_graphics!", 228 | glyph_cache, 229 | &c.draw_state, 230 | c.transform.trans(100.0, 300.0), 231 | g, 232 | ) 233 | .unwrap(); 234 | }); 235 | } 236 | -------------------------------------------------------------------------------- /src/texture.rs: -------------------------------------------------------------------------------- 1 | use gl::types::GLuint; 2 | use image::{self, DynamicImage, RgbaImage}; 3 | 4 | use std::path::Path; 5 | 6 | use crate::{ 7 | ops, CreateTexture, Filter, Format, ImageSize, TextureOp, TextureSettings, UpdateTexture, Wrap, 8 | }; 9 | 10 | trait GlSettings { 11 | fn get_gl_mag(&self) -> gl::types::GLenum; 12 | fn get_gl_min(&self) -> gl::types::GLenum; 13 | #[allow(dead_code)] 14 | fn get_gl_mipmap(&self) -> gl::types::GLenum; 15 | fn get_gl_wrap_u(&self) -> gl::types::GLenum; 16 | fn get_gl_wrap_v(&self) -> gl::types::GLenum; 17 | } 18 | 19 | impl GlSettings for TextureSettings { 20 | fn get_gl_mag(&self) -> gl::types::GLenum { 21 | match self.get_mag() { 22 | Filter::Linear => gl::LINEAR, 23 | Filter::Nearest => gl::NEAREST, 24 | } 25 | } 26 | 27 | fn get_gl_min(&self) -> gl::types::GLenum { 28 | match self.get_min() { 29 | Filter::Linear => { 30 | if self.get_generate_mipmap() { 31 | match self.get_mipmap() { 32 | Filter::Linear => gl::LINEAR_MIPMAP_LINEAR, 33 | Filter::Nearest => gl::LINEAR_MIPMAP_NEAREST, 34 | } 35 | } else { 36 | gl::LINEAR 37 | } 38 | } 39 | Filter::Nearest => { 40 | if self.get_generate_mipmap() { 41 | match self.get_mipmap() { 42 | Filter::Linear => gl::NEAREST_MIPMAP_LINEAR, 43 | Filter::Nearest => gl::NEAREST_MIPMAP_NEAREST, 44 | } 45 | } else { 46 | gl::NEAREST 47 | } 48 | } 49 | } 50 | } 51 | 52 | fn get_gl_mipmap(&self) -> gl::types::GLenum { 53 | match self.get_mipmap() { 54 | Filter::Linear => gl::LINEAR, 55 | Filter::Nearest => gl::NEAREST, 56 | } 57 | } 58 | 59 | fn get_gl_wrap_u(&self) -> gl::types::GLenum { 60 | match self.get_wrap_u() { 61 | Wrap::Repeat => gl::REPEAT, 62 | Wrap::MirroredRepeat => gl::MIRRORED_REPEAT, 63 | Wrap::ClampToEdge => gl::CLAMP_TO_EDGE, 64 | Wrap::ClampToBorder => gl::CLAMP_TO_BORDER, 65 | } 66 | } 67 | 68 | fn get_gl_wrap_v(&self) -> gl::types::GLenum { 69 | match self.get_wrap_v() { 70 | Wrap::Repeat => gl::REPEAT, 71 | Wrap::MirroredRepeat => gl::MIRRORED_REPEAT, 72 | Wrap::ClampToEdge => gl::CLAMP_TO_EDGE, 73 | Wrap::ClampToBorder => gl::CLAMP_TO_BORDER, 74 | } 75 | } 76 | } 77 | 78 | /// Wraps OpenGL texture data. 79 | /// The texture gets deleted when running out of scope. 80 | /// 81 | /// In order to create a texture the function `GenTextures` must be loaded. 82 | /// This is done automatically by the window back-ends in Piston. 83 | pub struct Texture { 84 | id: GLuint, 85 | width: u32, 86 | height: u32, 87 | } 88 | 89 | impl Texture { 90 | /// Creates a new texture. 91 | #[inline(always)] 92 | pub fn new(id: GLuint, width: u32, height: u32) -> Self { 93 | Texture { id, width, height } 94 | } 95 | 96 | /// Gets the OpenGL id of the texture. 97 | #[inline(always)] 98 | pub fn get_id(&self) -> GLuint { 99 | self.id 100 | } 101 | 102 | /// Returns empty texture. 103 | pub fn empty(settings: &TextureSettings) -> Result { 104 | CreateTexture::create(&mut (), Format::Rgba8, &[0u8; 4], [1, 1], settings) 105 | } 106 | 107 | /// Loads image from memory, the format is 8-bit greyscale. 108 | pub fn from_memory_alpha( 109 | buf: &[u8], 110 | width: u32, 111 | height: u32, 112 | settings: &TextureSettings, 113 | ) -> Result { 114 | let size = [width, height]; 115 | let buffer = ops::alpha_to_rgba8(buf, size); 116 | CreateTexture::create(&mut (), Format::Rgba8, &buffer, size, settings) 117 | } 118 | 119 | /// Loads image by relative file name to the asset root. 120 | pub fn from_path

(path: P, settings: &TextureSettings) -> Result 121 | where 122 | P: AsRef, 123 | { 124 | let path = path.as_ref(); 125 | 126 | let img = match image::open(path) { 127 | Ok(img) => img, 128 | Err(e) => { 129 | return Err(format!( 130 | "Could not load '{:?}': {:?}", 131 | path.file_name().unwrap(), 132 | e 133 | )) 134 | } 135 | }; 136 | 137 | let img = match img { 138 | DynamicImage::ImageRgba8(img) => img, 139 | x => x.to_rgba8(), 140 | }; 141 | 142 | Ok(Texture::from_image(&img, settings)) 143 | } 144 | 145 | /// Load image from bytes. 146 | pub fn from_bytes(bytes: &[u8], settings: &TextureSettings) -> Result { 147 | let img = match image::load_from_memory(bytes) { 148 | Ok(img) => img, 149 | Err(e) => return Err(format!("Could not load image from bytes. {:?}", e)), 150 | }; 151 | 152 | let img = match img { 153 | DynamicImage::ImageRgba8(img) => img, 154 | x => x.to_rgba8(), 155 | }; 156 | 157 | Ok(Texture::from_image(&img, settings)) 158 | } 159 | 160 | /// Creates a texture from image. 161 | pub fn from_image(img: &RgbaImage, settings: &TextureSettings) -> Self { 162 | let (width, height) = img.dimensions(); 163 | CreateTexture::create(&mut (), Format::Rgba8, img, [width, height], settings).unwrap() 164 | } 165 | 166 | /// Updates image with a new one. 167 | pub fn update(&mut self, img: &RgbaImage) { 168 | let (width, height) = img.dimensions(); 169 | 170 | UpdateTexture::update(self, &mut (), Format::Rgba8, img, [0, 0], [width, height]).unwrap(); 171 | } 172 | } 173 | 174 | impl Drop for Texture { 175 | fn drop(&mut self) { 176 | unsafe { 177 | let ids = [self.id]; 178 | gl::DeleteTextures(1, ids.as_ptr()); 179 | } 180 | } 181 | } 182 | 183 | impl ImageSize for Texture { 184 | fn get_size(&self) -> (u32, u32) { 185 | (self.width, self.height) 186 | } 187 | } 188 | 189 | impl TextureOp<()> for Texture { 190 | type Error = String; 191 | } 192 | 193 | impl CreateTexture<()> for Texture { 194 | fn create>( 195 | _factory: &mut (), 196 | _format: Format, 197 | memory: &[u8], 198 | size: S, 199 | settings: &TextureSettings, 200 | ) -> Result { 201 | let size = size.into(); 202 | let mut id: GLuint = 0; 203 | let internal_format = if settings.get_convert_gamma() { 204 | gl::RGBA 205 | } else { 206 | gl::SRGB_ALPHA 207 | }; 208 | unsafe { 209 | gl::GenTextures(1, &mut id); 210 | gl::BindTexture(gl::TEXTURE_2D, id); 211 | gl::TexParameteri( 212 | gl::TEXTURE_2D, 213 | gl::TEXTURE_MIN_FILTER, 214 | settings.get_gl_min() as i32, 215 | ); 216 | gl::TexParameteri( 217 | gl::TEXTURE_2D, 218 | gl::TEXTURE_MAG_FILTER, 219 | settings.get_gl_mag() as i32, 220 | ); 221 | gl::TexParameteri( 222 | gl::TEXTURE_2D, 223 | gl::TEXTURE_WRAP_S, 224 | settings.get_gl_wrap_u() as i32, 225 | ); 226 | gl::TexParameteri( 227 | gl::TEXTURE_2D, 228 | gl::TEXTURE_WRAP_T, 229 | settings.get_gl_wrap_v() as i32, 230 | ); 231 | if settings.get_wrap_u() == Wrap::ClampToBorder 232 | || settings.get_wrap_v() == Wrap::ClampToBorder 233 | { 234 | gl::TexParameterfv( 235 | gl::TEXTURE_2D, 236 | gl::TEXTURE_BORDER_COLOR, 237 | settings.get_border_color().as_ptr(), 238 | ); 239 | } 240 | if settings.get_generate_mipmap() { 241 | gl::GenerateMipmap(gl::TEXTURE_2D); 242 | } 243 | gl::TexImage2D( 244 | gl::TEXTURE_2D, 245 | 0, 246 | internal_format as i32, 247 | size[0] as i32, 248 | size[1] as i32, 249 | 0, 250 | gl::RGBA, 251 | gl::UNSIGNED_BYTE, 252 | memory.as_ptr() as *const _, 253 | ); 254 | } 255 | 256 | Ok(Texture::new(id, size[0], size[1])) 257 | } 258 | } 259 | 260 | impl UpdateTexture<()> for Texture { 261 | fn update, S: Into<[u32; 2]>>( 262 | &mut self, 263 | _factory: &mut (), 264 | _format: Format, 265 | memory: &[u8], 266 | offset: O, 267 | size: S, 268 | ) -> Result<(), Self::Error> { 269 | let offset = offset.into(); 270 | let size = size.into(); 271 | unsafe { 272 | gl::BindTexture(gl::TEXTURE_2D, self.id); 273 | gl::TexSubImage2D( 274 | gl::TEXTURE_2D, 275 | 0, 276 | offset[0] as i32, 277 | offset[1] as i32, 278 | size[0] as i32, 279 | size[1] as i32, 280 | gl::RGBA, 281 | gl::UNSIGNED_BYTE, 282 | memory.as_ptr() as *const _, 283 | ); 284 | } 285 | 286 | Ok(()) 287 | } 288 | } 289 | -------------------------------------------------------------------------------- /glow_wrap/src/lib.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::CStr; 2 | use std::sync::Arc; 3 | use std::sync::OnceLock; 4 | #[cfg(target_arch = "wasm32")] 5 | use std::{borrow::BorrowMut, cell::RefCell}; 6 | 7 | use glow::HasContext as _; 8 | 9 | mod key; 10 | use key::ConvertKey; 11 | 12 | #[cfg(target_arch = "wasm32")] 13 | struct Ref(T); 14 | #[cfg(target_arch = "wasm32")] 15 | unsafe impl Send for Ref {} 16 | #[cfg(target_arch = "wasm32")] 17 | unsafe impl Sync for Ref {} 18 | 19 | #[cfg(not(target_arch = "wasm32"))] 20 | static CONTEXT: OnceLock> = OnceLock::new(); 21 | #[cfg(target_arch = "wasm32")] 22 | static CONTEXT: OnceLock>> = OnceLock::new(); 23 | 24 | #[cfg(target_arch = "wasm32")] 25 | slotmap::new_key_type! { pub struct WebGlUniformLocationKey; } 26 | #[cfg(target_arch = "wasm32")] 27 | unsafe impl Send for WebGlUniformLocationKey {} 28 | #[cfg(target_arch = "wasm32")] 29 | unsafe impl Sync for WebGlUniformLocationKey {} 30 | 31 | #[cfg(target_arch = "wasm32")] 32 | static LOCATION: OnceLock< 33 | Ref>>, 34 | > = OnceLock::new(); 35 | 36 | #[cfg(not(target_arch = "wasm32"))] 37 | pub fn set_context(ctx: Arc) { 38 | let _ = CONTEXT.set(ctx); 39 | } 40 | 41 | #[cfg(not(target_arch = "wasm32"))] 42 | #[inline] 43 | fn gl() -> &'static glow::Context { 44 | CONTEXT.get().unwrap().as_ref() 45 | } 46 | 47 | #[cfg(target_arch = "wasm32")] 48 | pub fn set_context(ctx: Arc) { 49 | use slotmap::SlotMap; 50 | let _ = CONTEXT.set(Ref(ctx)); 51 | let _ = LOCATION.set(Ref(RefCell::new(SlotMap::with_key()))); 52 | } 53 | 54 | #[cfg(target_arch = "wasm32")] 55 | #[inline] 56 | fn gl() -> &'static glow::Context { 57 | CONTEXT.get().unwrap().0.as_ref() 58 | } 59 | 60 | pub mod types { 61 | use std::usize; 62 | 63 | pub type GLfloat = f32; 64 | pub type GLenum = u32; 65 | pub type GLuint = u32; 66 | pub type GLsizei = i32; 67 | pub type GLboolean = u8; 68 | pub type GLint = i32; 69 | pub type GLchar = std::ffi::c_char; 70 | pub type GLsizeiptr = usize; 71 | pub type GLbitfield = u32; 72 | } 73 | 74 | pub use glow::{ 75 | ARRAY_BUFFER, BLEND, CLAMP_TO_BORDER, CLAMP_TO_EDGE, COLOR_BUFFER_BIT, COMPILE_STATUS, 76 | CONSTANT_COLOR, CULL_FACE, DEPTH_BUFFER_BIT, DST_ALPHA, DST_COLOR, DYNAMIC_DRAW, EQUAL, FALSE, 77 | FLOAT, FRAGMENT_SHADER, FRAMEBUFFER_SRGB, FUNC_ADD, FUNC_SUBTRACT, INCR, KEEP, LINEAR, 78 | LINEAR_MIPMAP_LINEAR, LINEAR_MIPMAP_NEAREST, MIRRORED_REPEAT, NEAREST, NEAREST_MIPMAP_LINEAR, 79 | NEAREST_MIPMAP_NEAREST, NEVER, NOTEQUAL, ONE, ONE_MINUS_SRC_ALPHA, REPEAT, REPLACE, RGBA, 80 | SCISSOR_TEST, SRC_ALPHA, SRC_COLOR, SRGB_ALPHA, STENCIL_BUFFER_BIT, STENCIL_TEST, TEXTURE_2D, 81 | TEXTURE_BORDER_COLOR, TEXTURE_MAG_FILTER, TEXTURE_MIN_FILTER, TEXTURE_WRAP_S, TEXTURE_WRAP_T, 82 | TRIANGLES, TRUE, UNSIGNED_BYTE, VERTEX_SHADER, ZERO, 83 | }; 84 | 85 | #[allow(non_snake_case)] 86 | pub unsafe fn DeleteTextures(n: types::GLsizei, textures: *const types::GLuint) { 87 | let textures = std::slice::from_raw_parts(textures, n as usize); 88 | for texture in textures { 89 | gl().delete_texture(glow::Texture::from_key(*texture)); 90 | } 91 | } 92 | 93 | #[allow(non_snake_case)] 94 | pub unsafe fn GenTextures(_: types::GLsizei, textures: *mut types::GLuint) { 95 | if let Ok(id) = gl().create_texture() { 96 | *textures = glow::Texture::to_key(&id); 97 | } 98 | } 99 | 100 | #[allow(non_snake_case)] 101 | pub unsafe fn BindTexture(target: u32, texture: types::GLuint) { 102 | gl().bind_texture(target, Some(glow::Texture::from_key(texture))); 103 | } 104 | 105 | #[allow(non_snake_case)] 106 | pub unsafe fn TexParameteri(target: u32, parameter: u32, value: i32) { 107 | gl().tex_parameter_i32(target, parameter, value); 108 | } 109 | 110 | #[allow(non_snake_case)] 111 | pub unsafe fn TexParameterfv(target: u32, parameter: u32, values: *const f32) { 112 | let values = std::slice::from_raw_parts(values, 4); 113 | gl().tex_parameter_f32_slice(target, parameter, values); 114 | } 115 | 116 | #[allow(non_snake_case)] 117 | pub unsafe fn GenerateMipmap(target: u32) { 118 | gl().generate_mipmap(target); 119 | } 120 | 121 | #[allow(non_snake_case)] 122 | pub unsafe fn TexImage2D( 123 | target: u32, 124 | level: i32, 125 | internal_format: i32, 126 | width: i32, 127 | height: i32, 128 | border: i32, 129 | format: u32, 130 | ty: u32, 131 | pixels: *const u8, 132 | ) { 133 | debug_assert!(format == glow::SRGB_ALPHA || format == glow::RGBA); 134 | let pixels = std::slice::from_raw_parts(pixels, (width * height * 4) as usize); 135 | gl().tex_image_2d( 136 | target, 137 | level, 138 | internal_format, 139 | width, 140 | height, 141 | border, 142 | format, 143 | ty, 144 | glow::PixelUnpackData::Slice(Some(pixels)), 145 | ); 146 | } 147 | 148 | #[allow(non_snake_case)] 149 | pub unsafe fn TexSubImage2D( 150 | target: u32, 151 | level: i32, 152 | x_offset: i32, 153 | y_offset: i32, 154 | width: i32, 155 | height: i32, 156 | format: u32, 157 | ty: u32, 158 | pixels: *const u8, 159 | ) { 160 | debug_assert!(format == glow::SRGB_ALPHA || format == glow::RGBA); 161 | let pixels = std::slice::from_raw_parts(pixels, (width * height * 4) as usize); 162 | gl().tex_sub_image_2d( 163 | target, 164 | level, 165 | x_offset, 166 | y_offset, 167 | width, 168 | height, 169 | format, 170 | ty, 171 | glow::PixelUnpackData::Slice(Some(pixels)), 172 | ); 173 | } 174 | 175 | #[allow(non_snake_case)] 176 | pub unsafe fn GetUniformLocation( 177 | program: types::GLuint, 178 | name: *const types::GLchar, 179 | ) -> types::GLint { 180 | let name = char_ptr_to_str(name); 181 | let location = gl().get_uniform_location(glow::Program::from_key(program), name); 182 | 183 | if let Some(location) = location { 184 | #[cfg(target_arch = "wasm32")] 185 | { 186 | if let Some(Ref(slot)) = LOCATION.get().borrow_mut().as_mut() { 187 | let key = slot.borrow_mut().insert(location); 188 | return WebGlUniformLocationKey::to_key(&key) as types::GLint; 189 | } 190 | } 191 | #[cfg(not(target_arch = "wasm32"))] 192 | { 193 | return glow::UniformLocation::to_key(&location) as types::GLint; 194 | } 195 | } 196 | 197 | return 0; 198 | } 199 | 200 | #[cfg(target_arch = "wasm32")] 201 | unsafe fn get_uniform(location: types::GLuint, f: F) 202 | where 203 | F: FnOnce(&web_sys::WebGlUniformLocation), 204 | { 205 | let key = WebGlUniformLocationKey::from_key(location); 206 | if let Some(Ref(slot)) = LOCATION.get().borrow_mut().as_mut() { 207 | if let Some(location) = slot.borrow_mut().get(key) { 208 | f(&location); 209 | } 210 | } 211 | } 212 | 213 | #[cfg(not(target_arch = "wasm32"))] 214 | unsafe fn get_uniform(location: types::GLuint, f: F) 215 | where 216 | F: FnOnce(&glow::UniformLocation), 217 | { 218 | let location = glow::UniformLocation::from_key(location); 219 | f(&location); 220 | } 221 | 222 | #[allow(non_snake_case)] 223 | pub unsafe fn CreateProgram() -> types::GLuint { 224 | match gl().create_program() { 225 | Ok(program) => return glow::Program::to_key(&program), 226 | Err(_) => {} 227 | }; 228 | return 0; 229 | } 230 | 231 | #[allow(non_snake_case)] 232 | pub unsafe fn LinkProgram(program: types::GLuint) { 233 | let program = glow::Program::from_key(program); 234 | gl().link_program(program); 235 | } 236 | 237 | #[allow(non_snake_case)] 238 | pub unsafe fn UseProgram(program: types::GLuint) { 239 | let program = glow::Program::from_key(program); 240 | gl().use_program(Some(program)); 241 | } 242 | 243 | #[allow(non_snake_case)] 244 | pub unsafe fn DeleteProgram(program: types::GLuint) { 245 | let program = glow::Program::from_key(program); 246 | gl().delete_program(program); 247 | } 248 | 249 | #[allow(non_snake_case)] 250 | pub unsafe fn ProgramUniform1f(program: types::GLuint, location: types::GLint, value: f32) { 251 | let program = glow::Program::from_key(program); 252 | gl().use_program(Some(program)); 253 | 254 | get_uniform(location as u32, |location| { 255 | gl().uniform_1_f32(Some(&location), value); 256 | }); 257 | } 258 | 259 | #[allow(non_snake_case)] 260 | pub unsafe fn ProgramUniform1i(program: types::GLuint, location: types::GLint, value: i32) { 261 | let program = glow::Program::from_key(program); 262 | gl().use_program(Some(program)); 263 | get_uniform(location as u32, |location| { 264 | gl().uniform_1_i32(Some(&location), value); 265 | }); 266 | } 267 | 268 | #[allow(non_snake_case)] 269 | pub unsafe fn ProgramUniform2f(program: types::GLuint, location: types::GLint, x: f32, y: f32) { 270 | let program = glow::Program::from_key(program); 271 | gl().use_program(Some(program)); 272 | get_uniform(location as u32, |location| { 273 | gl().uniform_2_f32(Some(&location), x, y); 274 | }); 275 | } 276 | 277 | #[allow(non_snake_case)] 278 | pub unsafe fn ProgramUniform3f( 279 | program: types::GLuint, 280 | location: types::GLint, 281 | x: f32, 282 | y: f32, 283 | z: f32, 284 | ) { 285 | let program = glow::Program::from_key(program); 286 | gl().use_program(Some(program)); 287 | 288 | get_uniform(location as u32, |location| { 289 | gl().uniform_3_f32(Some(&location), x, y, z); 290 | }); 291 | } 292 | 293 | #[allow(non_snake_case)] 294 | pub unsafe fn ProgramUniform4f( 295 | program: types::GLuint, 296 | location: types::GLint, 297 | x: f32, 298 | y: f32, 299 | z: f32, 300 | w: f32, 301 | ) { 302 | let program = glow::Program::from_key(program); 303 | gl().use_program(Some(program)); 304 | get_uniform(location as u32, |location| { 305 | gl().uniform_4_f32(Some(&location), x, y, z, w); 306 | }); 307 | } 308 | 309 | #[allow(non_snake_case)] 310 | pub unsafe fn Uniform4f(location: types::GLint, x: f32, y: f32, z: f32, w: f32) { 311 | get_uniform(location as u32, |location| { 312 | gl().uniform_4_f32(Some(&location), x, y, z, w); 313 | }); 314 | } 315 | 316 | #[allow(non_snake_case)] 317 | pub unsafe fn ProgramUniformMatrix2fv( 318 | program: types::GLuint, 319 | location: types::GLint, 320 | count: types::GLint, 321 | transpose: types::GLboolean, 322 | value: *const f32, 323 | ) { 324 | let program = glow::Program::from_key(program); 325 | gl().use_program(Some(program)); 326 | let value = std::slice::from_raw_parts(value, count as usize * 4); 327 | 328 | get_uniform(location as u32, |location| { 329 | gl().uniform_matrix_2_f32_slice(Some(&location), transpose == TRUE, value); 330 | }); 331 | } 332 | 333 | #[allow(non_snake_case)] 334 | pub unsafe fn ProgramUniformMatrix3fv( 335 | program: types::GLuint, 336 | location: types::GLint, 337 | count: types::GLint, 338 | transpose: types::GLboolean, 339 | value: *const f32, 340 | ) { 341 | let program = glow::Program::from_key(program); 342 | gl().use_program(Some(program)); 343 | let value = std::slice::from_raw_parts(value, count as usize * 9); 344 | 345 | get_uniform(location as u32, |location| { 346 | gl().uniform_matrix_3_f32_slice(Some(&location), transpose == TRUE, value); 347 | }); 348 | } 349 | 350 | #[allow(non_snake_case)] 351 | pub unsafe fn ProgramUniformMatrix4fv( 352 | program: types::GLuint, 353 | location: types::GLint, 354 | count: types::GLint, 355 | transpose: types::GLboolean, 356 | value: *const f32, 357 | ) { 358 | let program = glow::Program::from_key(program); 359 | gl().use_program(Some(program)); 360 | let value = std::slice::from_raw_parts(value, count as usize * 16); 361 | 362 | get_uniform(location as u32, |location| { 363 | gl().uniform_matrix_4_f32_slice(Some(&location), transpose == TRUE, value); 364 | }); 365 | } 366 | 367 | #[allow(non_snake_case)] 368 | pub unsafe fn DeleteBuffers(_: types::GLsizei, buffer: &types::GLuint) { 369 | let buffer = glow::Buffer::from_key(*buffer); 370 | gl().delete_buffer(buffer) 371 | } 372 | 373 | #[allow(non_snake_case)] 374 | pub unsafe fn BindVertexArray(array: types::GLuint) { 375 | if array > 0 { 376 | let array = glow::VertexArray::from_key(array); 377 | gl().bind_vertex_array(Some(array)); 378 | } 379 | } 380 | 381 | #[allow(non_snake_case)] 382 | pub unsafe fn GenVertexArrays(_: types::GLsizei, arrays: *mut types::GLuint) { 383 | match gl().create_vertex_array() { 384 | Ok(array) => *arrays = glow::VertexArray::to_key(&array), 385 | Err(_) => {} 386 | }; 387 | } 388 | 389 | #[allow(non_snake_case)] 390 | pub unsafe fn DeleteVertexArrays(_: types::GLsizei, array: *const types::GLuint) { 391 | let array = glow::VertexArray::from_key(*array); 392 | gl().delete_vertex_array(array); 393 | } 394 | 395 | #[allow(non_snake_case)] 396 | pub unsafe fn BindBuffer(target: types::GLenum, buffer: types::GLuint) { 397 | let buffer = glow::Buffer::from_key(buffer); 398 | gl().bind_buffer(target, Some(buffer)); 399 | } 400 | 401 | #[allow(non_snake_case)] 402 | pub unsafe fn VertexAttribPointer( 403 | index: types::GLuint, 404 | size: types::GLint, 405 | data_type: types::GLenum, 406 | normalized: types::GLboolean, 407 | stride: types::GLint, 408 | offset: *const types::GLint, 409 | ) { 410 | let offset = if offset == std::ptr::null() { 411 | 0 412 | } else { 413 | *offset 414 | }; 415 | gl().vertex_attrib_pointer_f32( 416 | index, 417 | size as i32, 418 | data_type, 419 | normalized == TRUE, 420 | stride, 421 | offset, 422 | ); 423 | } 424 | 425 | #[allow(non_snake_case)] 426 | pub unsafe fn GenBuffers(_: types::GLsizei, buffer: &mut types::GLuint) { 427 | if let Ok(buf) = gl().create_buffer() { 428 | *buffer = glow::Buffer::to_key(&buf); 429 | } 430 | } 431 | 432 | #[allow(non_snake_case)] 433 | pub unsafe fn EnableVertexAttribArray(location: types::GLuint) { 434 | gl().enable_vertex_attrib_array(location); 435 | } 436 | 437 | #[allow(non_snake_case)] 438 | pub unsafe fn BufferData( 439 | target: types::GLenum, 440 | size: types::GLsizeiptr, 441 | data: *const f64, 442 | usage: types::GLenum, 443 | ) { 444 | let data: *const u8 = std::mem::transmute(data); 445 | let data = std::slice::from_raw_parts(data, size); 446 | gl().buffer_data_u8_slice(target, data, usage); 447 | } 448 | 449 | #[allow(non_snake_case)] 450 | pub unsafe fn CreateShader(shader_type: types::GLenum) -> types::GLuint { 451 | if let Ok(shader) = gl().create_shader(shader_type) { 452 | return glow::Shader::to_key(&shader); 453 | } 454 | return 0; 455 | } 456 | 457 | #[allow(non_snake_case)] 458 | pub unsafe fn ShaderSource( 459 | shader: types::GLuint, 460 | _count: types::GLsizei, 461 | source: *const *const types::GLchar, 462 | _length: *const types::GLint, 463 | ) { 464 | let shader = glow::Shader::from_key(shader); 465 | let source = char_ptr_to_str(*source); 466 | gl().shader_source(shader, source); 467 | } 468 | 469 | #[allow(non_snake_case)] 470 | pub unsafe fn CompileShader(shader: types::GLuint) { 471 | let shader = glow::Shader::from_key(shader); 472 | gl().compile_shader(shader); 473 | } 474 | 475 | #[allow(non_snake_case)] 476 | pub unsafe fn GetCompleStatus(shader: types::GLuint, params: *mut types::GLint) { 477 | let shader = glow::Shader::from_key(shader); 478 | let status = gl().get_shader_compile_status(shader); 479 | *params = status as types::GLint; 480 | } 481 | 482 | #[allow(non_snake_case)] 483 | pub unsafe fn GetShaderInfoLog(shader: types::GLuint) -> String { 484 | let shader = glow::Shader::from_key(shader); 485 | let log = gl().get_shader_info_log(shader); 486 | log 487 | } 488 | 489 | #[allow(non_snake_case)] 490 | pub unsafe fn AttachShader(program: types::GLuint, shader: types::GLuint) { 491 | let program = glow::Program::from_key(program); 492 | let shader = glow::Shader::from_key(shader); 493 | gl().attach_shader(program, shader); 494 | } 495 | 496 | #[allow(non_snake_case)] 497 | pub unsafe fn DeleteShader(shader: types::GLuint) { 498 | let shader = glow::Shader::from_key(shader); 499 | gl().delete_shader(shader); 500 | } 501 | 502 | #[allow(non_snake_case)] 503 | pub unsafe fn GetAttribLocation( 504 | program: types::GLuint, 505 | name: *const types::GLchar, 506 | ) -> types::GLint { 507 | let program = glow::Program::from_key(program); 508 | gl().get_attrib_location(program, char_ptr_to_str(name)) 509 | .map(|l| l as i32) 510 | .unwrap_or(-1) 511 | } 512 | 513 | #[allow(non_snake_case)] 514 | pub unsafe fn Enable(parameter: types::GLenum) { 515 | gl().enable(parameter) 516 | } 517 | 518 | #[allow(non_snake_case)] 519 | pub unsafe fn Disable(parameter: types::GLenum) { 520 | gl().disable(parameter) 521 | } 522 | 523 | #[allow(non_snake_case)] 524 | pub unsafe fn DrawArrays(mode: types::GLenum, first: types::GLint, count: types::GLsizei) { 525 | gl().draw_arrays(mode, first, count) 526 | } 527 | 528 | #[allow(non_snake_case)] 529 | pub unsafe fn BindFragDataLocation( 530 | program: types::GLuint, 531 | color: types::GLuint, 532 | name: *const types::GLchar, 533 | ) { 534 | let program = glow::Program::from_key(program); 535 | gl().bind_frag_data_location(program, color, char_ptr_to_str(name)) 536 | } 537 | 538 | #[allow(non_snake_case)] 539 | pub unsafe fn Viewport( 540 | x: types::GLint, 541 | y: types::GLint, 542 | width: types::GLsizei, 543 | height: types::GLsizei, 544 | ) { 545 | gl().viewport(x, y, width, height) 546 | } 547 | 548 | #[allow(non_snake_case)] 549 | pub unsafe fn ClearColor( 550 | red: types::GLfloat, 551 | green: types::GLfloat, 552 | blue: types::GLfloat, 553 | alpha: types::GLfloat, 554 | ) { 555 | gl().clear_color(red, green, blue, alpha); 556 | } 557 | 558 | #[allow(non_snake_case)] 559 | pub unsafe fn Clear(mask: types::GLbitfield) { 560 | gl().clear(mask); 561 | } 562 | 563 | #[allow(non_snake_case)] 564 | pub unsafe fn ClearStencil(stencil: types::GLint) { 565 | gl().clear_stencil(stencil); 566 | } 567 | 568 | #[allow(non_snake_case)] 569 | pub unsafe fn Scissor( 570 | x: types::GLint, 571 | y: types::GLint, 572 | width: types::GLsizei, 573 | height: types::GLsizei, 574 | ) { 575 | gl().scissor(x, y, width, height); 576 | } 577 | 578 | #[allow(non_snake_case)] 579 | pub unsafe fn StencilFunc(func: types::GLenum, ref_: types::GLint, mask: types::GLuint) { 580 | gl().stencil_func(func, ref_, mask); 581 | } 582 | 583 | #[allow(non_snake_case)] 584 | pub unsafe fn StencilMask(mask: types::GLuint) { 585 | gl().stencil_mask(mask); 586 | } 587 | 588 | #[allow(non_snake_case)] 589 | pub unsafe fn StencilOp(fail: types::GLenum, zfail: types::GLenum, zpass: types::GLenum) { 590 | gl().stencil_op(fail, zfail, zpass); 591 | } 592 | 593 | #[allow(non_snake_case)] 594 | pub unsafe fn BlendColor( 595 | red: types::GLfloat, 596 | green: types::GLfloat, 597 | blue: types::GLfloat, 598 | alpha: types::GLfloat, 599 | ) { 600 | gl().blend_color(red, green, blue, alpha); 601 | } 602 | 603 | #[allow(non_snake_case)] 604 | pub unsafe fn BlendEquationSeparate(modeRGB: types::GLenum, modeAlpha: types::GLenum) { 605 | gl().blend_equation_separate(modeRGB, modeAlpha); 606 | } 607 | 608 | #[allow(non_snake_case)] 609 | pub unsafe fn BlendFuncSeparate( 610 | sfactorRGB: types::GLenum, 611 | dfactorRGB: types::GLenum, 612 | sfactorAlpha: types::GLenum, 613 | dfactorAlpha: types::GLenum, 614 | ) { 615 | gl().blend_func_separate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); 616 | } 617 | 618 | #[inline] 619 | unsafe fn char_ptr_to_str<'a>(ptr: *const types::GLchar) -> &'a str { 620 | CStr::from_ptr(ptr).to_str().unwrap_or("") 621 | } 622 | 623 | #[allow(non_snake_case)] 624 | pub mod Enable { 625 | pub fn is_loaded() -> bool { 626 | true 627 | } 628 | } 629 | -------------------------------------------------------------------------------- /src/back_end.rs: -------------------------------------------------------------------------------- 1 | //! OpenGL back-end for Piston-Graphics. 2 | 3 | // External crates. 4 | use gl::types::{GLint, GLsizei, GLuint}; 5 | use graphics::color::gamma_srgb_to_linear; 6 | use graphics::BACK_END_MAX_VERTEX_COUNT as BUFFER_SIZE; 7 | use graphics::{Context, DrawState, Graphics, Viewport}; 8 | use shader_version::glsl::GLSL; 9 | use shader_version::{OpenGL, Shaders}; 10 | use std::ffi::CString; 11 | 12 | // Local crate. 13 | use crate::draw_state; 14 | use crate::shader_utils::{compile_shader, DynamicAttribute}; 15 | use crate::Texture; 16 | 17 | // The number of chunks to fill up before rendering. 18 | // Amount of memory used: `BUFFER_SIZE * CHUNKS * 4 * (2 + 4)` 19 | // `4` for bytes per f32, and `2 + 4` for position and color. 20 | const CHUNKS: usize = 100; 21 | 22 | // Whether to use WebGL-specific features; currently used to select appropriate shaders. 23 | const USE_WEBGL: bool = cfg!(all(target_arch = "wasm32", target_os = "unknown")) 24 | || cfg!(target_os = "emscripten") 25 | || cfg!(feature = "webgl"); 26 | 27 | /// Describes how to render colored objects. 28 | pub struct Colored { 29 | vao: GLuint, 30 | vertex_shader: GLuint, 31 | fragment_shader: GLuint, 32 | program: GLuint, 33 | pos: DynamicAttribute, 34 | color: DynamicAttribute, 35 | pos_buffer: Vec<[f32; 2]>, 36 | color_buffer: Vec<[f32; 4]>, 37 | offset: usize, 38 | } 39 | 40 | impl Drop for Colored { 41 | fn drop(&mut self) { 42 | unsafe { 43 | gl::DeleteVertexArrays(1, &self.vao); 44 | gl::DeleteProgram(self.program); 45 | gl::DeleteShader(self.vertex_shader); 46 | gl::DeleteShader(self.fragment_shader); 47 | } 48 | } 49 | } 50 | 51 | impl Colored { 52 | /// Generate using pass-through shaders. 53 | /// 54 | /// # Panics 55 | /// If the default pass-through shaders fail to compile 56 | pub fn new(glsl: GLSL) -> Self { 57 | use shaders::colored; 58 | let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) }; 59 | 60 | let mut vertex_shaders = Shaders::new(); 61 | if USE_WEBGL { 62 | vertex_shaders 63 | .set(GLSL::V1_20, src(colored::VERTEX_GLSL_120_WEBGL)) 64 | .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE_WEBGL)) 65 | } else { 66 | vertex_shaders 67 | .set(GLSL::V1_20, src(colored::VERTEX_GLSL_120)) 68 | .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE)) 69 | }; 70 | 71 | let mut fragment_shaders = Shaders::new(); 72 | if USE_WEBGL { 73 | fragment_shaders 74 | .set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120_WEBGL)) 75 | .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE_WEBGL)) 76 | } else { 77 | fragment_shaders 78 | .set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120)) 79 | .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE)) 80 | }; 81 | 82 | Colored::from_vs_fs(glsl, &vertex_shaders, &fragment_shaders).unwrap() 83 | } 84 | 85 | /// Generate using custom vertex and fragment shaders. 86 | pub fn from_vs_fs( 87 | glsl: GLSL, 88 | vertex_shaders: &Shaders, 89 | fragment_shaders: &Shaders, 90 | ) -> Result { 91 | let v_shader = vertex_shaders 92 | .get(glsl) 93 | .ok_or("No compatible vertex shader")?; 94 | 95 | let v_shader_compiled = compile_shader(gl::VERTEX_SHADER, v_shader) 96 | .map_err(|s| format!("Error compiling vertex shader: {}", s))?; 97 | 98 | let f_shader = fragment_shaders 99 | .get(glsl) 100 | .ok_or("No compatible fragment shader")?; 101 | 102 | let f_shader_compiled = compile_shader(gl::FRAGMENT_SHADER, f_shader) 103 | .map_err(|s| format!("Error compiling fragment shader: {}", s))?; 104 | 105 | let program; 106 | unsafe { 107 | program = gl::CreateProgram(); 108 | gl::AttachShader(program, v_shader_compiled); 109 | gl::AttachShader(program, f_shader_compiled); 110 | 111 | let c_o_color = CString::new("o_Color").unwrap(); 112 | if !USE_WEBGL { 113 | gl::BindFragDataLocation(program, 0, c_o_color.as_ptr()); 114 | } 115 | drop(c_o_color); 116 | } 117 | 118 | let mut vao = 0; 119 | unsafe { 120 | gl::GenVertexArrays(1, &mut vao); 121 | gl::LinkProgram(program); 122 | } 123 | let pos = DynamicAttribute::xy(program, "pos", vao).unwrap(); 124 | let color = DynamicAttribute::rgba(program, "color", vao).unwrap(); 125 | Ok(Colored { 126 | vao, 127 | vertex_shader: v_shader_compiled, 128 | fragment_shader: f_shader_compiled, 129 | program, 130 | pos, 131 | color, 132 | pos_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE], 133 | color_buffer: vec![[0.0; 4]; CHUNKS * BUFFER_SIZE], 134 | offset: 0, 135 | }) 136 | } 137 | 138 | fn flush(&mut self) { 139 | unsafe { 140 | gl::BindVertexArray(self.vao); 141 | // Render triangles whether they are facing 142 | // clockwise or counter clockwise. 143 | gl::Disable(gl::CULL_FACE); 144 | self.color.set(&self.color_buffer[..self.offset]); 145 | self.pos.set(&self.pos_buffer[..self.offset]); 146 | gl::DrawArrays(gl::TRIANGLES, 0, self.offset as i32); 147 | gl::BindVertexArray(0); 148 | } 149 | 150 | self.offset = 0; 151 | } 152 | } 153 | 154 | /// Describes how to render textured objects. 155 | pub struct Textured { 156 | vertex_shader: GLuint, 157 | fragment_shader: GLuint, 158 | program: GLuint, 159 | vao: GLuint, 160 | color: GLint, 161 | pos: DynamicAttribute, 162 | uv: DynamicAttribute, 163 | pos_buffer: Vec<[f32; 2]>, 164 | uv_buffer: Vec<[f32; 2]>, 165 | offset: usize, 166 | last_texture_id: GLuint, 167 | last_color: [f32; 4], 168 | } 169 | 170 | impl Drop for Textured { 171 | fn drop(&mut self) { 172 | unsafe { 173 | gl::DeleteVertexArrays(1, &self.vao); 174 | gl::DeleteProgram(self.program); 175 | gl::DeleteShader(self.vertex_shader); 176 | gl::DeleteShader(self.fragment_shader); 177 | } 178 | } 179 | } 180 | 181 | impl Textured { 182 | /// Generate using pass-through shaders. 183 | /// 184 | /// # Panics 185 | /// If the default pass-through shaders fail to compile 186 | pub fn new(glsl: GLSL) -> Self { 187 | use shaders::textured; 188 | let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) }; 189 | 190 | let mut vertex_shaders = Shaders::new(); 191 | if USE_WEBGL { 192 | vertex_shaders 193 | .set(GLSL::V1_20, src(textured::VERTEX_GLSL_120_WEBGL)) 194 | .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE_WEBGL)) 195 | } else { 196 | vertex_shaders 197 | .set(GLSL::V1_20, src(textured::VERTEX_GLSL_120)) 198 | .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE)) 199 | }; 200 | 201 | let mut fragment_shaders = Shaders::new(); 202 | if USE_WEBGL { 203 | fragment_shaders 204 | .set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120_WEBGL)) 205 | .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE_WEBGL)) 206 | } else { 207 | fragment_shaders 208 | .set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120)) 209 | .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE)) 210 | }; 211 | 212 | Textured::from_vs_fs(glsl, &vertex_shaders, &fragment_shaders).unwrap() 213 | } 214 | 215 | /// Generate using custom vertex and fragment shaders. 216 | pub fn from_vs_fs( 217 | glsl: GLSL, 218 | vertex_shaders: &Shaders, 219 | fragment_shaders: &Shaders, 220 | ) -> Result { 221 | let v_shader = vertex_shaders 222 | .get(glsl) 223 | .ok_or("No compatible vertex shader")?; 224 | 225 | let v_shader_compiled = compile_shader(gl::VERTEX_SHADER, v_shader) 226 | .map_err(|s| format!("Error compiling vertex shader: {}", s))?; 227 | 228 | let f_shader = fragment_shaders 229 | .get(glsl) 230 | .ok_or("No compatible fragment shader")?; 231 | 232 | let f_shader_compiled = compile_shader(gl::FRAGMENT_SHADER, f_shader) 233 | .map_err(|s| format!("Error compiling fragment shader: {}", s))?; 234 | 235 | let program; 236 | unsafe { 237 | program = gl::CreateProgram(); 238 | gl::AttachShader(program, v_shader_compiled); 239 | gl::AttachShader(program, f_shader_compiled); 240 | 241 | let c_o_color = CString::new("o_Color").unwrap(); 242 | if !USE_WEBGL { 243 | gl::BindFragDataLocation(program, 0, c_o_color.as_ptr()); 244 | } 245 | drop(c_o_color); 246 | } 247 | 248 | let mut vao = 0; 249 | unsafe { 250 | gl::GenVertexArrays(1, &mut vao); 251 | gl::LinkProgram(program); 252 | } 253 | let pos = DynamicAttribute::xy(program, "pos", vao).unwrap(); 254 | let c_color = CString::new("color").unwrap(); 255 | let color = unsafe { gl::GetUniformLocation(program, c_color.as_ptr()) }; 256 | drop(c_color); 257 | if color == -1 { 258 | panic!("Could not find uniform `color`"); 259 | } 260 | let uv = DynamicAttribute::uv(program, "uv", vao).unwrap(); 261 | Ok(Textured { 262 | vao, 263 | vertex_shader: v_shader_compiled, 264 | fragment_shader: f_shader_compiled, 265 | program, 266 | pos, 267 | color, 268 | uv, 269 | pos_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE], 270 | uv_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE], 271 | offset: 0, 272 | last_texture_id: 0, 273 | last_color: [0.0; 4], 274 | }) 275 | } 276 | 277 | fn flush(&mut self) { 278 | let texture_id = self.last_texture_id; 279 | let color = self.last_color; 280 | unsafe { 281 | gl::BindVertexArray(self.vao); 282 | gl::BindTexture(gl::TEXTURE_2D, texture_id); 283 | gl::Uniform4f(self.color, color[0], color[1], color[2], color[3]); 284 | // Render triangles whether they are facing 285 | // clockwise or counter clockwise. 286 | gl::Disable(gl::CULL_FACE); 287 | self.pos.set(&self.pos_buffer[..self.offset]); 288 | self.uv.set(&self.uv_buffer[..self.offset]); 289 | gl::DrawArrays(gl::TRIANGLES, 0, self.offset as i32); 290 | gl::BindVertexArray(0); 291 | } 292 | 293 | self.offset = 0; 294 | } 295 | } 296 | 297 | /// Describes how to render textured objects with individual vertex colors. 298 | pub struct TexturedColor { 299 | vertex_shader: GLuint, 300 | fragment_shader: GLuint, 301 | program: GLuint, 302 | vao: GLuint, 303 | pos: DynamicAttribute, 304 | uv: DynamicAttribute, 305 | color: DynamicAttribute, 306 | pos_buffer: Vec<[f32; 2]>, 307 | uv_buffer: Vec<[f32; 2]>, 308 | color_buffer: Vec<[f32; 4]>, 309 | offset: usize, 310 | last_texture_id: GLuint, 311 | } 312 | 313 | impl Drop for TexturedColor { 314 | fn drop(&mut self) { 315 | unsafe { 316 | gl::DeleteVertexArrays(1, &self.vao); 317 | gl::DeleteProgram(self.program); 318 | gl::DeleteShader(self.vertex_shader); 319 | gl::DeleteShader(self.fragment_shader); 320 | } 321 | } 322 | } 323 | 324 | impl TexturedColor { 325 | /// Generate using pass-through shaders. 326 | /// 327 | /// # Panics 328 | /// If the default pass-through shaders fail to compile 329 | pub fn new(glsl: GLSL) -> Self { 330 | use shaders::textured_color; 331 | let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) }; 332 | 333 | let mut vertex_shaders = Shaders::new(); 334 | if USE_WEBGL { 335 | vertex_shaders 336 | .set(GLSL::V1_20, src(textured_color::VERTEX_GLSL_120_WEBGL)) 337 | .set(GLSL::V1_50, src(textured_color::VERTEX_GLSL_150_CORE_WEBGL)) 338 | } else { 339 | vertex_shaders 340 | .set(GLSL::V1_20, src(textured_color::VERTEX_GLSL_120)) 341 | .set(GLSL::V1_50, src(textured_color::VERTEX_GLSL_150_CORE)) 342 | }; 343 | 344 | let mut fragment_shaders = Shaders::new(); 345 | if USE_WEBGL { 346 | fragment_shaders 347 | .set(GLSL::V1_20, src(textured_color::FRAGMENT_GLSL_120_WEBGL)) 348 | .set( 349 | GLSL::V1_50, 350 | src(textured_color::FRAGMENT_GLSL_150_CORE_WEBGL), 351 | ) 352 | } else { 353 | fragment_shaders 354 | .set(GLSL::V1_20, src(textured_color::FRAGMENT_GLSL_120)) 355 | .set(GLSL::V1_50, src(textured_color::FRAGMENT_GLSL_150_CORE)) 356 | }; 357 | 358 | TexturedColor::from_vs_fs(glsl, &vertex_shaders, &fragment_shaders).unwrap() 359 | } 360 | 361 | /// Generate using custom vertex and fragment shaders. 362 | pub fn from_vs_fs( 363 | glsl: GLSL, 364 | vertex_shaders: &Shaders, 365 | fragment_shaders: &Shaders, 366 | ) -> Result { 367 | let v_shader = vertex_shaders 368 | .get(glsl) 369 | .ok_or("No compatible vertex shader")?; 370 | 371 | let v_shader_compiled = compile_shader(gl::VERTEX_SHADER, v_shader) 372 | .map_err(|s| format!("Error compiling vertex shader: {}", s))?; 373 | 374 | let f_shader = fragment_shaders 375 | .get(glsl) 376 | .ok_or("No compatible fragment shader")?; 377 | 378 | let f_shader_compiled = compile_shader(gl::FRAGMENT_SHADER, f_shader) 379 | .map_err(|s| format!("Error compiling fragment shader: {}", s))?; 380 | 381 | let program; 382 | unsafe { 383 | program = gl::CreateProgram(); 384 | gl::AttachShader(program, v_shader_compiled); 385 | gl::AttachShader(program, f_shader_compiled); 386 | 387 | let c_o_color = CString::new("o_Color").unwrap(); 388 | if !USE_WEBGL { 389 | gl::BindFragDataLocation(program, 0, c_o_color.as_ptr()); 390 | } 391 | drop(c_o_color); 392 | } 393 | 394 | let mut vao = 0; 395 | unsafe { 396 | gl::GenVertexArrays(1, &mut vao); 397 | gl::LinkProgram(program); 398 | } 399 | let pos = DynamicAttribute::xy(program, "pos", vao).unwrap(); 400 | let color = DynamicAttribute::rgba(program, "color", vao).unwrap(); 401 | let uv = DynamicAttribute::uv(program, "uv", vao).unwrap(); 402 | Ok(TexturedColor { 403 | vao, 404 | vertex_shader: v_shader_compiled, 405 | fragment_shader: f_shader_compiled, 406 | program, 407 | pos, 408 | color, 409 | uv, 410 | pos_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE], 411 | uv_buffer: vec![[0.0; 2]; CHUNKS * BUFFER_SIZE], 412 | color_buffer: vec![[0.0; 4]; CHUNKS * BUFFER_SIZE], 413 | offset: 0, 414 | last_texture_id: 0, 415 | }) 416 | } 417 | 418 | fn flush(&mut self) { 419 | let texture_id = self.last_texture_id; 420 | unsafe { 421 | gl::BindVertexArray(self.vao); 422 | gl::BindTexture(gl::TEXTURE_2D, texture_id); 423 | // Render triangles whether they are facing 424 | // clockwise or counter clockwise. 425 | gl::Disable(gl::CULL_FACE); 426 | self.pos.set(&self.pos_buffer[..self.offset]); 427 | self.uv.set(&self.uv_buffer[..self.offset]); 428 | self.color.set(&self.color_buffer[..self.offset]); 429 | gl::DrawArrays(gl::TRIANGLES, 0, self.offset as i32); 430 | gl::BindVertexArray(0); 431 | } 432 | 433 | self.offset = 0; 434 | } 435 | } 436 | 437 | // Newlines and indents for cleaner panic message. 438 | const GL_FUNC_NOT_LOADED: &str = " 439 | OpenGL function pointers must be loaded before creating the `Gl` backend! 440 | For more info, see the following issue on GitHub: 441 | https://github.com/PistonDevelopers/opengl_graphics/issues/103 442 | "; 443 | 444 | /// Contains OpenGL data. 445 | pub struct GlGraphics { 446 | colored: Colored, 447 | textured: Textured, 448 | textured_color: TexturedColor, 449 | // Keeps track of the current shader program. 450 | current_program: Option, 451 | // Keeps track of the current draw state. 452 | current_draw_state: Option, 453 | // Keeps track of the current viewport 454 | current_viewport: Option, 455 | } 456 | 457 | impl GlGraphics { 458 | /// Creates a new OpenGL back-end. 459 | /// 460 | /// # Panics 461 | /// If the OpenGL function pointers have not been loaded yet. 462 | /// See https://github.com/PistonDevelopers/opengl_graphics/issues/103 for more info. 463 | pub fn new(opengl: OpenGL) -> Self { 464 | assert!(gl::Enable::is_loaded(), "{}", GL_FUNC_NOT_LOADED); 465 | 466 | let glsl = opengl.to_glsl(); 467 | // Load the vertices, color and texture coord buffers. 468 | GlGraphics { 469 | colored: Colored::new(glsl), 470 | textured: Textured::new(glsl), 471 | textured_color: TexturedColor::new(glsl), 472 | current_program: None, 473 | current_draw_state: None, 474 | current_viewport: None, 475 | } 476 | } 477 | 478 | /// Create a new OpenGL back-end with `Colored`, `Textured` and `TexturedColor` 479 | /// structs to describe how to render objects. 480 | /// 481 | /// # Panics 482 | /// If the OpenGL function pointers have not been loaded yet. 483 | /// See https://github.com/PistonDevelopers/opengl_graphics/issues/103 for more info. 484 | pub fn from_pieces( 485 | colored: Colored, 486 | textured: Textured, 487 | textured_color: TexturedColor, 488 | ) -> Self { 489 | assert!(gl::Enable::is_loaded(), "{}", GL_FUNC_NOT_LOADED); 490 | 491 | // Load the vertices, color and texture coord buffers. 492 | GlGraphics { 493 | colored, 494 | textured, 495 | textured_color, 496 | current_program: None, 497 | current_draw_state: None, 498 | current_viewport: None, 499 | } 500 | } 501 | 502 | /// Sets viewport with normalized coordinates and center as origin. 503 | fn viewport(&mut self, x: i32, y: i32, w: i32, h: i32) { 504 | unsafe { 505 | gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei); 506 | } 507 | } 508 | 509 | /// Returns the current program 510 | pub fn get_current_program(&self) -> Option { 511 | self.current_program 512 | } 513 | 514 | /// Sets the current program only if the program is not in use. 515 | pub fn use_program(&mut self, program: GLuint) { 516 | match self.current_program { 517 | None => {} 518 | Some(current_program) => { 519 | if program == current_program { 520 | return; 521 | } 522 | } 523 | } 524 | 525 | unsafe { 526 | gl::UseProgram(program); 527 | } 528 | self.current_program = Some(program); 529 | } 530 | 531 | /// Unset the current program. 532 | /// 533 | /// This forces the current program to be set on next drawing call. 534 | pub fn clear_program(&mut self) { 535 | self.current_program = None 536 | } 537 | 538 | /// Sets the current draw state, by detecting changes. 539 | pub fn use_draw_state(&mut self, draw_state: &DrawState) { 540 | match self.current_draw_state { 541 | None => { 542 | draw_state::bind_scissor(draw_state.scissor, &self.current_viewport); 543 | draw_state::bind_stencil(draw_state.stencil); 544 | draw_state::bind_blend(draw_state.blend); 545 | } 546 | Some(ref old_state) => { 547 | draw_state::bind_state(old_state, draw_state, &self.current_viewport); 548 | } 549 | } 550 | self.current_draw_state = Some(*draw_state); 551 | } 552 | 553 | /// Unsets the current draw state. 554 | /// 555 | /// This forces the current draw state to be set on next drawing call. 556 | pub fn clear_draw_state(&mut self) { 557 | self.current_draw_state = None; 558 | } 559 | 560 | /// Setup that should be called at the start of a frame's draw call. 561 | pub fn draw_begin(&mut self, viewport: Viewport) -> Context { 562 | let rect = viewport.rect; 563 | let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]); 564 | self.viewport(x, y, w, h); 565 | self.current_viewport = Some(viewport); 566 | self.clear_program(); 567 | unsafe { 568 | gl::Enable(gl::FRAMEBUFFER_SRGB); 569 | } 570 | Context::new_viewport(viewport) 571 | } 572 | 573 | /// Finalize the frame's draw calls. 574 | pub fn draw_end(&mut self) { 575 | if self.colored.offset > 0 { 576 | let program = self.colored.program; 577 | self.use_program(program); 578 | self.colored.flush(); 579 | } 580 | if self.textured.offset > 0 { 581 | let program = self.textured.program; 582 | self.use_program(program); 583 | self.textured.flush(); 584 | } 585 | if self.textured_color.offset > 0 { 586 | let program = self.textured_color.program; 587 | self.use_program(program); 588 | self.textured_color.flush(); 589 | } 590 | } 591 | 592 | /// Convenience for wrapping draw calls with the begin and end methods. 593 | /// 594 | /// This is preferred over using the draw_begin & draw_end methods 595 | /// explicitly but may be less flexible. 596 | pub fn draw(&mut self, viewport: Viewport, f: F) -> U 597 | where 598 | F: FnOnce(Context, &mut Self) -> U, 599 | { 600 | let c = self.draw_begin(viewport); 601 | let res = f(c, self); 602 | self.draw_end(); 603 | res 604 | } 605 | 606 | /// Assume all textures has alpha channel for now. 607 | pub fn has_texture_alpha(&self, _texture: &Texture) -> bool { 608 | true 609 | } 610 | } 611 | 612 | impl Graphics for GlGraphics { 613 | type Texture = Texture; 614 | 615 | fn clear_color(&mut self, color: [f32; 4]) { 616 | let color = gamma_srgb_to_linear(color); 617 | unsafe { 618 | let (r, g, b, a) = (color[0], color[1], color[2], color[3]); 619 | gl::ClearColor(r, g, b, a); 620 | gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT); 621 | } 622 | } 623 | 624 | fn clear_stencil(&mut self, value: u8) { 625 | unsafe { 626 | gl::ClearStencil(value as i32); 627 | gl::Clear(gl::STENCIL_BUFFER_BIT); 628 | } 629 | } 630 | 631 | fn tri_list(&mut self, draw_state: &DrawState, color: &[f32; 4], mut f: F) 632 | where 633 | F: FnMut(&mut dyn FnMut(&[[f32; 2]])), 634 | { 635 | let color = gamma_srgb_to_linear(*color); 636 | 637 | if self.textured.offset > 0 { 638 | let program = self.textured.program; 639 | self.use_program(program); 640 | self.textured.flush(); 641 | } 642 | if self.textured_color.offset > 0 { 643 | let program = self.textured_color.program; 644 | self.use_program(program); 645 | self.textured_color.flush(); 646 | } 647 | 648 | // Flush when draw state changes. 649 | if self.current_draw_state.is_none() 650 | || self.current_draw_state.as_ref().unwrap() != draw_state 651 | { 652 | let program = self.colored.program; 653 | self.use_program(program); 654 | if self.current_draw_state.is_none() { 655 | self.use_draw_state(&Default::default()); 656 | } 657 | if self.colored.offset > 0 { 658 | self.colored.flush(); 659 | } 660 | self.use_draw_state(draw_state); 661 | } 662 | 663 | f(&mut |vertices: &[[f32; 2]]| { 664 | let items = vertices.len(); 665 | 666 | // Render if there is not enough room. 667 | if self.colored.offset + items > BUFFER_SIZE * CHUNKS { 668 | let program = self.colored.program; 669 | self.use_program(program); 670 | self.colored.flush(); 671 | } 672 | 673 | let shader = &mut self.colored; 674 | for i in 0..items { 675 | shader.color_buffer[shader.offset + i] = color; 676 | } 677 | shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices); 678 | shader.offset += items; 679 | }); 680 | } 681 | 682 | fn tri_list_c(&mut self, draw_state: &DrawState, mut f: F) 683 | where 684 | F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 4]])), 685 | { 686 | if self.textured.offset > 0 { 687 | let program = self.textured.program; 688 | self.use_program(program); 689 | self.textured.flush(); 690 | } 691 | if self.textured_color.offset > 0 { 692 | let program = self.textured_color.program; 693 | self.use_program(program); 694 | self.textured_color.flush(); 695 | } 696 | 697 | // Flush when draw state changes. 698 | if self.current_draw_state.is_none() 699 | || self.current_draw_state.as_ref().unwrap() != draw_state 700 | { 701 | let program = self.colored.program; 702 | self.use_program(program); 703 | if self.current_draw_state.is_none() { 704 | self.use_draw_state(&Default::default()); 705 | } 706 | if self.colored.offset > 0 { 707 | self.colored.flush(); 708 | } 709 | self.use_draw_state(draw_state); 710 | } 711 | 712 | f(&mut |vertices: &[[f32; 2]], colors: &[[f32; 4]]| { 713 | let items = vertices.len(); 714 | 715 | // Render if there is not enough room. 716 | if self.colored.offset + items > BUFFER_SIZE * CHUNKS { 717 | let program = self.colored.program; 718 | self.use_program(program); 719 | self.colored.flush(); 720 | } 721 | 722 | let shader = &mut self.colored; 723 | for (i, color) in colors.iter().enumerate() { 724 | shader.color_buffer[shader.offset + i] = gamma_srgb_to_linear(*color); 725 | } 726 | shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices); 727 | shader.offset += items; 728 | }); 729 | } 730 | 731 | fn tri_list_uv( 732 | &mut self, 733 | draw_state: &DrawState, 734 | color: &[f32; 4], 735 | texture: &Texture, 736 | mut f: F, 737 | ) where 738 | F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]])), 739 | { 740 | let color = gamma_srgb_to_linear(*color); 741 | 742 | if self.colored.offset > 0 { 743 | let program = self.colored.program; 744 | self.use_program(program); 745 | self.colored.flush(); 746 | } 747 | if self.textured_color.offset > 0 { 748 | let program = self.textured_color.program; 749 | self.use_program(program); 750 | self.textured_color.flush(); 751 | } 752 | 753 | // Flush when draw state changes. 754 | if self.current_draw_state.is_none() 755 | || self.current_draw_state.as_ref().unwrap() != draw_state 756 | || self.textured.last_texture_id != texture.get_id() 757 | || self.textured.last_color != color 758 | { 759 | let program = self.textured.program; 760 | if self.current_draw_state.is_none() { 761 | self.use_draw_state(&Default::default()); 762 | } 763 | if self.textured.offset > 0 { 764 | self.use_program(program); 765 | self.textured.flush(); 766 | } 767 | self.use_draw_state(draw_state); 768 | } 769 | 770 | self.textured.last_texture_id = texture.get_id(); 771 | self.textured.last_color = color; 772 | f(&mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]]| { 773 | let items = vertices.len(); 774 | 775 | // Render if there is not enough room. 776 | if self.textured.offset + items > BUFFER_SIZE * CHUNKS { 777 | let shader_program = self.textured.program; 778 | self.use_program(shader_program); 779 | self.textured.flush(); 780 | } 781 | 782 | let shader = &mut self.textured; 783 | shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices); 784 | shader.uv_buffer[shader.offset..shader.offset + items].copy_from_slice(texture_coords); 785 | shader.offset += items; 786 | }); 787 | } 788 | 789 | fn tri_list_uv_c(&mut self, draw_state: &DrawState, texture: &Texture, mut f: F) 790 | where 791 | F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]], &[[f32; 4]])), 792 | { 793 | if self.colored.offset > 0 { 794 | let program = self.colored.program; 795 | self.use_program(program); 796 | self.colored.flush(); 797 | } 798 | if self.textured.offset > 0 { 799 | let program = self.textured.program; 800 | self.use_program(program); 801 | self.textured.flush(); 802 | } 803 | 804 | // Flush when draw state changes. 805 | if self.current_draw_state.is_none() 806 | || self.current_draw_state.as_ref().unwrap() != draw_state 807 | || self.textured_color.last_texture_id != texture.get_id() 808 | { 809 | let program = self.textured_color.program; 810 | if self.current_draw_state.is_none() { 811 | self.use_draw_state(&Default::default()); 812 | } 813 | if self.textured_color.offset > 0 { 814 | self.use_program(program); 815 | self.textured_color.flush(); 816 | } 817 | self.use_draw_state(draw_state); 818 | } 819 | 820 | self.textured_color.last_texture_id = texture.get_id(); 821 | f( 822 | &mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]], colors: &[[f32; 4]]| { 823 | let items = vertices.len(); 824 | 825 | // Render if there is not enough room. 826 | if self.textured_color.offset + items > BUFFER_SIZE * CHUNKS { 827 | let shader_program = self.textured_color.program; 828 | self.use_program(shader_program); 829 | self.textured_color.flush(); 830 | } 831 | 832 | let shader = &mut self.textured_color; 833 | for (i, color) in colors.iter().enumerate() { 834 | shader.color_buffer[shader.offset + i] = gamma_srgb_to_linear(*color); 835 | } 836 | shader.pos_buffer[shader.offset..shader.offset + items].copy_from_slice(vertices); 837 | shader.uv_buffer[shader.offset..shader.offset + items] 838 | .copy_from_slice(texture_coords); 839 | shader.offset += items; 840 | }, 841 | ); 842 | } 843 | } 844 | 845 | // Might not fail if previous tests loaded functions. 846 | #[test] 847 | #[should_panic] 848 | fn test_gl_loaded() { 849 | GlGraphics::new(OpenGL::V3_2); 850 | } 851 | --------------------------------------------------------------------------------