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

1386 lines
48 KiB

  1. /*
  2. * Copyright (C) 2020 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 "application.hpp"
  20. #include "configuration.hpp"
  21. #include "state/application-states.hpp"
  22. #include "math.hpp"
  23. // STL
  24. #include <cstdlib>
  25. #include <iostream>
  26. #include <stdexcept>
  27. #include <thread>
  28. #include <string>
  29. #include <iomanip>
  30. #include <filesystem>
  31. // External
  32. #include <glad/glad.h>
  33. #include <SDL2/SDL.h>
  34. #include "stb/stb_image_write.h"
  35. #include <physfs.h>
  36. #include <cxxopts.hpp>
  37. // Debug
  38. #include "debug/ansi-codes.hpp"
  39. #include "debug/console-commands.hpp"
  40. // Resources
  41. #include "resources/resource-manager.hpp"
  42. // Input
  43. #include "input/sdl-scancode-table.hpp"
  44. #include "input/sdl-game-controller-tables.hpp"
  45. #include "input/scancode.hpp"
  46. #include "input/input-mapping.hpp"
  47. // Rasterizer
  48. #include "rasterizer/rasterizer.hpp"
  49. #include "rasterizer/framebuffer.hpp"
  50. #include "rasterizer/texture-2d.hpp"
  51. #include "rasterizer/pixel-type.hpp"
  52. #include "rasterizer/pixel-format.hpp"
  53. #include "rasterizer/vertex-buffer.hpp"
  54. #include "rasterizer/vertex-array.hpp"
  55. #include "rasterizer/vertex-attribute-type.hpp"
  56. #include "rasterizer/texture-wrapping.hpp"
  57. #include "rasterizer/texture-filter.hpp"
  58. // Renderer
  59. #include "renderer/simple-render-pass.hpp"
  60. #include "renderer/passes/shadow-map-pass.hpp"
  61. #include "renderer/passes/sky-pass.hpp"
  62. #include "renderer/passes/clear-pass.hpp"
  63. #include "renderer/passes/material-pass.hpp"
  64. #include "renderer/passes/bloom-pass.hpp"
  65. #include "renderer/passes/final-pass.hpp"
  66. #include "renderer/vertex-attributes.hpp"
  67. #include "renderer/material-flags.hpp"
  68. #include "renderer/material-property.hpp"
  69. // Animation
  70. #include "animation/animation.hpp"
  71. #include "animation/animator.hpp"
  72. #include "animation/screen-transition.hpp"
  73. #include "animation/ease.hpp"
  74. // Scene
  75. #include "scene/billboard.hpp"
  76. #include "scene/model-instance.hpp"
  77. // Systems
  78. #include "systems/behavior-system.hpp"
  79. #include "systems/camera-system.hpp"
  80. #include "systems/collision-system.hpp"
  81. #include "systems/locomotion-system.hpp"
  82. #include "systems/render-system.hpp"
  83. #include "systems/nest-system.hpp"
  84. #include "systems/placement-system.hpp"
  85. #include "systems/samara-system.hpp"
  86. #include "systems/subterrain-system.hpp"
  87. #include "systems/terrain-system.hpp"
  88. #include "systems/vegetation-system.hpp"
  89. #include "systems/tool-system.hpp"
  90. #include "systems/control-system.hpp"
  91. #include "systems/ui-system.hpp"
  92. #include "systems/constraint-system.hpp"
  93. #include <dirent.h>
  94. // Entity components
  95. #include "entity/components/cavity-component.hpp"
  96. // Utilities
  97. #include "utility/paths.hpp"
  98. #include "utility/timestamp.hpp"
  99. using namespace vmq::operators;
  100. application::application(int argc, char** argv):
  101. closed(false),
  102. exit_status(EXIT_SUCCESS),
  103. initial_state(nullptr)
  104. {
  105. // Format log messages
  106. logger.set_warning_prefix("Warning: ");
  107. logger.set_error_prefix(std::string());
  108. logger.set_success_prefix(std::string());
  109. // Determine application name
  110. std::string application_name;
  111. #if defined(_WIN32)
  112. application_name = "Antkeeper";
  113. #else
  114. application_name = "antkeeper";
  115. #endif
  116. // Detect resource paths
  117. data_path = get_data_path(application_name);
  118. data_package_path = data_path + "data.zip";
  119. config_path = get_config_path(application_name);
  120. mods_path = config_path + "mods/";
  121. saves_path = config_path + "saves/";
  122. screenshots_path = config_path + "screenshots/";
  123. // Log resource paths
  124. logger.log("Detected data path as \"" + data_path + "\"");
  125. logger.log("Detected config path as \"" + config_path + "\"");
  126. // Create nonexistent config directories
  127. std::vector<std::string> config_paths;
  128. config_paths.push_back(config_path);
  129. config_paths.push_back(mods_path);
  130. config_paths.push_back(saves_path);
  131. config_paths.push_back(screenshots_path);
  132. for (const std::string& path: config_paths)
  133. {
  134. if (!path_exists(path))
  135. {
  136. logger.push_task("Creating directory \"" + path + "\"");
  137. if (create_directory(path))
  138. {
  139. logger.pop_task(EXIT_SUCCESS);
  140. }
  141. else
  142. {
  143. logger.pop_task(EXIT_FAILURE);
  144. }
  145. }
  146. }
  147. // Redirect logger output to log file on non-debug builds
  148. #if defined(NDEBUG)
  149. std::string log_filename = config_path + "log.txt";
  150. log_filestream.open(log_filename.c_str());
  151. logger.redirect(&log_filestream);
  152. #endif
  153. // Setup FSM
  154. setup_fsm();
  155. // Load configuration
  156. //load_config();
  157. fullscreen = true;
  158. vsync = false;
  159. // Parse command line options
  160. parse_options(argc, argv);
  161. // Init PhysicsFS
  162. logger.push_task("Initializing PhysicsFS");
  163. if (!PHYSFS_init(argv[0]))
  164. {
  165. logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  166. logger.pop_task(EXIT_FAILURE);
  167. }
  168. else
  169. {
  170. logger.pop_task(EXIT_SUCCESS);
  171. }
  172. // Mount mods
  173. struct dirent **files = nullptr;
  174. int n = scandir (mods_path.c_str(), &files, NULL, alphasort);
  175. if (n >= 0)
  176. {
  177. for (int i = 0; i < n; ++i)
  178. {
  179. struct dirent* file = files[i];
  180. switch (file->d_type)
  181. {
  182. case DT_REG:
  183. case DT_DIR:
  184. {
  185. std::string mod_name = file->d_name;
  186. // Skip hidden files and directories
  187. if (mod_name.front() == '.')
  188. break;
  189. std::string mod_path = mods_path + mod_name;
  190. logger.push_task("Mounting mod \"" + mod_path + "\"");
  191. if (!PHYSFS_mount(mod_path.c_str(), nullptr, 1))
  192. {
  193. logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  194. logger.pop_task(EXIT_FAILURE);
  195. }
  196. else
  197. {
  198. logger.pop_task(EXIT_SUCCESS);
  199. }
  200. break;
  201. }
  202. default:
  203. break;
  204. }
  205. }
  206. }
  207. // Mount data package
  208. logger.push_task("Mounting data package \"" + data_package_path + "\"");
  209. if (!PHYSFS_mount(data_package_path.c_str(), nullptr, 1))
  210. {
  211. logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  212. logger.pop_task(EXIT_FAILURE);
  213. }
  214. else
  215. {
  216. logger.pop_task(EXIT_SUCCESS);
  217. }
  218. // Setup resource manager
  219. resource_manager = new ::resource_manager();
  220. resource_manager->set_logger(&logger);
  221. // Include resource search paths in order of priority
  222. resource_manager->include("/shaders/");
  223. resource_manager->include("/models/");
  224. resource_manager->include("/textures/");
  225. resource_manager->include("/materials/");
  226. resource_manager->include("/entities/");
  227. resource_manager->include("/behaviors/");
  228. resource_manager->include("/controls/");
  229. // Get SDL compiled version
  230. SDL_version sdl_compiled_version;
  231. SDL_VERSION(&sdl_compiled_version);
  232. std::string sdl_compiled_version_string = std::to_string(sdl_compiled_version.major) + "." + std::to_string(sdl_compiled_version.minor) + "." + std::to_string(sdl_compiled_version.patch);
  233. logger.log("Compiled against SDL " + sdl_compiled_version_string);
  234. // Get SDL linked version
  235. SDL_version sdl_linked_version;
  236. SDL_GetVersion(&sdl_linked_version);
  237. std::string sdl_linked_version_string = std::to_string(sdl_linked_version.major) + "." + std::to_string(sdl_linked_version.minor) + "." + std::to_string(sdl_linked_version.patch);
  238. logger.log("Linking against SDL " + sdl_linked_version_string);
  239. // Init SDL
  240. logger.push_task("Initializing SDL");
  241. if (SDL_Init(SDL_INIT_VIDEO) != 0)
  242. {
  243. logger.pop_task(EXIT_FAILURE);
  244. throw std::runtime_error("Failed to initialize SDL");
  245. }
  246. else
  247. {
  248. logger.pop_task(EXIT_SUCCESS);
  249. }
  250. // Load default OpenGL library
  251. logger.push_task("Loading OpenGL library");
  252. if (SDL_GL_LoadLibrary(nullptr) != 0)
  253. {
  254. logger.pop_task(EXIT_FAILURE);
  255. }
  256. else
  257. {
  258. logger.pop_task(EXIT_SUCCESS);
  259. }
  260. // Set window creation hints
  261. SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
  262. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
  263. SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
  264. SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
  265. SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
  266. SDL_DisplayMode sdl_display_mode;
  267. if (SDL_GetDesktopDisplayMode(0, &sdl_display_mode) != 0)
  268. {
  269. logger.error("Failed to get desktop display mode: \"" + std::string(SDL_GetError()) + "\"");
  270. }
  271. else
  272. {
  273. logger.log("Detected " + std::to_string(sdl_display_mode.w) + "x" + std::to_string(sdl_display_mode.h) + " display");
  274. display_dimensions = {sdl_display_mode.w, sdl_display_mode.h};
  275. }
  276. int window_width = sdl_display_mode.w;
  277. int window_height = sdl_display_mode.h;
  278. if (!fullscreen)
  279. {
  280. window_width = 1280;
  281. window_height = 720;
  282. }
  283. viewport = {0.0f, 0.0f, static_cast<float>(window_width), static_cast<float>(window_height)};
  284. int window_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN;
  285. if (fullscreen)
  286. {
  287. window_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
  288. }
  289. // Create window
  290. logger.push_task("Creating " + std::to_string(window_width) + "x" + std::to_string(window_height) + " window");
  291. window = SDL_CreateWindow
  292. (
  293. "Antkeeper",
  294. SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
  295. window_width, window_height,
  296. window_flags
  297. );
  298. if (!window)
  299. {
  300. logger.pop_task(EXIT_FAILURE);
  301. throw std::runtime_error("Failed to create SDL window");
  302. }
  303. else
  304. {
  305. logger.pop_task(EXIT_SUCCESS);
  306. }
  307. // Create OpenGL context
  308. logger.push_task("Creating OpenGL 3.3 context");
  309. context = SDL_GL_CreateContext(window);
  310. if (!context)
  311. {
  312. logger.pop_task(EXIT_FAILURE);
  313. throw std::runtime_error("Failed to create OpenGL context");
  314. }
  315. else
  316. {
  317. logger.pop_task(EXIT_SUCCESS);
  318. }
  319. // Load OpenGL functions via GLAD
  320. logger.push_task("Loading OpenGL functions");
  321. if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress))
  322. {
  323. logger.pop_task(EXIT_FAILURE);
  324. }
  325. else
  326. {
  327. logger.pop_task(EXIT_SUCCESS);
  328. }
  329. // Set v-sync mode
  330. int swap_interval = (vsync) ? 1 : 0;
  331. logger.push_task((swap_interval) ? "Enabling v-sync" : "Disabling v-sync");
  332. if (SDL_GL_SetSwapInterval(swap_interval) != 0)
  333. {
  334. logger.pop_task(EXIT_FAILURE);
  335. }
  336. else
  337. {
  338. logger.pop_task(EXIT_SUCCESS);
  339. }
  340. // Setup rasterizer
  341. rasterizer = new ::rasterizer();
  342. // Show window
  343. SDL_ShowWindow(window);
  344. // Clear window to black
  345. rasterizer->set_clear_color(0.0f, 0.0f, 0.0f, 1.0f);
  346. rasterizer->clear_framebuffer(true, false, false);
  347. SDL_GL_SwapWindow(window);
  348. // Hide cursor
  349. //SDL_ShowCursor(SDL_DISABLE);
  350. // Init SDL joystick and game controller subsystems
  351. logger.push_task("Initializing SDL Joystick and Game Controller subsystems");
  352. if (SDL_InitSubSystem(SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER) != 0)
  353. {
  354. logger.pop_task(EXIT_FAILURE);
  355. throw std::runtime_error("Failed to initialize SDL Joystick or Game Controller subsystems");
  356. }
  357. else
  358. {
  359. logger.pop_task(EXIT_SUCCESS);
  360. }
  361. // Load SDL game controller mappings
  362. logger.push_task("Loading SDL game controller mappings from database");
  363. std::string gamecontrollerdb_path = data_path + "controls/gamecontrollerdb.txt";
  364. if (SDL_GameControllerAddMappingsFromFile(gamecontrollerdb_path.c_str()) == -1)
  365. {
  366. logger.pop_task(EXIT_FAILURE);
  367. }
  368. else
  369. {
  370. logger.pop_task(EXIT_SUCCESS);
  371. }
  372. // Setup billboard VAO
  373. {
  374. const float billboard_vertex_data[] =
  375. {
  376. -1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f,
  377. -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  378. 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
  379. 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
  380. -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
  381. 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f
  382. };
  383. std::size_t billboard_vertex_size = 8;
  384. std::size_t billboard_vertex_stride = sizeof(float) * billboard_vertex_size;
  385. std::size_t billboard_vertex_count = 6;
  386. billboard_vbo = new vertex_buffer(sizeof(float) * billboard_vertex_size * billboard_vertex_count, billboard_vertex_data);
  387. billboard_vao = new vertex_array();
  388. billboard_vao->bind_attribute(VERTEX_POSITION_LOCATION, *billboard_vbo, 3, vertex_attribute_type::float_32, billboard_vertex_stride, 0);
  389. billboard_vao->bind_attribute(VERTEX_TEXCOORD_LOCATION, *billboard_vbo, 2, vertex_attribute_type::float_32, billboard_vertex_stride, sizeof(float) * 3);
  390. billboard_vao->bind_attribute(VERTEX_BARYCENTRIC_LOCATION, *billboard_vbo, 3, vertex_attribute_type::float_32, billboard_vertex_stride, sizeof(float) * 5);
  391. }
  392. // Setup renderer
  393. renderer.set_billboard_vao(billboard_vao);
  394. // Load fallback material
  395. fallback_material = resource_manager->load<material>("fallback.mtl");
  396. // Create shadow map depth texture and framebuffer
  397. shadow_map_resolution = 4096;
  398. shadow_map_depth_texture = new texture_2d(shadow_map_resolution, shadow_map_resolution, pixel_type::float_32, pixel_format::d);
  399. shadow_map_depth_texture->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp);
  400. shadow_map_depth_texture->set_filters(texture_min_filter::linear, texture_mag_filter::linear);
  401. shadow_map_depth_texture->set_max_anisotropy(0.0f);
  402. shadow_map_framebuffer = new framebuffer(shadow_map_resolution, shadow_map_resolution);
  403. shadow_map_framebuffer->attach(framebuffer_attachment_type::depth, shadow_map_depth_texture);
  404. // Create HDR framebuffer (32F color, 32F depth)
  405. framebuffer_hdr_color = new texture_2d(window_width, window_height, pixel_type::float_32, pixel_format::rgb);
  406. framebuffer_hdr_color->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp);
  407. framebuffer_hdr_color->set_filters(texture_min_filter::linear, texture_mag_filter::linear);
  408. framebuffer_hdr_color->set_max_anisotropy(0.0f);
  409. framebuffer_hdr_depth = new texture_2d(window_width, window_height, pixel_type::float_32, pixel_format::d);
  410. framebuffer_hdr_depth->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp);
  411. framebuffer_hdr_depth->set_filters(texture_min_filter::linear, texture_mag_filter::linear);
  412. framebuffer_hdr_depth->set_max_anisotropy(0.0f);
  413. framebuffer_hdr = new framebuffer(window_width, window_height);
  414. framebuffer_hdr->attach(framebuffer_attachment_type::color, framebuffer_hdr_color);
  415. framebuffer_hdr->attach(framebuffer_attachment_type::depth, framebuffer_hdr_depth);
  416. // Create pingpong framebuffers (32F color, no depth)
  417. int bloom_width = window_width / 2;
  418. int bloom_height = window_height / 2;
  419. bloom_texture = new texture_2d(bloom_width, bloom_height, pixel_type::float_32, pixel_format::rgb);
  420. bloom_texture->set_wrapping(texture_wrapping::clamp, texture_wrapping::clamp);
  421. bloom_texture->set_filters(texture_min_filter::linear, texture_mag_filter::linear);
  422. bloom_texture->set_max_anisotropy(0.0f);
  423. framebuffer_bloom = new framebuffer(bloom_width, bloom_height);
  424. framebuffer_bloom->attach(framebuffer_attachment_type::color, bloom_texture);
  425. // Setup overworld passes
  426. shadow_map_clear_pass = new ::clear_pass(rasterizer, shadow_map_framebuffer);
  427. shadow_map_clear_pass->set_cleared_buffers(false, true, false);
  428. shadow_map_pass = new ::shadow_map_pass(rasterizer, shadow_map_framebuffer, resource_manager);
  429. shadow_map_pass->set_split_scheme_weight(0.75f);
  430. clear_pass = new ::clear_pass(rasterizer, framebuffer_hdr);
  431. clear_pass->set_cleared_buffers(true, true, false);
  432. sky_pass = new ::sky_pass(rasterizer, framebuffer_hdr, resource_manager);
  433. sky_pass->set_enabled(false);
  434. material_pass = new ::material_pass(rasterizer, framebuffer_hdr, resource_manager);
  435. material_pass->set_fallback_material(fallback_material);
  436. material_pass->set_time_tween(&time);
  437. material_pass->set_focal_point_tween(&focal_point_tween);
  438. material_pass->shadow_map_pass = shadow_map_pass;
  439. material_pass->shadow_map = shadow_map_depth_texture;
  440. bloom_pass = new ::bloom_pass(rasterizer, framebuffer_bloom, resource_manager);
  441. bloom_pass->set_source_texture(framebuffer_hdr_color);
  442. bloom_pass->set_brightness_threshold(1.0f);
  443. bloom_pass->set_blur_iterations(4);
  444. bloom_pass->set_enabled(false);
  445. final_pass = new ::final_pass(rasterizer, &rasterizer->get_default_framebuffer(), resource_manager);
  446. final_pass->set_color_texture(framebuffer_hdr_color);
  447. final_pass->set_bloom_texture(bloom_texture);
  448. // Setup overworld compositor
  449. overworld_compositor.add_pass(shadow_map_clear_pass);
  450. overworld_compositor.add_pass(shadow_map_pass);
  451. overworld_compositor.add_pass(clear_pass);
  452. overworld_compositor.add_pass(sky_pass);
  453. overworld_compositor.add_pass(material_pass);
  454. overworld_compositor.add_pass(bloom_pass);
  455. overworld_compositor.add_pass(final_pass);
  456. // Setup overworld camera
  457. overworld_camera.set_perspective(45.0f * vmq::pi<float> / 180.0f, (float)window_width / (float)window_height, 0.1f, 1000.0f);
  458. overworld_camera.set_compositor(&overworld_compositor);
  459. overworld_camera.set_composite_index(0);
  460. overworld_camera.set_active(true);
  461. // Setup underworld passes
  462. underworld_clear_pass = new ::clear_pass(rasterizer, framebuffer_hdr);
  463. underworld_clear_pass->set_cleared_buffers(true, true, false);
  464. underworld_material_pass = new ::material_pass(rasterizer, framebuffer_hdr, resource_manager);
  465. underworld_material_pass->set_fallback_material(fallback_material);
  466. underworld_material_pass->set_time_tween(&time);
  467. underworld_material_pass->set_focal_point_tween(&focal_point_tween);
  468. shader_program* underworld_final_shader = resource_manager->load<shader_program>("underground-final.glsl");
  469. underworld_final_pass = new simple_render_pass(rasterizer, &rasterizer->get_default_framebuffer(), underworld_final_shader);
  470. underworld_final_pass->set_time_tween(&time);
  471. underground_color_texture_property = underworld_final_pass->get_material()->add_property<const texture_2d*>("color_texture");
  472. underground_color_texture_property->set_value(framebuffer_hdr_color);
  473. underworld_final_pass->get_material()->update_tweens();
  474. // Setup underworld compositor
  475. underworld_compositor.add_pass(underworld_clear_pass);
  476. underworld_compositor.add_pass(underworld_material_pass);
  477. underworld_compositor.add_pass(underworld_final_pass);
  478. // Setup underworld camera
  479. underworld_camera.set_perspective(45.0f * vmq::pi<float> / 180.0f, (float)window_width / (float)window_height, 0.1f, 1000.0f);
  480. underworld_camera.look_at({0, 50, 0}, {0, 0, 0}, {0, 0, -1});
  481. underworld_camera.set_compositor(&underworld_compositor);
  482. underworld_camera.set_composite_index(0);
  483. underworld_camera.set_active(false);
  484. // Setup timeline system
  485. timeline.set_autoremove(true);
  486. // Setup animation system
  487. animator = new ::animator();
  488. // ECS
  489. terrain_system = new ::terrain_system(ecs_registry, resource_manager);
  490. terrain_system->set_patch_size(TERRAIN_PATCH_SIZE);
  491. vegetation_system = new ::vegetation_system(ecs_registry);
  492. vegetation_system->set_terrain_patch_size(TERRAIN_PATCH_SIZE);
  493. vegetation_system->set_vegetation_patch_resolution(VEGETATION_PATCH_RESOLUTION);
  494. vegetation_system->set_vegetation_density(1.0f);
  495. vegetation_system->set_vegetation_model(resource_manager->load<model>("grass-tuft.obj"));
  496. vegetation_system->set_scene(&overworld_scene);
  497. tool_system = new ::tool_system(ecs_registry);
  498. tool_system->set_camera(&overworld_camera);
  499. tool_system->set_orbit_cam(&orbit_cam);
  500. tool_system->set_viewport(viewport);
  501. camera_system = new ::camera_system(ecs_registry);
  502. camera_system->set_orbit_cam(&orbit_cam);
  503. camera_system->set_viewport(viewport);
  504. subterrain_system = new ::subterrain_system(ecs_registry, resource_manager);
  505. subterrain_system->set_scene(&underworld_scene);
  506. nest_system = new ::nest_system(ecs_registry, resource_manager);
  507. collision_system = new ::collision_system(ecs_registry);
  508. samara_system = new ::samara_system(ecs_registry);
  509. placement_system = new ::placement_system(ecs_registry);
  510. behavior_system = new ::behavior_system(ecs_registry);
  511. locomotion_system = new ::locomotion_system(ecs_registry);
  512. constraint_system = new ::constraint_system(ecs_registry);
  513. render_system = new ::render_system(ecs_registry);
  514. render_system->add_layer(&overworld_scene);
  515. render_system->add_layer(&underworld_scene);
  516. // Setup systems
  517. systems.push_back([this](double t, double dt){ this->overworld_scene.update_tweens(); this->underworld_scene.update_tweens(); this->ui_system->get_scene()->update_tweens(); focal_point_tween.update(); this->underworld_final_pass->get_material()->update_tweens(); });
  518. systems.push_back([this](double t, double dt){ this->translate_sdl_events(); });
  519. systems.push_back([this](double t, double dt){ this->event_dispatcher.update(t); });
  520. systems.push_back([this](double t, double dt){ this->timeline.advance(dt); });
  521. systems.push_back(std::bind(&terrain_system::update, terrain_system, std::placeholders::_1, std::placeholders::_2));
  522. systems.push_back(std::bind(&vegetation_system::update, vegetation_system, std::placeholders::_1, std::placeholders::_2));
  523. systems.push_back(std::bind(&placement_system::update, placement_system, std::placeholders::_1, std::placeholders::_2));
  524. systems.push_back(std::bind(&nest_system::update, nest_system, std::placeholders::_1, std::placeholders::_2));
  525. systems.push_back(std::bind(&subterrain_system::update, subterrain_system, std::placeholders::_1, std::placeholders::_2));
  526. systems.push_back(std::bind(&collision_system::update, collision_system, std::placeholders::_1, std::placeholders::_2));
  527. systems.push_back(std::bind(&samara_system::update, samara_system, std::placeholders::_1, std::placeholders::_2));
  528. systems.push_back(std::bind(&camera_system::update, camera_system, std::placeholders::_1, std::placeholders::_2));
  529. systems.push_back(std::bind(&behavior_system::update, behavior_system, std::placeholders::_1, std::placeholders::_2));
  530. systems.push_back(std::bind(&locomotion_system::update, locomotion_system, std::placeholders::_1, std::placeholders::_2));
  531. systems.push_back([this](double t, double dt){ this->control_system->update(dt); });
  532. systems.push_back(std::bind(&tool_system::update, tool_system, std::placeholders::_1, std::placeholders::_2));
  533. systems.push_back(std::bind(&constraint_system::update, constraint_system, std::placeholders::_1, std::placeholders::_2));
  534. systems.push_back([this](double t, double dt){
  535. this->subterrain_light.set_translation(orbit_cam.get_focal_point());
  536. this->lantern.set_translation(orbit_cam.get_focal_point());
  537. this->spotlight.set_transform(overworld_camera.get_transform());
  538. this->focal_point_tween[1] = orbit_cam.get_focal_point();
  539. });
  540. systems.push_back([this](double t, double dt){ this->ui_system->update(dt); });
  541. systems.push_back(std::bind(&render_system::update, render_system, std::placeholders::_1, std::placeholders::_2));
  542. systems.push_back([this](double t, double dt){ this->animator->animate(dt); });
  543. systems.push_back([this](double t, double dt){ this->application_controls.update(); this->menu_controls.update(); this->camera_controls->update(); });
  544. // Setup frame timing
  545. frame_scheduler.set_update_callback(std::bind(&application::update, this, std::placeholders::_1, std::placeholders::_2));
  546. frame_scheduler.set_render_callback(std::bind(&application::render, this, std::placeholders::_1));
  547. frame_scheduler.set_update_rate(60.0);
  548. frame_scheduler.set_max_frame_duration(0.25);
  549. // Setup performance sampling
  550. performance_sampler.set_sample_size(15);
  551. // Setup input event routing
  552. input_event_router.set_event_dispatcher(&event_dispatcher);
  553. input_mapper.set_event_dispatcher(&event_dispatcher);
  554. // Setup input devices
  555. keyboard.set_event_dispatcher(&event_dispatcher);
  556. mouse.set_event_dispatcher(&event_dispatcher);
  557. game_controller.set_event_dispatcher(&event_dispatcher);
  558. // Setup controls
  559. application_controls.add_control(&toggle_fullscreen_control);
  560. application_controls.add_control(&dig_control);
  561. application_controls.add_control(&screenshot_control);
  562. toggle_fullscreen_control.set_activated_callback(std::bind(&application::toggle_fullscreen, this));
  563. screenshot_control.set_activated_callback([this]()
  564. {
  565. take_screenshot();
  566. });
  567. menu_back_control.set_activated_callback(std::bind(&application::close, this, 0));
  568. menu_controls.add_control(&menu_back_control);
  569. menu_controls.add_control(&menu_select_control);
  570. orbit_cam.attach(&overworld_camera);
  571. control_system = new ::control_system();
  572. control_system->set_orbit_cam(&orbit_cam);
  573. control_system->set_viewport(viewport);
  574. control_system->set_underworld_camera(&underworld_camera);
  575. event_dispatcher.subscribe<mouse_moved_event>(control_system);
  576. event_dispatcher.subscribe<mouse_moved_event>(camera_system);
  577. event_dispatcher.subscribe<mouse_moved_event>(tool_system);
  578. camera_controls = control_system->get_control_set();
  579. // Application control mappings
  580. input_event_router.add_mapping(key_mapping(&toggle_fullscreen_control, nullptr, scancode::f11));
  581. input_event_router.add_mapping(key_mapping(&screenshot_control, nullptr, scancode::f12));
  582. // Add menu control mappings
  583. input_event_router.add_mapping(key_mapping(&menu_back_control, nullptr, scancode::escape));
  584. input_event_router.add_mapping(key_mapping(&menu_back_control, nullptr, scancode::backspace));
  585. input_event_router.add_mapping(game_controller_button_mapping(&menu_back_control, nullptr, game_controller_button::b));
  586. input_event_router.add_mapping(key_mapping(control_system->get_tool_menu_control(), nullptr, scancode::left_shift));
  587. input_event_router.add_mapping(game_controller_button_mapping(control_system->get_tool_menu_control(), nullptr, game_controller_button::x));
  588. input_event_router.add_mapping(key_mapping(&menu_select_control, nullptr, scancode::enter));
  589. input_event_router.add_mapping(key_mapping(&menu_select_control, nullptr, scancode::space));
  590. input_event_router.add_mapping(key_mapping(control_system->get_move_forward_control(), nullptr, scancode::w));
  591. input_event_router.add_mapping(key_mapping(control_system->get_toggle_view_control(), nullptr, scancode::tab));
  592. control_system->get_toggle_view_control()->set_activated_callback(
  593. [this]()
  594. {
  595. if (this->active_scene == &this->overworld_scene)
  596. {
  597. this->active_scene = &this->underworld_scene;
  598. this->radial_transition_inner->transition(0.5f, false, ease<float, double>::in_quad);
  599. auto switch_cameras = [this]()
  600. {
  601. this->overworld_camera.set_active(false);
  602. this->underworld_camera.set_active(true);
  603. this->fade_transition->transition(0.25f, true, ease<float, double>::out_quad);
  604. };
  605. float t = timeline.get_position();
  606. this->timeline.add_cue({t + 0.5f, switch_cameras});
  607. }
  608. else
  609. {
  610. this->active_scene = &this->overworld_scene;
  611. this->fade_transition->transition(0.25f, false, ease<float, double>::out_quad);
  612. auto switch_cameras = [this]()
  613. {
  614. this->overworld_camera.set_active(true);
  615. this->underworld_camera.set_active(false);
  616. this->radial_transition_inner->transition(0.5f, true, ease<float, double>::out_quad);
  617. };
  618. float t = timeline.get_position();
  619. this->timeline.add_cue({t + 0.25f, switch_cameras});
  620. }
  621. });
  622. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_forward_control(), nullptr, game_controller_axis::left_y, true));
  623. input_event_router.add_mapping(key_mapping(control_system->get_move_back_control(), nullptr, scancode::s));
  624. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_back_control(), nullptr, game_controller_axis::left_y, false));
  625. input_event_router.add_mapping(key_mapping(control_system->get_move_left_control(), nullptr, scancode::a));
  626. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_left_control(), nullptr, game_controller_axis::left_x, true));
  627. input_event_router.add_mapping(key_mapping(control_system->get_move_right_control(), nullptr, scancode::d));
  628. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_move_right_control(), nullptr, game_controller_axis::left_x, false));
  629. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_rotate_ccw_control(), nullptr, game_controller_axis::right_x, false));
  630. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_rotate_cw_control(), nullptr, game_controller_axis::right_x, true));
  631. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_tilt_up_control(), nullptr, game_controller_axis::right_y, false));
  632. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_tilt_down_control(), nullptr, game_controller_axis::right_y, true));
  633. input_event_router.add_mapping(mouse_wheel_mapping(control_system->get_zoom_in_control(), nullptr, mouse_wheel_axis::positive_y));
  634. input_event_router.add_mapping(mouse_wheel_mapping(control_system->get_zoom_out_control(), nullptr, mouse_wheel_axis::negative_y));
  635. input_event_router.add_mapping(mouse_button_mapping(control_system->get_adjust_camera_control(), nullptr, 3));
  636. input_event_router.add_mapping(game_controller_button_mapping(control_system->get_ascend_control(), nullptr, game_controller_button::y));
  637. input_event_router.add_mapping(game_controller_button_mapping(control_system->get_descend_control(), nullptr, game_controller_button::a));
  638. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_zoom_out_control(), nullptr, game_controller_axis::trigger_left, false));
  639. input_event_router.add_mapping(game_controller_axis_mapping(control_system->get_zoom_in_control(), nullptr, game_controller_axis::trigger_right, false));
  640. control_system->get_adjust_camera_control()->set_activated_callback([this](){ this->set_relative_mouse_mode(true); this->tool_system->set_pick(false); });
  641. control_system->get_adjust_camera_control()->set_deactivated_callback([this](){ this->set_relative_mouse_mode(false); this->tool_system->set_pick(true); });
  642. input_event_router.add_mapping(key_mapping(&dig_control, nullptr, scancode::one));
  643. dig_control.set_activated_callback(
  644. [this]()
  645. {
  646. const float r = 25.0f;
  647. ecs::cavity_component cavity;
  648. cavity.position =
  649. {
  650. frand(-r, r),
  651. frand(-r * 2, r),
  652. frand(-r, r)
  653. };
  654. cavity.radius = frand(0.75f, 1.25f);
  655. ecs_registry.assign<ecs::cavity_component>(ecs_registry.create(), cavity);
  656. });
  657. pheromones.rows = 256;
  658. pheromones.columns = 256;
  659. pheromones.buffers = new float*[2];
  660. pheromones.buffers[0] = new float[pheromones.rows * pheromones.columns];
  661. pheromones.buffers[1] = new float[pheromones.rows * pheromones.columns];
  662. pheromones.current = 0;
  663. //diffuse(&pheromones);
  664. control_system->set_tool(nullptr);
  665. // Setup UI
  666. ui_system = new ::ui_system(resource_manager);
  667. ui_system->set_viewport(viewport);
  668. ui_system->set_tool_menu_control(control_system->get_tool_menu_control());
  669. event_dispatcher.subscribe<mouse_moved_event>(ui_system);
  670. // Setup UI camera compositor
  671. ui_clear_pass = new ::clear_pass(rasterizer, &rasterizer->get_default_framebuffer());
  672. ui_clear_pass->set_cleared_buffers(false, true, false);
  673. ui_material_pass = new ::material_pass(rasterizer, &rasterizer->get_default_framebuffer(), resource_manager);
  674. ui_material_pass->set_fallback_material(fallback_material);
  675. ui_material_pass->set_time_tween(&time);
  676. ui_compositor.add_pass(ui_clear_pass);
  677. ui_compositor.add_pass(ui_material_pass);
  678. ui_system->get_camera()->set_compositor(&ui_compositor);
  679. // Setup lights
  680. sun_indirect.set_intensity(0.25f);
  681. sun_indirect.update_tweens();
  682. sun_direct.look_at({-1.0f, 5.0f, 1.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 1.0f, 0.0f});
  683. sun_direct.set_intensity(1.0f);
  684. sun_direct.update_tweens();
  685. subterrain_light.set_color({1, 1, 1});
  686. subterrain_light.set_intensity(1.0f);
  687. subterrain_light.set_attenuation({1.0f, 0.09f, 0.032f});
  688. subterrain_light.update_tweens();
  689. spotlight.set_color({1, 1, 1});
  690. spotlight.set_intensity(1.0f);
  691. spotlight.set_attenuation({1.0f, 0.09f, 0.032f});
  692. spotlight.set_cutoff({vmq::radians(15.0f), vmq::radians(30.0f)});
  693. spotlight.update_tweens();
  694. spotlight.set_active(false);
  695. underworld_ambient_light.set_color({1, 1, 1});
  696. underworld_ambient_light.set_intensity(0.1f);
  697. underworld_ambient_light.update_tweens();
  698. lantern.set_model(resource_manager->load<model>("lantern.obj"));
  699. // Cloud
  700. cloud.set_model(resource_manager->load<model>("cloud.obj"));
  701. cloud.set_translation({0, 0, 4500});
  702. cloud.set_scale(float3{1, 1, 1} * 100.0f);
  703. // Create depth debug billboard
  704. /*
  705. material* depth_debug_material = new material();
  706. depth_debug_material->set_shader_program(resource_manager->load<shader_program>("ui-element-textured.glsl"));
  707. depth_debug_material->add_property<const texture_2d*>("background")->set_value(shadow_map_depth_texture);
  708. depth_debug_material->add_property<float4>("tint")->set_value(float4{1, 1, 1, 1});
  709. billboard* depth_debug_billboard = new billboard();
  710. depth_debug_billboard->set_material(depth_debug_material);
  711. depth_debug_billboard->set_scale({128, 128, 1});
  712. depth_debug_billboard->set_translation({-960 + 128, 1080 * 0.5f - 128, 0});
  713. depth_debug_billboard->update_tweens();
  714. ui_system->get_scene()->add_object(depth_debug_billboard);
  715. */
  716. material* billboard_material = new material();
  717. billboard_material->set_shader_program(resource_manager->load<shader_program>("ui-element-textured.glsl"));
  718. billboard_material->add_property<const texture_2d*>("background")->set_value(resource_manager->load<texture_2d>("arrow.png"));
  719. billboard_material->add_property<float4>("tint")->set_value(float4{1, 1, 1, 1});
  720. billboard_material->set_flags(MATERIAL_FLAG_TRANSLUCENT | MATERIAL_FLAG_NOT_SHADOW_CASTER);
  721. billboard* arrow_billboard = new billboard();
  722. arrow_billboard->set_material(billboard_material);
  723. arrow_billboard->set_scale(float3{1, 1, 1} * 2.0f);
  724. arrow_billboard->set_translation({0, 10, 0});
  725. arrow_billboard->set_billboard_type(billboard_type::cylindrical);
  726. arrow_billboard->set_alignment_axis({0, 1, 0});
  727. arrow_billboard->update_tweens();
  728. billboard_material = new material();
  729. billboard_material->set_shader_program(resource_manager->load<shader_program>("portal-card.glsl"));
  730. billboard_material->add_property<float4>("color")->set_value(float4{1, 1, 1, 1});
  731. billboard_material->add_property<float2>("range")->set_value(float2{50.0f, 500.0f});
  732. billboard_material->set_flags(MATERIAL_FLAG_TRANSLUCENT | MATERIAL_FLAG_NOT_SHADOW_CASTER);
  733. billboard* portal_billboard = new billboard();
  734. portal_billboard->set_material(billboard_material);
  735. portal_billboard->set_scale(float3{1, 1, 1} * 10.0f);
  736. portal_billboard->set_translation({0.0f, 0, 0});
  737. portal_billboard->set_billboard_type(billboard_type::spherical);
  738. portal_billboard->set_alignment_axis({0, 1, 0});
  739. portal_billboard->update_tweens();
  740. // Setup overworld scene
  741. overworld_scene.add_object(&overworld_camera);
  742. overworld_scene.add_object(&sun_indirect);
  743. overworld_scene.add_object(&sun_direct);
  744. overworld_scene.add_object(&spotlight);
  745. overworld_scene.add_object(&cloud);
  746. overworld_scene.add_object(arrow_billboard);
  747. // Setup underworld scene
  748. underworld_scene.add_object(&underworld_camera);
  749. underworld_scene.add_object(&underworld_ambient_light);
  750. //underworld_scene.add_object(&lantern);
  751. //underworld_scene.add_object(&subterrain_light);
  752. //underworld_scene.add_object(portal_billboard);
  753. //model_instance* larva = new model_instance(resource_manager->load<model>("larva.obj"));
  754. //underworld_scene.add_object(larva);
  755. quaternion<float> flashlight_rotation = vmq::angle_axis(vmq::half_pi<float>, {0.0f, 1.0f, 0.0f});
  756. model_instance* flashlight = new model_instance(resource_manager->load<model>("flashlight.obj"));
  757. flashlight->set_rotation(flashlight_rotation);
  758. underworld_scene.add_object(flashlight);
  759. model_instance* flashlight_light_cone = new model_instance(resource_manager->load<model>("flashlight-light-cone.obj"));
  760. flashlight_light_cone->set_rotation(flashlight_rotation);
  761. underworld_scene.add_object(flashlight_light_cone);
  762. control_system->set_flashlight(flashlight, flashlight_light_cone);
  763. // Set overworld as active scene
  764. active_scene = &overworld_scene;
  765. // Setup UI
  766. const texture_2d* splash_texture = resource_manager->load<texture_2d>("splash.png");
  767. auto splash_dimensions = splash_texture->get_dimensions();
  768. splash_billboard_material = new material();
  769. splash_billboard_material->set_shader_program(resource_manager->load<shader_program>("ui-element-textured.glsl"));
  770. splash_billboard_material->add_property<const texture_2d*>("background")->set_value(splash_texture);
  771. splash_billboard_material->add_property<float4>("tint")->set_value(float4{1, 1, 1, 1});
  772. splash_billboard_material->update_tweens();
  773. splash_billboard = new billboard();
  774. splash_billboard->set_material(splash_billboard_material);
  775. splash_billboard->set_scale({(float)std::get<0>(splash_dimensions) * 0.5f, (float)std::get<1>(splash_dimensions) * 0.5f, 1.0f});
  776. splash_billboard->set_translation({0.0f, 0.0f, 0.0f});
  777. splash_billboard->update_tweens();
  778. // Create fade transition
  779. fade_transition = new screen_transition();
  780. fade_transition->get_material()->set_shader_program(resource_manager->load<shader_program>("fade-transition.glsl"));
  781. get_ui_scene()->add_object(fade_transition->get_billboard());
  782. animator->add_animation(fade_transition->get_animation());
  783. // Create inner radial transition
  784. radial_transition_inner = new screen_transition();
  785. radial_transition_inner->get_material()->set_shader_program(resource_manager->load<shader_program>("radial-transition-inner.glsl"));
  786. get_ui_scene()->add_object(radial_transition_inner->get_billboard());
  787. animator->add_animation(radial_transition_inner->get_animation());
  788. // Create outer radial transition
  789. radial_transition_outer = new screen_transition();
  790. radial_transition_outer->get_material()->set_shader_program(resource_manager->load<shader_program>("radial-transition-outer.glsl"));
  791. get_ui_scene()->add_object(radial_transition_outer->get_billboard());
  792. animator->add_animation(radial_transition_outer->get_animation());
  793. // Setup tweens
  794. focal_point_tween.set_interpolator(ease<float3>::linear);
  795. // Register CLI commands
  796. cli.register_command("echo", cc::echo);
  797. cli.register_command("exit", std::function<std::string()>(std::bind(&cc::exit, this)));
  798. cli.register_command("scrot", std::function<std::string()>(std::bind(&cc::scrot, this)));
  799. cli.register_command("cue", std::function<std::string(float, std::string)>(std::bind(&cc::cue, this, std::placeholders::_1, std::placeholders::_2)));
  800. //std::string cmd = "cue 20 exit";
  801. //logger.log(cmd);
  802. //logger.log(cli.interpret(cmd));
  803. }
  804. application::~application()
  805. {
  806. // Deinit PhysicsFS
  807. logger.push_task("Deinitializing PhysicsFS");
  808. if (!PHYSFS_deinit())
  809. {
  810. logger.error(std::string("PhysicsFS error: ") + PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode()));
  811. logger.pop_task(EXIT_FAILURE);
  812. }
  813. else
  814. {
  815. logger.pop_task(EXIT_SUCCESS);
  816. }
  817. // Destroy the SDL window
  818. SDL_DestroyWindow(window);
  819. // Shutdown SDL
  820. SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMECONTROLLER);
  821. SDL_Quit();
  822. }
  823. void application::close(int status)
  824. {
  825. closed = true;
  826. exit_status = status;
  827. }
  828. int application::execute()
  829. {
  830. // Enter inital state
  831. state_machine.change_state(*initial_state);
  832. // Perform initial update
  833. update(0.0, 0.0);
  834. // Reset frame scheduler
  835. frame_scheduler.reset();
  836. // Setup time tween
  837. time[0] = time[1] = 0.0;
  838. time.set_interpolator(ease<double>::linear);
  839. // Schedule frames until closed
  840. while (!closed)
  841. {
  842. // Tick frame scheduler
  843. frame_scheduler.tick();
  844. // Sample frame duration
  845. performance_sampler.sample(frame_scheduler.get_frame_duration());
  846. }
  847. // Exit current state
  848. state_machine.change_state({nullptr, nullptr});
  849. return exit_status;
  850. }
  851. void application::setup_fsm()
  852. {
  853. loading_state =
  854. {
  855. std::function<void()>(std::bind(enter_loading_state, this)),
  856. std::function<void()>(std::bind(exit_loading_state, this))
  857. };
  858. language_select_state =
  859. {
  860. std::function<void()>(std::bind(enter_language_select_state, this)),
  861. std::function<void()>(std::bind(exit_language_select_state, this))
  862. };
  863. splash_state =
  864. {
  865. std::function<void()>(std::bind(enter_splash_state, this)),
  866. std::function<void()>(std::bind(exit_splash_state, this))
  867. };
  868. title_state =
  869. {
  870. std::function<void()>(std::bind(enter_title_state, this)),
  871. std::function<void()>(std::bind(exit_title_state, this))
  872. };
  873. play_state =
  874. {
  875. std::function<void()>(std::bind(enter_play_state, this)),
  876. std::function<void()>(std::bind(exit_play_state, this))
  877. };
  878. pause_state =
  879. {
  880. std::function<void()>(std::bind(enter_pause_state, this)),
  881. std::function<void()>(std::bind(exit_pause_state, this))
  882. };
  883. initial_state = &splash_state;
  884. }
  885. void application::parse_options(int argc, char** argv)
  886. {
  887. cxxopts::Options options("Antkeeper", "Ant colony simulation game");
  888. options.add_options()
  889. ("q,quick-start", "Skips splash screen")
  890. ("c,continue", "Continues from last save")
  891. ("n,new-game", "Starts a new game")
  892. ("r,reset", "Restores all settings to default")
  893. ("f,fullscreen", "Starts in fullscreen mode")
  894. ("w,windowed", "Starts in windowed mode")
  895. ("v,vsync", "Enables or disables v-sync", cxxopts::value<int>())
  896. ("d,data", "Specifies the data package path", cxxopts::value<std::string>())
  897. ;
  898. logger.push_task("Parsing command line options");
  899. try
  900. {
  901. auto result = options.parse(argc, argv);
  902. // --quick-start
  903. if (result.count("quick-start"))
  904. {
  905. logger.log("Skipping splash screen");
  906. initial_state = &play_state;
  907. }
  908. // --continue
  909. if (result.count("continue"))
  910. {
  911. logger.log("Continuing from last save");
  912. }
  913. // --new-game
  914. if (result.count("new-game"))
  915. {
  916. logger.log("Starting a new game");
  917. }
  918. // --reset
  919. if (result.count("reset"))
  920. {
  921. logger.log("Restoring all settings to default");
  922. }
  923. // --fullscreen
  924. if (result.count("fullscreen"))
  925. {
  926. logger.log("Starting in fullscreen mode");
  927. fullscreen = true;
  928. }
  929. // --windowed
  930. if (result.count("windowed"))
  931. {
  932. logger.log("Starting in windowed mode");
  933. fullscreen = false;
  934. }
  935. // --vsync
  936. if (result.count("vsync"))
  937. {
  938. if (result["vsync"].as<int>())
  939. {
  940. logger.log("Turning on v-sync");
  941. vsync = true;
  942. }
  943. else
  944. {
  945. logger.log("Turning off v-sync");
  946. vsync = false;
  947. }
  948. }
  949. // --data
  950. if (result.count("data"))
  951. {
  952. data_package_path = result["data"].as<std::string>();
  953. if (std::filesystem::path(data_package_path).is_relative())
  954. {
  955. data_package_path = data_path + data_package_path;
  956. }
  957. logger.log("Set alternative data package path \"" + data_package_path + "\"");
  958. }
  959. }
  960. catch (const std::exception& e)
  961. {
  962. logger.error("Exception caught: \"" + std::string(e.what()) + "\"");
  963. logger.pop_task(EXIT_FAILURE);
  964. return;
  965. }
  966. logger.pop_task(EXIT_SUCCESS);
  967. }
  968. void application::update(double t, double dt)
  969. {
  970. // Update time tween
  971. time.update();
  972. time[1] = t;
  973. // Sequentially process systems
  974. for (const auto& system: systems)
  975. {
  976. system(t, dt);
  977. }
  978. }
  979. void application::render(double alpha)
  980. {
  981. /*
  982. std::cout << std::fixed;
  983. std::cout << std::setprecision(2);
  984. std::cout << performance_sampler.mean_frame_duration() * 1000.0 << std::endl;
  985. */
  986. renderer.render(alpha, overworld_scene);
  987. renderer.render(alpha, underworld_scene);
  988. //renderer.render(alpha, *active_scene);
  989. renderer.render(alpha, *ui_system->get_scene());
  990. SDL_GL_SwapWindow(window);
  991. }
  992. void application::translate_sdl_events()
  993. {
  994. SDL_Event sdl_event;
  995. while (SDL_PollEvent(&sdl_event))
  996. {
  997. switch (sdl_event.type)
  998. {
  999. case SDL_KEYDOWN:
  1000. case SDL_KEYUP:
  1001. {
  1002. if (sdl_event.key.repeat == 0)
  1003. {
  1004. scancode scancode = scancode::unknown;
  1005. if (sdl_event.key.keysym.scancode <= SDL_SCANCODE_APP2)
  1006. {
  1007. scancode = sdl_scancode_table[sdl_event.key.keysym.scancode];
  1008. }
  1009. if (sdl_event.type == SDL_KEYDOWN)
  1010. keyboard.press(scancode);
  1011. else
  1012. keyboard.release(scancode);
  1013. }
  1014. break;
  1015. }
  1016. case SDL_MOUSEMOTION:
  1017. {
  1018. mouse.move(sdl_event.motion.x, sdl_event.motion.y, sdl_event.motion.xrel, sdl_event.motion.yrel);
  1019. break;
  1020. }
  1021. case SDL_MOUSEBUTTONDOWN:
  1022. {
  1023. mouse.press(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y);
  1024. break;
  1025. }
  1026. case SDL_MOUSEBUTTONUP:
  1027. {
  1028. mouse.release(sdl_event.button.button, sdl_event.button.x, sdl_event.button.y);
  1029. break;
  1030. }
  1031. case SDL_MOUSEWHEEL:
  1032. {
  1033. int direction = (sdl_event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) ? -1 : 1;
  1034. mouse.scroll(sdl_event.wheel.x * direction, sdl_event.wheel.y * direction);
  1035. break;
  1036. }
  1037. case SDL_CONTROLLERBUTTONDOWN:
  1038. {
  1039. if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  1040. {
  1041. game_controller_button button = sdl_button_table[sdl_event.cbutton.button];
  1042. game_controller.press(button);
  1043. }
  1044. break;
  1045. }
  1046. case SDL_CONTROLLERBUTTONUP:
  1047. {
  1048. if (sdl_event.cbutton.button != SDL_CONTROLLER_BUTTON_INVALID)
  1049. {
  1050. game_controller_button button = sdl_button_table[sdl_event.cbutton.button];
  1051. game_controller.release(button);
  1052. }
  1053. break;
  1054. }
  1055. case SDL_CONTROLLERAXISMOTION:
  1056. {
  1057. if (sdl_event.caxis.axis != SDL_CONTROLLER_AXIS_INVALID)
  1058. {
  1059. game_controller_axis axis = sdl_axis_table[sdl_event.caxis.axis];
  1060. float value = sdl_event.caxis.value;
  1061. value /= (value < 0.0f) ? 32768.0f : 32767.0f;
  1062. game_controller.move(axis, value);
  1063. }
  1064. break;
  1065. }
  1066. case SDL_CONTROLLERDEVICEADDED:
  1067. {
  1068. if (SDL_IsGameController(sdl_event.cdevice.which))
  1069. {
  1070. SDL_GameController* sdl_controller = SDL_GameControllerOpen(sdl_event.cdevice.which);
  1071. std::string controller_name = SDL_GameControllerNameForIndex(sdl_event.cdevice.which);
  1072. if (sdl_controller)
  1073. {
  1074. logger.log("Connected game controller \"" + controller_name + "\"");
  1075. }
  1076. else
  1077. {
  1078. logger.error("Failed to connected game controller \"" + controller_name + "\"");
  1079. }
  1080. }
  1081. break;
  1082. }
  1083. case SDL_CONTROLLERDEVICEREMOVED:
  1084. {
  1085. SDL_GameController* sdl_controller = SDL_GameControllerFromInstanceID(sdl_event.cdevice.which);
  1086. if (sdl_controller)
  1087. {
  1088. SDL_GameControllerClose(sdl_controller);
  1089. logger.log("Disconnected game controller");
  1090. }
  1091. break;
  1092. }
  1093. case SDL_WINDOWEVENT:
  1094. {
  1095. if (sdl_event.window.event == SDL_WINDOWEVENT_RESIZED)
  1096. {
  1097. window_resized();
  1098. }
  1099. break;
  1100. }
  1101. case SDL_QUIT:
  1102. {
  1103. close(EXIT_SUCCESS);
  1104. break;
  1105. }
  1106. }
  1107. }
  1108. }
  1109. void application::set_relative_mouse_mode(bool enabled)
  1110. {
  1111. if (enabled)
  1112. {
  1113. SDL_GetMouseState(&std::get<0>(saved_mouse_position), &std::get<1>(saved_mouse_position));
  1114. }
  1115. SDL_SetRelativeMouseMode((enabled) ? SDL_TRUE : SDL_FALSE);
  1116. if (!enabled)
  1117. {
  1118. SDL_WarpMouseInWindow(window, std::get<0>(saved_mouse_position), std::get<1>(saved_mouse_position));
  1119. }
  1120. }
  1121. void application::toggle_fullscreen()
  1122. {
  1123. fullscreen = !fullscreen;
  1124. if (fullscreen)
  1125. {
  1126. SDL_GetWindowSize(window, &std::get<0>(window_dimensions), &std::get<1>(window_dimensions));
  1127. SDL_GetWindowPosition(window, &std::get<0>(window_position), &std::get<1>(window_position));
  1128. SDL_SetWindowBordered(window, SDL_FALSE);
  1129. SDL_SetWindowResizable(window, SDL_FALSE);
  1130. SDL_SetWindowPosition(window, 0, 0);
  1131. SDL_SetWindowSize(window, std::get<0>(display_dimensions), std::get<1>(display_dimensions));
  1132. }
  1133. else
  1134. {
  1135. SDL_SetWindowBordered(window, SDL_TRUE);
  1136. SDL_SetWindowResizable(window, SDL_TRUE);
  1137. SDL_SetWindowSize(window, std::get<0>(window_dimensions), std::get<1>(window_dimensions));
  1138. SDL_SetWindowPosition(window, std::get<0>(window_position), std::get<1>(window_position));
  1139. }
  1140. window_resized();
  1141. }
  1142. void application::window_resized()
  1143. {
  1144. int width, height;
  1145. SDL_GetWindowSize(window, &width, &height);
  1146. float aspect_ratio = static_cast<float>(width) / static_cast<float>(height);
  1147. viewport = {0.0f, 0.0f, static_cast<float>(width), static_cast<float>(height)};
  1148. rasterizer->window_resized(width, height);
  1149. overworld_camera.set_perspective(overworld_camera.get_fov(), aspect_ratio, overworld_camera.get_clip_near(), overworld_camera.get_clip_far());
  1150. underworld_camera.set_perspective(underworld_camera.get_fov(), aspect_ratio, underworld_camera.get_clip_near(), underworld_camera.get_clip_far());
  1151. control_system->set_viewport(viewport);
  1152. camera_system->set_viewport(viewport);
  1153. tool_system->set_viewport(viewport);
  1154. ui_system->set_viewport(viewport);
  1155. }
  1156. void application::take_screenshot()
  1157. {
  1158. std::string filename = screenshots_path + "antkeeper-" + timestamp() + ".png";
  1159. logger.push_task("Saving screenshot to \"" + filename + "\"");
  1160. int x = viewport[0];
  1161. int y = viewport[1];
  1162. int w = viewport[2];
  1163. int h = viewport[3];
  1164. // Read pixel data from framebuffer
  1165. unsigned char* pixels = new unsigned char[w * h * 3];
  1166. glReadBuffer(GL_BACK);
  1167. glReadPixels(x, y, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels);
  1168. std::thread screenshot_thread(application::save_image, filename, w, h, pixels);
  1169. screenshot_thread.detach();
  1170. logger.pop_task(EXIT_SUCCESS);
  1171. }
  1172. void application::save_image(const std::string& filename, int w, int h, const unsigned char* pixels)
  1173. {
  1174. stbi_flip_vertically_on_write(1);
  1175. stbi_write_png(filename.c_str(), w, h, 3, pixels, w * 3);
  1176. delete[] pixels;
  1177. }
  1178. scene* application::get_ui_scene()
  1179. {
  1180. return ui_system->get_scene();
  1181. }