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	/// Bind to the default framebuffer
51	pub fn default_bind(glcore: &GLCore) {
52		glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
53	}
54}
55
56impl<'a> FramebufferBind<'a> {
57	/// Create a new binding state to the framebuffer object, utilizing the RAII rules to manage the binding state.
58	fn new(framebuffer: &'a Framebuffer) -> Self {
59		framebuffer.glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer.name);
60		Self {
61			framebuffer,
62		}
63	}
64
65	/// Set up the framebuffer, apply `draw_targets`
66	pub fn setup(&self, program: &Shader) {
67		let draw_targets = &self.framebuffer.draw_targets;
68		assert!(!draw_targets.is_empty());
69		let glcore = self.framebuffer.glcore.clone();
70		let mut draw_buffers: Vec<u32> = Vec::with_capacity(draw_targets.len());
71		let mut max_width: u32 = 0;
72		let mut max_height: u32 = 0;
73		for (target_name, target) in draw_targets.iter() {
74			let location = glcore.glGetFragDataLocation(program.get_name(), target_name.as_ptr() as *const i8);
75			if location >= 0 {
76				let location = location as u32;
77				let (target, texture) = target;
78				let attachment = GL_COLOR_ATTACHMENT0 + location;
79				max_width = max(max_width, texture.get_width());
80				max_height = max(max_height, texture.get_height());
81				match texture.get_dim() {
82					TextureDimension::Tex1d =>		glcore.glFramebufferTexture1D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0),
83					TextureDimension::Tex2d =>		glcore.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0),
84					TextureDimension::Tex3d =>		glcore.glFramebufferTexture3D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0, target.layer_of_3d),
85					TextureDimension::TexCube =>	glcore.glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, attachment, target.texture_target as u32, texture.get_name(), 0),
86				}
87				draw_buffers.push(attachment);
88			} else {
89				eprintln!("Location of shader output `{target_name}` couldn't be found.");
90			}
91		}
92		glcore.glDrawBuffers(draw_buffers.len() as i32, draw_buffers.as_ptr());
93		glcore.glViewport(0, 0, max_width as i32, max_height as i32);
94	}
95
96	/// Explicitly unbind the framebuffer
97	pub fn unbind(self) {}
98}
99
100impl Drop for FramebufferBind<'_> {
101	fn drop(&mut self) {
102		self.framebuffer.glcore.glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
103	}
104}
105
106impl Drop for Framebuffer {
107	fn drop(&mut self) {
108		self.glcore.glDeleteFramebuffers(1, &self.name as *const _);
109	}
110}
111
112impl Debug for Framebuffer {
113	fn fmt(&self, f: &mut Formatter) -> fmt::Result {
114		f.debug_struct("Framebuffer")
115		.field("name", &self.name)
116		.finish()
117	}
118}