fere/graphics/
graphics.rs

1pub mod material;
2mod pass;
3mod prgs;
4mod read_buffer;
5pub mod texture_internal;
6
7use super::{
8    glmanager::{
9        shader::{self, Shader},
10        GlManager,
11    },
12    render_unit::RenderUnit,
13};
14use crate::graphics::glmanager::light::*;
15use crate::graphics::resources::Meshes;
16use fere_common::*;
17use fere_resources::*;
18use gl::types::GLuint;
19use serde::{Deserialize, Serialize};
20use texture_internal::{FrameBuffer, TextureInternal3D};
21
22pub fn deferred_mode(color: bool, depth: bool, index: bool) {
23    unsafe {
24        gl::DepthMask(depth as u8);
25        if !color && index {
26            gl::ColorMask(0, 0, 0, 0);
27            gl::ColorMaski(6, 1, 0, 0, 0);
28        } else if color && !index {
29            gl::ColorMask(1, 1, 1, 1);
30            gl::ColorMaski(6, 0, 0, 0, 0);
31        } else if color && index {
32            gl::ColorMask(1, 1, 1, 1);
33        } else {
34            gl::ColorMask(0, 0, 0, 0);
35        }
36    }
37}
38
39#[derive(Debug)]
40pub struct TextureFetcher {
41    _framebuffer: GLuint,
42    _color_attachment: GLuint,
43}
44
45impl TextureFetcher {
46    pub fn fetch(&self) -> u64 {
47        0
48    }
49
50    pub fn noop() -> Self {
51        Self {
52            _framebuffer: 0,
53            _color_attachment: 0,
54        }
55    }
56}
57
58#[derive(Debug, Serialize, Deserialize, Clone)]
59pub struct GraphicsConfig {
60    pub resolution: IVec2,
61    pub shadow_resolution: usize,
62    pub probe_resolution: usize,
63    pub max_major_lights: usize,
64
65    pub video_record: bool,
66}
67
68pub struct Graphics {
69    gl_manager: GlManager,
70
71    screen_size: IVec2,
72
73    // passes
74    pass_deferred1: FrameBuffer,
75    pass_final: FrameBuffer,
76    pass_shadow: Vec<FrameBuffer>,
77    pass_probe: FrameBuffer,
78
79    pass_yuv: Option<FrameBuffer>,
80
81    // useful meshes
82    meshes: Meshes,
83
84    pub prgs: prgs::Programs,
85}
86
87impl Graphics {
88    pub fn new(config: GraphicsConfig) -> Self {
89        let gl_manager = GlManager::new("".to_string());
90        let screen_size = config.resolution;
91        let max_major_lights = config.max_major_lights;
92
93        let pass_deferred1 = pass::create_deferred(screen_size);
94        let pass_final = pass::create_final(screen_size);
95
96        let pass_shadow = (0..max_major_lights)
97            .map(|_| pass::create_shadow(config.shadow_resolution as u32))
98            .collect::<Vec<_>>();
99        let pass_probe = pass::create_probe(config.probe_resolution as u32);
100        let pass_yuv = if config.video_record {
101            Some(pass::create_yuv(screen_size))
102        } else {
103            None
104        };
105
106        let meshes = Meshes::default();
107
108        let prgs = prgs::Programs::new(&gl_manager);
109
110        Graphics {
111            gl_manager,
112            screen_size,
113            pass_deferred1,
114            pass_final,
115            pass_shadow,
116            pass_probe,
117            pass_yuv,
118            meshes,
119            prgs,
120        }
121    }
122
123    pub fn meshes(&self) -> &Meshes {
124        &self.meshes
125    }
126
127    /// Returns (FrameBuffer, ColorAttachment Index) for the UI.
128    ///
129    /// You can fetch object index from the buffer.
130    pub fn get_object_index_fetcher(&self) -> TextureFetcher {
131        TextureFetcher {
132            _framebuffer: self.pass_deferred1.raw_get(),
133            _color_attachment: 6,
134        }
135    }
136
137    pub fn screen_size(&self) -> IVec2 {
138        self.screen_size
139    }
140
141    pub fn ru_set(&self, program: &shader::Shader, unit: &RenderUnit) {
142        deferred_mode(unit.color, unit.depth, unit.id.is_some());
143        unsafe {
144            if unit.depth_test {
145                gl::Enable(gl::DEPTH_TEST);
146            } else {
147                gl::Disable(gl::DEPTH_TEST);
148            }
149            if let Some(id) = unit.id {
150                let u = program.uloc_get(shader::Uniform::ObjectIndex);
151                gl::Uniform1ui(u, id);
152            }
153            if let Some(lighting) = unit.lighting.as_ref() {
154                let u = program.uloc_get(shader::Uniform::Lighting);
155                gl::Uniform1i(u, *lighting as i32);
156            }
157        }
158    }
159
160    pub fn get_gl(&self) -> &GlManager {
161        &self.gl_manager
162    }
163
164    /// We use stencil buffer trick to
165    /// 1. handle the case where the camera enters inside the volume
166    /// 2. reject those fragments BEHIND the volume, but not affected by volume
167    ///
168    /// https://kayru.org/articles/deferred-stencil/
169    fn draw_lighvolume_common(&self, mesh: &Mesh) {
170        mesh.bind();
171        deferred_mode(false, false, false);
172        unsafe {
173            gl::CullFace(gl::BACK);
174            gl::DepthFunc(gl::LEQUAL);
175            gl::Disable(gl::STENCIL_TEST);
176
177            gl::StencilFunc(gl::ALWAYS, 0, 0);
178            gl::StencilOp(gl::KEEP, gl::KEEP, gl::INCR);
179
180            gl::Disable(gl::BLEND);
181        }
182        mesh.draw();
183
184        deferred_mode(true, false, false);
185        unsafe {
186            gl::CullFace(gl::FRONT);
187            gl::DepthFunc(gl::GEQUAL);
188            gl::Disable(gl::STENCIL_TEST);
189
190            gl::StencilFunc(gl::NOTEQUAL, 0, 0xFF);
191            gl::StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
192
193            gl::Enable(gl::BLEND);
194            gl::BlendEquation(gl::FUNC_ADD);
195            gl::BlendFunc(gl::ONE, gl::ONE);
196        }
197        mesh.draw();
198    }
199
200    pub fn draw_lightvolume_uni(
201        &self,
202        program: &Shader,
203        light: &LightUni,
204        cpos: Vec3,
205        _range: bool,
206    ) {
207        program.uniform_light(&light.light, 0);
208        program.uniform_camera(&cpos);
209
210        let radius = light.radius;
211
212        program.uniform_model_s(
213            &glm::vec4_to_vec3(&light.light.pos),
214            &glm::identity(),
215            &Vec3::from_element(radius),
216            false,
217        );
218        self.draw_lighvolume_common(&self.meshes.sphere);
219    }
220
221    pub fn get_transform_for_lightvolume_dir(light: &LightDir) -> Mat4 {
222        let mut trans = glm::translate(&Mat4::identity(), &glm::vec4_to_vec3(&light.light.pos));
223
224        trans *= fere_common::geo::rotation_between(
225            &Vec3::new(1.0, 0.0, 0.0),
226            &Vec3::new(0.0, -1.0, 0.0),
227            &light.xdir,
228            &light.ydir,
229        );
230        trans = glm::scale(&trans, &Vec3::from_element(light.radius));
231        trans = glm::scale(
232            &trans,
233            &Vec3::new((light.angle / 2.0).tan(), (light.angle / 2.0).tan(), 1.0),
234        );
235        trans = glm::translate(&trans, &Vec3::new(0.0, 0.0, 1.0));
236        trans = glm::rotate(&trans, (180.0_f32).to_radians(), &Vec3::new(1.0, 0.0, 0.0));
237        trans
238    }
239
240    pub fn draw_lightvolume_dir(&self, program: &Shader, light: &LightDir, cpos: Vec3) {
241        program.uniform_light_dir(&light, 0);
242        program.uniform_camera(&cpos);
243
244        let trans = Self::get_transform_for_lightvolume_dir(light);
245        program.uniform_model(&trans, false);
246        self.draw_lighvolume_common(&self.meshes.pyramid);
247    }
248
249    pub fn draw_lightvolume_ambient(
250        &self,
251        program: &Shader,
252        cbpos: &Vec3,
253        cpos: &Vec3,
254        size: &Vec3,
255    ) {
256        self.meshes.cube.bind();
257
258        let trans = glm::translate(&glm::identity(), cbpos);
259        let trans = glm::scale(&trans, size);
260        let trans = glm::translate(&trans, &Vec3::new(0.0, 0.0, 0.5));
261        program.uniform_model(&trans, false);
262        program.uniform_camera(&cpos);
263        self.draw_lighvolume_common(&self.meshes.cube);
264    }
265
266    pub fn fill_screen(&self, prg: &Shader) {
267        prg.uniform_transformations(&Mat4::new_scaling(2.0), &Mat4::identity());
268        self.meshes.square.bind();
269        prg.uniform_model(&Mat4::identity(), false);
270        self.meshes.square.draw();
271    }
272
273    pub fn bind_deferred_pass1(&self) {
274        pass::bind_deferred_pass1(self)
275    }
276
277    pub fn bind_deferred_pass2(&self, clear: bool) {
278        pass::bind_deferred_pass2(self, clear)
279    }
280
281    pub fn bind_shadow(&self, index: usize) {
282        pass::bind_shadow(self, index);
283    }
284
285    pub fn bind_probe(&self) {
286        pass::bind_probe(self)
287    }
288
289    pub fn bind_shadow_map(&self, program: &Shader, index: usize) {
290        unsafe {
291            gl::ActiveTexture(gl::TEXTURE6);
292            gl::BindTexture(
293                gl::TEXTURE_2D,
294                self.pass_shadow[index].depth_get().tex_get().raw_get(),
295            );
296            gl::Uniform1i(program.uloc_get_tex()[6], 6)
297        }
298    }
299
300    pub fn bind_gbuffer(&self, program: &Shader, offset: usize) {
301        pass::bind_gbuffer(self, program, offset)
302    }
303
304    pub fn bind_probe_volume(
305        &self,
306        program: &Shader,
307        offset: usize,
308        illumination: &TextureInternal3D,
309        depth: &TextureInternal3D,
310    ) {
311        pass::bind_probe_volume(self, program, offset, illumination, depth)
312    }
313
314    pub fn render_final(&self) {
315        pass::render_final(self)
316    }
317
318    pub fn render_yuv(&self) {
319        pass::render_yuv(self)
320    }
321
322    pub fn bind_forward(&self) {
323        pass::bind_forward(self);
324    }
325
326    pub fn bind_2d(&self) {
327        pass::bind_2d(self)
328    }
329}
330
331// Various debug-purpose backdoors
332impl Graphics {
333    pub fn get_irradiance_volume_tex(&self) -> (u32, IVec2) {
334        (
335            self.pass_probe.outputs_get()[1].tex_get().raw_get(),
336            self.pass_probe.outputs_get()[1].size_get(),
337        )
338    }
339
340    pub fn get_gbuffer_normal(&self) -> (u32, IVec2) {
341        (
342            self.pass_deferred1.outputs_get()[1].tex_get().raw_get(),
343            self.pass_deferred1.outputs_get()[1].size_get(),
344        )
345    }
346}