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

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