Antkeeper  0.0.1
game.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/game.hpp"
22 #include "game/control-profile.hpp"
23 #include "game/controls.hpp"
24 #include "game/fonts.hpp"
25 #include "game/graphics.hpp"
26 #include "game/menu.hpp"
27 #include "game/settings.hpp"
30 #include "game/strings.hpp"
51 #include <algorithm>
52 #include <cctype>
58 #include <engine/color/color.hpp>
59 #include <engine/config.hpp>
60 #include <engine/debug/cli.hpp>
61 #include <engine/debug/log.hpp>
64 #include <engine/gl/pixel-type.hpp>
65 #include <engine/gl/texture.hpp>
69 #include <engine/input/gamepad.hpp>
71 #include <engine/input/mapper.hpp>
72 #include <engine/input/mouse.hpp>
85 #include <engine/scene/scene.hpp>
86 #include <engine/utility/dict.hpp>
88 #include <engine/utility/paths.hpp>
89 #include <entt/entt.hpp>
90 #include <execution>
91 #include <filesystem>
92 #include <functional>
93 #include <string>
94 #include <vector>
95 #include <chrono>
96 
97 // Prevent cxxopts from using RTTI
98 #define CXXOPTS_NO_RTTI
99 #include <cxxopts.hpp>
100 
101 using namespace hash::literals;
102 
103 game::game(int argc, const char* const* argv)
104 {
105  // Boot process
106  debug::log_trace("Booting up...");
107 
108  // Profile boot duration
109  #if !defined(NDEBUG)
110  auto boot_t0 = std::chrono::high_resolution_clock::now();
111  #endif
112 
113  parse_options(argc, argv);
114  setup_resources();
115  load_settings();
116  setup_window();
117  setup_audio();
118  setup_input();
119  load_strings();
120  setup_rendering();
121  setup_scenes();
122  setup_animation();
123  setup_ui();
124  setup_rng();
125  setup_entities();
126  setup_systems();
127  setup_controls();
128  setup_debugging();
129  setup_timing();
130 
131  active_ecoregion = nullptr;
132  closed = false;
133 
134  // Profile boot duration
135  #if !defined(NDEBUG)
136  auto boot_t1 = std::chrono::high_resolution_clock::now();
137  #endif
138 
139  debug::log_trace("Boot up complete");
140 
141  // Print boot duration
142  #if !defined(NDEBUG)
143  debug::log_info("Boot duration: {}", std::chrono::duration_cast<std::chrono::duration<double>>(boot_t1 - boot_t0));
144  #endif
145 }
146 
148 {
149  debug::log_trace("Booting down...");
150 
151  // Exit all active game states
152  while (!state_machine.empty())
153  {
154  state_machine.pop();
155  }
156 
157  // Update window settings
158  const auto& windowed_position = window->get_windowed_position();
159  const auto& windowed_size = window->get_windowed_size();
160  const bool maximized = window->is_maximized();
161  const bool fullscreen = window->is_fullscreen();
162  (*settings)["window_x"] = windowed_position.x();
163  (*settings)["window_y"] = windowed_position.y();
164  (*settings)["window_w"] = windowed_size.x();
165  (*settings)["window_h"] = windowed_size.y();
166  (*settings)["maximized"] = maximized;
167  (*settings)["fullscreen"] = fullscreen;
168 
169  // Destruct window
170  window.reset();
171 
172  // Save settings
173  resource_manager->set_write_path(shared_config_path);
174  resource_manager->save(*settings, "settings.cfg");
175 
176  // Destruct input and window managers
177  input_manager.reset();
178  window_manager.reset();
179 
180  // Shut down audio
181  shutdown_audio();
182 
183  debug::log_trace("Boot down complete");
184 }
185 
186 void game::parse_options(int argc, const char* const* argv)
187 {
188  if (argc <= 1)
189  {
190  // No command-line options specified
191  return;
192  }
193 
194  debug::log_trace("Parsing command-line options...");
195 
196  // Parse command-line options with cxxopts
197  try
198  {
199  cxxopts::Options options(config::application_name, config::application_name);
200  options.add_options()
201  ("c,continue", "Continues from the last save")
202  ("d,data", "Sets the data package path", cxxopts::value<std::string>())
203  ("f,fullscreen", "Starts in fullscreen mode")
204  ("n,new-game", "Starts a new game")
205  ("q,quick-start", "Skips to the main menu")
206  ("r,reset", "Resets all settings to default")
207  ("v,v-sync", "Enables or disables v-sync", cxxopts::value<int>())
208  ("w,windowed", "Starts in windowed mode");
209  auto result = options.parse(argc, argv);
210 
211  // --continue
212  if (result.count("continue"))
213  {
214  option_continue = true;
215  }
216 
217  // --data
218  if (result.count("data"))
219  {
220  option_data = result["data"].as<std::string>();
221  }
222 
223  // --fullscreen
224  if (result.count("fullscreen"))
225  {
226  option_fullscreen = true;
227  }
228 
229  // --new-game
230  if (result.count("new-game"))
231  {
232  option_new_game = true;
233  }
234 
235  // --quick-start
236  if (result.count("quick-start"))
237  {
238  option_quick_start = true;
239  }
240 
241  // --reset
242  if (result.count("reset"))
243  {
244  option_reset = true;
245  }
246 
247  // --v-sync
248  if (result.count("v-sync"))
249  {
250  option_v_sync = result["v-sync"].as<int>();
251  }
252 
253  // --windowed
254  if (result.count("windowed"))
255  {
256  option_windowed = true;
257  }
258 
259  debug::log_info("Parsed {} command-line options", argc);
260  }
261  catch (const std::exception& e)
262  {
263  debug::log_error("An error occurred while parsing command-line options: {}", e.what());
264  }
265 }
266 
267 void game::setup_resources()
268 {
269  // Allocate resource manager
270  resource_manager = std::make_unique<::resource_manager>();
271 
272  // Get executable data path
273  const auto data_path = get_executable_data_path();
274 
275  // Determine data package path
276  if (option_data)
277  {
278  // Handle command-line data path option
279  data_package_path = option_data.value();
280  if (data_package_path.is_relative())
281  {
282  data_package_path = data_path / data_package_path;
283  }
284  }
285  else
286  {
287  data_package_path = data_path / (config::application_slug + std::string("-data.zip"));
288  }
289 
290  // Determine mods path
291  mods_path = data_path / "mods";
292 
293  // Determine config paths
294  local_config_path = get_local_config_path() / config::application_name;
295  shared_config_path = get_shared_config_path() / config::application_name;
296  saves_path = shared_config_path / "saves";
297  screenshots_path = shared_config_path / "gallery";
298  controls_path = shared_config_path / "controls";
299 
300  // Log paths
301  debug::log_info("Data package path: \"{}\"", data_package_path.string());
302  debug::log_info("Local config path: \"{}\"", local_config_path.string());
303  debug::log_info("Shared config path: \"{}\"", shared_config_path.string());
304  debug::log_info("Mods path: \"{}\"", mods_path.string());
305 
306  // Create nonexistent config directories
307  std::vector<std::filesystem::path> config_paths;
308  config_paths.push_back(local_config_path);
309  config_paths.push_back(shared_config_path);
310  config_paths.push_back(saves_path);
311  config_paths.push_back(screenshots_path);
312  config_paths.push_back(controls_path);
313  for (const auto& path: config_paths)
314  {
315  try
316  {
317  if (std::filesystem::create_directories(path))
318  {
319  debug::log_info("Created directory \"{}\"", path.string());
320  }
321  }
322  catch (const std::filesystem::filesystem_error& e)
323  {
324  debug::log_error("Failed to create directory \"{}\": {}", path.string(), e.what());
325  }
326  }
327 
328  // Scan for mods
329  std::vector<std::filesystem::path> mod_paths;
330  if (std::filesystem::is_directory(mods_path))
331  {
332  for (const auto& entry: std::filesystem::directory_iterator{mods_path})
333  {
334  if (entry.is_directory() || (entry.is_regular_file() && entry.path().extension() == ".zip"))
335  {
336  mod_paths.push_back(entry.path());
337  debug::log_info("Found mod \"{}\"", entry.path().filename().string());
338  }
339  }
340  }
341 
342  // Mount mod paths
343  for (const std::filesystem::path& mod_path: mod_paths)
344  {
345  resource_manager->mount(mods_path / mod_path);
346  }
347 
348  // Mount config path
349  resource_manager->mount(local_config_path);
350  resource_manager->mount(shared_config_path);
351 
352  // Mount data package path
353  resource_manager->mount(data_package_path);
354 
355  // Mount controls path
356  resource_manager->mount(shared_config_path / "controls");
357 }
358 
359 void game::load_settings()
360 {
361  if (option_reset)
362  {
363  // Command-line reset option found, reset settings
364  settings = std::make_shared<dict<hash::fnv1a32_t>>();
365  resource_manager->set_write_path(shared_config_path);
366  resource_manager->save(*settings, "settings.cfg");
367  debug::log_info("Settings reset");
368  }
369  else
370  {
371  settings = resource_manager->load<dict<hash::fnv1a32_t>>("settings.cfg");
372  if (!settings)
373  {
374  debug::log_info("Settings not found");
375  settings = std::make_shared<dict<hash::fnv1a32_t>>();
376  }
377  }
378 }
379 
380 void game::setup_window()
381 {
382  // Construct window manager
383  window_manager = app::window_manager::instance();
384 
385  // Default window settings
386  std::string window_title = config::application_name;
387  int window_x = -1;
388  int window_y = -1;
389  int window_w = -1;
390  int window_h = -1;
391  bool maximized = true;
392  bool fullscreen = true;
393  bool v_sync = true;
394 
395  // Read window settings
396  bool resize = false;
397  read_or_write_setting(*this, "window_title", window_title);
398  read_or_write_setting(*this, "window_x", window_x);
399  read_or_write_setting(*this, "window_y", window_y);
400  if (!read_or_write_setting(*this, "window_w", window_w) ||
401  !read_or_write_setting(*this, "window_h", window_h))
402  {
403  resize = true;
404  }
405  read_or_write_setting(*this, "maximized", maximized);
406  read_or_write_setting(*this, "fullscreen", fullscreen);
407  read_or_write_setting(*this, "v_sync", v_sync);
408 
409  // If window size not set, resize and reposition relative to default display
410  if (resize)
411  {
412  const app::display& display = window_manager->get_display(0);
413  const auto& usable_bounds = display.get_usable_bounds();
414  const auto usable_bounds_center = usable_bounds.center();
415 
416  const float default_windowed_scale = 1.0f / 1.2f;
417 
418  window_w = static_cast<int>((usable_bounds.max.x() - usable_bounds.min.x()) * default_windowed_scale);
419  window_h = static_cast<int>((usable_bounds.max.y() - usable_bounds.min.y()) * default_windowed_scale);
420  window_x = usable_bounds_center.x() - window_w / 2;
421  window_y = usable_bounds_center.y() - window_h / 2;
422  }
423 
424  // Handle window-related command-line options
425  if (option_windowed)
426  {
427  // Start in windowed mode
428  maximized = false;
429  fullscreen = false;
430  }
431  if (option_fullscreen)
432  {
433  // Start in fullscreen mode
434  fullscreen = true;
435  }
436  if (option_v_sync)
437  {
438  v_sync = option_v_sync.value();
439  }
440 
441  // Construct window
442  window = window_manager->create_window
443  (
444  window_title,
445  {window_x, window_y},
446  {window_w, window_h},
447  maximized,
448  fullscreen,
449  v_sync
450  );
451 
452  // Restrict window size
453  window->set_minimum_size({160, 144});
454 
455  // Setup window closed callback
456  window_closed_subscription = window->get_closed_channel().subscribe
457  (
458  [&](const auto& event)
459  {
460  closed = true;
461  }
462  );
463 }
464 
465 void game::setup_audio()
466 {
467  debug::log_trace("Setting up audio...");
468 
469  // Default audio settings
470  master_volume = 1.0f;
471  ambience_volume = 1.0f;
472  effects_volume = 1.0f;
473  mono_audio = false;
474  captions = false;
475  captions_size = 1.0f;
476 
477  // Read audio settings
478  read_or_write_setting(*this, "master_volume", master_volume);
479  read_or_write_setting(*this, "ambience_volume", ambience_volume);
480  read_or_write_setting(*this, "effects_volume", effects_volume);
481  read_or_write_setting(*this, "mono_audio", mono_audio);
482  read_or_write_setting(*this, "captions", captions);
483  read_or_write_setting(*this, "captions_size", captions_size);
484 
485  // Open audio device
486  debug::log_trace("Opening audio device...");
487  alc_device = alcOpenDevice(nullptr);
488  if (!alc_device)
489  {
490  debug::log_error("Failed to open audio device: AL error code {}", alGetError());
491  return;
492  }
493  else
494  {
495  // Get audio device name
496  const ALCchar* alc_device_name = nullptr;
497  if (alcIsExtensionPresent(alc_device, "ALC_ENUMERATE_ALL_EXT"))
498  {
499  alc_device_name = alcGetString(alc_device, ALC_ALL_DEVICES_SPECIFIER);
500  }
501  if (alcGetError(alc_device) != AL_NO_ERROR || !alc_device_name)
502  {
503  alc_device_name = alcGetString(alc_device, ALC_DEVICE_SPECIFIER);
504  }
505 
506  // Log audio device name
507  debug::log_info("Opened audio device \"{}\"", alc_device_name);
508  }
509 
510  // Create audio context
511  debug::log_trace("Creating audio context...");
512  alc_context = alcCreateContext(alc_device, nullptr);
513  if (!alc_context)
514  {
515  debug::log_error("Failed to create audio context: ALC error code {}", alcGetError(alc_device));
516  alcCloseDevice(alc_device);
517  return;
518  }
519  else
520  {
521  debug::log_trace("Created audio context");
522  }
523 
524  // Make audio context current
525  debug::log_trace("Making audio context current...");
526  if (alcMakeContextCurrent(alc_context) == ALC_FALSE)
527  {
528  debug::log_error("Failed to make audio context current: ALC error code {}", alcGetError(alc_device));
529  if (alc_context != nullptr)
530  {
531  alcDestroyContext(alc_context);
532  }
533  alcCloseDevice(alc_device);
534  return;
535  }
536  else
537  {
538  debug::log_trace("Made audio context current");
539  }
540 
541  debug::log_trace("Set up audio");
542 }
543 
544 void game::setup_input()
545 {
546  // Construct input manager
547  input_manager = app::input_manager::instance();
548 
549  // Process initial input events, such as connecting gamepads
550  input_manager->update();
551 
552  // Setup application quit callback
553  application_quit_subscription = input_manager->get_event_dispatcher().subscribe<input::application_quit_event>
554  (
555  [&](const auto& event)
556  {
557  closed = true;
558  }
559  );
560 
561  /*
562  // Gamepad deactivation function
563  auto deactivate_gamepad = [&](const auto& event)
564  {
565  if (gamepad_active)
566  {
567  gamepad_active = false;
568 
569  // WARNING: huge source of lag
570  input_manager->set_cursor_visible(true);
571  }
572  };
573 
574  // Setup gamepad activation callbacks
575  gamepad_axis_moved_subscription = input_manager->get_event_dispatcher().subscribe<input::gamepad_axis_moved_event>
576  (
577  [&](const auto& event)
578  {
579  if (!gamepad_active && std::abs(event.position) > 0.5f)
580  {
581  gamepad_active = true;
582  input_manager->set_cursor_visible(false);
583  }
584  }
585  );
586  gamepad_button_pressed_subscription = input_manager->get_event_dispatcher().subscribe<input::gamepad_button_pressed_event>
587  (
588  [&](const auto& event)
589  {
590  if (!gamepad_active)
591  {
592  gamepad_active = true;
593  input_manager->set_cursor_visible(false);
594  }
595  }
596  );
597 
598  // Setup gamepad deactivation callbacks
599  mouse_button_pressed_subscription = input_manager->get_event_dispatcher().subscribe<input::mouse_button_pressed_event>
600  (
601  deactivate_gamepad
602  );
603  mouse_moved_subscription = input_manager->get_event_dispatcher().subscribe<input::mouse_moved_event>
604  (
605  deactivate_gamepad
606  );
607  mouse_scrolled_subscription = input_manager->get_event_dispatcher().subscribe<input::mouse_scrolled_event>
608  (
609  deactivate_gamepad
610  );
611 
612  // Activate gamepad if one is connected
613  if (!input_manager->get_gamepads().empty())
614  {
615  gamepad_active = true;
616  input_manager->set_cursor_visible(false);
617  }
618  else
619  {
620  gamepad_active = false;
621  }
622  */
623 }
624 
625 void game::load_strings()
626 {
627  debug::log_trace("Loading strings...");
628 
629  // Default strings settings
630  language_tag = "en";
631 
632  // Read strings settings
633  read_or_write_setting(*this, "language_tag", language_tag);
634 
635  // Slugify language tag
636  std::string language_slug = language_tag;
637  std::transform
638  (
639  language_slug.begin(),
640  language_slug.end(),
641  language_slug.begin(),
642  [](unsigned char c)
643  {
644  return std::tolower(c);
645  }
646  );
647 
648  // Load string map
649  string_map = resource_manager->load<i18n::string_map>(language_slug + ".str");
650 
651  // Log language info
652  debug::log_info("Language tag: {}", language_tag);
653 
654  // Change window title
655  const std::string window_title = get_string(*this, "window_title");
656  window->set_title(window_title);
657 
658  // Update window title setting
659  (*settings)["window_title"] = window_title;
660 
661  debug::log_trace("Loaded strings");
662 }
663 
664 void game::setup_rendering()
665 {
666  debug::log_trace("Setting up rendering...");
667 
668  // Default rendering settings
669  render_scale = 1.0f;
671  shadow_map_resolution = 4096;
672 
673  // Read rendering settings
674  read_or_write_setting(*this, "render_scale", render_scale);
675  read_or_write_setting(*this, "anti_aliasing_method", *reinterpret_cast<std::underlying_type_t<render::anti_aliasing_method>*>(&anti_aliasing_method));
676  read_or_write_setting(*this, "shadow_map_resolution", shadow_map_resolution);
677 
678  // Create framebuffers
680 
681  // Load fallback material
682  auto fallback_material = resource_manager->load<render::material>("fallback.mtl");
683 
684  // Setup common render passes
685  {
686  // Construct bloom pass
687  bloom_pass = std::make_unique<render::bloom_pass>(&window->get_graphics_pipeline(), resource_manager.get());
688  bloom_pass->set_source_texture(hdr_color_texture);
689  bloom_pass->set_mip_chain_length(5);
690  bloom_pass->set_filter_radius(0.005f);
691 
692  common_final_pass = std::make_unique<render::final_pass>(&window->get_graphics_pipeline(), nullptr, resource_manager.get());
693  common_final_pass->set_color_texture(hdr_color_texture);
694  common_final_pass->set_bloom_texture(bloom_pass->get_bloom_texture());
695  common_final_pass->set_bloom_weight(0.04f);
696  //common_final_pass->set_bloom_weight(0.0f);
697  common_final_pass->set_blue_noise_texture(resource_manager->load<gl::texture_2d>("blue-noise.tex"));
698 
699  resample_pass = std::make_unique<render::resample_pass>(&window->get_graphics_pipeline(), nullptr, resource_manager.get());
700  resample_pass->set_source_texture(ldr_color_texture_a);
701  resample_pass->set_enabled(false);
702  }
703 
704  // Setup UI compositor
705  {
706  ui_material_pass = std::make_unique<render::material_pass>(&window->get_graphics_pipeline(), nullptr, resource_manager.get());
707  ui_material_pass->set_fallback_material(fallback_material);
708 
709  ui_material_pass->set_clear_mask(gl::depth_clear_bit);
710  ui_material_pass->set_clear_value({{0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0});
711 
712  ui_compositor = std::make_unique<render::compositor>();
713  ui_compositor->add_pass(ui_material_pass.get());
714  }
715 
716  // Setup surface compositor
717  {
718  sky_pass = std::make_unique<render::sky_pass>(&window->get_graphics_pipeline(), hdr_framebuffer.get(), resource_manager.get());
719  sky_pass->set_clear_mask(gl::color_clear_bit | gl::depth_clear_bit | gl::stencil_clear_bit);
720  sky_pass->set_clear_value({{0.0f, 0.0f, 0.0f, 0.0f}, 0.0f, 0});
721  // sky_pass->set_magnification(3.0f);
722 
723  surface_material_pass = std::make_unique<render::material_pass>(&window->get_graphics_pipeline(), hdr_framebuffer.get(), resource_manager.get());
724  surface_material_pass->set_fallback_material(fallback_material);
725 
726  surface_compositor = std::make_unique<render::compositor>();
727  surface_compositor->add_pass(sky_pass.get());
728  surface_compositor->add_pass(surface_material_pass.get());
729  surface_compositor->add_pass(bloom_pass.get());
730  surface_compositor->add_pass(common_final_pass.get());
731  surface_compositor->add_pass(resample_pass.get());
732  }
733 
734  // Configure anti-aliasing according to settings
736 
737  // Configure render scaling according to settings
738  graphics::change_render_resolution(*this, render_scale);
739 
740  // Create renderer
741  renderer = std::make_unique<render::renderer>(window->get_graphics_pipeline(), *resource_manager);
742 
743  debug::log_trace("Set up rendering");
744 }
745 
746 void game::setup_scenes()
747 {
748  debug::log_trace("Setting up scenes...");
749 
750  // Ratio of meters to scene units.
751  constexpr float scene_scale = 1.0f / 100.0f;
752 
753  // Get default framebuffer
754  const auto& viewport_size = window->get_viewport_size();
755  const float viewport_aspect_ratio = static_cast<float>(viewport_size[0]) / static_cast<float>(viewport_size[1]);
756 
757  // Allocate and init surface scene
758  surface_scene = std::make_unique<scene::collection>();
759  surface_scene->set_scale(scene_scale);
760 
761  // Allocate and init surface camera
762  surface_camera = std::make_shared<scene::camera>();
763  surface_camera->set_perspective(math::radians<float>(45.0f), viewport_aspect_ratio, 0.5f);
764  surface_camera->set_compositor(surface_compositor.get());
765  surface_camera->set_composite_index(0);
766 
767  // Allocate and init underground scene
768  underground_scene = std::make_unique<scene::collection>();
769  underground_scene->set_scale(scene_scale);
770 
771  // Allocate and init underground camera
772  underground_camera = std::make_shared<scene::camera>();
773  underground_camera->set_perspective(math::radians<float>(45.0f), viewport_aspect_ratio, 0.5f);
774 
775  // Clear active scene
776  active_scene = nullptr;
777 
778  debug::log_trace("Set up scenes");
779 }
780 
781 void game::setup_animation()
782 {
783  // Setup timeline system
784  timeline = std::make_unique<::timeline>();
785  timeline->set_autoremove(true);
786 
787  // Setup animator
788  animator = std::make_unique<::animator>();
789 }
790 
791 void game::setup_ui()
792 {
793  // Default UI settings
794  font_scale = 1.0f;
795  debug_font_size_pt = 10.0f;
796  menu_font_size_pt = 22.0f;
797  title_font_size_pt = 80.0f;
798  dyslexia_font = false;
799 
800  // Read UI settings
801  read_or_write_setting(*this, "font_scale", font_scale);
802  read_or_write_setting(*this, "debug_font_size_pt", debug_font_size_pt);
803  read_or_write_setting(*this, "menu_font_size_pt", menu_font_size_pt);
804  read_or_write_setting(*this, "title_font_size_pt", title_font_size_pt);
805  read_or_write_setting(*this, "dyslexia_font", dyslexia_font);
806 
807  // Allocate font materials
808  debug_font_material = std::make_shared<render::material>();
809  menu_font_material = std::make_shared<render::material>();
810  title_font_material = std::make_shared<render::material>();
811 
812  // Load fonts
813  debug::log_trace("Loading fonts...");
814  try
815  {
816  ::load_fonts(*this);
817  debug::log_trace("Loaded fonts");
818  }
819  catch (const std::exception& e)
820  {
821  debug::log_error("Failed to load fonts: {}", e.what());
822  }
823 
824  // Get default framebuffer
825  const auto& viewport_size = window->get_viewport_size();
826  const float viewport_aspect_ratio = static_cast<float>(viewport_size[0]) / static_cast<float>(viewport_size[1]);
827 
828  // Setup UI scene
829  ui_scene = std::make_unique<scene::collection>();
830 
831  // Setup UI camera
832  ui_camera = std::make_unique<scene::camera>();
833  ui_camera->set_compositor(ui_compositor.get());
834  const float clip_left = 0.0f;
835  const float clip_right = static_cast<float>(viewport_size.x());
836  const float clip_top = 0.0f;
837  const float clip_bottom = static_cast<float>(viewport_size.y());
838  const float clip_near = -100.0f;
839  const float clip_far = 100.0f;
840  ui_camera->set_orthographic(clip_left, clip_right, clip_top, clip_bottom, clip_near, clip_far);
841  ui_camera->look_at({0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f});
842 
843  // Menu BG material
844  menu_bg_material = std::make_shared<render::material>();
845  menu_bg_material->set_shader_template(resource_manager->load<gl::shader_template>("ui-element-untextured.glsl"));
846  std::shared_ptr<render::matvar_fvec4> menu_bg_tint = std::make_shared<render::matvar_fvec4>(1, math::fvec4{0.0f, 0.0f, 0.0f, 0.5f});
847  menu_bg_material->set_variable("tint", menu_bg_tint);
848  menu_bg_material->set_blend_mode(render::material_blend_mode::translucent);
849 
850  // Menu BG billboard
851  menu_bg_billboard = std::make_unique<scene::billboard>();
852  menu_bg_billboard->set_material(menu_bg_material);
853  menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f});
854  menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f});
855 
856  // Create fade transition
857  fade_transition = std::make_unique<screen_transition>();
858  fade_transition->get_material()->set_shader_template(resource_manager->load<gl::shader_template>("fade-transition.glsl"));
859  fade_transition_color = std::make_shared<render::matvar_fvec3>(1, math::fvec3{0, 0, 0});
860  fade_transition->get_material()->set_variable("color", fade_transition_color);
861  fade_transition->get_billboard()->set_translation({0, 0, 98});
862 
863  // Create inner radial transition
864  radial_transition_inner = std::make_unique<screen_transition>();
865  radial_transition_inner->get_material()->set_shader_template(resource_manager->load<gl::shader_template>("radial-transition-inner.glsl"));
866 
867  // Create outer radial transition
868  radial_transition_outer = std::make_unique<screen_transition>();
869  radial_transition_outer->get_material()->set_shader_template(resource_manager->load<gl::shader_template>("radial-transition-outer.glsl"));
870 
871  // Menu BG animations
872  {
873  auto menu_bg_frame_callback = [menu_bg_tint](int channel, const float& opacity)
874  {
875  menu_bg_tint->set(math::fvec4{0.0f, 0.0f, 0.0f, opacity});
876  };
877 
878  // Menu BG fade in animation
879  menu_bg_fade_in_animation = std::make_unique<animation<float>>();
880  {
881  menu_bg_fade_in_animation->set_interpolator(ease<float>::out_cubic);
882  animation_channel<float>* channel = menu_bg_fade_in_animation->add_channel(0);
883  channel->insert_keyframe({0.0f, 0.0f});
884  channel->insert_keyframe({config::menu_fade_in_duration, config::menu_bg_opacity});
885  menu_bg_fade_in_animation->set_frame_callback(menu_bg_frame_callback);
886  menu_bg_fade_in_animation->set_start_callback
887  (
888  [&, menu_bg_tint]()
889  {
890  ui_scene->add_object(*menu_bg_billboard);
891 
892  menu_bg_tint->set(math::fvec4{0.0f, 0.0f, 0.0f, 0.0f});
893  //menu_bg_billboard->set_active(true);
894  }
895  );
896  }
897 
898  // Menu BG fade out animation
899  menu_bg_fade_out_animation = std::make_unique<animation<float>>();
900  {
901  menu_bg_fade_out_animation->set_interpolator(ease<float>::out_cubic);
902  animation_channel<float>* channel = menu_bg_fade_out_animation->add_channel(0);
903  channel->insert_keyframe({0.0f, config::menu_bg_opacity});
904  channel->insert_keyframe({config::menu_fade_out_duration, 0.0f});
905  menu_bg_fade_out_animation->set_frame_callback(menu_bg_frame_callback);
906  menu_bg_fade_out_animation->set_end_callback
907  (
908  [&]()
909  {
910  ui_scene->remove_object(*menu_bg_billboard);
911  //menu_bg_billboard->set_active(false);
912  }
913  );
914  }
915  }
916 
917  // Add UI scene objects to UI scene
918  ui_scene->add_object(*ui_camera);
919  ui_scene->add_object(*fade_transition->get_billboard());
920 
921  // Add UI animations to animator
922  animator->add_animation(fade_transition->get_animation());
923  animator->add_animation(menu_bg_fade_in_animation.get());
924  animator->add_animation(menu_bg_fade_out_animation.get());
925 
926  // Setup window resized callback
927  window_resized_subscription = window->get_resized_channel().subscribe
928  (
929  [&](const auto& event)
930  {
931  const auto& viewport_size = event.window->get_viewport_size();
932  const float viewport_aspect_ratio = static_cast<float>(viewport_size.x()) / static_cast<float>(viewport_size.y());
933 
934  // Resize framebuffers
935  ::graphics::change_render_resolution(*this, render_scale);
936 
937  // Update camera projection matrix
938  surface_camera->set_aspect_ratio(viewport_aspect_ratio);
939 
940  // Update UI camera projection matrix
941  ui_camera->set_orthographic
942  (
943  0.0f,
944  static_cast<float>(viewport_size.x()),
945  0.0f,
946  static_cast<float>(viewport_size.y()),
947  ui_camera->get_clip_near(),
948  ui_camera->get_clip_far()
949  );
950 
951  // Resize menu BG billboard
952  menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f});
953  menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f});
954 
955  // Re-align debug text
956  frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - debug_font.get_font_metrics().size), 99.0f});
957 
958  // Re-align menu text
959  ::menu::align_text(*this);
960  }
961  );
962 }
963 
964 void game::setup_rng()
965 {
966  std::random_device rd;
967  rng.seed(rd());
968 }
969 
970 void game::setup_entities()
971 {
972  // Create entity registry
973  entity_registry = std::make_unique<entt::registry>();
974 }
975 
976 void game::setup_systems()
977 {
978  const auto& viewport_size = window->get_viewport_size();
979  math::fvec4 viewport = {0.0f, 0.0f, static_cast<float>(viewport_size[0]), static_cast<float>(viewport_size[1])};
980 
981  // Setup terrain system
982  terrain_system = std::make_unique<::terrain_system>(*entity_registry);
983 
984  // Setup camera system
985  camera_system = std::make_unique<::camera_system>(*entity_registry);
986  camera_system->set_viewport(viewport);
987 
988  // Setup subterrain system
989  subterrain_system = std::make_unique<::subterrain_system>(*entity_registry, resource_manager.get());
990 
991  // Setup collision system
992  collision_system = std::make_unique<::collision_system>(*entity_registry);
993 
994  // Setup behavior system
995  behavior_system = std::make_unique<::behavior_system>(*entity_registry);
996 
997  // Setup steering system
998  steering_system = std::make_unique<::steering_system>(*entity_registry);
999 
1000  // Setup locomotion system
1001  locomotion_system = std::make_unique<::locomotion_system>(*entity_registry);
1002 
1003  // Setup IK system
1004  ik_system = std::make_unique<::ik_system>(*entity_registry);
1005 
1006  // Setup metabolic system
1007  metabolic_system = std::make_unique<::metabolic_system>(*entity_registry);
1008 
1009  // Setup metamorphosis system
1010  metamorphosis_system = std::make_unique<::metamorphosis_system>(*entity_registry);
1011 
1012  // Setup animation system
1013  animation_system = std::make_unique<::animation_system>(*entity_registry);
1014 
1015  // Setup physics system
1016  physics_system = std::make_unique<::physics_system>(*entity_registry);
1017  physics_system->set_gravity({0.0f, -9.80665f * 100.0f, 0.0f});
1018 
1019  // Setup reproductive system
1020  reproductive_system = std::make_unique<::reproductive_system>(*entity_registry);
1022 
1023  // Setup spatial system
1024  spatial_system = std::make_unique<::spatial_system>(*entity_registry);
1025 
1026  // Setup constraint system
1027  constraint_system = std::make_unique<::constraint_system>(*entity_registry);
1028 
1029  // Setup orbit system
1030  orbit_system = std::make_unique<::orbit_system>(*entity_registry);
1031 
1032  // Setup blackbody system
1033  blackbody_system = std::make_unique<::blackbody_system>(*entity_registry);
1034 
1035  // Setup atmosphere system
1036  atmosphere_system = std::make_unique<::atmosphere_system>(*entity_registry);
1037  atmosphere_system->set_sky_pass(sky_pass.get());
1038 
1039  // Setup astronomy system
1040  astronomy_system = std::make_unique<::astronomy_system>(*entity_registry);
1042  astronomy_system->set_sky_pass(sky_pass.get());
1043 
1044  // Setup render system
1045  render_system = std::make_unique<::render_system>(*entity_registry);
1046  render_system->set_renderer(renderer.get());
1047  render_system->add_layer(surface_scene.get());
1048  render_system->add_layer(underground_scene.get());
1049  render_system->add_layer(ui_scene.get());
1050 }
1051 
1052 void game::setup_controls()
1053 {
1054  debug::log_trace("Setting up controls...");
1055 
1056  // Load SDL game controller mappings database
1057  // debug::log_trace("Loading SDL game controller mappings...");
1058  // file_buffer* game_controller_db = resource_manager->load<file_buffer>("gamecontrollerdb.txt");
1059  // if (!game_controller_db)
1060  // {
1061  // debug::log_error("Failed to load SDL game controller mappings");
1062  // }
1063  // else
1064  // {
1065  // app->add_game_controller_mappings(game_controller_db->data(), game_controller_db->size());
1066  // debug::log_trace("Loaded SDL game controller mappings");
1067 
1068  // resource_manager->unload("gamecontrollerdb.txt");
1069  // }
1070 
1071  // Pass input event queue to action maps
1072  event::dispatcher* input_event_dispatcher = &input_manager->get_event_dispatcher();
1073  window_action_map.set_event_dispatcher(input_event_dispatcher);
1074  menu_action_map.set_event_dispatcher(input_event_dispatcher);
1075  movement_action_map.set_event_dispatcher(input_event_dispatcher);
1076  camera_action_map.set_event_dispatcher(input_event_dispatcher);
1077  ant_action_map.set_event_dispatcher(input_event_dispatcher);
1078  debug_action_map.set_event_dispatcher(input_event_dispatcher);
1079 
1080  // Default control profile settings
1081  control_profile_filename = "controls.cfg";
1082 
1083  // Read control profile settings
1084  if (read_or_write_setting(*this, "control_profile", control_profile_filename))
1085  {
1086  control_profile = resource_manager->load<::control_profile>(control_profile_filename);
1087  }
1088 
1089  if (!control_profile)
1090  {
1091  // Allocate control profile
1092  control_profile = std::make_shared<::control_profile>();
1093 
1094  // Reset control profile to default settings.
1096 
1097  // Save control profile
1098  resource_manager->set_write_path(controls_path);
1099  resource_manager->save(*control_profile, control_profile_filename);
1100  }
1101 
1102  // Apply control profile
1104 
1105  // Setup mouse sensitivity
1106  mouse_pan_factor = mouse_radians_per_pixel * mouse_pan_sensitivity * (mouse_invert_pan ? -1.0 : 1.0);
1107  mouse_tilt_factor = mouse_radians_per_pixel * mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0 : 1.0);
1108 
1109  // Setup gamepad sensitivity
1110  gamepad_pan_factor = gamepad_radians_per_second * gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0 : 1.0);
1111  gamepad_tilt_factor = gamepad_radians_per_second * gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0 : 1.0);
1112 
1113  // Setup action callbacks
1114  setup_window_controls(*this);
1115  setup_menu_controls(*this);
1116  setup_camera_controls(*this);
1117  setup_game_controls(*this);
1118  setup_ant_controls(*this);
1119 
1120  // Enable window controls
1121  enable_window_controls(*this);
1122 
1123  #if defined(DEBUG)
1124  // Setup and enable debug controls
1125  setup_debug_controls(*this);
1126  enable_debug_controls(*this);
1127  #endif
1128 
1129  debug::log_trace("Set up controls");
1130 }
1131 
1132 void game::setup_debugging()
1133 {
1134  cli = std::make_unique<debug::cli>();
1135 
1136  const auto& viewport_size = window->get_viewport_size();
1137 
1138  frame_time_text = std::make_unique<scene::text>();
1139  frame_time_text->set_material(debug_font_material);
1140  frame_time_text->set_color({1.0f, 1.0f, 0.0f, 1.0f});
1141  frame_time_text->set_font(&debug_font);
1142  frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - debug_font.get_font_metrics().size), 99.0f});
1143 
1144  #if defined(DEBUG)
1145  ui_scene->add_object(*frame_time_text);
1146  debug_ui_visible = true;
1147  #endif
1148 }
1149 
1150 void game::setup_timing()
1151 {
1152  // Init default settings
1153  max_frame_rate = static_cast<float>(window_manager->get_display(0).get_refresh_rate() * 2);
1154 
1155  // Read settings
1156  read_or_write_setting(*this, "fixed_update_rate", fixed_update_rate);
1157  read_or_write_setting(*this, "max_frame_rate", max_frame_rate);
1158  read_or_write_setting(*this, "limit_frame_rate", limit_frame_rate);
1159 
1160  const auto fixed_update_interval = std::chrono::duration_cast<::frame_scheduler::duration_type>(std::chrono::duration<double>(1.0 / fixed_update_rate));
1161  const auto min_frame_duration = (limit_frame_rate) ? std::chrono::duration_cast<::frame_scheduler::duration_type>(std::chrono::duration<double>(1.0 / max_frame_rate)) : frame_scheduler::duration_type::zero();
1162  const auto max_frame_duration = fixed_update_interval * 15;
1163 
1164  // Configure frame scheduler
1165  frame_scheduler.set_fixed_update_interval(fixed_update_interval);
1166  frame_scheduler.set_min_frame_duration(min_frame_duration);
1167  frame_scheduler.set_max_frame_duration(max_frame_duration);
1168  frame_scheduler.set_fixed_update_callback(std::bind_front(&game::fixed_update, this));
1169  frame_scheduler.set_variable_update_callback(std::bind_front(&game::variable_update, this));
1170 
1171  // Init frame duration average
1172  average_frame_duration.reserve(15);
1173 }
1174 
1175 void game::shutdown_audio()
1176 {
1177  debug::log_trace("Shutting down audio...");
1178 
1179  if (alc_context)
1180  {
1181  alcMakeContextCurrent(nullptr);
1182  alcDestroyContext(alc_context);
1183  }
1184 
1185  if (alc_device)
1186  {
1187  alcCloseDevice(alc_device);
1188  }
1189 
1190  debug::log_trace("Shut down audio");
1191 }
1192 
1193 void game::fixed_update(::frame_scheduler::duration_type fixed_update_time, ::frame_scheduler::duration_type fixed_update_interval)
1194 {
1195  const float t = std::chrono::duration<float>(fixed_update_time).count();
1196  const float dt = std::chrono::duration<float>(fixed_update_interval).count();
1197 
1198  // Update tweens
1199  sky_pass->update_tweens();
1200 
1201  // Process window events
1202  window_manager->update();
1203 
1204  // Process function queue
1205  while (!function_queue.empty())
1206  {
1207  function_queue.front()();
1208  function_queue.pop();
1209  }
1210 
1211  // Advance tline
1212  //timeline->advance(dt);
1213 
1214  // Update entity systems
1215  animation_system->update(t, dt);
1216 
1217  physics_system->update(t, dt);
1218 
1219  //terrain_system->update(t, dt);
1220  //subterrain_system->update(t, dt);
1221  collision_system->update(t, dt);
1222  behavior_system->update(t, dt);
1223  steering_system->update(t, dt);
1224  locomotion_system->update(t, dt);
1225  ik_system->update(t, dt);
1226  reproductive_system->update(t, dt);
1227  metabolic_system->update(t, dt);
1228  metamorphosis_system->update(t, dt);
1229  // physics_system->update(t, dt);
1230  orbit_system->update(t, dt);
1231  blackbody_system->update(t, dt);
1232  atmosphere_system->update(t, dt);
1233  astronomy_system->update(t, dt);
1234  spatial_system->update(t, dt);
1235  constraint_system->update(t, dt);
1236  animator->animate(dt);
1237  camera_system->update(t, dt);
1238  render_system->update(t, dt);
1239 }
1240 
1241 void game::variable_update(::frame_scheduler::duration_type fixed_update_time, ::frame_scheduler::duration_type fixed_update_interval, ::frame_scheduler::duration_type accumulated_time)
1242 {
1243  // Calculate subframe interpolation factor (`alpha`)
1244  const float alpha = static_cast<float>(std::chrono::duration<double, ::frame_scheduler::duration_type::period>{accumulated_time} / fixed_update_interval);
1245 
1246  // Sample average frame duration
1247  const float average_frame_ms = average_frame_duration(std::chrono::duration<float, std::milli>(frame_scheduler.get_frame_duration()).count());
1248  const float average_frame_fps = 1000.0f / average_frame_ms;
1249 
1250  // Update frame rate display
1251  frame_time_text->set_content(std::format("{:5.02f}ms / {:5.02f} FPS", average_frame_ms, average_frame_fps));
1252 
1253  // Process input events
1254  input_manager->update();
1255 
1256  // Interpolate physics
1257  physics_system->interpolate(alpha);
1258 
1259  // Interpolate animation
1260  animation_system->interpolate(alpha);
1261 
1262  // Render
1263  camera_system->interpolate(alpha);
1264  render_system->draw(alpha);
1265  window->swap_buffers();
1266 }
1267 
1269 {
1270  // Change to initial state
1271  state_machine.emplace(std::make_unique<main_menu_state>(*this, true));
1272 
1273  debug::log_trace("Entered main loop");
1274 
1276 
1277  while (!closed)
1278  {
1280  }
1281 
1282  debug::log_trace("Exited main loop");
1283 
1284  // Exit all active game states
1285  while (!state_machine.empty())
1286  {
1287  state_machine.pop();
1288  }
1289 }
void setup_ant_controls(::game &ctx)
void setup_camera_controls(::game &ctx)
Single channel in a keyframe animation.
void insert_keyframe(const keyframe &k)
Adds a keyframe to the animation.
void update(float t, float dt) override
Perform's a system's update() function.
void interpolate(float alpha)
Progresses a set of animations.
Definition: animator.hpp:32
void add_animation(animation_base *animation)
Adds an animation to the animator.
Definition: animator.cpp:39
void animate(float dt)
Progresses all active animations by dt.
Definition: animator.cpp:28
Virtual display.
Definition: display.hpp:35
const geom::rectangle< int > & get_usable_bounds() const noexcept
Returns the usable bounds of the display, which excludes areas reserved by the OS for things like men...
Definition: display.hpp:124
static std::unique_ptr< input_manager > instance()
Allocates and returns an input manager.
static std::unique_ptr< window_manager > instance()
Allocates and returns a window manager.
Calculates apparent properties of celestial bodies as seen by an observer.
virtual void update(float t, float dt)
Adds the timestep dt, scaled by set time scale, to the current time, then calculates apparent propert...
void set_sky_pass(::render::sky_pass *pass)
void set_transmittance_samples(std::size_t samples)
Sets the number of samples to take when integrating atmospheric transmittance.
Updates variables related to atmospheric scattering.
virtual void update(float t, float dt)
Perform's a system's update() function.
void set_sky_pass(::render::sky_pass *pass)
virtual void update(float t, float dt)
Perform's a system's update() function.
Calculates the color and luminance of blackbody radiators.
void update(float t, float dt) override
Perform's a system's update() function.
void interpolate(float alpha)
void set_viewport(const math::fvec4 &viewport)
void update(float t, float dt) override
Perform's a system's update() function.
Maintains a spatially partitioned set of collision meshes.
virtual void update(float t, float dt)
Perform's a system's update() function.
Applies constraint stacks to transform components.
virtual void update(float t, float dt)
Perform's a system's update() function.
Forwards messages from publishers to subscribers.
Definition: dispatcher.hpp:37
Schedules fixed- and variable-rate updates.
void set_min_frame_duration(duration_type duration) noexcept
Sets the minimum frame duration.
void set_max_frame_duration(duration_type duration) noexcept
Sets the maximum accumulated frame duration.
void refresh() noexcept
Resets the accumulated time (at) and frame timer, but not the elapsed fixed-rate update time.
void set_fixed_update_interval(duration_type interval) noexcept
Sets the interval (dt) at which fixed-rate updates are scheduled.
void tick()
Performs any scheduled fixed-rate updates followed by a single variable-rate update.
void set_fixed_update_callback(fixed_update_callback_type &&callback) noexcept
Sets the fixed-rate update callback.
duration_type get_frame_duration() const noexcept
Returns the duration of the previous frame.
clock_type::duration duration_type
Duration type matches the clock's duration type.
void set_variable_update_callback(variable_update_callback_type &&callback) noexcept
Sets the variable-rate update callback.
void execute()
Executes the game.
Definition: game.cpp:1268
~game()
Boots down the game.
Definition: game.cpp:147
game(int argc, const char *const *argv)
Boots up the game.
Definition: game.cpp:103
Template used to for generating one or more shader variants from a single source.
2D texture.
Definition: texture.hpp:141
void update(float t, float dt) override
Perform's a system's update() function.
Definition: ik-system.cpp:30
void update(float t, float dt) override
Perform's a system's update() function.
virtual void update(float t, float dt)
Perform's a system's update() function.
virtual void update(float t, float dt)
Perform's a system's update() function.
Updates the Cartesian position and velocity of orbiting bodies given their Keplerian orbital elements...
virtual void update(float t, float dt)
Scales then adds the timestep dt to the current time, then recalculates the positions of orbiting bod...
void set_gravity(const math::fvec3 &gravity) noexcept
Sets the gravity vector.
void interpolate(float alpha)
void update(float t, float dt) override
Perform's a system's update() function.
A material is associated with exactly one shader program and contains a set of material properties wh...
Definition: material.hpp:37
void add_layer(scene::collection *layer)
void set_renderer(::render::renderer *renderer)
void draw(float alpha)
virtual void update(float t, float dt)
Perform's a system's update() function.
void update(float t, float dt) override
Perform's a system's update() function.
constexpr void set_physics_system(physics_system *physics_system) noexcept
Manages the loading, caching, and saving of resources.
bool set_write_path(const std::filesystem::path &path)
Sets the path to a directory or archive where files can be written.
bool save(const T &resource, const std::filesystem::path &path) const
Saves a resource to a file.
bool mount(const std::filesystem::path &path)
Adds a directory or archive to the search path.
std::shared_ptr< T > load(const std::filesystem::path &path)
Loads and caches a resource.
virtual void update(float t, float dt)
Perform's a system's update() function.
virtual void update(float t, float dt)
Perform's a system's update() function.
Generates terrain patches and performs terrain patch LOD selection.
Timeline which executes cues (scheduled functions) when advanced over their respective positions in t...
Definition: timeline.hpp:31
void set_autoremove(bool enabled)
If enabled, cues will be automatically removed from the timeline when they are triggered.
Definition: timeline.cpp:94
void apply_control_profile(::game &ctx, const ::control_profile &profile)
Applies a control profile to the game context.
Definition: controls.cpp:193
void setup_game_controls(::game &ctx)
Definition: controls.cpp:376
void reset_control_profile(::control_profile &profile)
Resets a control profile to default settings.
Definition: controls.cpp:31
void enable_debug_controls(::game &ctx)
void setup_debug_controls(::game &ctx)
std::unordered_map< Key, std::any > dict
Unordered dictionary type.
Definition: dict.hpp:32
void load_fonts(::game &ctx)
Definition: fonts.cpp:64
void setup_menu_controls(::game &ctx)
log_message< log_message_severity::trace, Args... > log_trace
Formats and logs a trace message.
Definition: log.hpp:88
log_message< log_message_severity::error, Args... > log_error
Formats and logs an error message.
Definition: log.hpp:144
log_message< log_message_severity::info, Args... > log_info
Formats and logs an info message.
Definition: log.hpp:116
Publish-subscribe messaging.
Definition: channel.hpp:32
format
Image and vertex formats.
Definition: format.hpp:29
@ color_clear_bit
Indicates the color buffer should be cleared.
Definition: clear-bits.hpp:31
@ depth_clear_bit
Indicates the depth buffer should be cleared.
Definition: clear-bits.hpp:34
@ stencil_clear_bit
Indicates the stencil buffer should be cleared.
Definition: clear-bits.hpp:37
void select_anti_aliasing_method(::game &ctx, render::anti_aliasing_method method)
Definition: graphics.cpp:320
void create_framebuffers(::game &ctx)
Definition: graphics.cpp:211
void change_render_resolution(::game &ctx, float scale)
Definition: graphics.cpp:249
User-defined literals for compile-time string hashing.
Definition: fnv1a.hpp:232
std::stack< std::unique_ptr< T > > state_machine
Stack-based hierarchical state machine.
std::unordered_map< hash::fnv1a32_t, std::string > string_map
Maps 32-bit keys to strings.
Definition: string-map.hpp:32
constexpr T e
e.
Definition: numbers.hpp:37
constexpr vector< T, N > round(const vector< T, N > &x)
Performs a element-wise round operation.
Definition: vector.hpp:1489
constexpr vector< T, N > floor(const vector< T, N > &x)
Performs a element-wise floor operation.
Definition: vector.hpp:1184
constexpr vector< T, N > ceil(const vector< T, N > &x)
Performs an element-wise ceil operation.
Definition: vector.hpp:1056
void align_text(::game &ctx, bool center, bool has_back, float anchor_y)
Definition: menu.cpp:73
@ translucent
Material is translucent.
anti_aliasing_method
Anti-aliasing methods.
@ none
No anti-aliasing.
std::filesystem::path get_local_config_path()
Returns the absolute path to the directory containing user-specific, device-specific application data...
Definition: paths.cpp:68
std::filesystem::path get_executable_data_path()
Returns the absolute path to the directory containing application data.
Definition: paths.cpp:59
std::filesystem::path get_shared_config_path()
Returns the absolute path to the directory containing user-specific application data that may be shar...
Definition: paths.cpp:110
bool read_or_write_setting(::game &ctx, hash::fnv1a32_t key, T &value)
Reads a setting if found, inserts a setting if not found, and overwrites a setting if a type mismatch...
Definition: settings.hpp:39
std::string get_string(const ::game &ctx, hash::fnv1a32_t key)
Returns a localized string.
Definition: strings.cpp:23
Container for templated easing functions.
Definition: ease.hpp:71
Event generated when the application has been requested to quit.
n-dimensional vector.
Definition: vector.hpp:44
void enable_window_controls(::game &ctx)
void setup_window_controls(::game &ctx)