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

809 lines
24 KiB

  1. /*
  2. * Copyright (C) 2021 Christopher J. Howard
  3. *
  4. * This file is part of Antkeeper source code.
  5. *
  6. * Antkeeper source code is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * Antkeeper source code is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with Antkeeper source code. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include "game/states/loading.hpp"
  20. #include "application.hpp"
  21. #include "astro/illuminance.hpp"
  22. #include "color/color.hpp"
  23. #include "entity/components/atmosphere.hpp"
  24. #include "entity/components/blackbody.hpp"
  25. #include "entity/components/celestial-body.hpp"
  26. #include "entity/components/orbit.hpp"
  27. #include "entity/components/terrain.hpp"
  28. #include "entity/components/transform.hpp"
  29. #include "entity/systems/astronomy.hpp"
  30. #include "entity/systems/orbit.hpp"
  31. #include "entity/commands.hpp"
  32. #include "game/states/nuptial-flight.hpp"
  33. #include "game/states/splash.hpp"
  34. #include "game/states/forage.hpp"
  35. #include "geom/spherical.hpp"
  36. #include "gl/drawing-mode.hpp"
  37. #include "gl/vertex-array.hpp"
  38. #include "gl/vertex-attribute-type.hpp"
  39. #include "gl/vertex-buffer.hpp"
  40. #include "physics/light/photometry.hpp"
  41. #include "physics/orbit/orbit.hpp"
  42. #include "renderer/material.hpp"
  43. #include "renderer/model.hpp"
  44. #include "renderer/passes/shadow-map-pass.hpp"
  45. #include "renderer/vertex-attributes.hpp"
  46. #include "resources/resource-manager.hpp"
  47. #include "scene/ambient-light.hpp"
  48. #include "scene/directional-light.hpp"
  49. #include "utility/timestamp.hpp"
  50. namespace game {
  51. namespace state {
  52. namespace loading {
  53. /// Creates or loads control configuration
  54. static void load_controls(game::context* ctx);
  55. /// Creates the universe and solar system.
  56. static void cosmogenesis(game::context* ctx);
  57. /// Creates a sun.
  58. static void heliogenesis(game::context* ctx);
  59. /// Creates a planet.
  60. static void planetogenesis(game::context* ctx);
  61. /// Creates a moon.
  62. static void selenogenesis(game::context* ctx);
  63. /// Creates fixed stars.
  64. static void extrasolar_heliogenesis(game::context* ctx);
  65. /// Creates an ant colony
  66. static void colonigenesis(game::context* ctx);
  67. void enter(game::context* ctx)
  68. {
  69. // Load controls
  70. ctx->logger->push_task("Loading controls");
  71. try
  72. {
  73. load_controls(ctx);
  74. }
  75. catch (...)
  76. {
  77. ctx->logger->pop_task(EXIT_FAILURE);
  78. }
  79. ctx->logger->pop_task(EXIT_SUCCESS);
  80. // Create universe
  81. ctx->logger->push_task("Creating the universe");
  82. try
  83. {
  84. cosmogenesis(ctx);
  85. }
  86. catch (...)
  87. {
  88. ctx->logger->pop_task(EXIT_FAILURE);
  89. throw;
  90. }
  91. ctx->logger->pop_task(EXIT_SUCCESS);
  92. // Determine next game state
  93. application::state next_state;
  94. if (ctx->option_quick_start.has_value())
  95. {
  96. next_state.name = "forage";
  97. next_state.enter = std::bind(game::state::forage::enter, ctx);
  98. next_state.exit = std::bind(game::state::forage::exit, ctx);
  99. }
  100. else
  101. {
  102. next_state.name = "splash";
  103. next_state.enter = std::bind(game::state::splash::enter, ctx);
  104. next_state.exit = std::bind(game::state::splash::exit, ctx);
  105. }
  106. // Queue next game state
  107. ctx->app->queue_state(next_state);
  108. }
  109. void exit(game::context* ctx)
  110. {}
  111. void load_controls(game::context* ctx)
  112. {
  113. // Get keyboard and mouse devices
  114. input::keyboard* keyboard = ctx->app->get_keyboard();
  115. input::mouse* mouse = ctx->app->get_mouse();
  116. const std::unordered_map<std::string, input::game_controller_button> gamepad_button_map =
  117. {
  118. {"a", input::game_controller_button::a},
  119. {"b", input::game_controller_button::b},
  120. {"x", input::game_controller_button::x},
  121. {"y", input::game_controller_button::y},
  122. {"back", input::game_controller_button::back},
  123. {"guide", input::game_controller_button::guide},
  124. {"start", input::game_controller_button::start},
  125. {"leftstick", input::game_controller_button::left_stick},
  126. {"rightstick", input::game_controller_button::right_stick},
  127. {"leftshoulder", input::game_controller_button::left_shoulder},
  128. {"rightshoulder", input::game_controller_button::right_shoulder},
  129. {"dpup", input::game_controller_button::dpad_up},
  130. {"dpdown", input::game_controller_button::dpad_down},
  131. {"dpleft", input::game_controller_button::dpad_left},
  132. {"dpright", input::game_controller_button::dpad_right}
  133. };
  134. const std::unordered_map<std::string, input::game_controller_axis> gamepad_axis_map =
  135. {
  136. {"leftx", input::game_controller_axis::left_x},
  137. {"lefty", input::game_controller_axis::left_y},
  138. {"rightx", input::game_controller_axis::right_x},
  139. {"righty", input::game_controller_axis::right_y},
  140. {"lefttrigger", input::game_controller_axis::left_trigger},
  141. {"righttrigger", input::game_controller_axis::right_trigger}
  142. };
  143. // Check if a control profile is set in the config file
  144. if (ctx->config->contains("control_profile"))
  145. {
  146. // Load control profile
  147. json* profile = ctx->resource_manager->load<json>((*ctx->config)["control_profile"].get<std::string>());
  148. // Parse control profile
  149. for (auto it = profile->begin(); it != profile->end(); ++it)
  150. {
  151. // Parse control name
  152. if (!it->contains("name"))
  153. {
  154. ctx->logger->warning("Unnamed control in control profile");
  155. continue;
  156. }
  157. std::string name = (*it)["name"].get<std::string>();
  158. // Find or create control
  159. input::control* control;
  160. if (ctx->controls.count(name))
  161. {
  162. control = ctx->controls[name];
  163. }
  164. else
  165. {
  166. control = new input::control;
  167. control->set_deadzone(0.15f);
  168. ctx->controls[name] = control;
  169. }
  170. // Parse control device
  171. if (!it->contains("device"))
  172. {
  173. ctx->logger->warning("Control \"" + name + "\" not mapped to a device");
  174. continue;
  175. }
  176. std::string device = (*it)["device"].get<std::string>();
  177. if (device == "keyboard")
  178. {
  179. // Parse key name
  180. if (!it->contains("key"))
  181. {
  182. ctx->logger->warning("Control \"" + name + "\" has invalid keyboard mapping");
  183. continue;
  184. }
  185. std::string key = (*it)["key"].get<std::string>();
  186. // Get scancode from key name
  187. input::scancode scancode = keyboard->get_scancode_from_name(key.c_str());
  188. if (scancode == input::scancode::unknown)
  189. {
  190. ctx->logger->warning("Control \"" + name + "\" mapped to unknown keyboard key \"" + key + "\"");
  191. continue;
  192. }
  193. // Map control to keyboard key
  194. ctx->input_event_router->add_mapping(input::key_mapping(control, nullptr, scancode));
  195. ctx->logger->log("Mapped control \"" + name + "\" to keyboard key \"" + key + "\"");
  196. }
  197. else if (device == "mouse")
  198. {
  199. if (it->contains("button"))
  200. {
  201. // Parse mouse button index
  202. int button = (*it)["button"].get<int>();
  203. // Map control to mouse button
  204. ctx->input_event_router->add_mapping(input::mouse_button_mapping(control, nullptr, button));
  205. ctx->logger->log("Mapped control \"" + name + "\" to mouse button " + std::to_string(button));
  206. }
  207. else if (it->contains("wheel"))
  208. {
  209. // Parse mouse wheel axis
  210. std::string wheel = (*it)["wheel"].get<std::string>();
  211. input::mouse_wheel_axis axis;
  212. if (wheel == "+x")
  213. axis = input::mouse_wheel_axis::positive_x;
  214. else if (wheel == "-x")
  215. axis = input::mouse_wheel_axis::negative_x;
  216. else if (wheel == "+y")
  217. axis = input::mouse_wheel_axis::positive_y;
  218. else if (wheel == "-y")
  219. axis = input::mouse_wheel_axis::negative_y;
  220. else
  221. {
  222. ctx->logger->warning("Control \"" + name + "\" is mapped to invalid mouse wheel axis \"" + wheel + "\"");
  223. continue;
  224. }
  225. // Map control to mouse wheel axis
  226. ctx->input_event_router->add_mapping(input::mouse_wheel_mapping(control, nullptr, axis));
  227. ctx->logger->log("Mapped control \"" + name + "\" to mouse wheel axis " + wheel);
  228. }
  229. else
  230. {
  231. ctx->logger->warning("Control \"" + name + "\" has invalid mouse mapping");
  232. continue;
  233. }
  234. }
  235. else if (device == "gamepad")
  236. {
  237. if (it->contains("button"))
  238. {
  239. // Parse gamepad button
  240. std::string button = (*it)["button"].get<std::string>();
  241. auto button_it = gamepad_button_map.find(button);
  242. if (button_it == gamepad_button_map.end())
  243. {
  244. ctx->logger->warning("Control \"" + name + "\" is mapped to invalid gamepad button \"" + button + "\"");
  245. continue;
  246. }
  247. // Map control to gamepad button
  248. ctx->input_event_router->add_mapping(input::game_controller_button_mapping(control, nullptr, button_it->second));
  249. ctx->logger->log("Mapped control \"" + name + "\" to gamepad button " + button);
  250. }
  251. else if (it->contains("axis"))
  252. {
  253. std::string axis = (*it)["axis"].get<std::string>();
  254. // Parse gamepad axis name
  255. const std::string axis_name = axis.substr(0, axis.length() - 1);
  256. auto axis_it = gamepad_axis_map.find(axis_name);
  257. if (axis_it == gamepad_axis_map.end())
  258. {
  259. ctx->logger->warning("Control \"" + name + "\" is mapped to invalid gamepad axis \"" + axis_name + "\"");
  260. continue;
  261. }
  262. // Parse gamepad axis sign
  263. const char axis_sign = axis.back();
  264. if (axis_sign != '-' && axis_sign != '+')
  265. {
  266. ctx->logger->warning("Control \"" + name + "\" is mapped to gamepad axis with invalid sign \"" + axis_sign + "\"");
  267. continue;
  268. }
  269. bool axis_negative = (axis_sign == '-');
  270. // Map control to gamepad axis
  271. ctx->input_event_router->add_mapping(input::game_controller_axis_mapping(control, nullptr, axis_it->second, axis_negative));
  272. ctx->logger->log("Mapped control \"" + name + "\" to gamepad axis " + axis);
  273. }
  274. else
  275. {
  276. ctx->logger->log("Control \"" + name + "\" has invalid gamepad mapping");
  277. continue;
  278. }
  279. }
  280. else
  281. {
  282. ctx->logger->warning("Control \"" + name + "\" bound to unknown device \"" + device + "\"");
  283. }
  284. }
  285. }
  286. // Toggle fullscreen
  287. if (!ctx->controls.count("toggle_fullscreen"))
  288. {
  289. input::control* control = new input::control();
  290. ctx->controls["toggle_fullscreen"] = control;
  291. }
  292. ctx->controls["toggle_fullscreen"]->set_activated_callback
  293. (
  294. [ctx]()
  295. {
  296. bool fullscreen = !ctx->app->is_fullscreen();
  297. ctx->app->set_fullscreen(fullscreen);
  298. if (!fullscreen)
  299. {
  300. int2 resolution;
  301. resolution.x = (*ctx->config)["windowed_resolution"][0].get<int>();
  302. resolution.y = (*ctx->config)["windowed_resolution"][1].get<int>();
  303. ctx->app->resize_window(resolution.x, resolution.y);
  304. }
  305. (*ctx->config)["fullscreen"] = fullscreen;
  306. }
  307. );
  308. // Screenshot
  309. if (!ctx->controls.count("screenshot"))
  310. {
  311. input::control* control = new input::control();
  312. ctx->controls["screenshot"] = control;
  313. }
  314. ctx->controls["screenshot"]->set_activated_callback
  315. (
  316. [ctx]()
  317. {
  318. std::string path = ctx->screenshots_path + "antkeeper-" + timestamp() + ".png";
  319. ctx->app->save_frame(path);
  320. }
  321. );
  322. // Menu back
  323. if (!ctx->controls.count("menu_back"))
  324. {
  325. input::control* control = new input::control();
  326. ctx->controls["menu_back"] = control;
  327. }
  328. ctx->controls["menu_back"]->set_activated_callback
  329. (
  330. std::bind(&application::close, ctx->app, 0)
  331. );
  332. // Dolly forward
  333. if (!ctx->controls.count("dolly_forward"))
  334. {
  335. input::control* control = new input::control();
  336. ctx->controls["dolly_forward"] = control;
  337. }
  338. // Dolly backward
  339. if (!ctx->controls.count("dolly_backward"))
  340. {
  341. input::control* control = new input::control();
  342. ctx->controls["dolly_backward"] = control;
  343. }
  344. // Truck left
  345. if (!ctx->controls.count("truck_left"))
  346. {
  347. input::control* control = new input::control();
  348. ctx->controls["truck_left"] = control;
  349. }
  350. // Truck right
  351. if (!ctx->controls.count("truck_right"))
  352. {
  353. input::control* control = new input::control();
  354. ctx->controls["truck_right"] = control;
  355. }
  356. // Pedestal up
  357. if (!ctx->controls.count("pedestal_up"))
  358. {
  359. input::control* control = new input::control();
  360. ctx->controls["pedestal_up"] = control;
  361. }
  362. // Pedestal down
  363. if (!ctx->controls.count("pedestal_down"))
  364. {
  365. input::control* control = new input::control();
  366. ctx->controls["pedestal_down"] = control;
  367. }
  368. // Move slow
  369. if (!ctx->controls.count("move_slow"))
  370. {
  371. input::control* control = new input::control();
  372. ctx->controls["move_slow"] = control;
  373. }
  374. // Move fast
  375. if (!ctx->controls.count("move_fast"))
  376. {
  377. input::control* control = new input::control();
  378. ctx->controls["move_fast"] = control;
  379. }
  380. // Mouse rotate
  381. if (!ctx->controls.count("mouse_rotate"))
  382. {
  383. input::control* control = new input::control();
  384. ctx->controls["mouse_rotate"] = control;
  385. }
  386. // Pan left
  387. if (!ctx->controls.count("pan_left"))
  388. {
  389. input::control* control = new input::control();
  390. ctx->controls["pan_left"] = control;
  391. }
  392. if (!ctx->controls.count("pan_left_mouse"))
  393. {
  394. input::control* control = new input::control();
  395. ctx->input_event_router->add_mapping(input::mouse_motion_mapping(control, nullptr, input::mouse_motion_axis::negative_x));
  396. ctx->controls["pan_left_mouse"] = control;
  397. }
  398. // Pan right
  399. if (!ctx->controls.count("pan_right"))
  400. {
  401. input::control* control = new input::control();
  402. ctx->controls["pan_right"] = control;
  403. }
  404. if (!ctx->controls.count("pan_right_mouse"))
  405. {
  406. input::control* control = new input::control();
  407. ctx->input_event_router->add_mapping(input::mouse_motion_mapping(control, nullptr, input::mouse_motion_axis::positive_x));
  408. ctx->controls["pan_right_mouse"] = control;
  409. }
  410. // Tilt up
  411. if (!ctx->controls.count("tilt_up"))
  412. {
  413. input::control* control = new input::control();
  414. ctx->controls["tilt_up"] = control;
  415. }
  416. if (!ctx->controls.count("tilt_up_mouse"))
  417. {
  418. input::control* control = new input::control();
  419. ctx->input_event_router->add_mapping(input::mouse_motion_mapping(control, nullptr, input::mouse_motion_axis::negative_y));
  420. ctx->controls["tilt_up_mouse"] = control;
  421. }
  422. // Tilt down
  423. if (!ctx->controls.count("tilt_down"))
  424. {
  425. input::control* control = new input::control();
  426. ctx->controls["tilt_down"] = control;
  427. }
  428. if (!ctx->controls.count("tilt_down_mouse"))
  429. {
  430. input::control* control = new input::control();
  431. ctx->input_event_router->add_mapping(input::mouse_motion_mapping(control, nullptr, input::mouse_motion_axis::positive_y));
  432. ctx->controls["tilt_down_mouse"] = control;
  433. }
  434. // Use tool
  435. if (!ctx->controls.count("use_tool"))
  436. {
  437. input::control* control = new input::control();
  438. ctx->controls["use_tool"] = control;
  439. }
  440. }
  441. void cosmogenesis(game::context* ctx)
  442. {
  443. // Init time
  444. const double time = 0.0;
  445. ctx->astronomy_system->set_universal_time(time);
  446. ctx->orbit_system->set_universal_time(time);
  447. // Create sun
  448. ctx->logger->push_task("Creating the sun");
  449. try
  450. {
  451. heliogenesis(ctx);
  452. }
  453. catch (...)
  454. {
  455. ctx->logger->pop_task(EXIT_FAILURE);
  456. throw;
  457. }
  458. ctx->logger->pop_task(EXIT_SUCCESS);
  459. // Create planet
  460. ctx->logger->push_task("Creating the planet");
  461. try
  462. {
  463. planetogenesis(ctx);
  464. }
  465. catch (...)
  466. {
  467. ctx->logger->pop_task(EXIT_FAILURE);
  468. throw;
  469. }
  470. ctx->logger->pop_task(EXIT_SUCCESS);
  471. // Create moon
  472. ctx->logger->push_task("Creating the moon");
  473. try
  474. {
  475. selenogenesis(ctx);
  476. }
  477. catch (...)
  478. {
  479. ctx->logger->pop_task(EXIT_FAILURE);
  480. throw;
  481. }
  482. ctx->logger->pop_task(EXIT_SUCCESS);
  483. // Create fixed stars
  484. ctx->logger->push_task("Creating fixed stars");
  485. try
  486. {
  487. extrasolar_heliogenesis(ctx);
  488. }
  489. catch (...)
  490. {
  491. ctx->logger->pop_task(EXIT_FAILURE);
  492. throw;
  493. }
  494. ctx->logger->pop_task(EXIT_SUCCESS);
  495. // Create ant colony
  496. ctx->logger->push_task("Creating ant colony");
  497. try
  498. {
  499. colonigenesis(ctx);
  500. }
  501. catch (...)
  502. {
  503. ctx->logger->pop_task(EXIT_FAILURE);
  504. throw;
  505. }
  506. ctx->logger->pop_task(EXIT_SUCCESS);
  507. }
  508. void heliogenesis(game::context* ctx)
  509. {
  510. // Create solar entity
  511. entity::id sun_eid = ctx->entity_registry->create();
  512. ctx->entities["sun"] = sun_eid;
  513. // Assign solar celestial body component
  514. entity::component::celestial_body body;
  515. body.radius = 6.957e+8;
  516. body.axial_tilt = math::radians(0.0);
  517. body.axial_rotation = math::radians(0.0);
  518. body.angular_frequency = math::radians(0.0);
  519. ctx->entity_registry->assign<entity::component::celestial_body>(sun_eid, body);
  520. // Assign solar orbit component
  521. entity::component::orbit orbit;
  522. orbit.elements.a = 0.0;
  523. orbit.elements.e = 0.0;
  524. orbit.elements.i = math::radians(0.0);
  525. orbit.elements.raan = math::radians(0.0);
  526. orbit.elements.w = math::radians(0.0);
  527. orbit.elements.ta = math::radians(0.0);
  528. ctx->entity_registry->assign<entity::component::orbit>(sun_eid, orbit);
  529. // Assign solar blackbody component
  530. entity::component::blackbody blackbody;
  531. blackbody.temperature = 5778.0;
  532. ctx->entity_registry->assign<entity::component::blackbody>(sun_eid, blackbody);
  533. // Assign solar transform component
  534. entity::component::transform transform;
  535. transform.local = math::identity_transform<float>;
  536. transform.warp = true;
  537. ctx->entity_registry->assign<entity::component::transform>(sun_eid, transform);
  538. // Create direct sun light scene object
  539. scene::directional_light* sun_direct = new scene::directional_light();
  540. // Create ambient sun light scene object
  541. scene::ambient_light* sun_ambient = new scene::ambient_light();
  542. sun_ambient->set_color({1, 1, 1});
  543. sun_ambient->set_intensity(0.0f);
  544. sun_ambient->update_tweens();
  545. // Add sun light scene objects to surface scene
  546. ctx->surface_scene->add_object(sun_direct);
  547. ctx->surface_scene->add_object(sun_ambient);
  548. // Pass direct sun light scene object to shadow map pass and astronomy system
  549. ctx->surface_shadow_map_pass->set_light(sun_direct);
  550. ctx->astronomy_system->set_sun_light(sun_direct);
  551. }
  552. void planetogenesis(game::context* ctx)
  553. {
  554. // Create planetary entity
  555. entity::id planet_eid = ctx->entity_registry->create();
  556. ctx->entities["planet"] = planet_eid;
  557. // Assign planetary celestial body component
  558. entity::component::celestial_body body;
  559. body.radius = 6.3781e6;
  560. body.axial_tilt = math::radians(23.4393);
  561. body.axial_rotation = math::radians(280.46061837504);
  562. body.angular_frequency = math::radians(360.9856122880876128);
  563. ctx->entity_registry->assign<entity::component::celestial_body>(planet_eid, body);
  564. // Assign planetary orbit component
  565. entity::component::orbit orbit;
  566. orbit.elements.a = 1.496e+11;
  567. orbit.elements.e = 0.01671123;
  568. orbit.elements.i = math::radians(-0.00001531);
  569. orbit.elements.raan = math::radians(0.0);
  570. const double longitude_periapsis = math::radians(102.93768193);
  571. orbit.elements.w = longitude_periapsis - orbit.elements.raan;
  572. orbit.elements.ta = math::radians(100.46457166) - longitude_periapsis;
  573. ctx->entity_registry->assign<entity::component::orbit>(planet_eid, orbit);
  574. // Assign planetary terrain component
  575. entity::component::terrain terrain;
  576. terrain.elevation = [](double, double) -> double
  577. {
  578. //return math::random<double>(0.0, 1.0);
  579. return 0.0;
  580. };
  581. terrain.max_lod = 0;
  582. terrain.patch_material = nullptr;
  583. ctx->entity_registry->assign<entity::component::terrain>(planet_eid, terrain);
  584. // Assign planetary atmosphere component
  585. entity::component::atmosphere atmosphere;
  586. atmosphere.exosphere_altitude = 65e3;
  587. atmosphere.index_of_refraction = 1.000293;
  588. atmosphere.rayleigh_density = 2.545e25;
  589. atmosphere.rayleigh_scale_height = 8000.0;
  590. atmosphere.mie_density = 14.8875;
  591. atmosphere.mie_scale_height = 1200.0;
  592. atmosphere.mie_anisotropy = 0.8;
  593. ctx->entity_registry->assign<entity::component::atmosphere>(planet_eid, atmosphere);
  594. // Assign planetary transform component
  595. entity::component::transform transform;
  596. transform.local = math::identity_transform<float>;
  597. transform.warp = true;
  598. ctx->entity_registry->assign<entity::component::transform>(planet_eid, transform);
  599. // Pass planet to astronomy system as reference body
  600. ctx->astronomy_system->set_reference_body(planet_eid);
  601. // Load sky model
  602. ctx->surface_sky_pass->set_sky_model(ctx->resource_manager->load<model>("sky-dome.mdl"));
  603. }
  604. void selenogenesis(game::context* ctx)
  605. {
  606. // Create lunar entity
  607. entity::id moon_eid = ctx->entity_registry->create();
  608. ctx->entities["moon"] = moon_eid;
  609. // Pass moon model to sky pass
  610. ctx->surface_sky_pass->set_moon_model(ctx->resource_manager->load<model>("moon.mdl"));
  611. }
  612. void extrasolar_heliogenesis(game::context* ctx)
  613. {
  614. // Load star catalog
  615. string_table* star_catalog = ctx->resource_manager->load<string_table>("stars.csv");
  616. // Allocate star catalog vertex data
  617. std::size_t star_count = 0;
  618. if (star_catalog->size() > 0)
  619. star_count = star_catalog->size() - 1;
  620. std::size_t star_vertex_size = 6;
  621. std::size_t star_vertex_stride = star_vertex_size * sizeof(float);
  622. float* star_vertex_data = new float[star_count * star_vertex_size];
  623. float* star_vertex = star_vertex_data;
  624. // Build star catalog vertex data
  625. for (std::size_t i = 1; i < star_catalog->size(); ++i)
  626. {
  627. const string_table_row& catalog_row = (*star_catalog)[i];
  628. double ra = 0.0;
  629. double dec = 0.0;
  630. double vmag = 0.0;
  631. double bv_color = 0.0;
  632. // Parse star catalog entry
  633. try
  634. {
  635. ra = std::stod(catalog_row[1]);
  636. dec = std::stod(catalog_row[2]);
  637. vmag = std::stod(catalog_row[3]);
  638. bv_color = std::stod(catalog_row[4]);
  639. }
  640. catch (const std::exception& e)
  641. {
  642. continue;
  643. }
  644. // Convert right ascension and declination from degrees to radians
  645. ra = math::wrap_radians(math::radians(ra));
  646. dec = math::wrap_radians(math::radians(dec));
  647. // Transform spherical equatorial coordinates to rectangular equatorial coordinates
  648. double3 position_bci = geom::spherical::to_cartesian(double3{1.0, dec, ra});
  649. // Transform coordinates from equatorial space to inertial space
  650. physics::frame<double> bci_to_inertial = physics::orbit::inertial::to_bci({0, 0, 0}, 0.0, math::radians(23.4393)).inverse();
  651. double3 position_inertial = bci_to_inertial * position_bci;
  652. // Convert color index to color temperature
  653. double cct = color::index::bv_to_cct(bv_color);
  654. // Calculate XYZ color from color temperature
  655. double3 color_xyz = color::cct::to_xyz(cct);
  656. // Transform XYZ color to ACEScg colorspace
  657. double3 color_acescg = color::xyz::to_acescg(color_xyz);
  658. // Convert apparent magnitude to irradiance (W/m^2)
  659. double vmag_irradiance = std::pow(10.0, 0.4 * (-vmag - 19.0 + 0.4));
  660. // Convert irradiance to illuminance
  661. double vmag_illuminance = vmag_irradiance * (683.0 * 0.14);
  662. // Scale color by illuminance
  663. double3 scaled_color = color_acescg * vmag_illuminance;
  664. // Build vertex
  665. *(star_vertex++) = static_cast<float>(position_inertial.x);
  666. *(star_vertex++) = static_cast<float>(position_inertial.y);
  667. *(star_vertex++) = static_cast<float>(position_inertial.z);
  668. *(star_vertex++) = static_cast<float>(scaled_color.x);
  669. *(star_vertex++) = static_cast<float>(scaled_color.y);
  670. *(star_vertex++) = static_cast<float>(scaled_color.z);
  671. }
  672. // Unload star catalog
  673. ctx->resource_manager->unload("stars.csv");
  674. // Allocate stars model
  675. model* stars_model = new model();
  676. // Resize model VBO and upload vertex data
  677. gl::vertex_buffer* vbo = stars_model->get_vertex_buffer();
  678. vbo->resize(star_count * star_vertex_stride, star_vertex_data);
  679. // Free star catalog vertex data
  680. delete[] star_vertex_data;
  681. // Bind vertex attributes to model VAO
  682. gl::vertex_array* vao = stars_model->get_vertex_array();
  683. std::size_t vao_offset = 0;
  684. vao->bind_attribute(VERTEX_POSITION_LOCATION, *vbo, 3, gl::vertex_attribute_type::float_32, star_vertex_stride, 0);
  685. vao_offset += 3;
  686. vao->bind_attribute(VERTEX_COLOR_LOCATION, *vbo, 3, gl::vertex_attribute_type::float_32, star_vertex_stride, sizeof(float) * vao_offset);
  687. // Load star material
  688. material* star_material = ctx->resource_manager->load<material>("fixed-star.mtl");
  689. // Create model group
  690. model_group* stars_model_group = stars_model->add_group("stars");
  691. stars_model_group->set_material(star_material);
  692. stars_model_group->set_drawing_mode(gl::drawing_mode::points);
  693. stars_model_group->set_start_index(0);
  694. stars_model_group->set_index_count(star_count);
  695. // Pass stars model to sky pass
  696. ctx->surface_sky_pass->set_stars_model(stars_model);
  697. }
  698. void colonigenesis(game::context* ctx)
  699. {}
  700. } // namespace loading
  701. } // namespace state
  702. } // namespace game