::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 |
--------------------------------------------------------------------------------
]