inox2d_opengl/
lib.rs

1mod gl_buffer;
2mod shader;
3mod shaders;
4pub mod texture;
5
6use std::cell::RefCell;
7use std::mem;
8use std::ops::Deref;
9
10use gl_buffer::RenderCtxOpenglExt;
11use glam::{uvec2, Mat4, UVec2, Vec2, Vec3};
12use glow::HasContext;
13use inox2d::texture::{decode_model_textures, TextureId};
14
15use inox2d::math::camera::Camera;
16use inox2d::model::{Model, ModelTexture};
17use inox2d::nodes::node_data::{BlendMode, Composite, Part};
18use inox2d::puppet::Puppet;
19use inox2d::render::{InoxRenderer, InoxRendererCommon, NodeRenderCtx, PartRenderCtx};
20
21use self::shader::ShaderCompileError;
22use self::shaders::{CompositeMaskShader, CompositeShader, PartMaskShader, PartShader};
23use self::texture::{Texture, TextureError};
24
25#[derive(Debug, thiserror::Error)]
26#[error("Could not initialize OpenGL renderer: {0}")]
27pub enum OpenglRendererError {
28	ShaderCompile(#[from] ShaderCompileError),
29	Opengl(String),
30}
31
32#[derive(Default, Clone)]
33pub struct GlCache {
34	pub camera: Option<Camera>,
35	pub viewport: Option<UVec2>,
36	pub blend_mode: Option<BlendMode>,
37	pub program: Option<glow::Program>,
38	pub vao: Option<glow::VertexArray>,
39	pub albedo: Option<TextureId>,
40}
41
42impl GlCache {
43	pub fn update_camera(&mut self, camera: &Camera) -> bool {
44		if let Some(prev_camera) = &mut self.camera {
45			let mut changed = false;
46
47			if prev_camera.position != camera.position {
48				prev_camera.position = camera.position;
49				changed = true;
50			}
51			if prev_camera.rotation != camera.rotation {
52				prev_camera.rotation = camera.rotation;
53				changed = true;
54			}
55			if prev_camera.scale != camera.scale {
56				prev_camera.scale = camera.scale;
57				changed = true;
58			}
59
60			changed
61		} else {
62			self.camera = Some(camera.clone());
63			true
64		}
65	}
66
67	pub fn update_viewport(&mut self, viewport: UVec2) -> bool {
68		if let Some(prev_viewport) = self.viewport.replace(viewport) {
69			prev_viewport != viewport
70		} else {
71			true
72		}
73	}
74
75	pub fn update_blend_mode(&mut self, blend_mode: BlendMode) -> bool {
76		if let Some(prev_mode) = self.blend_mode.replace(blend_mode) {
77			prev_mode != blend_mode
78		} else {
79			true
80		}
81	}
82
83	pub fn update_program(&mut self, program: glow::Program) -> bool {
84		if let Some(prev_program) = self.program.replace(program) {
85			prev_program != program
86		} else {
87			true
88		}
89	}
90
91	pub fn update_vao(&mut self, vao: glow::VertexArray) -> bool {
92		if let Some(prev_vao) = self.vao.replace(vao) {
93			prev_vao != vao
94		} else {
95			true
96		}
97	}
98
99	pub fn update_albedo(&mut self, albedo: TextureId) -> bool {
100		if let Some(prev_texture) = self.albedo.replace(albedo) {
101			prev_texture != albedo
102		} else {
103			true
104		}
105	}
106}
107
108#[allow(unused)]
109pub struct OpenglRenderer {
110	gl: glow::Context,
111	support_debug_extension: bool,
112	pub camera: Camera,
113	pub viewport: UVec2,
114	cache: RefCell<GlCache>,
115
116	vao: glow::VertexArray,
117
118	composite_framebuffer: glow::Framebuffer,
119	cf_albedo: glow::Texture,
120	cf_emissive: glow::Texture,
121	cf_bump: glow::Texture,
122	cf_stencil: glow::Texture,
123
124	part_shader: PartShader,
125	part_mask_shader: PartMaskShader,
126	composite_shader: CompositeShader,
127	composite_mask_shader: CompositeMaskShader,
128
129	textures: Vec<Texture>,
130}
131
132// TODO: remove the #[allow(unused)]
133#[allow(unused)]
134impl OpenglRenderer {
135	pub fn new(gl: glow::Context) -> Result<Self, OpenglRendererError> {
136		let vao = unsafe { gl.create_vertex_array().map_err(OpenglRendererError::Opengl)? };
137
138		// Initialize framebuffers
139		let composite_framebuffer;
140		let cf_albedo;
141		let cf_emissive;
142		let cf_bump;
143		let cf_stencil;
144		unsafe {
145			cf_albedo = gl.create_texture().map_err(OpenglRendererError::Opengl)?;
146			cf_emissive = gl.create_texture().map_err(OpenglRendererError::Opengl)?;
147			cf_bump = gl.create_texture().map_err(OpenglRendererError::Opengl)?;
148			cf_stencil = gl.create_texture().map_err(OpenglRendererError::Opengl)?;
149
150			composite_framebuffer = gl.create_framebuffer().map_err(OpenglRendererError::Opengl)?;
151		}
152
153		// Shaders
154		let part_shader = PartShader::new(&gl)?;
155		let part_mask_shader = PartMaskShader::new(&gl)?;
156		let composite_shader = CompositeShader::new(&gl)?;
157		let composite_mask_shader = CompositeMaskShader::new(&gl)?;
158
159		let support_debug_extension = gl.supported_extensions().contains("GL_KHR_debug");
160
161		let renderer = Self {
162			gl,
163			support_debug_extension,
164			camera: Camera::default(),
165			viewport: UVec2::default(),
166			cache: RefCell::new(GlCache::default()),
167
168			vao,
169
170			composite_framebuffer,
171			cf_albedo,
172			cf_emissive,
173			cf_bump,
174			cf_stencil,
175
176			part_shader,
177			part_mask_shader,
178			composite_shader,
179			composite_mask_shader,
180
181			textures: Vec::new(),
182		};
183
184		// Set emission strength once (it doesn't change anywhere else)
185		renderer.bind_shader(&renderer.part_shader);
186		renderer.part_shader.set_emission_strength(&renderer.gl, 1.);
187
188		Ok(renderer)
189	}
190
191	fn upload_model_textures(&mut self, model_textures: &[ModelTexture]) -> Result<(), TextureError> {
192		// decode textures in parallel
193		let shalltexs = decode_model_textures(model_textures.iter());
194
195		// upload textures
196		for (i, shalltex) in shalltexs.iter().enumerate() {
197			tracing::debug!("Uploading shallow texture {:?}", i);
198			let tex = texture::Texture::from_shallow_texture(&self.gl, shalltex)?;
199			self.textures.push(tex);
200		}
201
202		Ok(())
203	}
204
205	/// Pushes an OpenGL debug group.
206	/// This is very useful to debug OpenGL calls per node with `apitrace`, as it will nest calls inside of labels,
207	/// making it trivial to know which calls correspond to which nodes.
208	///
209	/// It is a no-op on platforms that don't support it (like Apple *OS).
210	#[inline]
211	fn push_debug_group(&self, name: &str) {
212		if self.support_debug_extension {
213			unsafe {
214				self.gl.push_debug_group(glow::DEBUG_SOURCE_APPLICATION, 0, name);
215			}
216		}
217	}
218
219	/// Pops the last OpenGL debug group.
220	///
221	/// It is a no-op on platforms that don't support it (like Apple *OS).
222	#[inline]
223	fn pop_debug_group(&self) {
224		if self.support_debug_extension {
225			unsafe {
226				self.gl.pop_debug_group();
227			}
228		}
229	}
230
231	/// Updates the camera in the GL cache and returns whether it changed.
232	fn update_camera(&self) -> bool {
233		{
234			let mut cache = self.cache.borrow_mut();
235			if !cache.update_camera(&self.camera) && !cache.update_viewport(self.viewport) {
236				return false;
237			}
238		}
239
240		let matrix = self.camera.matrix(self.viewport.as_vec2());
241
242		self.bind_shader(&self.composite_shader);
243		self.composite_shader.set_mvp(&self.gl, matrix);
244
245		self.bind_shader(&self.composite_mask_shader);
246		self.composite_mask_shader.set_mvp(&self.gl, matrix);
247
248		true
249	}
250
251	/// Set blending mode. See `BlendMode` for supported blend modes.
252	pub fn set_blend_mode(&self, blend_mode: BlendMode) {
253		if !self.cache.borrow_mut().update_blend_mode(blend_mode) {
254			return;
255		}
256
257		let gl = &self.gl;
258		unsafe {
259			match blend_mode {
260				BlendMode::Normal => {
261					gl.blend_equation(glow::FUNC_ADD);
262					gl.blend_func(glow::ONE, glow::ONE_MINUS_SRC_ALPHA);
263				}
264				BlendMode::Multiply => {
265					gl.blend_equation(glow::FUNC_ADD);
266					gl.blend_func(glow::DST_COLOR, glow::ONE_MINUS_SRC_ALPHA);
267				}
268				BlendMode::ColorDodge => {
269					gl.blend_equation(glow::FUNC_ADD);
270					gl.blend_func(glow::DST_COLOR, glow::ONE);
271				}
272				BlendMode::LinearDodge => {
273					gl.blend_equation(glow::FUNC_ADD);
274					gl.blend_func(glow::ONE, glow::ONE);
275				}
276				BlendMode::Screen => {
277					gl.blend_equation(glow::FUNC_ADD);
278					gl.blend_func(glow::ONE, glow::ONE_MINUS_SRC_COLOR);
279				}
280				BlendMode::ClipToLower => {
281					gl.blend_equation(glow::FUNC_ADD);
282					gl.blend_func(glow::DST_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
283				}
284				BlendMode::SliceFromLower => {
285					gl.blend_equation(glow::FUNC_SUBTRACT);
286					gl.blend_func(glow::ONE_MINUS_DST_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
287				}
288			}
289		}
290	}
291
292	fn bind_shader<S: Deref<Target = glow::Program>>(&self, shader: &S) {
293		let program = **shader;
294		if !self.cache.borrow_mut().update_program(program) {
295			return;
296		}
297
298		unsafe { self.gl.use_program(Some(program)) };
299	}
300
301	fn bind_part_textures(&self, part: &Part) {
302		if !self.cache.borrow_mut().update_albedo(part.tex_albedo) {
303			return;
304		}
305
306		let gl = &self.gl;
307		self.textures[part.tex_albedo.raw()].bind_on(gl, 0);
308		self.textures[part.tex_bumpmap.raw()].bind_on(gl, 1);
309		self.textures[part.tex_emissive.raw()].bind_on(gl, 2);
310	}
311
312	/// Clear the texture cache
313	/// This one method missing made me pull my hair out for an entire month.
314	pub fn clear_texture_cache(&self) {
315		self.cache.borrow_mut().albedo = None;
316	}
317
318	unsafe fn attach_framebuffer_textures(&self) {
319		let gl = &self.gl;
320		gl.bind_framebuffer(glow::FRAMEBUFFER, Some(self.composite_framebuffer));
321
322		gl.framebuffer_texture_2d(
323			glow::FRAMEBUFFER,
324			glow::COLOR_ATTACHMENT0,
325			glow::TEXTURE_2D,
326			Some(self.cf_albedo),
327			0,
328		);
329		gl.framebuffer_texture_2d(
330			glow::FRAMEBUFFER,
331			glow::COLOR_ATTACHMENT1,
332			glow::TEXTURE_2D,
333			Some(self.cf_emissive),
334			0,
335		);
336		gl.framebuffer_texture_2d(
337			glow::FRAMEBUFFER,
338			glow::COLOR_ATTACHMENT2,
339			glow::TEXTURE_2D,
340			Some(self.cf_bump),
341			0,
342		);
343		gl.framebuffer_texture_2d(
344			glow::FRAMEBUFFER,
345			glow::DEPTH_STENCIL_ATTACHMENT,
346			glow::TEXTURE_2D,
347			Some(self.cf_stencil),
348			0,
349		);
350
351		gl.bind_framebuffer(glow::FRAMEBUFFER, None);
352	}
353}
354
355impl InoxRenderer for OpenglRenderer {
356	type Error = OpenglRendererError;
357
358	fn prepare(&mut self, model: &Model) -> Result<(), Self::Error> {
359		unsafe { model.puppet.render_ctx.setup_gl_buffers(&self.gl, self.vao)? };
360
361		match self.upload_model_textures(&model.textures) {
362			Ok(_) => Ok(()),
363			Err(_) => Err(OpenglRendererError::Opengl("Texture Upload Error.".to_string())),
364		}
365	}
366
367	fn resize(&mut self, w: u32, h: u32) {
368		self.viewport = uvec2(w, h);
369
370		let gl = &self.gl;
371		unsafe {
372			gl.viewport(0, 0, w as i32, h as i32);
373
374			// Reupload composite framebuffer textures
375			texture::upload_empty(gl, self.cf_albedo, w, h, glow::UNSIGNED_BYTE);
376			texture::upload_empty(gl, self.cf_emissive, w, h, glow::FLOAT);
377			texture::upload_empty(gl, self.cf_bump, w, h, glow::UNSIGNED_BYTE);
378
379			gl.bind_texture(glow::TEXTURE_2D, Some(self.cf_stencil));
380			gl.tex_image_2d(
381				glow::TEXTURE_2D,
382				0,
383				glow::DEPTH24_STENCIL8 as i32,
384				w as i32,
385				h as i32,
386				0,
387				glow::DEPTH_STENCIL,
388				glow::UNSIGNED_INT_24_8,
389				None,
390			);
391
392			self.attach_framebuffer_textures();
393		}
394		self.update_camera();
395	}
396
397	fn clear(&self) {
398		unsafe {
399			self.gl.clear(glow::COLOR_BUFFER_BIT);
400		}
401	}
402
403	/*
404		These functions should be reworked together:
405		setup_gl_buffers -> should set up in a way so that the draw functions draws into a texture
406		on_begin/end_scene -> prepares and ends drawing to texture. also post-processing
407		draw_scene -> actually makes things appear on a surface
408	*/
409
410	fn on_begin_scene(&self) {
411		todo!()
412	}
413
414	fn render(&self, puppet: &Puppet) {
415		let gl = &self.gl;
416		unsafe {
417			puppet.render_ctx.upload_deforms_to_gl(gl);
418			gl.enable(glow::BLEND);
419			gl.disable(glow::DEPTH_TEST);
420		}
421
422		let camera = self
423			.camera
424			.matrix(Vec2::new(self.viewport.x as f32, self.viewport.y as f32));
425		self.draw(&camera, puppet);
426	}
427
428	fn on_end_scene(&self) {
429		todo!()
430	}
431
432	fn draw_scene(&self) {
433		todo!()
434	}
435
436	fn on_begin_mask(&self, has_mask: bool) {
437		let gl = &self.gl;
438		unsafe {
439			gl.enable(glow::STENCIL_TEST);
440			gl.clear_stencil(!has_mask as i32);
441			gl.clear(glow::STENCIL_BUFFER_BIT);
442
443			gl.color_mask(false, false, false, false);
444			gl.stencil_op(glow::KEEP, glow::KEEP, glow::REPLACE);
445			gl.stencil_mask(0xff);
446		}
447	}
448
449	fn set_mask_mode(&self, dodge: bool) {
450		let gl = &self.gl;
451		unsafe {
452			gl.stencil_func(glow::ALWAYS, !dodge as i32, 0xff);
453		}
454	}
455
456	fn on_begin_masked_content(&self) {
457		let gl = &self.gl;
458		unsafe {
459			gl.stencil_func(glow::EQUAL, 1, 0xff);
460			gl.stencil_mask(0x00);
461
462			gl.color_mask(true, true, true, true);
463		}
464	}
465
466	fn on_end_mask(&self) {
467		let gl = &self.gl;
468		unsafe {
469			gl.stencil_mask(0xff);
470			gl.stencil_func(glow::ALWAYS, 1, 0xff);
471			gl.disable(glow::STENCIL_TEST);
472		}
473	}
474
475	fn draw_mesh_self(&self, _as_mask: bool, _camera: &Mat4) {
476		// TODO
477
478		/*
479		maskShader.use();
480		maskShader.setUniform(offset, data.origin);
481		maskShader.setUniform(mvp, inGetCamera().matrix * transform.matrix());
482
483		// Enable points array
484		glEnableVertexAttribArray(0);
485		glBindBuffer(GL_ARRAY_BUFFER, vbo);
486		glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, null);
487
488		// Bind index buffer
489		this.bindIndex();
490
491		// Disable the vertex attribs after use
492		glDisableVertexAttribArray(0);
493		*/
494		todo!()
495	}
496
497	fn draw_part_self(
498		&self,
499		as_mask: bool,
500		camera: &Mat4,
501		node_render_ctx: &NodeRenderCtx,
502		part: &Part,
503		part_render_ctx: &PartRenderCtx,
504	) {
505		let gl = &self.gl;
506
507		self.bind_part_textures(part);
508		self.set_blend_mode(part.draw_state.blend_mode);
509
510		let part_shader = &self.part_shader;
511		self.bind_shader(part_shader);
512		// vert uniforms
513		let mvp = *camera * node_render_ctx.trans;
514
515		if as_mask {
516			let part_mask_shader = &self.part_mask_shader;
517			self.bind_shader(part_mask_shader);
518
519			// vert uniforms
520			part_mask_shader.set_mvp(gl, mvp);
521
522			// frag uniforms
523			part_mask_shader.set_threshold(gl, part.draw_state.mask_threshold.clamp(0.0, 1.0));
524		} else {
525			let part_shader = &self.part_shader;
526			self.bind_shader(part_shader);
527
528			// vert uniforms
529			part_shader.set_mvp(gl, mvp);
530
531			// frag uniforms
532			part_shader.set_opacity(gl, part.draw_state.opacity);
533			part_shader.set_mult_color(gl, part.draw_state.tint);
534			part_shader.set_screen_color(gl, part.draw_state.screen_tint);
535		}
536
537		unsafe {
538			gl.bind_vertex_array(Some(self.vao));
539			gl.draw_elements(
540				glow::TRIANGLES,
541				part.mesh.indices.len() as i32,
542				glow::UNSIGNED_SHORT,
543				part_render_ctx.index_offset as i32 * mem::size_of::<u16>() as i32,
544			);
545		}
546	}
547
548	fn begin_composite_content(&self) {
549		self.clear_texture_cache();
550
551		let gl = &self.gl;
552		unsafe {
553			gl.bind_framebuffer(glow::DRAW_FRAMEBUFFER, Some(self.composite_framebuffer));
554			gl.disable(glow::DEPTH_TEST);
555			gl.draw_buffers(&[
556				glow::COLOR_ATTACHMENT0,
557				glow::COLOR_ATTACHMENT1,
558				glow::COLOR_ATTACHMENT2,
559			]);
560			gl.clear_color(0.0, 0.0, 0.0, 0.0);
561			gl.clear(glow::COLOR_BUFFER_BIT);
562
563			// Everything else is the actual texture used by the meshes at id 0
564			gl.active_texture(glow::TEXTURE0);
565			gl.blend_func(glow::ONE, glow::ONE_MINUS_SRC_ALPHA);
566		}
567	}
568
569	fn finish_composite_content(&self, as_mask: bool, composite: &Composite) {
570		let gl = &self.gl;
571
572		self.clear_texture_cache();
573		unsafe {
574			gl.bind_framebuffer(glow::FRAMEBUFFER, None);
575		}
576
577		let comp = &composite.draw_state;
578		if as_mask {
579			/*
580			cShaderMask.use();
581			cShaderMask.setUniform(mopacity, opacity);
582			cShaderMask.setUniform(mthreshold, threshold);
583			glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
584			*/
585			todo!()
586		} else {
587			unsafe {
588				gl.bind_vertex_array(Some(self.vao));
589
590				gl.active_texture(glow::TEXTURE0);
591				gl.bind_texture(glow::TEXTURE_2D, Some(self.cf_albedo));
592				gl.active_texture(glow::TEXTURE1);
593				gl.bind_texture(glow::TEXTURE_2D, Some(self.cf_emissive));
594				gl.active_texture(glow::TEXTURE2);
595				gl.bind_texture(glow::TEXTURE_2D, Some(self.cf_bump));
596			}
597
598			self.set_blend_mode(comp.blend_mode);
599
600			let opacity = comp.opacity.clamp(0.0, 1.0);
601			let tint = comp.tint.clamp(Vec3::ZERO, Vec3::ONE);
602			let screen_tint = comp.screen_tint.clamp(Vec3::ZERO, Vec3::ONE);
603
604			let composite_shader = &self.composite_shader;
605			self.bind_shader(composite_shader);
606			composite_shader.set_opacity(gl, opacity);
607			composite_shader.set_mult_color(gl, tint);
608			composite_shader.set_screen_color(gl, screen_tint);
609		}
610
611		unsafe {
612			gl.draw_elements(glow::TRIANGLES, 6, glow::UNSIGNED_SHORT, 0);
613		}
614	}
615}