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

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