awi/render/opengl/
mod.rs

1// Copyright Jeron A. Lau 2018.
2// Dual-licensed under either the MIT License or the Boost Software License,
3// Version 1.0.  (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6//! OpenGL implementation for adi_gpu.
7
8use std::cell::Cell;
9
10mod asi;
11
12use std::mem;
13use Matrix;
14use Rotation;
15
16use WindowConnection;
17
18pub use self::base::Shape;
19pub use self::base::Gradient;
20pub use self::base::Model;
21pub use self::base::TexCoords;
22pub use self::base::Texture;
23
24use self::asi::{
25	OpenGL, OpenGLBuilder, VertexData, Program, Buffer, UniformData,
26	Feature, Topology,
27};
28use super::base;
29use super::base::*;
30
31const SHADER_SOLID_FRAG: &'static [u8] = include_bytes!("shaders/solid-frag.glsl");
32const SHADER_SOLID_VERT: &'static [u8] = include_bytes!("shaders/solid-vert.glsl");
33const SHADER_GRADIENT_FRAG: &'static [u8] = include_bytes!("shaders/gradient-frag.glsl");
34const SHADER_GRADIENT_VERT: &'static [u8] = include_bytes!("shaders/gradient-vert.glsl");
35const SHADER_TEX_FRAG: &'static [u8] = include_bytes!("shaders/texture-frag.glsl");
36const SHADER_TEX_VERT: &'static [u8] = include_bytes!("shaders/texture-vert.glsl");
37const SHADER_FADED_VERT: &'static [u8] = include_bytes!("shaders/faded-vert.glsl");
38const SHADER_TINTED_FRAG: &'static [u8] = include_bytes!("shaders/tinted-frag.glsl");
39const SHADER_COMPLEX_VERT: &'static [u8] = include_bytes!("shaders/complex-vert.glsl");
40const SHADER_COMPLEX_FRAG: &'static [u8] = include_bytes!("shaders/complex-frag.glsl");
41
42const STYLE_GRADIENT: usize = 0;
43const STYLE_TEXTURE: usize = 1;
44const STYLE_FADED: usize = 2;
45const STYLE_TINTED: usize = 3;
46const STYLE_SOLID: usize = 4;
47const STYLE_COMPLEX: usize = 5;
48
49struct Style {
50	shader: Program,
51	matrix_uniform: UniformData,
52	has_camera: UniformData,
53	camera_uniform: UniformData,
54	has_fog: UniformData,
55	fog: UniformData,
56	range: UniformData,
57	alpha: UniformData,
58	color: UniformData,
59	position: VertexData,
60	texpos: VertexData,
61	acolor: VertexData,
62}
63
64impl Style {
65	// Create a new style.
66	fn new(context: &OpenGL, vert: &[u8], frag: &[u8]) -> Style {
67		let shader = Program::new(context, vert, frag);
68		let matrix_uniform = shader.uniform(b"models_tfm\0");
69		let has_camera = shader.uniform(b"has_camera\0");
70		let camera_uniform = shader.uniform(b"matrix\0");
71		let has_fog = shader.uniform(b"has_fog\0");
72		let fog = shader.uniform(b"fog\0");
73		let range = shader.uniform(b"range\0");
74		let alpha = shader.uniform(b"alpha\0");
75		let color = shader.uniform(b"color\0");
76		let position = shader.vertex_data(b"position\0");
77		let texpos = shader.vertex_data(b"texpos\0");
78		let acolor = shader.vertex_data(b"acolor\0");
79
80		Style {
81			shader, matrix_uniform, has_camera, camera_uniform, fog,
82			range, position, texpos, alpha, has_fog, color, acolor,
83		}
84	}
85}
86
87struct ShapeData {
88	style: usize,
89	buffers: [Option<Buffer>; 2],
90	has_fog: bool,
91	alpha: Option<f32>,
92	color: Option<[f32; 4]>,
93	transform: Matrix, // Transformation matrix.
94	texture: Option<asi::Texture>,
95	vertex_buffer: Buffer,
96	fans: Vec<(u32, u32)>,
97}
98
99impl base::Point for ShapeData {
100	fn point(&self) -> Vector {
101		// Position vector at origin * object transform.
102		self.transform * (vector!(0f32, 0f32, 0f32), 1f32)
103	}
104}
105
106struct ModelData {
107	vertex_buffer: Buffer,
108	// TODO alot could be in base as duplicate
109	vertex_count: u32,
110	fans: Vec<(u32, u32)>,
111}
112
113struct TexcoordsData {
114	vertex_buffer: Buffer,
115	vertex_count: u32,
116}
117
118struct GradientData {
119	vertex_buffer: Buffer,
120	vertex_count: u32,
121}
122
123struct TextureData {
124	t: asi::Texture,
125}
126
127/// To render anything with adi_gpu, you have to make a `Display`
128pub struct Display {
129	window: ::Window,
130	context: OpenGL,
131	color: (f32, f32, f32),
132	opaque_ind: Vec<u32>,
133	alpha_ind: Vec<u32>,
134	opaque_vec: Cell<Vec<ShapeData>>,
135	alpha_vec: Cell<Vec<ShapeData>>,
136	gui_vec: Cell<Vec<ShapeData>>,
137	models: Vec<ModelData>,
138	texcoords: Vec<TexcoordsData>,
139	gradients: Vec<GradientData>,
140	textures: Vec<TextureData>,
141	styles: [Style; 6],
142	xyz: Vector,
143	rotate_xyz: Vector,
144	ar: f32,
145	projection: Matrix,
146}
147
148pub fn new() -> Result<Box<Display>, &'static str> {
149	if let Some(tuple) = OpenGLBuilder::new() {
150		let (builder, v) = tuple;
151		let window = ::Window::new(Some(v));
152
153		let context = builder.to_opengl(match window.get_connection() {
154			WindowConnection::Xcb(_, window) => // |
155			//	WindowConnection::Windows(_, window) =>
156			{
157				unsafe {mem::transmute(window as usize)}
158			},
159			WindowConnection::Windows(_, window) => {
160				window
161			}
162			WindowConnection::Wayland => return Err(
163				"OpenGL support on Wayland is WIP"),
164			WindowConnection::DirectFB => return Err(
165				"OpenGL support on DirectFB is WIP"),
166			WindowConnection::Android => return Err(
167				"OpenGL support on Android is WIP"),
168			WindowConnection::IOS => return Err(
169				"OpenGL support on iOS is WIP"),
170			WindowConnection::AldaronsOS => return Err(
171				"AldaronsOS doesn't support OpenGL"),
172			WindowConnection::Arduino => return Err(
173				"Arduino doesn't support OpenGL"),
174			WindowConnection::Switch => return Err(
175				"Nintendo Switch doesn't support OpenGL"),
176			WindowConnection::Web => return Err(
177				"WebGL support is WIP"),
178			WindowConnection::NoOS => return Err(
179				"NoOS doesn't support OpenGL"),
180		});
181
182		// Set the settings.
183		context.disable(Feature::Dither);
184		context.enable(Feature::CullFace);
185		context.enable(Feature::Blend);
186		context.blend();
187
188		// Load shaders
189		let style_solid = Style::new(&context,
190			SHADER_SOLID_VERT, SHADER_SOLID_FRAG);
191		let style_gradient = Style::new(&context,
192			SHADER_GRADIENT_VERT, SHADER_GRADIENT_FRAG);
193		let style_texture = Style::new(&context,
194			SHADER_TEX_VERT, SHADER_TEX_FRAG);
195		let style_faded = Style::new(&context,
196			SHADER_FADED_VERT, SHADER_TEX_FRAG);
197		let style_tinted = Style::new(&context,
198			SHADER_TEX_VERT, SHADER_TINTED_FRAG);
199		let style_complex = Style::new(&context,
200			SHADER_COMPLEX_VERT, SHADER_COMPLEX_FRAG);
201
202		let wh = window.wh();
203		let ar = wh.0 as f32 / wh.1 as f32;
204
205		let projection = base::projection(ar, 0.5 * PI);
206
207		// Adjust the viewport
208		context.viewport(wh.0, wh.1);
209
210		let mut display = self::Display {
211			window,
212			context,
213			color: (0.0, 0.0, 0.0),
214			alpha_ind: vec![],
215			opaque_ind: vec![],
216			alpha_vec: Cell::new(vec![]),
217			opaque_vec: Cell::new(vec![]),
218			gui_vec: Cell::new(vec![]),
219			models: vec![],
220			texcoords: vec![],
221			gradients: vec![],
222			textures: vec![],
223			styles: [
224				style_gradient,
225				style_texture,
226				style_faded,
227				style_tinted,
228				style_solid,
229				style_complex,
230			],
231			xyz: vector!(0.0, 0.0, 0.0),
232			rotate_xyz: vector!(0.0, 0.0, 0.0),
233			ar,
234			projection,
235		};
236
237		use self::base::Display;
238
239		Ok(Box::new(display))
240	} else {
241		Err("Couldn't find OpenGL!")
242	}
243}
244
245fn as_mut(slf: &Cell<Vec<ShapeData>>) -> &mut Vec<ShapeData> {
246	unsafe {
247		::std::mem::transmute(slf.as_ptr())
248	}
249}
250
251impl base::Display for Display {
252	fn color(&mut self, color: (u8, u8, u8)) {
253		self.color = (color.0 as f32 / 255.0,
254			color.1 as f32 / 255.0, color.2 as f32 / 255.0);
255		self.context.color(self.color.0, self.color.1, self.color.2);
256	}
257
258	fn input(&mut self) -> Option<base::Event> {
259		self.window.update()
260	}
261
262	fn update(&mut self) -> f32 {
263		// Opaque & Alpha Shapes need a camera.
264		for i in (&self.styles).iter() {
265			i.has_camera.set_int1(1);
266		}
267
268		// Enable for 3D depth testing
269		self.context.enable(Feature::DepthTest);
270
271		// sort nearest
272		base::zsort(&mut self.opaque_ind, self.opaque_vec.get_mut(),
273			true, self.xyz);
274		for shape in as_mut(&self.opaque_vec).iter() {
275			draw_shape(&self.styles[shape.style], shape);
276		}
277
278		// sort farthest
279		base::zsort(&mut self.alpha_ind, &self.alpha_vec.get_mut(),
280			false, self.xyz);
281		for shape in as_mut(&self.alpha_vec).iter() {
282			draw_shape(&self.styles[shape.style], shape);
283		}
284
285		// Disable Depth Testing for GUI
286		self.context.disable(Feature::DepthTest);
287
288		// No need to sort gui elements.
289		for shape in as_mut(&self.gui_vec).iter() {
290			draw_shape(&self.styles[shape.style], shape);
291		}
292
293		self.context.update()
294	}
295
296	fn model(&mut self, vertices: &[f32], fans: Vec<(u32, u32)>) -> Model {
297		// TODO most is duplicate from other implementation.
298		let index = self.models.len();
299
300		let buffer = Buffer::new(&self.context);
301
302		let vertex_buffer = buffer;
303		vertex_buffer.set(vertices);
304
305
306		self.models.push(ModelData {
307			vertex_buffer, vertex_count: vertices.len() as u32 / 4,
308			fans
309		});
310
311		Model(index)
312	}
313
314	fn texture(&mut self, wh: (u16,u16), graphic: &VFrame) -> Texture {
315		let (w, h) = wh;
316		let pixels = graphic.0.as_slice();
317
318		let t = self.context.texture();
319
320		t.set(w, h, pixels);
321
322		let a = self.textures.len();
323
324		self.textures.push(TextureData { t });
325
326		Texture(a, w, h)
327	}
328
329	fn gradient(&mut self, colors: &[f32]) -> Gradient {
330		// TODO: A lot of duplication here from adi_gpu_vulkan.  Put in
331		// base.
332		let vertex_buffer = Buffer::new(&self.context);
333		vertex_buffer.set(colors);
334
335		let a = self.gradients.len();
336
337		self.gradients.push(GradientData {
338			vertex_buffer,
339			vertex_count: colors.len() as u32 / 4,
340		});
341
342		Gradient(a)
343	}
344
345	fn texcoords(&mut self, texcoords: &[(f32, f32)]) -> TexCoords {
346		// TODO: A lot of duplication here from adi_gpu_vulkan.  Put in
347		// base.
348		let vertex_buffer = Buffer::new(&self.context);
349		let mut buffer = vec![];
350		for i in texcoords {
351			buffer.push(i.0);
352			buffer.push(i.1);
353			buffer.push(1.0);
354			buffer.push(1.0);
355		}
356		vertex_buffer.set(buffer.as_slice());
357
358		let a = self.texcoords.len();
359
360		self.texcoords.push(TexcoordsData {
361			vertex_buffer,
362			vertex_count: texcoords.len() as u32,
363		});
364
365		TexCoords(a)
366	}
367
368	fn set_texture(&mut self, texture: &mut Texture, wh: (u16,u16),
369		graphic: &VFrame)
370	{
371		self.textures[texture.0].t.set(wh.0, wh.1,
372			graphic.0.as_slice());
373	}
374
375	#[inline(always)]
376	fn shape_solid(&mut self, model: &Model, transform: Matrix,
377		color: [f32; 4], blending: bool, fog: bool, camera: bool)
378		-> Shape
379	{
380		let shape = ShapeData {
381			style: STYLE_SOLID,
382			buffers: [None, None],
383			has_fog: fog,
384			alpha: None,
385			color: Some(color),
386			texture: None,
387			vertex_buffer: self.models[model.0].vertex_buffer.clone(),
388			transform, // Transformation matrix.
389			fans: self.models[model.0].fans.clone(),
390		};
391
392		base::new_shape(if blending {
393			let alpha_vec = self.alpha_vec.get_mut();
394			let index = alpha_vec.len() as u32;
395			alpha_vec.push(shape);
396			self.alpha_ind.push(index);
397			base::ShapeHandle::Alpha(index)
398		} else {
399			let opaque_vec = self.opaque_vec.get_mut();
400			let index = opaque_vec.len() as u32;
401			opaque_vec.push(shape);
402			self.opaque_ind.push(index);
403			base::ShapeHandle::Opaque(index)
404		})
405	}
406
407	#[inline(always)]
408	fn shape_gradient(&mut self, model: &Model, transform: Matrix,
409		colors: Gradient, blending: bool, fog: bool, camera: bool)
410		-> Shape
411	{
412		// TODO: is copied from adi_gpu_vulkan, move to base
413		if self.models[model.0].vertex_count
414			!= self.gradients[colors.0].vertex_count
415		{
416			panic!("TexCoord length doesn't match gradient length");
417		}
418
419		let shape = ShapeData {
420			style: STYLE_GRADIENT,
421			buffers: [
422				Some(self.gradients[colors.0].vertex_buffer.clone()),
423				None
424			],
425			has_fog: fog,
426			alpha: None,
427			color: None,
428			texture: None,
429			vertex_buffer: self.models[model.0].vertex_buffer.clone(),
430			transform, // Transformation matrix.
431			fans: self.models[model.0].fans.clone(),
432		};
433
434		base::new_shape(if blending {
435			let alpha_vec = self.alpha_vec.get_mut();
436			let index = alpha_vec.len() as u32;
437			alpha_vec.push(shape);
438			self.alpha_ind.push(index);
439			base::ShapeHandle::Alpha(index)
440		} else {
441			let opaque_vec = self.opaque_vec.get_mut();
442			let index = opaque_vec.len() as u32;
443			opaque_vec.push(shape);
444			self.opaque_ind.push(index);
445			base::ShapeHandle::Opaque(index)
446		})
447	}
448
449	#[inline(always)]
450	fn shape_texture(&mut self, model: &Model, transform: Matrix,
451		texture: &Texture, tc: TexCoords, blending: bool, fog: bool,
452		camera: bool) -> Shape
453	{
454		// TODO: from adi_gpu_vulkan, move to the base
455		if self.models[model.0].vertex_count
456			!= self.texcoords[tc.0].vertex_count
457		{
458			panic!("TexCoord length doesn't match vertex length");
459		}
460
461		let shape = ShapeData {
462			style: STYLE_TEXTURE,
463			buffers: [
464				Some(self.texcoords[tc.0].vertex_buffer.clone()),
465				None
466			],
467			has_fog: fog,
468			alpha: None,
469			color: None,
470			texture: Some(self.textures[texture.0].t.clone()),
471			vertex_buffer: self.models[model.0].vertex_buffer.clone(),
472			transform, // Transformation matrix.
473			fans: self.models[model.0].fans.clone(),
474		};
475
476		base::new_shape(if blending {
477			let alpha_vec = self.alpha_vec.get_mut();
478			let index = alpha_vec.len() as u32;
479			alpha_vec.push(shape);
480			self.alpha_ind.push(index);
481			base::ShapeHandle::Alpha(index)
482		} else {
483			let opaque_vec = self.opaque_vec.get_mut();
484			let index = opaque_vec.len() as u32;
485			opaque_vec.push(shape);
486			self.opaque_ind.push(index);
487			base::ShapeHandle::Opaque(index)
488		})
489	}
490
491	#[inline(always)]
492	fn shape_faded(&mut self, model: &Model, transform: Matrix,
493		texture: &Texture, tc: TexCoords, alpha: f32, fog: bool,
494		camera: bool) -> Shape
495	{
496		// TODO: from adi_gpu_vulkan, move to the base
497		if self.models[model.0].vertex_count
498			!= self.texcoords[tc.0].vertex_count
499		{
500			panic!("TexCoord length doesn't match vertex length");
501		}
502
503		let shape = ShapeData {
504			style: STYLE_FADED,
505			buffers: [
506				Some(self.texcoords[tc.0].vertex_buffer.clone()),
507				None
508			],
509			has_fog: fog,
510			alpha: Some(alpha),
511			color: None,
512			texture: Some(self.textures[texture.0].t.clone()),
513			vertex_buffer: self.models[model.0].vertex_buffer.clone(),
514			transform, // Transformation matrix.
515			fans: self.models[model.0].fans.clone(),
516		};
517
518		base::new_shape({
519			let alpha_vec = self.alpha_vec.get_mut();
520			let index = alpha_vec.len() as u32;
521			alpha_vec.push(shape);
522			self.alpha_ind.push(index);
523			base::ShapeHandle::Alpha(index)
524		})
525	}
526
527	#[inline(always)]
528	fn shape_tinted(&mut self, model: &Model, transform: Matrix,
529		texture: &Texture, tc: TexCoords, tint: [f32; 4], blending: bool,
530		fog: bool, camera: bool) -> Shape
531	{
532		// TODO: from adi_gpu_vulkan, move to the base
533		if self.models[model.0].vertex_count
534			!= self.texcoords[tc.0].vertex_count
535		{
536			panic!("TexCoord length doesn't match vertex length");
537		}
538
539		let shape = ShapeData {
540			style: STYLE_TINTED,
541			buffers: [
542				Some(self.texcoords[tc.0].vertex_buffer.clone()),
543				None,
544			],
545			has_fog: fog,
546			alpha: None,
547			color: Some(tint),
548			texture: Some(self.textures[texture.0].t.clone()),
549			vertex_buffer: self.models[model.0].vertex_buffer.clone(),
550			transform, // Transformation matrix.
551			fans: self.models[model.0].fans.clone(),
552		};
553
554		base::new_shape(if blending {
555			let alpha_vec = self.alpha_vec.get_mut();
556			let index = alpha_vec.len() as u32;
557			alpha_vec.push(shape);
558			self.alpha_ind.push(index);
559			base::ShapeHandle::Alpha(index)
560		} else {
561			let opaque_vec = self.opaque_vec.get_mut();
562			let index = opaque_vec.len() as u32;
563			opaque_vec.push(shape);
564			self.opaque_ind.push(index);
565			base::ShapeHandle::Opaque(index)
566		})
567	}
568
569	#[inline(always)]
570	fn shape_complex(&mut self, model: &Model, transform: Matrix,
571		texture: &Texture, tc: TexCoords, tints: Gradient,
572		blending: bool, fog: bool, camera: bool) -> Shape
573	{
574		// TODO: from adi_gpu_vulkan, move to the base
575		if self.models[model.0].vertex_count
576			!= self.texcoords[tc.0].vertex_count
577		{
578			panic!("TexCoord length doesn't match vertex length");
579		}
580
581		// TODO: is copied from adi_gpu_vulkan, move to base
582		if self.models[model.0].vertex_count
583			!= self.gradients[tints.0].vertex_count
584		{
585			panic!("TexCoord length doesn't match gradient length");
586		}
587
588		let shape = ShapeData {
589			style: STYLE_COMPLEX,
590			buffers: [
591				Some(self.texcoords[tc.0].vertex_buffer.clone()),
592				Some(self.gradients[tints.0].vertex_buffer.clone()),
593			],
594			has_fog: fog,
595			alpha: None,
596			color: None,
597			texture: Some(self.textures[texture.0].t.clone()),
598			vertex_buffer: self.models[model.0].vertex_buffer.clone(),
599			transform, // Transformation matrix.
600			fans: self.models[model.0].fans.clone(),
601		};
602
603		base::new_shape(if blending {
604			let alpha_vec = self.alpha_vec.get_mut();
605			let index = alpha_vec.len() as u32;
606			alpha_vec.push(shape);
607			self.alpha_ind.push(index);
608			base::ShapeHandle::Alpha(index)
609		} else {
610			let opaque_vec = self.opaque_vec.get_mut();
611			let index = opaque_vec.len() as u32;
612			opaque_vec.push(shape);
613			self.opaque_ind.push(index);
614			base::ShapeHandle::Opaque(index)
615		})
616	}
617
618	#[inline(always)]
619	fn drop_shape(&mut self, shape: &Shape) {
620		match get_shape(&shape) {
621			ShapeHandle::Opaque(x) => {
622				let index = self.opaque_ind.iter()
623					.position(|y| *y == x).unwrap();
624				self.opaque_ind.remove(index);
625			}
626			ShapeHandle::Alpha(x) => {
627				let index = self.alpha_ind.iter()
628					.position(|y| *y == x).unwrap();
629				self.alpha_ind.remove(index);
630			}
631		}
632	}
633
634	fn transform(&self, shape: &Shape, transform: Matrix) {
635		// TODO: put in base, some is copy from vulkan implementation.
636		match base::get_shape(shape) {
637			ShapeHandle::Opaque(x) => {
638				let x = x as usize; // for indexing
639				as_mut(&self.opaque_vec)[x].transform = transform;
640			}
641			ShapeHandle::Alpha(x) => {
642				let x = x as usize; // for indexing
643				as_mut(&self.alpha_vec)[x].transform = transform;
644			}
645		}
646	}
647
648	fn resize(&mut self, wh: (u16, u16)) -> () {
649		let xyz = self.xyz;
650		let rotate_xyz = self.rotate_xyz;
651
652		self.ar = wh.0 as f32 / wh.1 as f32;
653		self.context.viewport(wh.0, wh.1);
654
655		self.projection = super::base::projection(self.ar, 0.5 * PI);
656	}
657
658	fn wh(&self) -> (u16, u16) {
659		self.window.wh()
660	}
661
662	fn draw(&self, _writer: &Fn(u16, u16) -> [u8; 4]) {
663		// TODO
664	}
665}
666
667fn draw_shape(style: &Style, shape: &ShapeData) {
668	style.matrix_uniform.set_mat4(shape.transform.into());
669
670	if !style.texpos.is_none() {
671		// Set texpos for the program from the texpos buffer.
672		style.texpos.set(shape.buffers[0].as_ref().unwrap());
673		// Bind the texture
674		shape.texture.as_ref().unwrap().bind();
675	}
676
677	if !style.acolor.is_none() {
678		// Set colors for the program from the color buffer.
679		// TODO: probably shouldn't be same buffer as texpos.
680		style.acolor.set(shape.buffers[0].as_ref().unwrap());
681	}
682
683	if !style.alpha.is_none() {
684		style.alpha.set_vec1(shape.alpha.unwrap());
685	}
686
687	if !style.color.is_none() {
688		style.color.set_vec4(&shape.color.unwrap());
689	}
690
691	if shape.has_fog {
692		style.has_fog.set_int1(1);
693	} else {
694		style.has_fog.set_int1(0);
695	}
696
697	// Set vertices for the program from the vertex buffer.
698	style.position.set(&shape.vertex_buffer);
699	for i in shape.fans.iter() {
700		style.shader.draw_arrays(Topology::TriangleFan, i.0..i.1);
701	}
702}