Antkeeper  0.0.1
graphics.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2023 Christopher J. Howard
3  *
4  * This file is part of Antkeeper source code.
5  *
6  * Antkeeper source code is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Antkeeper source code is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "game/graphics.hpp"
21 #include <engine/config.hpp>
22 #include <engine/debug/log.hpp>
24 #include <engine/gl/texture.hpp>
30 #include <chrono>
31 #include <filesystem>
32 #include <format>
33 #include <glad/gl.h>
34 #include <stb/stb_image_write.h>
35 #include <thread>
36 
37 namespace graphics {
38 
39 static void reroute_framebuffers(::game& ctx);
40 
41 static void rebuild_hdr_framebuffer(::game& ctx)
42 {
43  // Construct HDR framebuffer sampler
44  auto hdr_sampler = std::make_shared<gl::sampler>
45  (
51  );
52 
53  // Construct HDR framebuffer color texture
54  ctx.hdr_color_texture = std::make_shared<gl::texture_2d>
55  (
56  std::make_shared<gl::image_view_2d>
57  (
58  std::make_shared<gl::image_2d>
59  (
61  ctx.render_resolution.x(),
62  ctx.render_resolution.y()
63  )
64  ),
65  hdr_sampler
66  );
67 
68  // Construct HDR framebuffer depth texture
69  ctx.hdr_depth_texture = std::make_shared<gl::texture_2d>
70  (
71  std::make_shared<gl::image_view_2d>
72  (
73  std::make_shared<gl::image_2d>
74  (
76  ctx.render_resolution.x(),
77  ctx.render_resolution.y()
78  )
79  ),
80  hdr_sampler
81  );
82 
83  // Construct HDR framebuffer
84  const gl::framebuffer_attachment hdr_attachments[2] =
85  {
86  {
88  ctx.hdr_color_texture->get_image_view(),
89  0
90  },
91  {
93  ctx.hdr_depth_texture->get_image_view(),
94  0
95  }
96  };
97  ctx.hdr_framebuffer = std::make_shared<gl::framebuffer>(hdr_attachments, ctx.render_resolution.x(), ctx.render_resolution.y());
98 }
99 
100 static void rebuild_ldr_framebuffers(::game& ctx)
101 {
102  auto ldr_sampler = std::make_shared<gl::sampler>
103  (
109  );
110 
111  // Construct LDR framebuffer A color texture
112  ctx.ldr_color_texture_a = std::make_shared<gl::texture_2d>
113  (
114  std::make_shared<gl::image_view_2d>
115  (
116  std::make_shared<gl::image_2d>
117  (
119  ctx.render_resolution.x(),
120  ctx.render_resolution.y()
121  )
122  ),
123  ldr_sampler
124  );
125 
126  // Construct LDR framebuffer A
127  const gl::framebuffer_attachment ldr_attachments_a[1] =
128  {
129  {
131  ctx.ldr_color_texture_a->get_image_view(),
132  0
133  }
134  };
135  ctx.ldr_framebuffer_a = std::make_shared<gl::framebuffer>(ldr_attachments_a, ctx.render_resolution.x(), ctx.render_resolution.y());
136 
137  // Construct LDR framebuffer B color texture
138  ctx.ldr_color_texture_b = std::make_shared<gl::texture_2d>
139  (
140  std::make_shared<gl::image_view_2d>
141  (
142  std::make_shared<gl::image_2d>
143  (
145  ctx.render_resolution.x(),
146  ctx.render_resolution.y()
147  )
148  ),
149  ldr_sampler
150  );
151 
152  // Construct LDR framebuffer B
153  const gl::framebuffer_attachment ldr_attachments_b[1] =
154  {
155  {
157  ctx.ldr_color_texture_b->get_image_view(),
158  0
159  }
160  };
161  ctx.ldr_framebuffer_b = std::make_shared<gl::framebuffer>(ldr_attachments_b, ctx.render_resolution.x(), ctx.render_resolution.y());
162 }
163 
165 {
166  // Construct shadow map sampler
167  auto shadow_sampler = std::make_shared<gl::sampler>
168  (
175  0.0f,
176  0.0f,
177  true,
179  -1000.0f,
180  1000.0f,
181  std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f}
182  );
183 
184  // Construct shadow map framebuffer depth texture
185  ctx.shadow_map_depth_texture = std::make_shared<gl::texture_2d>
186  (
187  std::make_shared<gl::image_view_2d>
188  (
189  std::make_shared<gl::image_2d>
190  (
194  )
195  ),
196  shadow_sampler
197  );
198 
199  // Construct shadow map framebuffer
200  const gl::framebuffer_attachment shadow_map_attachments[1] =
201  {
202  {
204  ctx.shadow_map_depth_texture->get_image_view(),
205  0
206  }
207  };
208  ctx.shadow_map_framebuffer = std::make_shared<gl::framebuffer>(shadow_map_attachments, ctx.shadow_map_resolution, ctx.shadow_map_resolution);
209 }
210 
212 {
213  debug::log_trace("Creating framebuffers...");
214 
215  // Calculate render resolution
216  const math::ivec2& viewport_size = ctx.window->get_viewport_size();
217  ctx.render_resolution = {static_cast<int>(viewport_size.x() * ctx.render_scale + 0.5f), static_cast<int>(viewport_size.y() * ctx.render_scale + 0.5f)};
218 
219  rebuild_hdr_framebuffer(ctx);
220  rebuild_ldr_framebuffers(ctx);
222 
223  debug::log_trace("Created framebuffers");
224 }
225 
227 {
228  debug::log_trace("Destroying framebuffers...");
229 
230  // Delete HDR framebuffer and its attachments
231  ctx.hdr_framebuffer.reset();
232  ctx.hdr_color_texture.reset();
233  ctx.hdr_depth_texture.reset();
234 
235  // Delete LDR framebuffers and attachments
236  ctx.ldr_framebuffer_a.reset();
237  ctx.ldr_color_texture_a.reset();
238 
239  ctx.ldr_framebuffer_b.reset();
240  ctx.ldr_color_texture_b.reset();
241 
242  // Delete shadow map framebuffer and its attachments
243  ctx.shadow_map_framebuffer.reset();
244  ctx.shadow_map_depth_texture.reset();
245 
246  debug::log_trace("Destroyed framebuffers");
247 }
248 
250 {
251  // Recalculate render resolution
252  const math::ivec2& viewport_size = ctx.window->get_viewport_size();
253  const auto render_resolution = math::ivec2{static_cast<int>(viewport_size.x() * scale + 0.5f), static_cast<int>(viewport_size.y() * scale + 0.5f)};
254 
255  if (ctx.render_resolution == render_resolution)
256  {
257  return;
258  }
259 
260  debug::log_trace("Changing render resolution to {}...", scale);
261 
262  // Update render resolution scale
263  ctx.render_scale = scale;
264  ctx.render_resolution = render_resolution;
265 
266  rebuild_hdr_framebuffer(ctx);
267  rebuild_ldr_framebuffers(ctx);
268 
269  // Enable or disable resample pass
270  if (viewport_size.x() != ctx.render_resolution.x() || viewport_size.y() != ctx.render_resolution.y())
271  {
272  ctx.resample_pass->set_enabled(true);
273  debug::log_debug("Resample pass enabled");
274  }
275  else
276  {
277  ctx.resample_pass->set_enabled(false);
278  debug::log_debug("Resample pass disabled");
279  }
280 
281  reroute_framebuffers(ctx);
282 
283  debug::log_trace("Changed render resolution to {}", scale);
284 }
285 
286 void save_screenshot(::game& ctx)
287 {
288  // Determine timestamped screenshot filename
289  const auto time = std::chrono::floor<std::chrono::milliseconds>(std::chrono::system_clock::now());
290  const std::string screenshot_filename = std::format("{0}-{1:%Y%m%d}T{1:%H%M%S}Z.png", config::application_name, time);
291 
292  // Determine path to screenshot file
293  std::filesystem::path screenshot_filepath = ctx.screenshots_path / screenshot_filename;
294  std::string screenshot_filepath_string = screenshot_filepath.string();
295  debug::log_debug("Saving screenshot to \"{}\"...", screenshot_filepath_string);
296 
297  // Get viewport dimensions
298  const auto& viewport_size = ctx.window->get_viewport_size();
299 
300  // Allocate screenshot pixel data buffer
301  std::unique_ptr<std::byte[]> frame = std::make_unique<std::byte[]>(viewport_size.x() * viewport_size.y() * 3);
302 
303  // Read pixel data from backbuffer into pixel data buffer
304  glReadBuffer(GL_BACK);
305  glReadPixels(0, 0, viewport_size.x(), viewport_size.y(), GL_RGB, GL_UNSIGNED_BYTE, frame.get());
306 
307  // Write screenshot file in separate thread
308  std::thread
309  (
310  [frame = std::move(frame), w = viewport_size.x(), h = viewport_size.y(), path = std::move(screenshot_filepath_string)]
311  {
312  stbi_flip_vertically_on_write(1);
313  stbi_write_png(path.c_str(), w, h, 3, frame.get(), w * 3);
314 
315  debug::log_debug("Saved screenshot to \"{}\"", path);
316  }
317  ).detach();
318 }
319 
321 {
322  // Switch AA method
323  switch (method)
324  {
325  // Off
327  debug::log_debug("Anti-aliasing disabled");
328  reroute_framebuffers(ctx);
329  break;
330 
331  // FXAA
333  debug::log_debug("Anti-aliasing enabled (FXAA)");
334  reroute_framebuffers(ctx);
335  break;
336  }
337 
338  // Update AA method setting
339  ctx.anti_aliasing_method = method;
340 }
341 
342 void reroute_framebuffers(::game& ctx)
343 {
344  if (ctx.resample_pass->is_enabled())
345  {
346  ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a.get());
347  }
348  else
349  {
350  ctx.common_final_pass->set_framebuffer(nullptr);
351  }
352 
353  ctx.sky_pass->set_framebuffer(ctx.hdr_framebuffer.get());
354  ctx.surface_material_pass->set_framebuffer(ctx.hdr_framebuffer.get());
355  ctx.bloom_pass->set_source_texture(ctx.hdr_color_texture);
356  ctx.common_final_pass->set_color_texture(ctx.hdr_color_texture);
357  ctx.common_final_pass->set_bloom_texture(ctx.bloom_pass->get_bloom_texture());
358 }
359 
360 } // namespace graphics
Definition: game.hpp:121
std::shared_ptr< gl::texture_2d > shadow_map_depth_texture
Definition: game.hpp:313
std::shared_ptr< gl::texture_2d > ldr_color_texture_a
Definition: game.hpp:309
std::shared_ptr< gl::texture_2d > hdr_color_texture
Definition: game.hpp:306
std::shared_ptr< gl::framebuffer > hdr_framebuffer
Definition: game.hpp:308
std::shared_ptr< gl::framebuffer > shadow_map_framebuffer
Definition: game.hpp:314
std::unique_ptr< render::material_pass > surface_material_pass
Definition: game.hpp:328
float render_scale
Definition: game.hpp:318
std::shared_ptr< gl::texture_2d > hdr_depth_texture
Definition: game.hpp:307
std::unique_ptr< render::bloom_pass > bloom_pass
Definition: game.hpp:322
std::shared_ptr< gl::texture_2d > ldr_color_texture_b
Definition: game.hpp:311
std::shared_ptr< gl::framebuffer > ldr_framebuffer_a
Definition: game.hpp:310
std::filesystem::path screenshots_path
Definition: game.hpp:158
std::unique_ptr< render::resample_pass > resample_pass
Definition: game.hpp:324
render::anti_aliasing_method anti_aliasing_method
Definition: game.hpp:427
std::unique_ptr< render::sky_pass > sky_pass
Definition: game.hpp:327
std::shared_ptr< app::window > window
Definition: game.hpp:166
std::unique_ptr< render::final_pass > common_final_pass
Definition: game.hpp:323
int shadow_map_resolution
Definition: game.hpp:319
math::ivec2 render_resolution
Definition: game.hpp:317
std::shared_ptr< gl::framebuffer > ldr_framebuffer_b
Definition: game.hpp:312
log_message< log_message_severity::trace, Args... > log_trace
Formats and logs a trace message.
Definition: log.hpp:88
log_message< log_message_severity::debug, Args... > log_debug
Formats and logs a debug message.
Definition: log.hpp:102
format
Image and vertex formats.
Definition: format.hpp:29
@ d32_sfloat_s8_uint
@ clamp_to_edge
Clamp to edge wrap mode.
@ clamp_to_border
Clamp to border wrap mode.
@ depth_stencil_attachment_bits
Framebuffer depth/stencil attachment.
@ color_attachment_bit
Framebuffer color attachment.
@ depth_attachment_bit
Framebuffer depth attachment.
@ linear
Linear filtering.
@ linear
Linear filtering.
@ greater
Comparison evaluates reference > test.
void destroy_framebuffers(::game &ctx)
Definition: graphics.cpp:226
void select_anti_aliasing_method(::game &ctx, render::anti_aliasing_method method)
Definition: graphics.cpp:320
void save_screenshot(::game &ctx)
Definition: graphics.cpp:286
void create_framebuffers(::game &ctx)
Definition: graphics.cpp:211
void change_render_resolution(::game &ctx, float scale)
Definition: graphics.cpp:249
void rebuild_shadow_framebuffer(::game &ctx)
Definition: graphics.cpp:164
constexpr mat4< T > scale(const vec3< T > &v)
Constructs a scale matrix.
anti_aliasing_method
Anti-aliasing methods.
@ none
No anti-aliasing.
@ fxaa
Fast approximate anti-aliasing (FXAA).
n-dimensional vector.
Definition: vector.hpp:44
constexpr element_type & x() noexcept
Returns a reference to the first element.
Definition: vector.hpp:164
constexpr element_type & y() noexcept
Returns a reference to the second element.
Definition: vector.hpp:180