💿🐜 Antkeeper source code https://antkeeper.com
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

360 lines
9.8 KiB

  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. #include "game/graphics.hpp"
  20. #include <engine/config.hpp>
  21. #include <engine/debug/log.hpp>
  22. #include <engine/gl/framebuffer.hpp>
  23. #include <engine/gl/texture.hpp>
  24. #include <engine/render/passes/bloom-pass.hpp>
  25. #include <engine/render/passes/final-pass.hpp>
  26. #include <engine/render/passes/resample-pass.hpp>
  27. #include <engine/render/passes/sky-pass.hpp>
  28. #include <engine/render/passes/material-pass.hpp>
  29. #include <chrono>
  30. #include <filesystem>
  31. #include <format>
  32. #include <glad/gl.h>
  33. #include <stb/stb_image_write.h>
  34. #include <thread>
  35. namespace graphics {
  36. static void reroute_framebuffers(::game& ctx);
  37. static void rebuild_hdr_framebuffer(::game& ctx)
  38. {
  39. // Construct HDR framebuffer sampler
  40. auto hdr_sampler = std::make_shared<gl::sampler>
  41. (
  42. gl::sampler_filter::linear,
  43. gl::sampler_filter::linear,
  44. gl::sampler_mipmap_mode::linear,
  45. gl::sampler_address_mode::clamp_to_edge,
  46. gl::sampler_address_mode::clamp_to_edge
  47. );
  48. // Construct HDR framebuffer color texture
  49. ctx.hdr_color_texture = std::make_shared<gl::texture_2d>
  50. (
  51. std::make_shared<gl::image_view_2d>
  52. (
  53. std::make_shared<gl::image_2d>
  54. (
  55. gl::format::r32g32b32_sfloat,
  56. ctx.render_resolution.x(),
  57. ctx.render_resolution.y()
  58. )
  59. ),
  60. hdr_sampler
  61. );
  62. // Construct HDR framebuffer depth texture
  63. ctx.hdr_depth_texture = std::make_shared<gl::texture_2d>
  64. (
  65. std::make_shared<gl::image_view_2d>
  66. (
  67. std::make_shared<gl::image_2d>
  68. (
  69. gl::format::d32_sfloat_s8_uint,
  70. ctx.render_resolution.x(),
  71. ctx.render_resolution.y()
  72. )
  73. ),
  74. hdr_sampler
  75. );
  76. // Construct HDR framebuffer
  77. const gl::framebuffer_attachment hdr_attachments[2] =
  78. {
  79. {
  80. gl::color_attachment_bit,
  81. ctx.hdr_color_texture->get_image_view(),
  82. 0
  83. },
  84. {
  85. gl::depth_stencil_attachment_bits,
  86. ctx.hdr_depth_texture->get_image_view(),
  87. 0
  88. }
  89. };
  90. ctx.hdr_framebuffer = std::make_shared<gl::framebuffer>(hdr_attachments, ctx.render_resolution.x(), ctx.render_resolution.y());
  91. }
  92. static void rebuild_ldr_framebuffers(::game& ctx)
  93. {
  94. auto ldr_sampler = std::make_shared<gl::sampler>
  95. (
  96. gl::sampler_filter::linear,
  97. gl::sampler_filter::linear,
  98. gl::sampler_mipmap_mode::linear,
  99. gl::sampler_address_mode::clamp_to_edge,
  100. gl::sampler_address_mode::clamp_to_edge
  101. );
  102. // Construct LDR framebuffer A color texture
  103. ctx.ldr_color_texture_a = std::make_shared<gl::texture_2d>
  104. (
  105. std::make_shared<gl::image_view_2d>
  106. (
  107. std::make_shared<gl::image_2d>
  108. (
  109. gl::format::r8g8b8_unorm,
  110. ctx.render_resolution.x(),
  111. ctx.render_resolution.y()
  112. )
  113. ),
  114. ldr_sampler
  115. );
  116. // Construct LDR framebuffer A
  117. const gl::framebuffer_attachment ldr_attachments_a[1] =
  118. {
  119. {
  120. gl::color_attachment_bit,
  121. ctx.ldr_color_texture_a->get_image_view(),
  122. 0
  123. }
  124. };
  125. ctx.ldr_framebuffer_a = std::make_shared<gl::framebuffer>(ldr_attachments_a, ctx.render_resolution.x(), ctx.render_resolution.y());
  126. // Construct LDR framebuffer B color texture
  127. ctx.ldr_color_texture_b = std::make_shared<gl::texture_2d>
  128. (
  129. std::make_shared<gl::image_view_2d>
  130. (
  131. std::make_shared<gl::image_2d>
  132. (
  133. gl::format::r8g8b8_unorm,
  134. ctx.render_resolution.x(),
  135. ctx.render_resolution.y()
  136. )
  137. ),
  138. ldr_sampler
  139. );
  140. // Construct LDR framebuffer B
  141. const gl::framebuffer_attachment ldr_attachments_b[1] =
  142. {
  143. {
  144. gl::color_attachment_bit,
  145. ctx.ldr_color_texture_b->get_image_view(),
  146. 0
  147. }
  148. };
  149. ctx.ldr_framebuffer_b = std::make_shared<gl::framebuffer>(ldr_attachments_b, ctx.render_resolution.x(), ctx.render_resolution.y());
  150. }
  151. void rebuild_shadow_framebuffer(::game& ctx)
  152. {
  153. // Construct shadow map sampler
  154. auto shadow_sampler = std::make_shared<gl::sampler>
  155. (
  156. gl::sampler_filter::linear,
  157. gl::sampler_filter::linear,
  158. gl::sampler_mipmap_mode::linear,
  159. gl::sampler_address_mode::clamp_to_border,
  160. gl::sampler_address_mode::clamp_to_border,
  161. gl::sampler_address_mode::clamp_to_border,
  162. 0.0f,
  163. 0.0f,
  164. true,
  165. gl::compare_op::greater,
  166. -1000.0f,
  167. 1000.0f,
  168. std::array<float, 4>{0.0f, 0.0f, 0.0f, 0.0f}
  169. );
  170. // Construct shadow map framebuffer depth texture
  171. ctx.shadow_map_depth_texture = std::make_shared<gl::texture_2d>
  172. (
  173. std::make_shared<gl::image_view_2d>
  174. (
  175. std::make_shared<gl::image_2d>
  176. (
  177. gl::format::d32_sfloat,
  178. ctx.shadow_map_resolution,
  179. ctx.shadow_map_resolution
  180. )
  181. ),
  182. shadow_sampler
  183. );
  184. // Construct shadow map framebuffer
  185. const gl::framebuffer_attachment shadow_map_attachments[1] =
  186. {
  187. {
  188. gl::depth_attachment_bit,
  189. ctx.shadow_map_depth_texture->get_image_view(),
  190. 0
  191. }
  192. };
  193. ctx.shadow_map_framebuffer = std::make_shared<gl::framebuffer>(shadow_map_attachments, ctx.shadow_map_resolution, ctx.shadow_map_resolution);
  194. }
  195. void create_framebuffers(::game& ctx)
  196. {
  197. debug::log_trace("Creating framebuffers...");
  198. // Calculate render resolution
  199. const math::ivec2& viewport_size = ctx.window->get_viewport_size();
  200. 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)};
  201. rebuild_hdr_framebuffer(ctx);
  202. rebuild_ldr_framebuffers(ctx);
  203. rebuild_shadow_framebuffer(ctx);
  204. debug::log_trace("Created framebuffers");
  205. }
  206. void destroy_framebuffers(::game& ctx)
  207. {
  208. debug::log_trace("Destroying framebuffers...");
  209. // Delete HDR framebuffer and its attachments
  210. ctx.hdr_framebuffer.reset();
  211. ctx.hdr_color_texture.reset();
  212. ctx.hdr_depth_texture.reset();
  213. // Delete LDR framebuffers and attachments
  214. ctx.ldr_framebuffer_a.reset();
  215. ctx.ldr_color_texture_a.reset();
  216. ctx.ldr_framebuffer_b.reset();
  217. ctx.ldr_color_texture_b.reset();
  218. // Delete shadow map framebuffer and its attachments
  219. ctx.shadow_map_framebuffer.reset();
  220. ctx.shadow_map_depth_texture.reset();
  221. debug::log_trace("Destroyed framebuffers");
  222. }
  223. void change_render_resolution(::game& ctx, float scale)
  224. {
  225. // Recalculate render resolution
  226. const math::ivec2& viewport_size = ctx.window->get_viewport_size();
  227. const auto render_resolution = math::ivec2{static_cast<int>(viewport_size.x() * scale + 0.5f), static_cast<int>(viewport_size.y() * scale + 0.5f)};
  228. if (ctx.render_resolution == render_resolution)
  229. {
  230. return;
  231. }
  232. debug::log_trace("Changing render resolution to {}...", scale);
  233. // Update render resolution scale
  234. ctx.render_scale = scale;
  235. ctx.render_resolution = render_resolution;
  236. rebuild_hdr_framebuffer(ctx);
  237. rebuild_ldr_framebuffers(ctx);
  238. // Enable or disable resample pass
  239. if (viewport_size.x() != ctx.render_resolution.x() || viewport_size.y() != ctx.render_resolution.y())
  240. {
  241. ctx.resample_pass->set_enabled(true);
  242. debug::log_debug("Resample pass enabled");
  243. }
  244. else
  245. {
  246. ctx.resample_pass->set_enabled(false);
  247. debug::log_debug("Resample pass disabled");
  248. }
  249. reroute_framebuffers(ctx);
  250. debug::log_trace("Changed render resolution to {}", scale);
  251. }
  252. void save_screenshot(::game& ctx)
  253. {
  254. // Determine timestamped screenshot filename
  255. const auto time = std::chrono::floor<std::chrono::milliseconds>(std::chrono::system_clock::now());
  256. const std::string screenshot_filename = std::format("{0}-{1:%Y%m%d}T{1:%H%M%S}Z.png", config::application_name, time);
  257. // Determine path to screenshot file
  258. std::filesystem::path screenshot_filepath = ctx.screenshots_path / screenshot_filename;
  259. std::string screenshot_filepath_string = screenshot_filepath.string();
  260. debug::log_debug("Saving screenshot to \"{}\"...", screenshot_filepath_string);
  261. // Get viewport dimensions
  262. const auto& viewport_size = ctx.window->get_viewport_size();
  263. // Allocate screenshot pixel data buffer
  264. std::unique_ptr<std::byte[]> frame = std::make_unique<std::byte[]>(viewport_size.x() * viewport_size.y() * 3);
  265. // Read pixel data from backbuffer into pixel data buffer
  266. glReadBuffer(GL_BACK);
  267. glReadPixels(0, 0, viewport_size.x(), viewport_size.y(), GL_RGB, GL_UNSIGNED_BYTE, frame.get());
  268. // Write screenshot file in separate thread
  269. std::thread
  270. (
  271. [frame = std::move(frame), w = viewport_size.x(), h = viewport_size.y(), path = std::move(screenshot_filepath_string)]
  272. {
  273. stbi_flip_vertically_on_write(1);
  274. stbi_write_png(path.c_str(), w, h, 3, frame.get(), w * 3);
  275. debug::log_debug("Saved screenshot to \"{}\"", path);
  276. }
  277. ).detach();
  278. }
  279. void select_anti_aliasing_method(::game& ctx, render::anti_aliasing_method method)
  280. {
  281. // Switch AA method
  282. switch (method)
  283. {
  284. // Off
  285. case render::anti_aliasing_method::none:
  286. debug::log_debug("Anti-aliasing disabled");
  287. reroute_framebuffers(ctx);
  288. break;
  289. // FXAA
  290. case render::anti_aliasing_method::fxaa:
  291. debug::log_debug("Anti-aliasing enabled (FXAA)");
  292. reroute_framebuffers(ctx);
  293. break;
  294. }
  295. // Update AA method setting
  296. ctx.anti_aliasing_method = method;
  297. }
  298. void reroute_framebuffers(::game& ctx)
  299. {
  300. if (ctx.resample_pass->is_enabled())
  301. {
  302. ctx.common_final_pass->set_framebuffer(ctx.ldr_framebuffer_a.get());
  303. }
  304. else
  305. {
  306. ctx.common_final_pass->set_framebuffer(nullptr);
  307. }
  308. ctx.sky_pass->set_framebuffer(ctx.hdr_framebuffer.get());
  309. ctx.surface_material_pass->set_framebuffer(ctx.hdr_framebuffer.get());
  310. ctx.bloom_pass->set_source_texture(ctx.hdr_color_texture);
  311. ctx.common_final_pass->set_color_texture(ctx.hdr_color_texture);
  312. ctx.common_final_pass->set_bloom_texture(ctx.bloom_pass->get_bloom_texture());
  313. }
  314. } // namespace graphics