1use glium::backend::Facade;
2use glium::index::{NoIndices, PrimitiveType};
3use glium::{Frame, Program, Surface, VertexBuffer};
4use graphics::color::gamma_srgb_to_linear;
5use graphics::{self, DrawState, Graphics, Viewport};
6use shader_version::glsl::GLSL;
7use shader_version::{OpenGL, Shaders};
8use Texture;
9
10use draw_state;
11
12const CHUNKS: usize = 100;
13
14#[derive(Copy, Clone)]
15struct PlainVertex {
16 color: [f32; 4],
17 pos: [f32; 2],
18}
19
20implement_vertex!(PlainVertex, color, pos);
21
22#[derive(Copy, Clone)]
23struct TexturedVertex {
24 pos: [f32; 2],
25 uv: [f32; 2],
26}
27
28implement_vertex!(TexturedVertex, pos, uv);
29
30#[derive(Copy, Clone)]
31struct TexturedColorVertex {
32 pos: [f32; 2],
33 uv: [f32; 2],
34 color: [f32; 4],
35}
36
37implement_vertex!(TexturedColorVertex, pos, uv, color);
38
39pub struct Glium2d {
41 colored_offset: usize,
43 colored_draw_state: DrawState,
45 plain_buffer: VertexBuffer<PlainVertex>,
46 textured_buffer: VertexBuffer<TexturedVertex>,
47 textured_color_buffer: VertexBuffer<TexturedColorVertex>,
48 shader_texture_color: Program,
49 shader_texture: Program,
50 shader_color: Program,
51}
52
53impl Glium2d {
54 pub fn new<W>(opengl: OpenGL, window: &W) -> Glium2d
56 where
57 W: Facade,
58 {
59 use shaders::{colored, textured, textured_color};
60
61 let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };
62 let glsl = opengl.to_glsl();
63
64 let plain_buffer =
65 VertexBuffer::empty_dynamic(window, CHUNKS * graphics::BACK_END_MAX_VERTEX_COUNT)
66 .unwrap();
67 Glium2d {
68 colored_offset: 0,
69 colored_draw_state: Default::default(),
70 plain_buffer: plain_buffer,
71 textured_buffer: VertexBuffer::empty_dynamic(
72 window,
73 graphics::BACK_END_MAX_VERTEX_COUNT,
74 )
75 .unwrap(),
76 textured_color_buffer: VertexBuffer::empty_dynamic(
77 window,
78 graphics::BACK_END_MAX_VERTEX_COUNT,
79 )
80 .unwrap(),
81 shader_texture_color: Program::from_source(
82 window,
83 Shaders::new()
84 .set(GLSL::V1_20, src(textured_color::VERTEX_GLSL_120))
85 .set(GLSL::V1_50, src(textured_color::VERTEX_GLSL_150_CORE))
86 .get(glsl)
87 .unwrap(),
88 Shaders::new()
89 .set(GLSL::V1_20, src(textured_color::FRAGMENT_GLSL_120))
90 .set(GLSL::V1_50, src(textured_color::FRAGMENT_GLSL_150_CORE))
91 .get(glsl)
92 .unwrap(),
93 None,
94 )
95 .ok()
96 .expect("failed to initialize textured color shader"),
97 shader_texture: Program::from_source(
98 window,
99 Shaders::new()
100 .set(GLSL::V1_20, src(textured::VERTEX_GLSL_120))
101 .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE))
102 .get(glsl)
103 .unwrap(),
104 Shaders::new()
105 .set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120))
106 .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE))
107 .get(glsl)
108 .unwrap(),
109 None,
110 )
111 .ok()
112 .expect("failed to initialize textured shader"),
113 shader_color: Program::from_source(
114 window,
115 Shaders::new()
116 .set(GLSL::V1_20, src(colored::VERTEX_GLSL_120))
117 .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE))
118 .get(glsl)
119 .unwrap(),
120 Shaders::new()
121 .set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120))
122 .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE))
123 .get(glsl)
124 .unwrap(),
125 None,
126 )
127 .ok()
128 .expect("failed to initialize colored shader"),
129 }
130 }
131
132 pub fn draw<F, U>(&mut self, target: &mut Frame, viewport: Viewport, f: F) -> U
134 where
135 F: FnOnce(graphics::Context, &mut GliumGraphics<Frame>) -> U,
136 {
137 use graphics::Context;
138
139 let ref mut g = GliumGraphics::new(self, target);
140 let c = Context::new_viewport(viewport);
141 let res = f(c, g);
142 if g.system.colored_offset > 0 {
143 g.flush_colored();
144 }
145 res
146 }
147}
148
149pub struct GliumGraphics<'d, 's, S: 's> {
151 system: &'d mut Glium2d,
152 surface: &'s mut S,
153}
154
155impl<'d, 's, S: Surface> GliumGraphics<'d, 's, S> {
156 pub fn new(system: &'d mut Glium2d, surface: &'s mut S) -> GliumGraphics<'d, 's, S> {
158 GliumGraphics {
159 system: system,
160 surface: surface,
161 }
162 }
163
164 fn flush_colored(&mut self) {
165 let slice = self
166 .system
167 .plain_buffer
168 .slice(0..self.system.colored_offset)
169 .unwrap();
170
171 self.surface
172 .draw(
173 slice,
174 &NoIndices(PrimitiveType::TrianglesList),
175 &self.system.shader_color,
176 &uniform! {},
177 &draw_state::convert_draw_state(&self.system.colored_draw_state),
178 )
179 .ok()
180 .expect("failed to draw triangle list");
181
182 self.system.colored_offset = 0;
183 self.system.plain_buffer.invalidate();
184 }
185}
186
187impl<'d, 's, S: Surface> Graphics for GliumGraphics<'d, 's, S> {
189 type Texture = Texture;
190
191 fn clear_color(&mut self, color: [f32; 4]) {
193 let color = gamma_srgb_to_linear(color);
194 let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
195 self.surface.clear_color(r, g, b, a);
196 }
197
198 fn clear_stencil(&mut self, value: u8) {
199 self.surface.clear_stencil(value as i32);
200 }
201
202 fn tri_list<F>(&mut self, draw_state: &DrawState, color: &[f32; 4], mut f: F)
204 where
205 F: FnMut(&mut dyn FnMut(&[[f32; 2]])),
206 {
207 let color = gamma_srgb_to_linear(*color);
208 if &self.system.colored_draw_state != draw_state {
210 self.flush_colored();
211 self.system.colored_draw_state = *draw_state;
212 }
213 f(&mut |vertices: &[[f32; 2]]| {
214 let n = vertices.len();
215 if self.system.colored_offset + n > CHUNKS * graphics::BACK_END_MAX_VERTEX_COUNT {
216 self.flush_colored();
217 }
218 let slice = self
219 .system
220 .plain_buffer
221 .slice(self.system.colored_offset..self.system.colored_offset + n)
222 .unwrap();
223 slice.write({
224 &(0..n)
225 .map(|i| PlainVertex {
226 color: color,
227 pos: vertices[i],
228 })
229 .collect::<Vec<_>>()
230 });
231 self.system.colored_offset += n;
232 })
233 }
234
235 fn tri_list_c<F>(&mut self, draw_state: &DrawState, mut f: F)
236 where
237 F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 4]])),
238 {
239 if &self.system.colored_draw_state != draw_state {
241 self.flush_colored();
242 self.system.colored_draw_state = *draw_state;
243 }
244 f(&mut |vertices: &[[f32; 2]], colors: &[[f32; 4]]| {
245 let n = vertices.len();
246 if self.system.colored_offset + n > CHUNKS * graphics::BACK_END_MAX_VERTEX_COUNT {
247 self.flush_colored();
248 }
249 let slice = self
250 .system
251 .plain_buffer
252 .slice(self.system.colored_offset..self.system.colored_offset + n)
253 .unwrap();
254 slice.write({
255 &(0..n)
256 .map(|i| PlainVertex {
257 color: gamma_srgb_to_linear(colors[i]),
258 pos: vertices[i],
259 })
260 .collect::<Vec<_>>()
261 });
262 self.system.colored_offset += n;
263 })
264 }
265
266 fn tri_list_uv<F>(
271 &mut self,
272 draw_state: &DrawState,
273 color: &[f32; 4],
274 texture: &Texture,
275 mut f: F,
276 ) where
277 F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]])),
278 {
279 use glium::uniforms::{Sampler, SamplerWrapFunction};
280 use std::cmp::min;
281
282 let mut sampler = Sampler::new(&texture.0);
283 sampler.1.wrap_function = (texture.1[0], texture.1[1], SamplerWrapFunction::Clamp);
284
285 let color = gamma_srgb_to_linear(*color);
286 if self.system.colored_offset > 0 {
287 self.flush_colored();
288 }
289 f(&mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]]| {
290 let len = min(vertices.len(), texture_coords.len());
291
292 self.system.textured_buffer.invalidate();
293 let slice = self.system.textured_buffer.slice(0..len).unwrap();
294
295 slice.write({
296 &(0..len)
297 .map(|i| TexturedVertex {
298 pos: vertices[i],
299 uv: [texture_coords[i][0], 1.0 - texture_coords[i][1]],
301 })
302 .collect::<Vec<_>>()
303 });
304
305 self.surface
306 .draw(
307 slice,
308 &NoIndices(PrimitiveType::TrianglesList),
309 &self.system.shader_texture,
310 &uniform! {
311 color: color,
312 s_texture: sampler
313 },
314 &draw_state::convert_draw_state(draw_state),
315 )
316 .ok()
317 .expect("failed to draw triangle list");
318 })
319 }
320
321 fn tri_list_uv_c<F>(
322 &mut self,
323 draw_state: &DrawState,
324 texture: &Texture,
325 mut f: F,
326 ) where
327 F: FnMut(&mut dyn FnMut(&[[f32; 2]], &[[f32; 2]], &[[f32; 4]])),
328 {
329 use glium::uniforms::{Sampler, SamplerWrapFunction};
330 use std::cmp::min;
331
332 let mut sampler = Sampler::new(&texture.0);
333 sampler.1.wrap_function = (texture.1[0], texture.1[1], SamplerWrapFunction::Clamp);
334
335 if self.system.colored_offset > 0 {
336 self.flush_colored();
337 }
338 f(&mut |vertices: &[[f32; 2]], texture_coords: &[[f32; 2]], colors: &[[f32; 4]]| {
339 let len = min(min(vertices.len(), texture_coords.len()), colors.len());
340
341 self.system.textured_color_buffer.invalidate();
342 let slice = self.system.textured_color_buffer.slice(0..len).unwrap();
343
344 slice.write({
345 &(0..len)
346 .map(|i| TexturedColorVertex {
347 pos: vertices[i],
348 uv: [texture_coords[i][0], 1.0 - texture_coords[i][1]],
350 color: gamma_srgb_to_linear(colors[i]),
351 })
352 .collect::<Vec<_>>()
353 });
354
355 self.surface
356 .draw(
357 slice,
358 &NoIndices(PrimitiveType::TrianglesList),
359 &self.system.shader_texture_color,
360 &uniform! {
361 s_texture: sampler
362 },
363 &draw_state::convert_draw_state(draw_state),
364 )
365 .ok()
366 .expect("failed to draw triangle list");
367 })
368 }
369}