💿🐜 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.

1344 lines
42 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/game.hpp"
  20. #include "game/commands/commands.hpp"
  21. #include "game/control-profile.hpp"
  22. #include "game/controls.hpp"
  23. #include "game/fonts.hpp"
  24. #include "game/graphics.hpp"
  25. #include "game/menu.hpp"
  26. #include "game/settings.hpp"
  27. #include "game/states/main-menu-state.hpp"
  28. #include "game/states/splash-state.hpp"
  29. #include "game/strings.hpp"
  30. #include "game/systems/astronomy-system.hpp"
  31. #include "game/systems/atmosphere-system.hpp"
  32. #include "game/systems/behavior-system.hpp"
  33. #include "game/systems/blackbody-system.hpp"
  34. #include "game/systems/camera-system.hpp"
  35. #include "game/systems/collision-system.hpp"
  36. #include "game/systems/constraint-system.hpp"
  37. #include "game/systems/locomotion-system.hpp"
  38. #include "game/systems/ik-system.hpp"
  39. #include "game/systems/animation-system.hpp"
  40. #include "game/systems/orbit-system.hpp"
  41. #include "game/systems/render-system.hpp"
  42. #include "game/systems/spatial-system.hpp"
  43. #include "game/systems/steering-system.hpp"
  44. #include "game/systems/physics-system.hpp"
  45. #include "game/systems/subterrain-system.hpp"
  46. #include "game/systems/terrain-system.hpp"
  47. #include "game/systems/reproductive-system.hpp"
  48. #include "game/systems/metabolic-system.hpp"
  49. #include "game/systems/metamorphosis-system.hpp"
  50. #include <algorithm>
  51. #include <cctype>
  52. #include <engine/animation/animation.hpp>
  53. #include <engine/animation/animator.hpp>
  54. #include <engine/animation/ease.hpp>
  55. #include <engine/animation/screen-transition.hpp>
  56. #include <engine/animation/timeline.hpp>
  57. #include <engine/color/color.hpp>
  58. #include <engine/config.hpp>
  59. #include <engine/debug/cli.hpp>
  60. #include <engine/debug/log.hpp>
  61. #include <engine/gl/framebuffer.hpp>
  62. #include <engine/gl/pixel-format.hpp>
  63. #include <engine/gl/pixel-type.hpp>
  64. #include <engine/gl/rasterizer.hpp>
  65. #include <engine/gl/texture-2d.hpp>
  66. #include <engine/gl/texture-filter.hpp>
  67. #include <engine/gl/texture-wrapping.hpp>
  68. #include <engine/gl/vertex-array.hpp>
  69. #include <engine/gl/vertex-attribute.hpp>
  70. #include <engine/gl/vertex-buffer.hpp>
  71. #include <engine/input/application-events.hpp>
  72. #include <engine/input/gamepad.hpp>
  73. #include <engine/input/keyboard.hpp>
  74. #include <engine/input/mapper.hpp>
  75. #include <engine/input/mouse.hpp>
  76. #include <engine/input/scancode.hpp>
  77. #include <engine/render/compositor.hpp>
  78. #include <engine/render/material-flags.hpp>
  79. #include <engine/render/material-variable.hpp>
  80. #include <engine/render/passes/bloom-pass.hpp>
  81. #include <engine/render/passes/clear-pass.hpp>
  82. #include <engine/render/passes/final-pass.hpp>
  83. #include <engine/render/passes/fxaa-pass.hpp>
  84. #include <engine/render/passes/material-pass.hpp>
  85. #include <engine/render/passes/outline-pass.hpp>
  86. #include <engine/render/passes/resample-pass.hpp>
  87. #include <engine/render/passes/cascaded-shadow-map-pass.hpp>
  88. #include <engine/render/passes/sky-pass.hpp>
  89. #include <engine/render/renderer.hpp>
  90. #include <engine/render/vertex-attribute.hpp>
  91. #include <engine/resources/resource-manager.hpp>
  92. #include <engine/scene/scene.hpp>
  93. #include <engine/utility/dict.hpp>
  94. #include <engine/utility/hash/fnv1a.hpp>
  95. #include <engine/utility/paths.hpp>
  96. #include <entt/entt.hpp>
  97. #include <execution>
  98. #include <filesystem>
  99. #include <functional>
  100. #include <string>
  101. #include <vector>
  102. #include <chrono>
  103. // Prevent cxxopts from using RTTI
  104. #define CXXOPTS_NO_RTTI
  105. #include <cxxopts.hpp>
  106. using namespace hash::literals;
  107. game::game(int argc, const char* const* argv)
  108. {
  109. // Boot process
  110. debug::log::trace("Booting up...");
  111. // Profile boot duration
  112. #if !defined(NDEBUG)
  113. auto boot_t0 = std::chrono::high_resolution_clock::now();
  114. #endif
  115. parse_options(argc, argv);
  116. setup_resources();
  117. load_settings();
  118. setup_window();
  119. setup_audio();
  120. setup_input();
  121. load_strings();
  122. setup_rendering();
  123. setup_scenes();
  124. setup_animation();
  125. setup_ui();
  126. setup_rng();
  127. setup_entities();
  128. setup_systems();
  129. setup_controls();
  130. setup_debugging();
  131. setup_timing();
  132. active_ecoregion = nullptr;
  133. closed = false;
  134. // Profile boot duration
  135. #if !defined(NDEBUG)
  136. auto boot_t1 = std::chrono::high_resolution_clock::now();
  137. #endif
  138. debug::log::trace("Boot up complete");
  139. // Print boot duration
  140. #if !defined(NDEBUG)
  141. debug::log::info("Boot duration: {}", std::chrono::duration_cast<std::chrono::duration<double>>(boot_t1 - boot_t0));
  142. #endif
  143. }
  144. game::~game()
  145. {
  146. debug::log::trace("Booting down...");
  147. // Exit all active game states
  148. while (!state_machine.empty())
  149. {
  150. state_machine.pop();
  151. }
  152. // Update window settings
  153. const auto& windowed_position = window->get_windowed_position();
  154. const auto& windowed_size = window->get_windowed_size();
  155. const bool maximized = window->is_maximized();
  156. const bool fullscreen = window->is_fullscreen();
  157. (*settings)["window_x"] = windowed_position.x();
  158. (*settings)["window_y"] = windowed_position.y();
  159. (*settings)["window_w"] = windowed_size.x();
  160. (*settings)["window_h"] = windowed_size.y();
  161. (*settings)["maximized"] = maximized;
  162. (*settings)["fullscreen"] = fullscreen;
  163. // Destruct window
  164. window.reset();
  165. // Save settings
  166. resource_manager->set_write_path(shared_config_path);
  167. resource_manager->save(*settings, "settings.cfg");
  168. // Destruct input and window managers
  169. input_manager.reset();
  170. window_manager.reset();
  171. // Shut down audio
  172. shutdown_audio();
  173. debug::log::trace("Boot down complete");
  174. }
  175. void game::parse_options(int argc, const char* const* argv)
  176. {
  177. if (argc <= 1)
  178. {
  179. // No command-line options specified
  180. return;
  181. }
  182. debug::log::trace("Parsing command-line options...");
  183. // Parse command-line options with cxxopts
  184. try
  185. {
  186. cxxopts::Options options(config::application_name, config::application_name);
  187. options.add_options()
  188. ("c,continue", "Continues from the last save")
  189. ("d,data", "Sets the data package path", cxxopts::value<std::string>())
  190. ("f,fullscreen", "Starts in fullscreen mode")
  191. ("n,new-game", "Starts a new game")
  192. ("q,quick-start", "Skips to the main menu")
  193. ("r,reset", "Resets all settings to default")
  194. ("v,v-sync", "Enables or disables v-sync", cxxopts::value<int>())
  195. ("w,windowed", "Starts in windowed mode");
  196. auto result = options.parse(argc, argv);
  197. // --continue
  198. if (result.count("continue"))
  199. {
  200. option_continue = true;
  201. }
  202. // --data
  203. if (result.count("data"))
  204. {
  205. option_data = result["data"].as<std::string>();
  206. }
  207. // --fullscreen
  208. if (result.count("fullscreen"))
  209. {
  210. option_fullscreen = true;
  211. }
  212. // --new-game
  213. if (result.count("new-game"))
  214. {
  215. option_new_game = true;
  216. }
  217. // --quick-start
  218. if (result.count("quick-start"))
  219. {
  220. option_quick_start = true;
  221. }
  222. // --reset
  223. if (result.count("reset"))
  224. {
  225. option_reset = true;
  226. }
  227. // --v-sync
  228. if (result.count("v-sync"))
  229. {
  230. option_v_sync = result["v-sync"].as<int>();
  231. }
  232. // --windowed
  233. if (result.count("windowed"))
  234. {
  235. option_windowed = true;
  236. }
  237. debug::log::info("Parsed {} command-line options", argc);
  238. }
  239. catch (const std::exception& e)
  240. {
  241. debug::log::error("An error occurred while parsing command-line options: {}", e.what());
  242. }
  243. }
  244. void game::setup_resources()
  245. {
  246. // Allocate resource manager
  247. resource_manager = std::make_unique<::resource_manager>();
  248. // Get executable data path
  249. const auto data_path = get_executable_data_path();
  250. // Determine data package path
  251. if (option_data)
  252. {
  253. // Handle command-line data path option
  254. data_package_path = option_data.value();
  255. if (data_package_path.is_relative())
  256. {
  257. data_package_path = data_path / data_package_path;
  258. }
  259. }
  260. else
  261. {
  262. data_package_path = data_path / (config::application_slug + std::string("-data.zip"));
  263. }
  264. // Determine mods path
  265. mods_path = data_path / "mods";
  266. // Determine config paths
  267. local_config_path = get_local_config_path() / config::application_name;
  268. shared_config_path = get_shared_config_path() / config::application_name;
  269. saves_path = shared_config_path / "saves";
  270. screenshots_path = shared_config_path / "gallery";
  271. controls_path = shared_config_path / "controls";
  272. // Log paths
  273. debug::log::info("Data package path: \"{}\"", data_package_path.string());
  274. debug::log::info("Local config path: \"{}\"", local_config_path.string());
  275. debug::log::info("Shared config path: \"{}\"", shared_config_path.string());
  276. debug::log::info("Mods path: \"{}\"", mods_path.string());
  277. // Create nonexistent config directories
  278. std::vector<std::filesystem::path> config_paths;
  279. config_paths.push_back(local_config_path);
  280. config_paths.push_back(shared_config_path);
  281. config_paths.push_back(saves_path);
  282. config_paths.push_back(screenshots_path);
  283. config_paths.push_back(controls_path);
  284. for (const auto& path: config_paths)
  285. {
  286. try
  287. {
  288. if (std::filesystem::create_directories(path))
  289. {
  290. debug::log::info("Created directory \"{}\"", path.string());
  291. }
  292. }
  293. catch (const std::filesystem::filesystem_error& e)
  294. {
  295. debug::log::error("Failed to create directory \"{}\": {}", path.string(), e.what());
  296. }
  297. }
  298. // Scan for mods
  299. std::vector<std::filesystem::path> mod_paths;
  300. if (std::filesystem::is_directory(mods_path))
  301. {
  302. for (const auto& entry: std::filesystem::directory_iterator{mods_path})
  303. {
  304. if (entry.is_directory() || (entry.is_regular_file() && entry.path().extension() == ".zip"))
  305. {
  306. mod_paths.push_back(entry.path());
  307. debug::log::info("Found mod \"{}\"", entry.path().filename().string());
  308. }
  309. }
  310. }
  311. // Mount mod paths
  312. for (const std::filesystem::path& mod_path: mod_paths)
  313. {
  314. resource_manager->mount(mods_path / mod_path);
  315. }
  316. // Mount config path
  317. resource_manager->mount(local_config_path);
  318. resource_manager->mount(shared_config_path);
  319. // Mount data package path
  320. resource_manager->mount(data_package_path);
  321. // Mount controls path
  322. resource_manager->mount(shared_config_path / "controls");
  323. }
  324. void game::load_settings()
  325. {
  326. if (option_reset)
  327. {
  328. // Command-line reset option found, reset settings
  329. settings = std::make_shared<dict<hash::fnv1a32_t>>();
  330. resource_manager->set_write_path(shared_config_path);
  331. resource_manager->save(*settings, "settings.cfg");
  332. debug::log::info("Settings reset");
  333. }
  334. else
  335. {
  336. settings = resource_manager->load<dict<hash::fnv1a32_t>>("settings.cfg");
  337. if (!settings)
  338. {
  339. debug::log::info("Settings not found");
  340. settings = std::make_shared<dict<hash::fnv1a32_t>>();
  341. }
  342. }
  343. }
  344. void game::setup_window()
  345. {
  346. // Construct window manager
  347. window_manager = app::window_manager::instance();
  348. // Default window settings
  349. std::string window_title = config::application_name;
  350. int window_x = -1;
  351. int window_y = -1;
  352. int window_w = -1;
  353. int window_h = -1;
  354. bool maximized = true;
  355. bool fullscreen = true;
  356. bool v_sync = true;
  357. // Read window settings
  358. bool resize = false;
  359. read_or_write_setting(*this, "window_title", window_title);
  360. read_or_write_setting(*this, "window_x", window_x);
  361. read_or_write_setting(*this, "window_y", window_y);
  362. if (!read_or_write_setting(*this, "window_w", window_w) ||
  363. !read_or_write_setting(*this, "window_h", window_h))
  364. {
  365. resize = true;
  366. }
  367. read_or_write_setting(*this, "maximized", maximized);
  368. read_or_write_setting(*this, "fullscreen", fullscreen);
  369. read_or_write_setting(*this, "v_sync", v_sync);
  370. // If window size not set, resize and reposition relative to default display
  371. if (resize)
  372. {
  373. const app::display& display = window_manager->get_display(0);
  374. const auto& usable_bounds = display.get_usable_bounds();
  375. const auto usable_bounds_center = usable_bounds.center();
  376. const float default_windowed_scale = 1.0f / 1.2f;
  377. window_w = static_cast<int>((usable_bounds.max.x() - usable_bounds.min.x()) * default_windowed_scale);
  378. window_h = static_cast<int>((usable_bounds.max.y() - usable_bounds.min.y()) * default_windowed_scale);
  379. window_x = usable_bounds_center.x() - window_w / 2;
  380. window_y = usable_bounds_center.y() - window_h / 2;
  381. }
  382. // Handle window-related command-line options
  383. if (option_windowed)
  384. {
  385. // Start in windowed mode
  386. maximized = false;
  387. fullscreen = false;
  388. }
  389. if (option_fullscreen)
  390. {
  391. // Start in fullscreen mode
  392. fullscreen = true;
  393. }
  394. if (option_v_sync)
  395. {
  396. v_sync = option_v_sync.value();
  397. }
  398. // Construct window
  399. window = window_manager->create_window
  400. (
  401. window_title,
  402. {window_x, window_y},
  403. {window_w, window_h},
  404. maximized,
  405. fullscreen,
  406. v_sync
  407. );
  408. // Restrict window size
  409. window->set_minimum_size({160, 144});
  410. // Setup window closed callback
  411. window_closed_subscription = window->get_closed_channel().subscribe
  412. (
  413. [&](const auto& event)
  414. {
  415. closed = true;
  416. }
  417. );
  418. }
  419. void game::setup_audio()
  420. {
  421. debug::log::trace("Setting up audio...");
  422. // Default audio settings
  423. master_volume = 1.0f;
  424. ambience_volume = 1.0f;
  425. effects_volume = 1.0f;
  426. mono_audio = false;
  427. captions = false;
  428. captions_size = 1.0f;
  429. // Read audio settings
  430. read_or_write_setting(*this, "master_volume", master_volume);
  431. read_or_write_setting(*this, "ambience_volume", ambience_volume);
  432. read_or_write_setting(*this, "effects_volume", effects_volume);
  433. read_or_write_setting(*this, "mono_audio", mono_audio);
  434. read_or_write_setting(*this, "captions", captions);
  435. read_or_write_setting(*this, "captions_size", captions_size);
  436. // Open audio device
  437. debug::log::trace("Opening audio device...");
  438. alc_device = alcOpenDevice(nullptr);
  439. if (!alc_device)
  440. {
  441. debug::log::error("Failed to open audio device: AL error code {}", alGetError());
  442. return;
  443. }
  444. else
  445. {
  446. // Get audio device name
  447. const ALCchar* alc_device_name = nullptr;
  448. if (alcIsExtensionPresent(alc_device, "ALC_ENUMERATE_ALL_EXT"))
  449. {
  450. alc_device_name = alcGetString(alc_device, ALC_ALL_DEVICES_SPECIFIER);
  451. }
  452. if (alcGetError(alc_device) != AL_NO_ERROR || !alc_device_name)
  453. {
  454. alc_device_name = alcGetString(alc_device, ALC_DEVICE_SPECIFIER);
  455. }
  456. // Log audio device name
  457. debug::log::info("Opened audio device \"{}\"", alc_device_name);
  458. }
  459. // Create audio context
  460. debug::log::trace("Creating audio context...");
  461. alc_context = alcCreateContext(alc_device, nullptr);
  462. if (!alc_context)
  463. {
  464. debug::log::error("Failed to create audio context: ALC error code {}", alcGetError(alc_device));
  465. alcCloseDevice(alc_device);
  466. return;
  467. }
  468. else
  469. {
  470. debug::log::trace("Created audio context");
  471. }
  472. // Make audio context current
  473. debug::log::trace("Making audio context current...");
  474. if (alcMakeContextCurrent(alc_context) == ALC_FALSE)
  475. {
  476. debug::log::error("Failed to make audio context current: ALC error code {}", alcGetError(alc_device));
  477. if (alc_context != nullptr)
  478. {
  479. alcDestroyContext(alc_context);
  480. }
  481. alcCloseDevice(alc_device);
  482. return;
  483. }
  484. else
  485. {
  486. debug::log::trace("Made audio context current");
  487. }
  488. debug::log::trace("Set up audio");
  489. }
  490. void game::setup_input()
  491. {
  492. // Construct input manager
  493. input_manager = app::input_manager::instance();
  494. // Process initial input events, such as connecting gamepads
  495. input_manager->update();
  496. // Setup application quit callback
  497. application_quit_subscription = input_manager->get_event_dispatcher().subscribe<input::application_quit_event>
  498. (
  499. [&](const auto& event)
  500. {
  501. closed = true;
  502. }
  503. );
  504. /*
  505. // Gamepad deactivation function
  506. auto deactivate_gamepad = [&](const auto& event)
  507. {
  508. if (gamepad_active)
  509. {
  510. gamepad_active = false;
  511. // WARNING: huge source of lag
  512. input_manager->set_cursor_visible(true);
  513. }
  514. };
  515. // Setup gamepad activation callbacks
  516. gamepad_axis_moved_subscription = input_manager->get_event_dispatcher().subscribe<input::gamepad_axis_moved_event>
  517. (
  518. [&](const auto& event)
  519. {
  520. if (!gamepad_active && std::abs(event.position) > 0.5f)
  521. {
  522. gamepad_active = true;
  523. input_manager->set_cursor_visible(false);
  524. }
  525. }
  526. );
  527. gamepad_button_pressed_subscription = input_manager->get_event_dispatcher().subscribe<input::gamepad_button_pressed_event>
  528. (
  529. [&](const auto& event)
  530. {
  531. if (!gamepad_active)
  532. {
  533. gamepad_active = true;
  534. input_manager->set_cursor_visible(false);
  535. }
  536. }
  537. );
  538. // Setup gamepad deactivation callbacks
  539. mouse_button_pressed_subscription = input_manager->get_event_dispatcher().subscribe<input::mouse_button_pressed_event>
  540. (
  541. deactivate_gamepad
  542. );
  543. mouse_moved_subscription = input_manager->get_event_dispatcher().subscribe<input::mouse_moved_event>
  544. (
  545. deactivate_gamepad
  546. );
  547. mouse_scrolled_subscription = input_manager->get_event_dispatcher().subscribe<input::mouse_scrolled_event>
  548. (
  549. deactivate_gamepad
  550. );
  551. // Activate gamepad if one is connected
  552. if (!input_manager->get_gamepads().empty())
  553. {
  554. gamepad_active = true;
  555. input_manager->set_cursor_visible(false);
  556. }
  557. else
  558. {
  559. gamepad_active = false;
  560. }
  561. */
  562. }
  563. void game::load_strings()
  564. {
  565. debug::log::trace("Loading strings...");
  566. // Default strings settings
  567. language_tag = "en";
  568. // Read strings settings
  569. read_or_write_setting(*this, "language_tag", language_tag);
  570. // Slugify language tag
  571. std::string language_slug = language_tag;
  572. std::transform
  573. (
  574. language_slug.begin(),
  575. language_slug.end(),
  576. language_slug.begin(),
  577. [](unsigned char c)
  578. {
  579. return std::tolower(c);
  580. }
  581. );
  582. // Load string map
  583. string_map = resource_manager->load<i18n::string_map>(language_slug + ".str");
  584. // Log language info
  585. debug::log::info("Language tag: {}", language_tag);
  586. // Change window title
  587. const std::string window_title = get_string(*this, "window_title");
  588. window->set_title(window_title);
  589. // Update window title setting
  590. (*settings)["window_title"] = window_title;
  591. debug::log::trace("Loaded strings");
  592. }
  593. void game::setup_rendering()
  594. {
  595. debug::log::trace("Setting up rendering...");
  596. // Default rendering settings
  597. render_scale = 1.0f;
  598. anti_aliasing_method = render::anti_aliasing_method::none;
  599. shadow_map_resolution = 4096;
  600. // Read rendering settings
  601. read_or_write_setting(*this, "render_scale", render_scale);
  602. read_or_write_setting(*this, "anti_aliasing_method", *reinterpret_cast<std::underlying_type_t<render::anti_aliasing_method>*>(&anti_aliasing_method));
  603. read_or_write_setting(*this, "shadow_map_resolution", shadow_map_resolution);
  604. // Create framebuffers
  605. ::graphics::create_framebuffers(*this);
  606. // Load fallback material
  607. auto fallback_material = resource_manager->load<render::material>("fallback.mtl");
  608. // Setup common render passes
  609. {
  610. // Construct bloom pass
  611. bloom_pass = std::make_unique<render::bloom_pass>(window->get_rasterizer(), resource_manager.get());
  612. bloom_pass->set_source_texture(hdr_color_texture.get());
  613. bloom_pass->set_mip_chain_length(5);
  614. //bloom_pass->set_mip_chain_length(0);
  615. bloom_pass->set_filter_radius(0.005f);
  616. common_final_pass = std::make_unique<render::final_pass>(window->get_rasterizer(), ldr_framebuffer_a.get(), resource_manager.get());
  617. common_final_pass->set_color_texture(hdr_color_texture.get());
  618. common_final_pass->set_bloom_texture(bloom_pass->get_bloom_texture());
  619. common_final_pass->set_bloom_weight(0.04f);
  620. //common_final_pass->set_bloom_weight(0.0f);
  621. common_final_pass->set_blue_noise_texture(resource_manager->load<gl::texture_2d>("blue-noise.tex"));
  622. fxaa_pass = std::make_unique<render::fxaa_pass>(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get());
  623. fxaa_pass->set_source_texture(ldr_color_texture_a.get());
  624. resample_pass = std::make_unique<render::resample_pass>(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get());
  625. resample_pass->set_source_texture(ldr_color_texture_b.get());
  626. resample_pass->set_enabled(false);
  627. // Configure anti-aliasing according to settings
  628. graphics::select_anti_aliasing_method(*this, anti_aliasing_method);
  629. // Configure render scaling according to settings
  630. graphics::change_render_resolution(*this, render_scale);
  631. }
  632. // Setup UI compositor
  633. {
  634. ui_clear_pass = std::make_unique<render::clear_pass>(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer());
  635. ui_clear_pass->set_cleared_buffers(false, true, false);
  636. ui_clear_pass->set_clear_depth(0.0f);
  637. ui_material_pass = std::make_unique<render::material_pass>(window->get_rasterizer(), &window->get_rasterizer()->get_default_framebuffer(), resource_manager.get());
  638. ui_material_pass->set_fallback_material(fallback_material);
  639. ui_compositor = std::make_unique<render::compositor>();
  640. ui_compositor->add_pass(ui_clear_pass.get());
  641. ui_compositor->add_pass(ui_material_pass.get());
  642. }
  643. // Setup surface compositor
  644. {
  645. surface_cascaded_shadow_map_pass = std::make_unique<render::cascaded_shadow_map_pass>(window->get_rasterizer(), resource_manager.get());
  646. surface_clear_pass = std::make_unique<render::clear_pass>(window->get_rasterizer(), hdr_framebuffer.get());
  647. surface_clear_pass->set_clear_color({0.0f, 0.0f, 0.0f, 1.0f});
  648. surface_clear_pass->set_clear_depth(0.0f);
  649. surface_clear_pass->set_clear_stencil(0);
  650. surface_clear_pass->set_cleared_buffers(true, true, true);
  651. sky_pass = std::make_unique<render::sky_pass>(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get());
  652. // sky_pass->set_magnification(3.0f);
  653. surface_material_pass = std::make_unique<render::material_pass>(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get());
  654. surface_material_pass->set_fallback_material(fallback_material);
  655. surface_outline_pass = std::make_unique<render::outline_pass>(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get());
  656. surface_outline_pass->set_outline_width(0.25f);
  657. surface_outline_pass->set_outline_color(math::fvec4{1.0f, 1.0f, 1.0f, 1.0f});
  658. surface_compositor = std::make_unique<render::compositor>();
  659. surface_compositor->add_pass(surface_cascaded_shadow_map_pass.get());
  660. surface_compositor->add_pass(surface_clear_pass.get());
  661. surface_compositor->add_pass(sky_pass.get());
  662. surface_compositor->add_pass(surface_material_pass.get());
  663. //surface_compositor->add_pass(surface_outline_pass.get());
  664. surface_compositor->add_pass(bloom_pass.get());
  665. surface_compositor->add_pass(common_final_pass.get());
  666. surface_compositor->add_pass(fxaa_pass.get());
  667. surface_compositor->add_pass(resample_pass.get());
  668. }
  669. // Setup underground compositor
  670. {
  671. underground_clear_pass = std::make_unique<render::clear_pass>(window->get_rasterizer(), hdr_framebuffer.get());
  672. underground_clear_pass->set_cleared_buffers(true, true, false);
  673. underground_clear_pass->set_clear_color({0, 0, 0, 1});
  674. underground_clear_pass->set_clear_depth(0.0f);
  675. underground_material_pass = std::make_unique<render::material_pass>(window->get_rasterizer(), hdr_framebuffer.get(), resource_manager.get());
  676. underground_material_pass->set_fallback_material(fallback_material);
  677. underground_compositor = std::make_unique<render::compositor>();
  678. underground_compositor->add_pass(underground_clear_pass.get());
  679. underground_compositor->add_pass(underground_material_pass.get());
  680. underground_compositor->add_pass(bloom_pass.get());
  681. underground_compositor->add_pass(common_final_pass.get());
  682. underground_compositor->add_pass(fxaa_pass.get());
  683. underground_compositor->add_pass(resample_pass.get());
  684. }
  685. // Create renderer
  686. renderer = std::make_unique<render::renderer>(*window->get_rasterizer(), *resource_manager);
  687. debug::log::trace("Set up rendering");
  688. }
  689. void game::setup_scenes()
  690. {
  691. debug::log::trace("Setting up scenes...");
  692. // Ratio of meters to scene units.
  693. constexpr float scene_scale = 1.0f / 100.0f;
  694. // Get default framebuffer
  695. const auto& viewport_size = window->get_viewport_size();
  696. const float viewport_aspect_ratio = static_cast<float>(viewport_size[0]) / static_cast<float>(viewport_size[1]);
  697. // Allocate and init surface scene
  698. surface_scene = std::make_unique<scene::collection>();
  699. surface_scene->set_scale(scene_scale);
  700. // Allocate and init surface camera
  701. surface_camera = std::make_shared<scene::camera>();
  702. surface_camera->set_perspective(math::radians<float>(45.0f), viewport_aspect_ratio, 0.5f, 1000.0f);
  703. surface_camera->set_compositor(surface_compositor.get());
  704. surface_camera->set_composite_index(0);
  705. // Allocate and init underground scene
  706. underground_scene = std::make_unique<scene::collection>();
  707. underground_scene->set_scale(scene_scale);
  708. // Allocate and init underground camera
  709. underground_camera = std::make_shared<scene::camera>();
  710. underground_camera->set_perspective(math::radians<float>(45.0f), viewport_aspect_ratio, 0.1f, 200.0f);
  711. underground_camera->set_compositor(underground_compositor.get());
  712. underground_camera->set_composite_index(0);
  713. // Clear active scene
  714. active_scene = nullptr;
  715. debug::log::trace("Set up scenes");
  716. }
  717. void game::setup_animation()
  718. {
  719. // Setup timeline system
  720. timeline = std::make_unique<::timeline>();
  721. timeline->set_autoremove(true);
  722. // Setup animator
  723. animator = std::make_unique<::animator>();
  724. }
  725. void game::setup_ui()
  726. {
  727. // Default UI settings
  728. font_scale = 1.0f;
  729. debug_font_size_pt = 10.0f;
  730. menu_font_size_pt = 22.0f;
  731. title_font_size_pt = 80.0f;
  732. dyslexia_font = false;
  733. // Read UI settings
  734. read_or_write_setting(*this, "font_scale", font_scale);
  735. read_or_write_setting(*this, "debug_font_size_pt", debug_font_size_pt);
  736. read_or_write_setting(*this, "menu_font_size_pt", menu_font_size_pt);
  737. read_or_write_setting(*this, "title_font_size_pt", title_font_size_pt);
  738. read_or_write_setting(*this, "dyslexia_font", dyslexia_font);
  739. // Allocate font materials
  740. debug_font_material = std::make_shared<render::material>();
  741. menu_font_material = std::make_shared<render::material>();
  742. title_font_material = std::make_shared<render::material>();
  743. // Load fonts
  744. debug::log::trace("Loading fonts...");
  745. try
  746. {
  747. ::load_fonts(*this);
  748. debug::log::trace("Loaded fonts");
  749. }
  750. catch (...)
  751. {
  752. debug::log::error("Failed to load fonts");
  753. }
  754. // Get default framebuffer
  755. const auto& viewport_size = window->get_viewport_size();
  756. const float viewport_aspect_ratio = static_cast<float>(viewport_size[0]) / static_cast<float>(viewport_size[1]);
  757. // Setup UI scene
  758. ui_scene = std::make_unique<scene::collection>();
  759. // Setup UI camera
  760. ui_camera = std::make_unique<scene::camera>();
  761. ui_camera->set_compositor(ui_compositor.get());
  762. const float clip_left = 0.0f;
  763. const float clip_right = static_cast<float>(viewport_size.x());
  764. const float clip_top = 0.0f;
  765. const float clip_bottom = static_cast<float>(viewport_size.y());
  766. const float clip_near = -100.0f;
  767. const float clip_far = 100.0f;
  768. ui_camera->set_orthographic(clip_left, clip_right, clip_top, clip_bottom, clip_near, clip_far);
  769. ui_camera->look_at({0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, -1.0f}, {0.0f, 1.0f, 0.0f});
  770. // Menu BG material
  771. menu_bg_material = std::make_shared<render::material>();
  772. menu_bg_material->set_shader_template(resource_manager->load<gl::shader_template>("ui-element-untextured.glsl"));
  773. 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});
  774. menu_bg_material->set_variable("tint", menu_bg_tint);
  775. menu_bg_material->set_blend_mode(render::material_blend_mode::translucent);
  776. // Menu BG billboard
  777. menu_bg_billboard = std::make_unique<scene::billboard>();
  778. menu_bg_billboard->set_material(menu_bg_material);
  779. menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f});
  780. menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f});
  781. // Create fade transition
  782. fade_transition = std::make_unique<screen_transition>();
  783. fade_transition->get_material()->set_shader_template(resource_manager->load<gl::shader_template>("fade-transition.glsl"));
  784. fade_transition_color = std::make_shared<render::matvar_fvec3>(1, math::fvec3{0, 0, 0});
  785. fade_transition->get_material()->set_variable("color", fade_transition_color);
  786. fade_transition->get_billboard()->set_translation({0, 0, 98});
  787. // Create inner radial transition
  788. radial_transition_inner = std::make_unique<screen_transition>();
  789. radial_transition_inner->get_material()->set_shader_template(resource_manager->load<gl::shader_template>("radial-transition-inner.glsl"));
  790. // Create outer radial transition
  791. radial_transition_outer = std::make_unique<screen_transition>();
  792. radial_transition_outer->get_material()->set_shader_template(resource_manager->load<gl::shader_template>("radial-transition-outer.glsl"));
  793. // Menu BG animations
  794. {
  795. auto menu_bg_frame_callback = [menu_bg_tint](int channel, const float& opacity)
  796. {
  797. menu_bg_tint->set(math::fvec4{0.0f, 0.0f, 0.0f, opacity});
  798. };
  799. // Menu BG fade in animation
  800. menu_bg_fade_in_animation = std::make_unique<animation<float>>();
  801. {
  802. menu_bg_fade_in_animation->set_interpolator(ease<float>::out_cubic);
  803. animation_channel<float>* channel = menu_bg_fade_in_animation->add_channel(0);
  804. channel->insert_keyframe({0.0f, 0.0f});
  805. channel->insert_keyframe({config::menu_fade_in_duration, config::menu_bg_opacity});
  806. menu_bg_fade_in_animation->set_frame_callback(menu_bg_frame_callback);
  807. menu_bg_fade_in_animation->set_start_callback
  808. (
  809. [&, menu_bg_tint]()
  810. {
  811. ui_scene->add_object(*menu_bg_billboard);
  812. menu_bg_tint->set(math::fvec4{0.0f, 0.0f, 0.0f, 0.0f});
  813. //menu_bg_billboard->set_active(true);
  814. }
  815. );
  816. }
  817. // Menu BG fade out animation
  818. menu_bg_fade_out_animation = std::make_unique<animation<float>>();
  819. {
  820. menu_bg_fade_out_animation->set_interpolator(ease<float>::out_cubic);
  821. animation_channel<float>* channel = menu_bg_fade_out_animation->add_channel(0);
  822. channel->insert_keyframe({0.0f, config::menu_bg_opacity});
  823. channel->insert_keyframe({config::menu_fade_out_duration, 0.0f});
  824. menu_bg_fade_out_animation->set_frame_callback(menu_bg_frame_callback);
  825. menu_bg_fade_out_animation->set_end_callback
  826. (
  827. [&]()
  828. {
  829. ui_scene->remove_object(*menu_bg_billboard);
  830. //menu_bg_billboard->set_active(false);
  831. }
  832. );
  833. }
  834. }
  835. // Add UI scene objects to UI scene
  836. ui_scene->add_object(*ui_camera);
  837. ui_scene->add_object(*fade_transition->get_billboard());
  838. // Add UI animations to animator
  839. animator->add_animation(fade_transition->get_animation());
  840. animator->add_animation(menu_bg_fade_in_animation.get());
  841. animator->add_animation(menu_bg_fade_out_animation.get());
  842. // Setup window resized callback
  843. window_resized_subscription = window->get_resized_channel().subscribe
  844. (
  845. [&](const auto& event)
  846. {
  847. const auto& viewport_size = event.window->get_viewport_size();
  848. const float viewport_aspect_ratio = static_cast<float>(viewport_size.x()) / static_cast<float>(viewport_size.y());
  849. // Resize framebuffers
  850. ::graphics::change_render_resolution(*this, render_scale);
  851. // Update camera projection matrix
  852. surface_camera->set_perspective
  853. (
  854. surface_camera->get_vertical_fov(),
  855. viewport_aspect_ratio,
  856. surface_camera->get_clip_near(),
  857. surface_camera->get_clip_far()
  858. );
  859. // Update UI camera projection matrix
  860. ui_camera->set_orthographic
  861. (
  862. 0.0f,
  863. static_cast<float>(viewport_size.x()),
  864. 0.0f,
  865. static_cast<float>(viewport_size.y()),
  866. ui_camera->get_clip_near(),
  867. ui_camera->get_clip_far()
  868. );
  869. // Resize menu BG billboard
  870. menu_bg_billboard->set_scale({std::ceil(viewport_size.x() * 0.5f), std::ceil(viewport_size.y() * 0.5f), 1.0f});
  871. menu_bg_billboard->set_translation({std::floor(viewport_size.x() * 0.5f), std::floor(viewport_size.y() * 0.5f), -100.0f});
  872. // Re-align debug text
  873. frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - debug_font.get_font_metrics().size), 99.0f});
  874. // Re-align menu text
  875. ::menu::align_text(*this);
  876. }
  877. );
  878. }
  879. void game::setup_rng()
  880. {
  881. std::random_device rd;
  882. rng.seed(rd());
  883. }
  884. void game::setup_entities()
  885. {
  886. // Create entity registry
  887. entity_registry = std::make_unique<entt::registry>();
  888. }
  889. void game::setup_systems()
  890. {
  891. const auto& viewport_size = window->get_viewport_size();
  892. math::fvec4 viewport = {0.0f, 0.0f, static_cast<float>(viewport_size[0]), static_cast<float>(viewport_size[1])};
  893. // Setup terrain system
  894. terrain_system = std::make_unique<::terrain_system>(*entity_registry);
  895. // Setup camera system
  896. camera_system = std::make_unique<::camera_system>(*entity_registry);
  897. camera_system->set_viewport(viewport);
  898. // Setup subterrain system
  899. subterrain_system = std::make_unique<::subterrain_system>(*entity_registry, resource_manager.get());
  900. // Setup collision system
  901. collision_system = std::make_unique<::collision_system>(*entity_registry);
  902. // Setup behavior system
  903. behavior_system = std::make_unique<::behavior_system>(*entity_registry);
  904. // Setup steering system
  905. steering_system = std::make_unique<::steering_system>(*entity_registry);
  906. // Setup locomotion system
  907. locomotion_system = std::make_unique<::locomotion_system>(*entity_registry);
  908. // Setup IK system
  909. ik_system = std::make_unique<::ik_system>(*entity_registry);
  910. // Setup metabolic system
  911. metabolic_system = std::make_unique<::metabolic_system>(*entity_registry);
  912. // Setup metamorphosis system
  913. metamorphosis_system = std::make_unique<::metamorphosis_system>(*entity_registry);
  914. // Setup animation system
  915. animation_system = std::make_unique<::animation_system>(*entity_registry);
  916. // Setup physics system
  917. physics_system = std::make_unique<::physics_system>(*entity_registry);
  918. physics_system->set_gravity({0.0f, -9.80665f * 100.0f, 0.0f});
  919. // Setup reproductive system
  920. reproductive_system = std::make_unique<::reproductive_system>(*entity_registry);
  921. reproductive_system->set_physics_system(physics_system.get());
  922. // Setup spatial system
  923. spatial_system = std::make_unique<::spatial_system>(*entity_registry);
  924. // Setup constraint system
  925. constraint_system = std::make_unique<::constraint_system>(*entity_registry);
  926. // Setup orbit system
  927. orbit_system = std::make_unique<::orbit_system>(*entity_registry);
  928. // Setup blackbody system
  929. blackbody_system = std::make_unique<::blackbody_system>(*entity_registry);
  930. // Setup atmosphere system
  931. atmosphere_system = std::make_unique<::atmosphere_system>(*entity_registry);
  932. atmosphere_system->set_sky_pass(sky_pass.get());
  933. // Setup astronomy system
  934. astronomy_system = std::make_unique<::astronomy_system>(*entity_registry);
  935. astronomy_system->set_transmittance_samples(16);
  936. astronomy_system->set_sky_pass(sky_pass.get());
  937. // Setup render system
  938. render_system = std::make_unique<::render_system>(*entity_registry);
  939. render_system->set_renderer(renderer.get());
  940. render_system->add_layer(surface_scene.get());
  941. render_system->add_layer(underground_scene.get());
  942. render_system->add_layer(ui_scene.get());
  943. }
  944. void game::setup_controls()
  945. {
  946. debug::log::trace("Setting up controls...");
  947. // Load SDL game controller mappings database
  948. // debug::log::trace("Loading SDL game controller mappings...");
  949. // file_buffer* game_controller_db = resource_manager->load<file_buffer>("gamecontrollerdb.txt");
  950. // if (!game_controller_db)
  951. // {
  952. // debug::log::error("Failed to load SDL game controller mappings");
  953. // }
  954. // else
  955. // {
  956. // app->add_game_controller_mappings(game_controller_db->data(), game_controller_db->size());
  957. // debug::log::trace("Loaded SDL game controller mappings");
  958. // resource_manager->unload("gamecontrollerdb.txt");
  959. // }
  960. // Pass input event queue to action maps
  961. event::dispatcher* input_event_dispatcher = &input_manager->get_event_dispatcher();
  962. window_action_map.set_event_dispatcher(input_event_dispatcher);
  963. menu_action_map.set_event_dispatcher(input_event_dispatcher);
  964. movement_action_map.set_event_dispatcher(input_event_dispatcher);
  965. camera_action_map.set_event_dispatcher(input_event_dispatcher);
  966. ant_action_map.set_event_dispatcher(input_event_dispatcher);
  967. debug_action_map.set_event_dispatcher(input_event_dispatcher);
  968. // Default control profile settings
  969. control_profile_filename = "controls.cfg";
  970. // Read control profile settings
  971. if (read_or_write_setting(*this, "control_profile", control_profile_filename))
  972. {
  973. control_profile = resource_manager->load<::control_profile>(control_profile_filename);
  974. }
  975. if (!control_profile)
  976. {
  977. // Allocate control profile
  978. control_profile = std::make_shared<::control_profile>();
  979. // Reset control profile to default settings.
  980. ::reset_control_profile(*control_profile);
  981. // Save control profile
  982. resource_manager->set_write_path(controls_path);
  983. resource_manager->save(*control_profile, control_profile_filename);
  984. }
  985. // Apply control profile
  986. ::apply_control_profile(*this, *control_profile);
  987. // Setup mouse sensitivity
  988. mouse_pan_factor = mouse_radians_per_pixel * mouse_pan_sensitivity * (mouse_invert_pan ? -1.0 : 1.0);
  989. mouse_tilt_factor = mouse_radians_per_pixel * mouse_tilt_sensitivity * (mouse_invert_tilt ? -1.0 : 1.0);
  990. // Setup gamepad sensitivity
  991. gamepad_pan_factor = gamepad_radians_per_second * gamepad_pan_sensitivity * (gamepad_invert_pan ? -1.0 : 1.0);
  992. gamepad_tilt_factor = gamepad_radians_per_second * gamepad_tilt_sensitivity * (gamepad_invert_tilt ? -1.0 : 1.0);
  993. // Setup action callbacks
  994. setup_window_controls(*this);
  995. setup_menu_controls(*this);
  996. setup_camera_controls(*this);
  997. setup_game_controls(*this);
  998. setup_ant_controls(*this);
  999. // Enable window controls
  1000. enable_window_controls(*this);
  1001. #if defined(DEBUG)
  1002. // Setup and enable debug controls
  1003. setup_debug_controls(*this);
  1004. enable_debug_controls(*this);
  1005. #endif
  1006. debug::log::trace("Set up controls");
  1007. }
  1008. void game::setup_debugging()
  1009. {
  1010. cli = std::make_unique<debug::cli>();
  1011. const auto& viewport_size = window->get_viewport_size();
  1012. frame_time_text = std::make_unique<scene::text>();
  1013. frame_time_text->set_material(debug_font_material);
  1014. frame_time_text->set_color({1.0f, 1.0f, 0.0f, 1.0f});
  1015. frame_time_text->set_font(&debug_font);
  1016. frame_time_text->set_translation({std::round(0.0f), std::round(viewport_size.y() - debug_font.get_font_metrics().size), 99.0f});
  1017. #if defined(DEBUG)
  1018. ui_scene->add_object(*frame_time_text);
  1019. debug_ui_visible = true;
  1020. #endif
  1021. }
  1022. void game::setup_timing()
  1023. {
  1024. // Init default settings
  1025. max_frame_rate = static_cast<float>(window_manager->get_display(0).get_refresh_rate() * 2);
  1026. // Read settings
  1027. read_or_write_setting(*this, "fixed_update_rate", fixed_update_rate);
  1028. read_or_write_setting(*this, "max_frame_rate", max_frame_rate);
  1029. read_or_write_setting(*this, "limit_frame_rate", limit_frame_rate);
  1030. const auto fixed_update_interval = std::chrono::duration_cast<::frame_scheduler::duration_type>(std::chrono::duration<double>(1.0 / fixed_update_rate));
  1031. 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();
  1032. const auto max_frame_duration = fixed_update_interval * 15;
  1033. // Configure frame scheduler
  1034. frame_scheduler.set_fixed_update_interval(fixed_update_interval);
  1035. frame_scheduler.set_min_frame_duration(min_frame_duration);
  1036. frame_scheduler.set_max_frame_duration(max_frame_duration);
  1037. frame_scheduler.set_fixed_update_callback(std::bind_front(&game::fixed_update, this));
  1038. frame_scheduler.set_variable_update_callback(std::bind_front(&game::variable_update, this));
  1039. // Init frame duration average
  1040. average_frame_duration.reserve(15);
  1041. }
  1042. void game::shutdown_audio()
  1043. {
  1044. debug::log::trace("Shutting down audio...");
  1045. if (alc_context)
  1046. {
  1047. alcMakeContextCurrent(nullptr);
  1048. alcDestroyContext(alc_context);
  1049. }
  1050. if (alc_device)
  1051. {
  1052. alcCloseDevice(alc_device);
  1053. }
  1054. debug::log::trace("Shut down audio");
  1055. }
  1056. void game::fixed_update(::frame_scheduler::duration_type fixed_update_time, ::frame_scheduler::duration_type fixed_update_interval)
  1057. {
  1058. const float t = std::chrono::duration<float>(fixed_update_time).count();
  1059. const float dt = std::chrono::duration<float>(fixed_update_interval).count();
  1060. // Update tweens
  1061. sky_pass->update_tweens();
  1062. // Process window events
  1063. window_manager->update();
  1064. // Process function queue
  1065. while (!function_queue.empty())
  1066. {
  1067. function_queue.front()();
  1068. function_queue.pop();
  1069. }
  1070. // Advance tline
  1071. //timeline->advance(dt);
  1072. // Update entity systems
  1073. animation_system->update(t, dt);
  1074. physics_system->update(t, dt);
  1075. //terrain_system->update(t, dt);
  1076. //subterrain_system->update(t, dt);
  1077. collision_system->update(t, dt);
  1078. behavior_system->update(t, dt);
  1079. steering_system->update(t, dt);
  1080. locomotion_system->update(t, dt);
  1081. ik_system->update(t, dt);
  1082. reproductive_system->update(t, dt);
  1083. metabolic_system->update(t, dt);
  1084. metamorphosis_system->update(t, dt);
  1085. // physics_system->update(t, dt);
  1086. orbit_system->update(t, dt);
  1087. blackbody_system->update(t, dt);
  1088. atmosphere_system->update(t, dt);
  1089. astronomy_system->update(t, dt);
  1090. spatial_system->update(t, dt);
  1091. constraint_system->update(t, dt);
  1092. animator->animate(dt);
  1093. camera_system->update(t, dt);
  1094. render_system->update(t, dt);
  1095. }
  1096. void game::variable_update(::frame_scheduler::duration_type fixed_update_time, ::frame_scheduler::duration_type fixed_update_interval, ::frame_scheduler::duration_type accumulated_time)
  1097. {
  1098. // Calculate subframe interpolation factor (`alpha`)
  1099. const float alpha = static_cast<float>(std::chrono::duration<double, ::frame_scheduler::duration_type::period>{accumulated_time} / fixed_update_interval);
  1100. // Sample average frame duration
  1101. const float average_frame_ms = average_frame_duration(std::chrono::duration<float, std::milli>(frame_scheduler.get_frame_duration()).count());
  1102. const float average_frame_fps = 1000.0f / average_frame_ms;
  1103. // Update frame rate display
  1104. frame_time_text->set_content(std::format("{:5.02f}ms / {:5.02f} FPS", average_frame_ms, average_frame_fps));
  1105. // Process input events
  1106. input_manager->update();
  1107. // Interpolate physics
  1108. physics_system->interpolate(alpha);
  1109. // Interpolate animation
  1110. animation_system->interpolate(alpha);
  1111. // Render
  1112. camera_system->interpolate(alpha);
  1113. render_system->draw(alpha);
  1114. window->swap_buffers();
  1115. }
  1116. void game::execute()
  1117. {
  1118. // Change to initial state
  1119. state_machine.emplace(std::make_unique<main_menu_state>(*this, true));
  1120. debug::log::trace("Entered main loop");
  1121. frame_scheduler.refresh();
  1122. while (!closed)
  1123. {
  1124. frame_scheduler.tick();
  1125. }
  1126. debug::log::trace("Exited main loop");
  1127. // Exit all active game states
  1128. while (!state_machine.empty())
  1129. {
  1130. state_machine.pop();
  1131. }
  1132. }