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

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