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

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