globject_rs/
glframebuffer.rs

1
2use crate::prelude::*;
3use std::{
4	cmp::max,
5	collections::BTreeMap,
6	fmt::{self, Debug, Formatter},
7	rc::Rc,
8};
9
10/// The framebuffer render target type
11pub struct FramebufferTarget {
12	/// The texture binding target
13	pub texture_target: TextureTarget,
14
15	/// The layer index of the 3D texture to bind (Only bind a 2D layer to the framebuffer)
16	pub layer_of_3d: i32,
17}
18
19/// The framebuffer object type
20pub struct Framebuffer {
21	pub glcore: Rc<GLCore>,
22	name: u32,
23
24	/// The name of the draw targets and the binding target and the texture
25	pub draw_targets: BTreeMap<String, (FramebufferTarget, Rc<dyn GenericTexture>)>,
26}
27
28/// The binding guard of the framebuffer
29pub struct FramebufferBind<'a> {
30	framebuffer: &'a Framebuffer,
31}
32
33impl Framebuffer {
34	/// Create a new empty framebuffer object
35	pub fn new(glcore: Rc<GLCore>) -> Self {
36		let mut name: u32 = 0;
37		glcore.glGenFramebuffers(1, &mut name as *mut _);
38		Self {
39			glcore,
40			name,
41			draw_targets: BTreeMap::new(),
42		}
43	}
44
45	/// Utilize the RAII rules to manage binding states.
46	pub fn bind<'a>(&'a self) -> FramebufferBind<'a> {
47		FramebufferBind::new(self)
48	}
49}
50
51impl<'a> FramebufferBind<'a> {
52	/// Create a new binding state to the framebuffer object, utilizing the RAII rules to manage the binding state.
53	fn new(framebuffer: &'a Framebuffer) -> Self {
54		framebuffer.glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.name);
55		Self {
56			framebuffer,
57		}
58	}
59
60	/// Set up the framebuffer, apply `draw_targets`
61	pub fn setup(&self, program: &Shader) {
62		let draw_targets = &self.framebuffer.draw_targets;
63		assert!(!draw_targets.is_empty());
64		let glcore = self.framebuffer.glcore.clone();
65		let mut draw_buffers: Vec<u32> = Vec::with_capacity(draw_targets.len());
66		let mut max_width: u32 = 0;
67		let mut max_height: u32 = 0;
68		for (target_name, target) in draw_targets.iter() {
69			let location = glcore.glGetFragDataLocation(program.get_name(), target_name.as_ptr() as *const i8);
70			if location >= 0 {
71				let location = location as u32;
72				let (target, texture) = target;
73				let attachment = GL_COLOR_ATTACHMENT0 + location;
74				max_width = max(max_width, texture.get_width());
75				max_height = max(max_height, texture.get_height());
76				match texture.get_dim() {
77					TextureDimension::Tex1d =>		glcore.glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0),
78					TextureDimension::Tex2d =>		glcore.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0),
79					TextureDimension::Tex3d =>		glcore.glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0, target.layer_of_3d),
80					TextureDimension::TexCube =>	glcore.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0),
81				}
82				draw_buffers.push(attachment);
83			} else {
84				eprintln!("Location of shader output `{target_name}` couldn't be found.");
85			}
86		}
87		glcore.glDrawBuffers(draw_buffers.len() as i32, draw_buffers.as_ptr());
88		glcore.glViewport(0, 0, max_width as i32, max_height as i32);
89	}
90
91	/// Explicitly unbind the framebuffer
92	pub fn unbind(self) {}
93}
94
95impl Drop for FramebufferBind<'_> {
96	fn drop(&mut self) {
97		self.framebuffer.glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
98	}
99}
100
101impl Drop for Framebuffer {
102	fn drop(&mut self) {
103		self.glcore.glDeleteFramebuffers(1, &self.name as *const _);
104	}
105}
106
107impl Debug for Framebuffer {
108	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
109		f.debug_struct("Framebuffer")
110		.field("name", &self.name)
111		.finish()
112	}
113}